[PATCH] nvme/io_uring: enable dma-buf backed passthrough I/O
Anuj Gupta
anuj1072538 at gmail.com
Mon Oct 19 06:40:22 PDT 2026
On Sun, Oct 18, 2026 at 11:06:16PM +0100, Pavel Begunkov wrote:
> On 10/16/26 11:34, Anuj Gupta wrote:
> > From: Anuj Gupta <anuj20.g at samsung.com>
> > diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
> > index 3aa5d2f2dfbb..6343da1f6c05 100644
> > --- a/drivers/nvme/host/ioctl.c
> > +++ b/drivers/nvme/host/ioctl.c
> > @@ -458,6 +458,7 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req,
> > status = blk_status_to_errno(err);
> > }
> > io_uring_cmd_set_res32(ioucmd, status, result);
> > + io_uring_cmd_detach_dmabuf(ioucmd);
>
> It's not great spilling the responsibility for [de,a]ttaching
> between nvme and uring_cmd code. Is there a way to stash it
> all under io_uring?
The natural place would be __io_uring_cmd_done(), but that runs from
task_work, while the ref has to be dropped at hardware completion, like
io_complete_rw() does.
Hooking io_uring_cmd_do_in_task_lazy() would avoid changing the NVMe
call site, but would add a non-obvious side effect to a generic
task_work helper. How about a separate helper that drivers call from
end_io instead? This keeps the dma-buf handling in io_uring while NVMe
only reports that it has finished accessing the data.
Something like this on top of this patch:
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index 6343da1f6c05..6a7d1cc7b716 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -458,7 +458,6 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req,
status = blk_status_to_errno(err);
}
io_uring_cmd_set_res32(ioucmd, status, result);
- io_uring_cmd_detach_dmabuf(ioucmd);
/*
* For IOPOLL, check if this completion is happening in the context
@@ -473,7 +472,7 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req,
blk_rq_unmap_user(pdu->bio);
__io_uring_cmd_done(ioucmd, 0);
} else {
- io_uring_cmd_do_in_task_lazy(ioucmd, nvme_uring_task_cb);
+ io_uring_cmd_hw_done(ioucmd, nvme_uring_task_cb);
}
return RQ_END_IO_FREE;
}
diff --git a/include/linux/io_uring/cmd.h b/include/linux/io_uring/cmd.h
index 33e0ebeea5a0..0a89f5faf88c 100644
--- a/include/linux/io_uring/cmd.h
+++ b/include/linux/io_uring/cmd.h
@@ -70,7 +70,8 @@ int io_uring_cmd_import_fixed_vec(struct io_uring_cmd *ioucmd,
size_t uvec_segs,
int ddir, struct iov_iter *iter,
unsigned issue_flags);
-void io_uring_cmd_detach_dmabuf(struct io_uring_cmd *ioucmd);
+void io_uring_cmd_hw_done(struct io_uring_cmd *ioucmd,
+ io_req_tw_func_t task_work_cb);
/*
* Completes the request, i.e. posts an io_uring CQE and deallocates @ioucmd
@@ -138,7 +139,8 @@ static inline int io_uring_cmd_import_fixed_vec(struct io_uring_cmd *ioucmd,
return -EOPNOTSUPP;
}
-static inline void io_uring_cmd_detach_dmabuf(struct io_uring_cmd *ioucmd)
+static inline void io_uring_cmd_hw_done(struct io_uring_cmd *ioucmd,
+ io_req_tw_func_t task_work_cb)
{
}
diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c
index 0e0509edec6f..11a8ed1ba03a 100644
--- a/io_uring/uring_cmd.c
+++ b/io_uring/uring_cmd.c
@@ -50,12 +50,6 @@ void io_uring_cmd_cleanup(struct io_kiocb *req)
io_req_uring_cleanup(req, 0);
}
-void io_uring_cmd_detach_dmabuf(struct io_uring_cmd *ioucmd)
-{
- io_detach_dmabuf_map(cmd_to_io_kiocb(ioucmd));
-}
-EXPORT_SYMBOL_GPL(io_uring_cmd_detach_dmabuf);
-
bool io_uring_try_cancel_uring_cmd(struct io_ring_ctx *ctx,
struct io_uring_task *tctx)
{
@@ -146,6 +140,18 @@ void __io_uring_cmd_do_in_task(struct io_uring_cmd *ioucmd,
}
EXPORT_SYMBOL_GPL(__io_uring_cmd_do_in_task);
+/*
+ * Queue completion in task context after dropping references that must be
+ * released at hardware completion rather than from task_work.
+ */
+void io_uring_cmd_hw_done(struct io_uring_cmd *ioucmd,
+ io_req_tw_func_t task_work_cb)
+{
+ io_detach_dmabuf_map(cmd_to_io_kiocb(ioucmd));
+ __io_uring_cmd_do_in_task(ioucmd, task_work_cb, IOU_F_TWQ_LAZY_WAKE);
+}
+EXPORT_SYMBOL_GPL(io_uring_cmd_hw_done);
+
/*
* Called by consumers of io_uring_cmd, if they originally returned
* -EIOCBQUEUED upon receiving the command.
More information about the Linux-nvme
mailing list