[haiku-commits] haiku: hrev53079 - src/add-ons/kernel/drivers/disk/nvme

  • From: waddlesplash <waddlesplash@xxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 17 Apr 2019 23:20:52 -0400 (EDT)

hrev53079 adds 3 changesets to branch 'master'
old head: 16525505c6c41729d900780e4234291c2cf849b9
new head: 2bd1323ab88a8f1d422960ac89b39f32eeef38b6
overview: 
https://git.haiku-os.org/haiku/log/?qt=range&q=2bd1323ab88a+%5E16525505c6c4

----------------------------------------------------------------------------

872c8209b008: nvme_disk: Allocate up to 8 qpairs and round-robin them for I/O.
  
  Before this commit, only one qpair was allocated and it was
  locked for the duration of the transfer. Now we allocate more
  qpairs if the device supports it and lock only when actually
  calling read(), enabling multiple reads to both be queued
  and execute simultaneously.
  
  This is probably a significant performance improvement,
  even more so than it is/would be for SCSI, as SSDs
  can actually read from as many sectors as bandwidth
  allows at once.

a3fa1e70f67b: nvme_disk: Implement write support.
  
  Tested only lightly. Please use caution when trying it!

2bd1323ab88a: nvme_disk: Respect the maximum transfer size by segmenting I/O.
  
  On VirtualBox, the reported maximum transfer size is 2MB; but all
  I/O >= 753KB fails in an identical way to the "maximum size" failures.
  In other news, there are multiple open tickets in the VirtualBox
  tracker about Linux systems failing to boot off NVMe...
  
  I tested with a hacked-in maximum segment size of 752KB and everything
  seemed to work just fine. Writing a HPKG to a FAT partition on NVMe,
  rebooting, and then reading back the HPKG showed it had the same
  sha256sum as the one stored in /system did. So this is working!

                              [ Augustin Cavalier <waddlesplash@xxxxxxxxx> ]

----------------------------------------------------------------------------

1 file changed, 215 insertions(+), 39 deletions(-)
.../kernel/drivers/disk/nvme/nvme_disk.cpp       | 254 ++++++++++++++++---

############################################################################

Commit:      872c8209b00865954cd9cbea78f58cb961a9c25f
URL:         https://git.haiku-os.org/haiku/commit/?id=872c8209b008
Author:      Augustin Cavalier <waddlesplash@xxxxxxxxx>
Date:        Wed Apr 17 23:44:23 2019 UTC

nvme_disk: Allocate up to 8 qpairs and round-robin them for I/O.

Before this commit, only one qpair was allocated and it was
locked for the duration of the transfer. Now we allocate more
qpairs if the device supports it and lock only when actually
calling read(), enabling multiple reads to both be queued
and execute simultaneously.

This is probably a significant performance improvement,
even more so than it is/would be for SCSI, as SSDs
can actually read from as many sectors as bandwidth
allows at once.

----------------------------------------------------------------------------

diff --git a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp 
b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
index db4e386a29..3170390602 100644
--- a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
+++ b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
@@ -27,6 +27,7 @@ extern "C" {
 #else
 #      define TRACE(x...) ;
 #endif
+#define TRACE_ALWAYS(x...)     dprintf("nvme_disk: " x)
 #define TRACE_ERROR(x...)      dprintf("\33[33mnvme_disk:\33[0m " x)
 #define CALLED()                       TRACE("CALLED %s\n", 
__PRETTY_FUNCTION__)
 
@@ -62,6 +63,8 @@ static const uint8 kDriveIcon[] = {
 #define NVME_DISK_DEVICE_MODULE_NAME   "drivers/disk/nvme_disk/device_v1"
 #define NVME_DISK_DEVICE_ID_GENERATOR  "nvme_disk/device_id"
 
+#define NVME_MAX_QPAIRS                                        (8)
+
 
 static device_manager_info* sDeviceManager;
 
@@ -76,9 +79,14 @@ typedef struct {
        uint32                                  block_size;
        status_t                                media_status;
 
-       struct nvme_qpair*              qpair;
-       mutex                                   qpair_mtx;
+       struct qpair_info {
+               struct nvme_qpair*      qpair;
+               mutex                           mtx;
+       }                                               qpairs[NVME_MAX_QPAIRS];
+       uint32                                  qpair_count;
+       uint32                                  next_qpair;
 } nvme_disk_driver_info;
+typedef nvme_disk_driver_info::qpair_info qpair_info;
 
 
 typedef struct {
@@ -185,6 +193,8 @@ nvme_disk_init_device(void* _info, void** _cookie)
                return err;
        }
 
+       TRACE_ALWAYS("attached to NVMe device \"%s (%s)\n", cstat.mn, cstat.sn);
+
        // TODO: export more than just the first namespace!
        info->ns = nvme_ns_open(info->ctrlr, cstat.ns_ids[0]);
        if (info->ns == NULL) {
@@ -205,15 +215,21 @@ nvme_disk_init_device(void* _info, void** _cookie)
        TRACE("capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n",
                info->capacity, info->block_size);
 
-       // allocate a qpair
-       // TODO: allocate more than one qpair
-       info->qpair = nvme_ioqp_get(info->ctrlr, (enum nvme_qprio)0, 0);
-       if (info->qpair == NULL) {
-               TRACE_ERROR("failed to allocate qpair!\n");
-               return B_ERROR;
-       }
+       // allocate qpairs
+       info->qpair_count = info->next_qpair = 0;
+       for (uint32 i = 0; i < NVME_MAX_QPAIRS && i < cstat.io_qpairs; i++) {
+               info->qpairs[i].qpair = nvme_ioqp_get(info->ctrlr,
+                       (enum nvme_qprio)0, 0);
+               if (info->qpairs[i].qpair == NULL)
+                       break;
 
-       mutex_init(&info->qpair_mtx, "qpair mtx");
+               mutex_init(&info->qpairs[i].mtx, "qpair mutex");
+               info->qpair_count++;
+       }
+       if (info->qpair_count == 0) {
+               TRACE_ERROR("failed to allocate qpairs!\n");
+               return B_NO_MEMORY;
+       }
 
        *_cookie = info;
        return B_OK;
@@ -270,8 +286,16 @@ nvme_disk_free(void* cookie)
 // #pragma mark - I/O functions
 
 
+static qpair_info*
+get_next_qpair(nvme_disk_driver_info* info)
+{
+       return &info->qpairs[atomic_add((int32*)&info->next_qpair, 1)
+               % info->qpair_count];
+}
+
+
 static void
-disk_read_callback(status_t* status, const struct nvme_cpl* cpl)
+disk_io_callback(status_t* status, const struct nvme_cpl* cpl)
 {
        *status = nvme_cpl_is_error(cpl) ? B_IO_ERROR : B_OK;
 }
@@ -313,16 +337,23 @@ nvme_disk_read(void* cookie, off_t pos, void* buffer, 
size_t* length)
        }
 
        // Actually perform the read.
-       MutexLocker _(handle->info->qpair_mtx);
-       int ret = nvme_ns_read(handle->info->ns, handle->info->qpair,
-               buffer, rounded_pos / block_size, rounded_len / block_size,
-               (nvme_cmd_cb)disk_read_callback, &status, 0);
+       qpair_info* qpinfo = get_next_qpair(handle->info);
+       mutex_lock(&qpinfo->mtx);
+       int ret = nvme_ns_read(handle->info->ns, qpinfo->qpair, buffer,
+               rounded_pos / block_size, rounded_len / block_size,
+               (nvme_cmd_cb)disk_io_callback, &status, 0);
+       mutex_unlock(&qpinfo->mtx);
        if (ret != 0)
                return ret;
 
        while (status == EINPROGRESS) {
-               nvme_ioqp_poll(handle->info->qpair, 1);
-               snooze(5);
+               // nvme_ioqp_poll uses locking internally on the entire device,
+               // not just this qpair, so it is entirely possible that it could
+               // return 0 (i.e. no completions processed) and yet our status
+               // changed, because some other thread processed the completion
+               // before we got to it. So, recheck it before sleeping.
+               if (nvme_ioqp_poll(qpinfo->qpair, 0) == 0 && status == 
EINPROGRESS)
+                       snooze(5);
        }
 
        if (status != B_OK)

############################################################################

Commit:      a3fa1e70f67b7f63f4cde1b8d9d5747917d77978
URL:         https://git.haiku-os.org/haiku/commit/?id=a3fa1e70f67b
Author:      Augustin Cavalier <waddlesplash@xxxxxxxxx>
Date:        Thu Apr 18 02:06:59 2019 UTC

nvme_disk: Implement write support.

Tested only lightly. Please use caution when trying it!

----------------------------------------------------------------------------

diff --git a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp 
b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
index 3170390602..8a61a688a4 100644
--- a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
+++ b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <AutoDeleter.h>
 #include <kernel.h>
 #include <util/AutoLock.h>
 
@@ -104,7 +105,7 @@ get_geometry(nvme_disk_handle* handle, device_geometry* 
geometry)
        geometry->device_type = B_DISK;
        geometry->removable = false;
 
-       geometry->read_only = true; /* TODO: Write support! */
+       geometry->read_only = false;
        geometry->write_once = false;
 
        TRACE("get_geometry(): %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ", %" 
B_PRId32 ", %d, %d, %d, %d\n",
@@ -193,7 +194,7 @@ nvme_disk_init_device(void* _info, void** _cookie)
                return err;
        }
 
-       TRACE_ALWAYS("attached to NVMe device \"%s (%s)\n", cstat.mn, cstat.sn);
+       TRACE_ALWAYS("attached to NVMe device \"%s (%s)\"\n", cstat.mn, 
cstat.sn);
 
        // TODO: export more than just the first namespace!
        info->ns = nvme_ns_open(info->ctrlr, cstat.ns_ids[0]);
@@ -301,6 +302,54 @@ disk_io_callback(status_t* status, const struct nvme_cpl* 
cpl)
 }
 
 
+static void
+await_status(struct nvme_qpair* qpair, status_t& status)
+{
+       while (status == EINPROGRESS) {
+               // nvme_ioqp_poll uses locking internally on the entire device,
+               // not just this qpair, so it is entirely possible that it could
+               // return 0 (i.e. no completions processed) and yet our status
+               // changed, because some other thread processed the completion
+               // before we got to it. So, recheck it before sleeping.
+               if (nvme_ioqp_poll(qpair, 0) == 0 && status == EINPROGRESS)
+                       snooze(5);
+       }
+}
+
+
+static status_t
+do_nvme_io(nvme_disk_driver_info* info, off_t rounded_pos, void* buffer,
+       size_t* rounded_len, bool write = false)
+{
+       CALLED();
+       const size_t block_size = info->block_size;
+
+       status_t status = EINPROGRESS;
+
+       qpair_info* qpinfo = get_next_qpair(info);
+       mutex_lock(&qpinfo->mtx);
+       int ret = -1;
+       if (write) {
+               ret = nvme_ns_write(info->ns, qpinfo->qpair, buffer,
+                       rounded_pos / block_size, *rounded_len / block_size,
+                       (nvme_cmd_cb)disk_io_callback, &status, 0);
+       } else {
+               ret = nvme_ns_read(info->ns, qpinfo->qpair, buffer,
+                       rounded_pos / block_size, *rounded_len / block_size,
+                       (nvme_cmd_cb)disk_io_callback, &status, 0);
+       }
+       mutex_unlock(&qpinfo->mtx);
+       if (ret != 0)
+               return ret;
+
+       await_status(qpinfo->qpair, status);
+
+       if (status != B_OK)
+               *rounded_len = 0;
+       return status;
+}
+
+
 static status_t
 nvme_disk_read(void* cookie, off_t pos, void* buffer, size_t* length)
 {
@@ -308,8 +357,6 @@ nvme_disk_read(void* cookie, off_t pos, void* buffer, 
size_t* length)
        nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
        const size_t block_size = handle->info->block_size;
 
-       status_t status = EINPROGRESS;
-
        // libnvme does transfers in units of device sectors, so if we have to
        // round either the position or the length, we will need a bounce 
buffer.
        const off_t rounded_pos = ROUNDDOWN(pos, block_size);
@@ -317,10 +364,11 @@ nvme_disk_read(void* cookie, off_t pos, void* buffer, 
size_t* length)
        if (rounded_pos != pos || rounded_len != *length
                        || IS_USER_ADDRESS(buffer)) {
                void* bounceBuffer = malloc(rounded_len);
+               MemoryDeleter _(bounceBuffer);
                if (bounceBuffer == NULL)
                        return B_NO_MEMORY;
 
-               status = nvme_disk_read(cookie, rounded_pos, bounceBuffer,
+               status_t status = nvme_disk_read(cookie, rounded_pos, 
bounceBuffer,
                        &rounded_len);
                if (status != B_OK) {
                        *length = 0;
@@ -332,44 +380,83 @@ nvme_disk_read(void* cookie, off_t pos, void* buffer, 
size_t* length)
                        status = user_memcpy(buffer, offsetBuffer, *length);
                else
                        memcpy(buffer, offsetBuffer, *length);
-               free(bounceBuffer);
                return status;
        }
 
-       // Actually perform the read.
-       qpair_info* qpinfo = get_next_qpair(handle->info);
-       mutex_lock(&qpinfo->mtx);
-       int ret = nvme_ns_read(handle->info->ns, qpinfo->qpair, buffer,
-               rounded_pos / block_size, rounded_len / block_size,
-               (nvme_cmd_cb)disk_io_callback, &status, 0);
-       mutex_unlock(&qpinfo->mtx);
-       if (ret != 0)
-               return ret;
+       // If we got here, that means the arguments are already rounded to LBAs,
+       // so just do the I/O directly.
+       return do_nvme_io(handle->info, pos, buffer, length);
+}
 
-       while (status == EINPROGRESS) {
-               // nvme_ioqp_poll uses locking internally on the entire device,
-               // not just this qpair, so it is entirely possible that it could
-               // return 0 (i.e. no completions processed) and yet our status
-               // changed, because some other thread processed the completion
-               // before we got to it. So, recheck it before sleeping.
-               if (nvme_ioqp_poll(qpinfo->qpair, 0) == 0 && status == 
EINPROGRESS)
-                       snooze(5);
+
+static status_t
+nvme_disk_write(void* cookie, off_t pos, const void* buffer, size_t* length)
+{
+       CALLED();
+       nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
+       const size_t block_size = handle->info->block_size;
+
+       const off_t rounded_pos = ROUNDDOWN(pos, block_size);
+       size_t rounded_len = ROUNDUP((*length) + (pos - rounded_pos), 
block_size);
+       if (rounded_pos != pos || rounded_len != *length
+                       || IS_USER_ADDRESS(buffer)) {
+               void* bounceBuffer = malloc(rounded_len);
+               MemoryDeleter _(bounceBuffer);
+               if (bounceBuffer == NULL)
+                       return B_NO_MEMORY;
+
+               // Since we rounded, we need to read in the first and last 
logical
+               // blocks before we copy our information to the bounce buffer.
+               // TODO: This would be faster if we queued both reads at once!
+               size_t readlen = block_size;
+               status_t status = do_nvme_io(handle->info, rounded_pos, 
bounceBuffer,
+                       &readlen);
+               if (status != B_OK)
+                       return status;
+               if (rounded_len > block_size) {
+                       off_t offset = rounded_len - block_size;
+                       status = do_nvme_io(handle->info, rounded_pos + offset,
+                               ((int8*)bounceBuffer) + offset, &readlen);
+                       if (status != B_OK)
+                               return status;
+               }
+
+               void* offsetBuffer = ((int8*)bounceBuffer) + (pos - 
rounded_pos);
+               if (IS_USER_ADDRESS(buffer))
+                       status = user_memcpy(offsetBuffer, buffer, *length);
+               else
+                       memcpy(offsetBuffer, buffer, *length);
+               if (status != B_OK)
+                       return status;
+
+               status = nvme_disk_write(cookie, rounded_pos, bounceBuffer,
+                       &rounded_len);
+               if (status != B_OK)
+                       *length = 0;
+               return status;
        }
 
-       if (status != B_OK)
-               *length = 0;
-       return status;
+       // If we got here, that means the arguments are already rounded to LBAs,
+       // so just do the I/O directly.
+       return do_nvme_io(handle->info, pos, (void*)buffer, length, true);
 }
 
 
 static status_t
-nvme_disk_write(void* cookie, off_t pos, const void* buffer,
-       size_t* _length)
+nvme_disk_flush(nvme_disk_driver_info* info)
 {
-       CALLED();
-       nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
+       status_t status = EINPROGRESS;
+
+       qpair_info* qpinfo = get_next_qpair(info);
+       mutex_lock(&qpinfo->mtx);
+       int ret = nvme_ns_flush(info->ns, qpinfo->qpair,
+               (nvme_cmd_cb)disk_io_callback, &status);
+       mutex_unlock(&qpinfo->mtx);
+       if (ret != 0)
+               return ret;
 
-       return B_NOT_SUPPORTED;
+       await_status(qpinfo->qpair, status);
+       return status;
 }
 
 
@@ -432,8 +519,8 @@ nvme_disk_ioctl(void* cookie, uint32 op, void* buffer, 
size_t length)
                        return user_memcpy(buffer, &iconData, 
sizeof(device_icon));
                }
 
-               /*case B_FLUSH_DRIVE_CACHE:
-                       return synchronize_cache(info);*/
+               case B_FLUSH_DRIVE_CACHE:
+                       return nvme_disk_flush(info);
        }
 
        return B_DEV_INVALID_IOCTL;

############################################################################

Revision:    hrev53079
Commit:      2bd1323ab88a8f1d422960ac89b39f32eeef38b6
URL:         https://git.haiku-os.org/haiku/commit/?id=2bd1323ab88a
Author:      Augustin Cavalier <waddlesplash@xxxxxxxxx>
Date:        Thu Apr 18 03:15:06 2019 UTC

nvme_disk: Respect the maximum transfer size by segmenting I/O.

On VirtualBox, the reported maximum transfer size is 2MB; but all
I/O >= 753KB fails in an identical way to the "maximum size" failures.
In other news, there are multiple open tickets in the VirtualBox
tracker about Linux systems failing to boot off NVMe...

I tested with a hacked-in maximum segment size of 752KB and everything
seemed to work just fine. Writing a HPKG to a FAT partition on NVMe,
rebooting, and then reading back the HPKG showed it had the same
sha256sum as the one stored in /system did. So this is working!

----------------------------------------------------------------------------

diff --git a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp 
b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
index 8a61a688a4..9a69cb8346 100644
--- a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
+++ b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <algorithm>
 #include <AutoDeleter.h>
 #include <kernel.h>
 #include <util/AutoLock.h>
@@ -78,6 +79,7 @@ typedef struct {
 
        uint64                                  capacity;
        uint32                                  block_size;
+       size_t                                  max_transfer_size;
        status_t                                media_status;
 
        struct qpair_info {
@@ -195,6 +197,8 @@ nvme_disk_init_device(void* _info, void** _cookie)
        }
 
        TRACE_ALWAYS("attached to NVMe device \"%s (%s)\"\n", cstat.mn, 
cstat.sn);
+       TRACE_ALWAYS("\tmaximum transfer size: %" B_PRIuSIZE "\n", 
cstat.max_xfer_size);
+       TRACE_ALWAYS("\tqpair count: %d\n", cstat.io_qpairs);
 
        // TODO: export more than just the first namespace!
        info->ns = nvme_ns_open(info->ctrlr, cstat.ns_ids[0]);
@@ -212,6 +216,8 @@ nvme_disk_init_device(void* _info, void** _cookie)
 
        // store capacity information
        nvme_disk_set_capacity(info, nsstat.sectors, nsstat.sector_size);
+       info->max_transfer_size = ROUNDDOWN(cstat.max_xfer_size,
+               nsstat.sector_size);
 
        TRACE("capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n",
                info->capacity, info->block_size);
@@ -350,6 +356,42 @@ do_nvme_io(nvme_disk_driver_info* info, off_t rounded_pos, 
void* buffer,
 }
 
 
+static status_t
+do_nvme_segmented_io(nvme_disk_driver_info* info, off_t rounded_pos,
+       void* buffer, size_t* rounded_len, bool write = false)
+{
+       // The max transfer size is already a multiple of the block size,
+       // so divide and iterate appropriately. In the case where the length
+       // is less than the maximum transfer size, we'll wind up with 0 in the
+       // division, and only one transfer to take care of.
+       const size_t max_xfer = info->max_transfer_size;
+       int32 transfers = *rounded_len / max_xfer;
+       if ((*rounded_len % max_xfer) != 0)
+               transfers++;
+
+       size_t transferred = 0;
+       for (int32 i = 0; i < transfers; i++) {
+               size_t transfer_len = max_xfer;
+               // The last transfer will usually be smaller.
+               if (i == (transfers - 1))
+                       transfer_len = *rounded_len - transferred;
+
+               status_t status = do_nvme_io(info, rounded_pos, buffer,
+                       &transfer_len, write);
+               if (status != B_OK) {
+                       *rounded_len = transferred;
+                       return transferred > 0 ? (write ? B_PARTIAL_WRITE : 
B_PARTIAL_READ)
+                               : status;
+               }
+
+               transferred += transfer_len;
+               rounded_pos += transfer_len;
+               buffer = ((int8*)buffer) + transfer_len;
+       }
+       return B_OK;
+}
+
+
 static status_t
 nvme_disk_read(void* cookie, off_t pos, void* buffer, size_t* length)
 {
@@ -365,14 +407,20 @@ nvme_disk_read(void* cookie, off_t pos, void* buffer, 
size_t* length)
                        || IS_USER_ADDRESS(buffer)) {
                void* bounceBuffer = malloc(rounded_len);
                MemoryDeleter _(bounceBuffer);
-               if (bounceBuffer == NULL)
+               if (bounceBuffer == NULL) {
+                       *length = 0;
                        return B_NO_MEMORY;
+               }
 
                status_t status = nvme_disk_read(cookie, rounded_pos, 
bounceBuffer,
                        &rounded_len);
                if (status != B_OK) {
-                       *length = 0;
-                       return status;
+                       // The "rounded_len" will be the actual transferred 
length, but
+                       // of course it will contain the padding.
+                       *length = std::min(*length, (size_t)std::max((off_t)0,
+                               rounded_len - (pos - rounded_pos)));
+                       if (*length == 0)
+                               return status;
                }
 
                void* offsetBuffer = ((int8*)bounceBuffer) + (pos - 
rounded_pos);
@@ -385,7 +433,7 @@ nvme_disk_read(void* cookie, off_t pos, void* buffer, 
size_t* length)
 
        // If we got here, that means the arguments are already rounded to LBAs,
        // so just do the I/O directly.
-       return do_nvme_io(handle->info, pos, buffer, length);
+       return do_nvme_segmented_io(handle->info, pos, buffer, length);
 }
 
 
@@ -402,8 +450,10 @@ nvme_disk_write(void* cookie, off_t pos, const void* 
buffer, size_t* length)
                        || IS_USER_ADDRESS(buffer)) {
                void* bounceBuffer = malloc(rounded_len);
                MemoryDeleter _(bounceBuffer);
-               if (bounceBuffer == NULL)
+               if (bounceBuffer == NULL) {
+                       *length = 0;
                        return B_NO_MEMORY;
+               }
 
                // Since we rounded, we need to read in the first and last 
logical
                // blocks before we copy our information to the bounce buffer.
@@ -411,14 +461,18 @@ nvme_disk_write(void* cookie, off_t pos, const void* 
buffer, size_t* length)
                size_t readlen = block_size;
                status_t status = do_nvme_io(handle->info, rounded_pos, 
bounceBuffer,
                        &readlen);
-               if (status != B_OK)
+               if (status != B_OK) {
+                       *length = 0;
                        return status;
+               }
                if (rounded_len > block_size) {
                        off_t offset = rounded_len - block_size;
                        status = do_nvme_io(handle->info, rounded_pos + offset,
                                ((int8*)bounceBuffer) + offset, &readlen);
-                       if (status != B_OK)
+                       if (status != B_OK) {
+                               *length = 0;
                                return status;
+                       }
                }
 
                void* offsetBuffer = ((int8*)bounceBuffer) + (pos - 
rounded_pos);
@@ -426,19 +480,23 @@ nvme_disk_write(void* cookie, off_t pos, const void* 
buffer, size_t* length)
                        status = user_memcpy(offsetBuffer, buffer, *length);
                else
                        memcpy(offsetBuffer, buffer, *length);
-               if (status != B_OK)
+               if (status != B_OK) {
+                       *length = 0;
                        return status;
+               }
 
                status = nvme_disk_write(cookie, rounded_pos, bounceBuffer,
                        &rounded_len);
-               if (status != B_OK)
-                       *length = 0;
+               if (status != B_OK) {
+                       *length = std::min(*length, (size_t)std::max((off_t)0,
+                               rounded_len - (pos - rounded_pos)));
+               }
                return status;
        }
 
        // If we got here, that means the arguments are already rounded to LBAs,
        // so just do the I/O directly.
-       return do_nvme_io(handle->info, pos, (void*)buffer, length, true);
+       return do_nvme_segmented_io(handle->info, pos, (void*)buffer, length, 
true);
 }
 
 


Other related posts:

  • » [haiku-commits] haiku: hrev53079 - src/add-ons/kernel/drivers/disk/nvme - waddlesplash