Thanks, Devkate.
To follow EDKII open forum review process, I add the patch below to see if
others have comments.
Subject: [PATCH] MdeModulePkg/NvmExpressDxe: Correct Prp list creation
algorithm.
Fix the algo of Prp list creation to calculate the number of Prp lists and the
number of entries in the last Prp List correctly.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Feng Tian <feng.t...@intel.com>
Reviewed-by: Baban Devkate <baban.devk...@seagate.com>
---
MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c
b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c
index 4687858..1b8751d 100644
--- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c
@@ -3,7 +3,7 @@
NVM Express specification.
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
- Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD
License
which accompanies this distribution. The full text of the license may be
found at
@@ -256,9 +256,15 @@ NvmeCreatePrpList (
//
// Calculate total PrpList number.
//
- *PrpListNo = (UINTN)DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo,
&Remainder);
- if (Remainder != 0) {
+ *PrpListNo = (UINTN)DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo -
1, &Remainder);
+ if (*PrpListNo == 0) {
+ *PrpListNo = 1;
+ } else if (Remainder != 0) && (Remainder != 1) {
*PrpListNo += 1;
+ } else if (Remainder == 1) {
+ Remainder = PrpEntryNo;
+ } else if (Remainder == 0) {
+ Remainder = PrpEntryNo - 1;
}
Status = PciIo->AllocateBuffer (
@@ -314,7 +320,7 @@ NvmeCreatePrpList (
// Fill last PRP list.
//
PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
- for (PrpEntryIndex = 0; PrpEntryIndex < ((Remainder != 0) ? Remainder :
PrpEntryNo); ++PrpEntryIndex) {
+ for (PrpEntryIndex = 0; PrpEntryIndex < Remainder; ++PrpEntryIndex) {
*((UINT64*)(UINTN)PrpListBase + PrpEntryIndex) = PhysicalAddr;
PhysicalAddr += EFI_PAGE_SIZE;
}
From: Baban Devkate [mailto:baban.devk...@seagate.com]
Sent: Friday, April 24, 2015 14:14
To: Tian, Feng
Cc: edk2-devel@lists.sourceforge.net; baban...@gmail.com
Subject: Re: MdeModulePkg:Fix required in NvmExpressDxe driver
Hi Feng,
This modified code does calculate PrpListNo and Remainder correctly for both
aligned and unaligned cases.
Thanks & regards,
Baban Devkate
On Thu, Apr 23, 2015 at 1:05 PM, Tian, Feng
<feng.t...@intel.com<mailto:feng.t...@intel.com>> wrote:
Hi, Devkate
So the key point should be the *PrpListNo and Remainder doesn’t get calculated
correctly (assume PrpListNo means the number of Prp Lists and Remainder means
the number of entries in last Prp List). Do you agree?
So the correct algo should:
1) If Pages >= 1 and Pages <=512, the algo should return PrpListNo = 1 and
Remainder = Pages.
2) If Pages >= 511*1 + 2 and Pages <= 511*1 + 512, the algo should return
PrpListNo = 2 and Remainder = 2 to 512.
3) …
4) If Pages >= 511*n + 2 and Pages <= 511*n + 512, the algo should return
PrpListNo = n and Remainder = 2 to 512.
I enhanced the code like this, could you help review the attachment?
Thanks
Feng
From: Baban Devkate
[mailto:baban.devk...@seagate.com<mailto:baban.devk...@seagate.com>]
Sent: Wednesday, April 22, 2015 14:22
To: Tian, Feng
Subject: Re: MdeModulePkg:Fix required in NvmExpressDxe driver
Hi Feng,
Actually case 3 was incorrect scenario:(
My concern was with below case....
5) Offset = 0KB, Transfer Length = 4K * 514 blocks = 2MB + 8K
Prp1 = 0K to 4KB
Prp2 = pointer to a Prp List which is followed by another Prp List.
The first Prp List contains 511 entries to read data from offset 4KB to offset
2MB. the last one entry is a pointer to 2nd Prp List. The second Prp List
should contain 2 entry to read data from offset 2MB to offset 2MB +8k.
So for the fifth sample, EFI_SIZE_TO_PAGES(Offset + Bytes) – 1 = 513(i.e
Pages=513). In NvmeCreatePrpList(), PrpListNo = 2 for third sample.
*PrpListNo =2 with Remainder=1 when Pages=513 and PrpEntryNo=512
and this means only first entry of 2nd Prp Lists will get populated and 2nd
entry of second Prp List will not get populated.
So the 2MB + 8K is requested data but 2MB+4KB is the actual payload.
On Wed, Apr 22, 2015 at 10:29 AM, Tian, Feng
<feng.t...@intel.com<mailto:feng.t...@intel.com>> wrote:
Hi, Devkate
I have got feedback from a member of NVMe org and he has confirmed that PRP
Lists shall be minimally sized with packed entries starting with entry 0.
To make us on the same page, I draw two figures to clarify our understanding. I
think you agree the figure 1 is correct and figure 2 is incorrect for a 2M data
buffer.
So for the case 3 you list, why you think it has problem?
3) Offset = 0KB, Transfer Length = 4K * 513 blocks = 2MB + 4K
Prp1 = 0K to 4KB
Prp2 = pointer to a Prp List which contains 512 entries to read data from
offset 4KB to offset 2MB + 4K.
EFI_SIZE_TO_PAGES(Offset + Bytes) – 1 = 512(i.e Pages=512). In
NvmeCreatePrpList(), *PrpListNo =1 with Remainder=0 when Pages=512 and
PrpEntryNo=512.
Thanks
Feng
From: Tian, Feng
Sent: Wednesday, April 15, 2015 09:00
To: Baban Devkate
Cc: baban...@gmail.com<mailto:baban...@gmail.com>;
edk2-devel@lists.sourceforge.net<mailto:edk2-devel@lists.sourceforge.net>;
Tian, Feng
Subject: RE: MdeModulePkg:Fix required in NvmExpressDxe driver
Hi, Devkate
If the last entry in the first Prp list points to the last 4K data range rather
than a next Prp list, how does h/w distinguish the last entry is a pointer to
next Prp list or a valid entry to buffer?
I am consulting NVMe org and if I have some feedbacks I will get back to you.
Thanks
Feng
From: Baban Devkate [mailto:baban.devk...@seagate.com]
Sent: Tuesday, April 14, 2015 13:20
To: Tian, Feng
Cc: baban...@gmail.com<mailto:baban...@gmail.com>;
edk2-devel@lists.sourceforge.net<mailto:edk2-devel@lists.sourceforge.net>
Subject: Re: MdeModulePkg:Fix required in NvmExpressDxe driver
Feng, you were correct that we don't need the 2nd Prp list for such case( case
3) and the last entry in the first Prp list points to last 4K range rather than
a pointer to next Prp List). Which is inline with NVMe spec.
Excerpt from NVM Express 1.1, section 4.3:
The first PRP entry contained within the command may have a non-zero offset
within the memory page. The first PRP List entry (i.e. the first pointer to a
memory page containing additional PRP entries) that if present is contained in
the PRP Entry 2 location within the command, shall be Qword aligned and may
also have a non-zero offset within the memory page. All other PRP and PRP List
entries shall have a memory page offset of 0h, i.e. the entries are memory page
aligned based on the value in CC.MPS. The last entry within a memory page, as
indicated by the memory page size in the CC.MPS field, shall be a PRP List
pointer if there is more than a single memory page of data to be transferred.
PRP Lists shall be minimally sized with packed entries starting with entry 0.
If more PRP List pages are required, then the last entry of the PRP List page
is a pointer to the next PRP List page. The total number of PRP entries is
implied by the command parameters and memory page size.
So with the latest patch, case 4 will generate error.
4) Offset = 0KB, Transfer Length = 4K * 1024 blocks = 4MB
Prp1 = 0K to 4KB
Prp2 = pointer to a Prp List which is followed by another Prp List.
The first Prp List contains 511 entries to read data from offset 4KB to offset
2MB and last one entry is a pointer to 2nd Prp List.
Second Prp List contains next 511 entries to read data from offset 2MB to
offset 4MB-4K and last one entry is a pointer to 3rd Prp List.
But as per NVMe spec, pointer to 3rd Prp List will read data from offset
4MB-4KB to 4MB since there is no more than single memory page data to be
transferred.
So I suggest we should go with below fix which will cater both aligned and
unaligned accesses.
Prp = NvmeCreatePrpList (PciIo, PhyAddr, ((Offset!= 0) ?
EFI_SIZE_TO_PAGES(Offset + Bytes) – 1: EFI_SIZE_TO_PAGES(Offset + Bytes) ),
&PrpListHost, &PrpListNo, &MapPrpList);
On Fri, Apr 10, 2015 at 11:20 AM, Tian, Feng
<feng.t...@intel.com<mailto:feng.t...@intel.com>> wrote:
Got your point now.
My original thought for the 3rd case is we only need a Prp list which contains
512 entries to describe the whole 2M data buffer (I thought we don’t need the
2nd Prp list for such case and the last entry in the first Prp list points to
last 4K range rather than a pointer to next Prp List).
Looks like my original thought is wrong as the h/w will have no way to
distinguish the last entry is a pointer to next Prp list or a valid entry to
buffer.
I made a little adjustment on your proposal. Please help review the attachment.
I think this way may be better to correct callee’s algorithm to create those
right Prp lists, do you agree?
Thanks
Feng
From: Baban Devkate
[mailto:baban.devk...@seagate.com<mailto:baban.devk...@seagate.com>]
Sent: Thursday, April 9, 2015 5:56 PM
To: Tian, Feng; baban...@gmail.com<mailto:baban...@gmail.com>
Cc: edk2-devel@lists.sourceforge.net<mailto:edk2-devel@lists.sourceforge.net>
Subject: Re: MdeModulePkg:Fix required in NvmExpressDxe driver
+ adding self
On Thu, Apr 9, 2015 at 3:19 PM, Baban Devkate
<baban.devk...@seagate.com<mailto:baban.devk...@seagate.com>> wrote:
Let's take one more sample......
3) Offset = 0KB, Transfer Length = 4K * 513 blocks = 2MB + 4K
Prp1 = 0K to 4KB
Prp2 = pointer to a Prp List which is followed by another Prp List.
The first Prp List contains 511 entries to read data from offset 4KB to offset
2MB. the last one entry is a pointer to 2nd Prp List. The second Prp List
should contain 1 entry to read data from offset 2MB to offset 2MB +4k.
So for the third sample, EFI_SIZE_TO_PAGES(Offset + Bytes) – 1 = 512(i.e
Pages=512). In NvmeCreatePrpList(), PrpListNo = 1 for third sample.
*PrpListNo =1 with Remainder=0 when Pages=512 and PrpEntryNo=512
and this means only first Prp Lists entries will get populated and second Prp
List will not come into picture at all.
So the 2MB + 4K is requested data but 2MB is the actual payload.
We should have two cases each for Offset =0 and !=0.
So the fix should be like this:
Prp = NvmeCreatePrpList (PciIo, PhyAddr, ((Offset!= 0) ?
EFI_SIZE_TO_PAGES(Offset + Bytes) – 1: EFI_SIZE_TO_PAGES(Offset + Bytes) ),
&PrpListHost, &PrpListNo, &MapPrpList);
On Thu, Apr 9, 2015 at 1:26 PM, Tian, Feng
<feng.t...@intel.com<mailto:feng.t...@intel.com>> wrote:
Hi, Devkate
I agree the first one is a bug and need to be fixed like your proposal.
For the second one, sorry I couldn’t catch your point.
When the requested buffer size is larger than 2M, it means we need multiple PRP
list pages.
For example, when block size is 4K, memory page size is 4K
1) Offset = 0B, Transfer Length = 4K * 512 blocks = 2MB
Prp1 = 0 to 4KB
Prp2 = pointer to a Prp List which contains 511 entries to read data from
offset 4KB to offset 2MB.
2) Offset = 2KB, Transfer Length = 4K * 513 blocks = 2MB + 4K
Prp1 = 2K to 4KB
Prp2 = pointer to a Prp List which is followed by another Prp List.
The first Prp List contains 511 entries to read data from offset 4KB to offset
2MB. the last one entry is a pointer to 2nd Prp List. The second Prp List
contains 2 entries to read data from offset 2MB to offset 2MB +4k and from
offset 2MB + 4K to offset 2MB + 6K.
So for the first sample, EFI_SIZE_TO_PAGES(Offset + Bytes) – 1 = 511. For the
second sample, EFI_SIZE_TO_PAGES(Offset + Bytes) – 1 = 513. In
NvmeCreatePrpList(), PrpListNo = 1 for first sample and PrpListNo = 2 for
second sample.
So do I misunderstand something?
Thanks
Feng
From: Baban Devkate
[mailto:baban.devk...@seagate.com<mailto:baban.devk...@seagate.com>]
Sent: Thursday, April 09, 2015 09:37
To: edk2-devel@lists.sourceforge.net<mailto:edk2-devel@lists.sourceforge.net>
Cc: Tian, Feng
Subject: MdeModulePkg:Fix required in NvmExpressDxe driver
Hi,
Commit Message
============
MdeModulePkg: Fixed Pointer truncation and incorrect bytes passed for PRP list
creation.
There were two issues in NvmExpressPassthru.c(
MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c)-
1.PRP list entries were zeroed out as "PrpListBase" was getting truncated to
UINT8, changed to UINT64.
Old code- PrpListBase = *(UINT8*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
New code- PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
2. Number of bytes passed to PRP list creation function corrected.
Actual payload length will not match requested whenever nested PRP list is
required. With current 1024 max transfer size and 4K Blocksize if the
requested buffer is greater than 2MB then nested PRP list will be required and
NvmeCreatePrpList (.., Pages-1 , ..) will result into above failure.
Old code-
Prp = NvmeCreatePrpList (PciIo, PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes) - 1,
&PrpListHost, &PrpListNo, &MapPrpList);
New code-
Prp = NvmeCreatePrpList (PciIo, PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes),
&PrpListHost, &PrpListNo, &MapPrpList);
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: baban.devk...@seagate.com<mailto:baban.devk...@seagate.com>
Name-Baban Devkate
email-baban.devk...@seagate.com<mailto:email-baban.devk...@seagate.com>
------------------------------------------------------------------------------
One dashboard for servers and applications across Physical-Virtual-Cloud
Widest out-of-the-box monitoring support with 50+ applications
Performance metrics, stats and reports that give you Actionable Insights
Deep dive visibility with transaction tracing using APM Insight.
http://ad.doubleclick.net/ddm/clk/290420510;117567292;y
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel