From f9167b8529c0f49f72f0a834e28bde52537b0c64 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Fri, 8 May 2026 14:59:51 -0400 Subject: [PATCH 1/3] libnvme: internalize uring dispatch in libnvme_submit_admin_passthru() Move the io_uring vs. ioctl routing decision out of callers and into libnvme_submit_admin_passthru() itself. Signed-off-by: Martin Belanger Assisted-by: Claude:claude-sonnet-4-6 [Claude Code] --- libnvme/src/nvme/ioctl.c | 3 +++ libnvme/src/nvme/nvme-cmds.c | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libnvme/src/nvme/ioctl.c b/libnvme/src/nvme/ioctl.c index ad6b828810..5d9dad158c 100644 --- a/libnvme/src/nvme/ioctl.c +++ b/libnvme/src/nvme/ioctl.c @@ -205,6 +205,9 @@ __public int libnvme_submit_io_passthru(struct libnvme_transport_handle *hdl, __public int libnvme_submit_admin_passthru(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd) { + if (hdl->uring_enabled) + return libnvme_submit_admin_passthru_async(hdl, cmd); + if (!cmd->timeout_ms && hdl->timeout) cmd->timeout_ms = hdl->timeout; diff --git a/libnvme/src/nvme/nvme-cmds.c b/libnvme/src/nvme/nvme-cmds.c index f3c378e811..bef19fb838 100644 --- a/libnvme/src/nvme/nvme-cmds.c +++ b/libnvme/src/nvme/nvme-cmds.c @@ -85,10 +85,7 @@ __public int libnvme_get_log(struct libnvme_transport_handle *hdl, cmd->data_len = xfer; cmd->addr = (__u64)(uintptr_t)ptr; - if (hdl->uring_enabled) - ret = libnvme_submit_admin_passthru_async(hdl, cmd); - else - ret = libnvme_submit_admin_passthru(hdl, cmd); + ret = libnvme_submit_admin_passthru(hdl, cmd); if (ret) return ret; From 9e4abdb1828d823870ab3d9df547d154e4889d61 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Fri, 8 May 2026 15:33:55 -0400 Subject: [PATCH 2/3] libnvme: add missing libnvme_wait_complete_passthru() call sites Every caller of libnvme_submit_admin_passthru() must follow up with libnvme_wait_complete_passthru() to drain io_uring completions before inspecting response data. Functions with two sequential submits where the second depends on the first's response (libnvme_set_etdas, libnvme_clear_etdas, libnvme_get_uuid_list) get an intermediate wait between the two submits. libnvme_get_log() already had a conditional wait guarded by hdl->uring_enabled; make it unconditional now that libnvme_wait_complete_passthru() is a true no-op in the no-uring build. Signed-off-by: Martin Belanger Assisted-by: Claude:claude-sonnet-4-6 [Claude Code] --- libnvme/src/nvme/fabrics.c | 3 ++- libnvme/src/nvme/mi.c | 6 ++++++ libnvme/src/nvme/no-uring.c | 2 +- libnvme/src/nvme/nvme-cmds.c | 33 ++++++++++++++++++++++++--------- libnvme/src/nvme/tree.c | 10 +++++++--- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 326287ec69..5cb698d061 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -1713,7 +1713,8 @@ static int nvmf_dim(libnvme_ctrl_t c, enum nvmf_dim_tas tas, __u8 trtype, nvmf_fill_die(die, c->s->h, tel, trtype, adrfam, reg_addr, tsas); nvme_init_dim_send(&cmd, tas, dim, tdl); - return libnvme_submit_admin_passthru(hdl, &cmd); + ret = libnvme_submit_admin_passthru(hdl, &cmd); + return ret ? ret : libnvme_wait_complete_passthru(hdl); } /** diff --git a/libnvme/src/nvme/mi.c b/libnvme/src/nvme/mi.c index 457cceb681..734c65f346 100644 --- a/libnvme/src/nvme/mi.c +++ b/libnvme/src/nvme/mi.c @@ -201,6 +201,12 @@ void libnvme_mi_ep_probe(struct libnvme_mi_ep *ep) "Identify Controller failed, no quirks applied\n"); goto out_close; } + rc = libnvme_wait_complete_passthru(hdl); + if (rc) { + libnvme_msg(ep->ctx, LIBNVME_LOG_WARN, + "Identify Controller failed, no quirks applied\n"); + goto out_close; + } /* Samsung MZUL2512: cannot receive commands sent within ~1ms of * the previous response. Set an inter-command delay of 1.2ms for diff --git a/libnvme/src/nvme/no-uring.c b/libnvme/src/nvme/no-uring.c index abaadd7afa..e7502c9b5e 100644 --- a/libnvme/src/nvme/no-uring.c +++ b/libnvme/src/nvme/no-uring.c @@ -35,5 +35,5 @@ int libnvme_submit_admin_passthru_async(struct libnvme_transport_handle *hdl, int libnvme_wait_complete_passthru(struct libnvme_transport_handle *hdl) { - return -ENOTSUP; + return 0; } diff --git a/libnvme/src/nvme/nvme-cmds.c b/libnvme/src/nvme/nvme-cmds.c index bef19fb838..849e76d3f4 100644 --- a/libnvme/src/nvme/nvme-cmds.c +++ b/libnvme/src/nvme/nvme-cmds.c @@ -93,13 +93,7 @@ __public int libnvme_get_log(struct libnvme_transport_handle *hdl, ptr += xfer; } while (offset < data_len); - if (hdl->uring_enabled) { - ret = libnvme_wait_complete_passthru(hdl); - if (ret) - return ret; - } - - return 0; + return libnvme_wait_complete_passthru(hdl); } static int read_ana_chunk(struct libnvme_transport_handle *hdl, @@ -237,6 +231,9 @@ __public int libnvme_set_etdas(struct libnvme_transport_handle *hdl, bool *chang nvme_init_get_features_host_behavior(&cmd, 0, &da4); err = libnvme_submit_admin_passthru(hdl, &cmd); + if (err) + return err; + err = libnvme_wait_complete_passthru(hdl); if (err) return err; @@ -253,7 +250,7 @@ __public int libnvme_set_etdas(struct libnvme_transport_handle *hdl, bool *chang return err; *changed = true; - return 0; + return libnvme_wait_complete_passthru(hdl); } __public int libnvme_clear_etdas(struct libnvme_transport_handle *hdl, bool *changed) @@ -264,6 +261,9 @@ __public int libnvme_clear_etdas(struct libnvme_transport_handle *hdl, bool *cha nvme_init_get_features_host_behavior(&cmd, 0, &da4); err = libnvme_submit_admin_passthru(hdl, &cmd); + if (err) + return err; + err = libnvme_wait_complete_passthru(hdl); if (err) return err; @@ -279,7 +279,7 @@ __public int libnvme_clear_etdas(struct libnvme_transport_handle *hdl, bool *cha return err; *changed = true; - return 0; + return libnvme_wait_complete_passthru(hdl); } __public int libnvme_get_uuid_list(struct libnvme_transport_handle *hdl, @@ -297,11 +297,17 @@ __public int libnvme_get_uuid_list(struct libnvme_transport_handle *hdl, "ERROR: nvme_identify_ctrl() failed 0x%x\n", err); return err; } + err = libnvme_wait_complete_passthru(hdl); + if (err) + return err; if ((ctrl.ctratt & NVME_CTRL_CTRATT_UUID_LIST) == NVME_CTRL_CTRATT_UUID_LIST) { nvme_init_identify_uuid_list(&cmd, uuid_list); err = libnvme_submit_admin_passthru(hdl, &cmd); + if (err) + return err; + err = libnvme_wait_complete_passthru(hdl); } return err; @@ -320,6 +326,9 @@ __public int libnvme_get_telemetry_max(struct libnvme_transport_handle *hdl, nvme_init_identify_ctrl(&cmd, id_ctrl); err = libnvme_submit_admin_passthru(hdl, &cmd); + if (err) + return err; + err = libnvme_wait_complete_passthru(hdl); if (err) return err; @@ -539,6 +548,9 @@ __public int libnvme_get_ana_log_len(struct libnvme_transport_handle *hdl, size_ nvme_init_identify_ctrl(&cmd, ctrl); ret = libnvme_submit_admin_passthru(hdl, &cmd); + if (ret) + return ret; + ret = libnvme_wait_complete_passthru(hdl); if (ret) return ret; @@ -560,6 +572,9 @@ __public int libnvme_get_logical_block_size(struct libnvme_transport_handle *hdl nvme_init_identify_ns(&cmd, nsid, ns); ret = libnvme_submit_admin_passthru(hdl, &cmd); + if (ret) + return ret; + ret = libnvme_wait_complete_passthru(hdl); if (ret) return ret; diff --git a/libnvme/src/nvme/tree.c b/libnvme/src/nvme/tree.c index 36dc94671f..100b1e1f76 100644 --- a/libnvme/src/nvme/tree.c +++ b/libnvme/src/nvme/tree.c @@ -1496,9 +1496,11 @@ __public int libnvme_ctrl_identify(libnvme_ctrl_t c, struct nvme_id_ctrl *id) struct libnvme_transport_handle *hdl = libnvme_ctrl_get_transport_handle(c); struct libnvme_passthru_cmd cmd; + int ret; nvme_init_identify_ctrl(&cmd, id); - return libnvme_submit_admin_passthru(hdl, &cmd); + ret = libnvme_submit_admin_passthru(hdl, &cmd); + return ret ? ret : libnvme_wait_complete_passthru(hdl); } __public libnvme_ns_t libnvme_ctrl_first_ns(libnvme_ctrl_t c) @@ -2679,7 +2681,8 @@ __public int libnvme_ns_identify(libnvme_ns_t n, struct nvme_id_ns *ns) return err; nvme_init_identify_ns(&cmd, libnvme_ns_get_nsid(n), ns); - return libnvme_submit_admin_passthru(hdl, &cmd); + err = libnvme_submit_admin_passthru(hdl, &cmd); + return err ? err : libnvme_wait_complete_passthru(hdl); } int libnvme_ns_identify_descs(libnvme_ns_t n, struct nvme_ns_id_desc *descs) @@ -2693,7 +2696,8 @@ int libnvme_ns_identify_descs(libnvme_ns_t n, struct nvme_ns_id_desc *descs) return err; nvme_init_identify_ns_descs_list(&cmd, libnvme_ns_get_nsid(n), descs); - return libnvme_submit_admin_passthru(hdl, &cmd); + err = libnvme_submit_admin_passthru(hdl, &cmd); + return err ? err : libnvme_wait_complete_passthru(hdl); } __public int libnvme_ns_verify(libnvme_ns_t n, off_t offset, size_t count) From 16dc0db98151b873b5da9c8c9ea7955c1cddc22a Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Fri, 8 May 2026 15:37:12 -0400 Subject: [PATCH 3/3] libnvme: guard libnvme_submit_admin/io_passthru() against NULL hdl libnvme_ctrl_get_transport_handle() can return NULL when libnvme_open() fails during rapid device teardown. Add a NULL check at the top of both libnvme_submit_admin_passthru() and libnvme_submit_io_passthru() so the condition is caught centrally rather than causing a NULL dereference in callers. Signed-off-by: Martin Belanger Assisted-by: Claude:claude-sonnet-4-6 [Claude Code] --- libnvme/src/nvme/ioctl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libnvme/src/nvme/ioctl.c b/libnvme/src/nvme/ioctl.c index 5d9dad158c..4d53893c39 100644 --- a/libnvme/src/nvme/ioctl.c +++ b/libnvme/src/nvme/ioctl.c @@ -193,6 +193,9 @@ static int libnvme_submit_passthru64(struct libnvme_transport_handle *hdl, __public int libnvme_submit_io_passthru(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd) { + if (!hdl) + return -ENODEV; + if (!cmd->timeout_ms && hdl->timeout) cmd->timeout_ms = hdl->timeout; @@ -205,6 +208,9 @@ __public int libnvme_submit_io_passthru(struct libnvme_transport_handle *hdl, __public int libnvme_submit_admin_passthru(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd) { + if (!hdl) + return -ENODEV; + if (hdl->uring_enabled) return libnvme_submit_admin_passthru_async(hdl, cmd);