[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