[PATCH] hw/block/nvme: map prp fix if prp2 contains non-zero offset

Padmakar Kalghatgi posted 1 patch 3 years ago
Test checkpatch passed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20210408162313.22749-1-p.kalghatgi@samsung.com
Maintainers: Klaus Jensen <its@irrelevant.dk>, Keith Busch <kbusch@kernel.org>, Kevin Wolf <kwolf@redhat.com>, Max Reitz <mreitz@redhat.com>
There is a newer version of this series
hw/block/nvme.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
[PATCH] hw/block/nvme: map prp fix if prp2 contains non-zero offset
Posted by Padmakar Kalghatgi 3 years ago
From: padmakar <p.kalghatgi@samsung.com>

nvme_map_prp needs to calculate the number of list entries based on the
offset value. For the subsequent PRP2 list, need to ensure the number of
entries is within the MAX number of PRP entries for a page.

Signed-off-by: Padmakar Kalghatgi <p.kalghatgi@samsung.com>
---
 hw/block/nvme.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index d439e44..efb7368 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -577,7 +577,12 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1,
             uint32_t nents, prp_trans;
             int i = 0;
 
-            nents = (len + n->page_size - 1) >> n->page_bits;
+            /*
+             *   The first PRP list entry, pointed by PRP2 can contain
+             *   offsets. Hence, we need calculate the no of entries in
+             *   prp2 based on the offset it has.
+             */
+            nents = (n->page_size - (prp2 % n->page_size)) >> 3;
             prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
             ret = nvme_addr_read(n, prp2, (void *)prp_list, prp_trans);
             if (ret) {
@@ -588,7 +593,7 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1,
             while (len != 0) {
                 uint64_t prp_ent = le64_to_cpu(prp_list[i]);
 
-                if (i == n->max_prp_ents - 1 && len > n->page_size) {
+                if (i == nents - 1 && len > n->page_size) {
                     if (unlikely(prp_ent & (n->page_size - 1))) {
                         trace_pci_nvme_err_invalid_prplist_ent(prp_ent);
                         status = NVME_INVALID_PRP_OFFSET | NVME_DNR;
@@ -597,7 +602,8 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1,
 
                     i = 0;
                     nents = (len + n->page_size - 1) >> n->page_bits;
-                    prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
+                    nents = MIN(nents, n->max_prp_ents);
+                    prp_trans = nents * sizeof(uint64_t);
                     ret = nvme_addr_read(n, prp_ent, (void *)prp_list,
                                          prp_trans);
                     if (ret) {
-- 
2.7.0.windows.1


Re: [PATCH] hw/block/nvme: map prp fix if prp2 contains non-zero offset
Posted by Klaus Jensen 3 years ago
On Apr  8 21:53, Padmakar Kalghatgi wrote:
>From: padmakar <p.kalghatgi@samsung.com>
>
>nvme_map_prp needs to calculate the number of list entries based on the
>offset value. For the subsequent PRP2 list, need to ensure the number of
>entries is within the MAX number of PRP entries for a page.
>
>Signed-off-by: Padmakar Kalghatgi <p.kalghatgi@samsung.com>
>---
> hw/block/nvme.c | 12 +++++++++---
> 1 file changed, 9 insertions(+), 3 deletions(-)
>
>diff --git a/hw/block/nvme.c b/hw/block/nvme.c
>index d439e44..efb7368 100644
>--- a/hw/block/nvme.c
>+++ b/hw/block/nvme.c
>@@ -577,7 +577,12 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1,
>             uint32_t nents, prp_trans;
>             int i = 0;
>
>-            nents = (len + n->page_size - 1) >> n->page_bits;
>+            /*
>+             *   The first PRP list entry, pointed by PRP2 can contain
>+             *   offsets. Hence, we need calculate the no of entries in
>+             *   prp2 based on the offset it has.
>+             */
>+            nents = (n->page_size - (prp2 % n->page_size)) >> 3;
>             prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
>             ret = nvme_addr_read(n, prp2, (void *)prp_list, prp_trans);
>             if (ret) {
>@@ -588,7 +593,7 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1,
>             while (len != 0) {
>                 uint64_t prp_ent = le64_to_cpu(prp_list[i]);
>
>-                if (i == n->max_prp_ents - 1 && len > n->page_size) {
>+                if (i == nents - 1 && len > n->page_size) {
>                     if (unlikely(prp_ent & (n->page_size - 1))) {
>                         trace_pci_nvme_err_invalid_prplist_ent(prp_ent);
>                         status = NVME_INVALID_PRP_OFFSET | NVME_DNR;
>@@ -597,7 +602,8 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1,
>
>                     i = 0;
>                     nents = (len + n->page_size - 1) >> n->page_bits;
>-                    prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
>+                    nents = MIN(nents, n->max_prp_ents);
>+                    prp_trans = nents * sizeof(uint64_t);
>                     ret = nvme_addr_read(n, prp_ent, (void *)prp_list,
>                                          prp_trans);
>                     if (ret) {
>-- 
>2.7.0.windows.1
>
>

LGTM.

Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Re: [PATCH] hw/block/nvme: map prp fix if prp2 contains non-zero offset
Posted by Keith Busch 3 years ago
On Thu, Apr 08, 2021 at 09:53:13PM +0530, Padmakar Kalghatgi wrote:
> +            /*
> +             *   The first PRP list entry, pointed by PRP2 can contain
> +             *   offsets. Hence, we need calculate the no of entries in
> +             *   prp2 based on the offset it has.
> +             */

This comment has some unnecessary spacing at the beginning.

> +            nents = (n->page_size - (prp2 % n->page_size)) >> 3;

page_size is a always a power of two, so let's replace the costly modulo
with:

	nents = (n->page_size - (prp2 & (n->page_size - 1))) >> 3;