]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme: fix block count and data size logic
authorMartin George <marting@netapp.com>
Tue, 21 Mar 2023 08:49:11 +0000 (14:19 +0530)
committerDaniel Wagner <wagi@monom.org>
Wed, 22 Mar 2023 14:19:58 +0000 (15:19 +0100)
The required block count and data size is not automatically set
for a namespace device in submit_io(). Also it is the user specified
block count and not the actual required block count that is passed
to the ioctl data structure, often leading to nvme passthrough io
errors. For e.g. an nvme read on a 512b block size namespace device
with data size set to 4K ends up with the below error:

nvme read /dev/nvme0n1 -s 0 -z 4096
NVMe status: Data SGL Length Invalid: The length of a Data SGL is too
short or too long and the controller does not support SGL transfers
longer than the amount of data to be transferred(0x400f)

This read is successful only when the appropriate block count is
passed from the command line:

nvme read /dev/nvme0n1 -s 0 -z 4096 -c 7
read: Success

So fix this by deriving the required block count and data size based
on the logical block size and passing them appropriately to the
respective ioctl data structure. And while we are at it, remove a
couple of superfluous braces in this part of the code.

Signed-off-by: Martin George <marting@netapp.com>
nvme.c

diff --git a/nvme.c b/nvme.c
index c852f69ea2d7cd22edae3fffb40a0ce4ca36fc98..52b10d3b63642da6ca63f6eeaa4022c90dee9e1c 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -7213,7 +7213,7 @@ static int submit_io(int opcode, char *command, const char *desc,
        int dfd, mfd;
        int flags = opcode & 1 ? O_RDONLY : O_WRONLY | O_CREAT;
        int mode = S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP| S_IROTH;
-       __u16 control = 0;
+       __u16 control = 0, nblocks = 0;
        __u32 dsmgmt = 0;
        int logical_block_size = 0;
        unsigned long long buffer_size = 0, mbuffer_size = 0;
@@ -7403,9 +7403,14 @@ static int submit_io(int opcode, char *command, const char *desc,
        if (cfg.data_size < buffer_size) {
                fprintf(stderr, "Rounding data size to fit block count (%lld bytes)\n",
                                buffer_size);
-       } else {
+       } else
                buffer_size = cfg.data_size;
-       }
+
+       /* Get the required block count. Note this is a zeroes based value. */
+       nblocks = ((buffer_size + (logical_block_size - 1)) / logical_block_size) - 1;
+
+       /* Update the data size based on the required block count */
+       buffer_size = (nblocks + 1) * logical_block_size;
 
        buffer = nvme_alloc(buffer_size, &huge);
        if (!buffer) {
@@ -7478,7 +7483,7 @@ static int submit_io(int opcode, char *command, const char *desc,
                printf("nsid         : %02x\n", cfg.namespace_id);
                printf("flags        : %02x\n", 0);
                printf("control      : %04x\n", control);
-               printf("nblocks      : %04x\n", cfg.block_count);
+               printf("nblocks      : %04x\n", nblocks);
                printf("metadata     : %"PRIx64"\n", (uint64_t)(uintptr_t)mbuffer);
                printf("addr         : %"PRIx64"\n", (uint64_t)(uintptr_t)buffer);
                printf("slba         : %"PRIx64"\n", (uint64_t)cfg.start_block);
@@ -7499,7 +7504,7 @@ static int submit_io(int opcode, char *command, const char *desc,
                .fd             = dev_fd(dev),
                .nsid           = cfg.namespace_id,
                .slba           = cfg.start_block,
-               .nlb            = cfg.block_count,
+               .nlb            = nblocks,
                .control        = control,
                .dsm            = cfg.dsmgmt,
                .sts            = sts,