* [PATCH v1 0/2] fuse io_uring: support registered buffers
@ 2025-10-22 20:20 Joanne Koong
2025-10-22 20:20 ` [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() Joanne Koong
` (3 more replies)
0 siblings, 4 replies; 17+ messages in thread
From: Joanne Koong @ 2025-10-22 20:20 UTC (permalink / raw)
To: miklos, axboe
Cc: linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li
This adds support for daemons who preregister buffers to minimize the overhead
of pinning/unpinning user pages and translating virtual addresses. Registered
buffers pay the cost once during registration then reuse the pre-pinned pages,
which helps reduce the per-op overhead.
This is on top of commit 211ddde0823f in the iouring tree.
Joanne Koong (2):
io-uring: add io_uring_cmd_get_buffer_info()
fuse: support io-uring registered buffers
fs/fuse/dev_uring.c | 216 ++++++++++++++++++++++++++++++++---
fs/fuse/dev_uring_i.h | 17 ++-
include/linux/io_uring/cmd.h | 2 +
io_uring/rsrc.c | 21 ++++
4 files changed, 236 insertions(+), 20 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() 2025-10-22 20:20 [PATCH v1 0/2] fuse io_uring: support registered buffers Joanne Koong @ 2025-10-22 20:20 ` Joanne Koong 2025-10-23 3:16 ` Caleb Sander Mateos 2025-10-22 20:20 ` [PATCH v1 2/2] fuse: support io-uring registered buffers Joanne Koong ` (2 subsequent siblings) 3 siblings, 1 reply; 17+ messages in thread From: Joanne Koong @ 2025-10-22 20:20 UTC (permalink / raw) To: miklos, axboe Cc: linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li Add io_uring_cmd_get_buffer_info() to fetch buffer information that will be necessary for constructing an iov iter for it. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> --- include/linux/io_uring/cmd.h | 2 ++ io_uring/rsrc.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/linux/io_uring/cmd.h b/include/linux/io_uring/cmd.h index 7509025b4071..a92e810f37f9 100644 --- a/include/linux/io_uring/cmd.h +++ b/include/linux/io_uring/cmd.h @@ -177,4 +177,6 @@ int io_buffer_register_bvec(struct io_uring_cmd *cmd, struct request *rq, int io_buffer_unregister_bvec(struct io_uring_cmd *cmd, unsigned int index, unsigned int issue_flags); +int io_uring_cmd_get_buffer_info(struct io_uring_cmd *cmd, u64 *ubuf, + unsigned int *len); #endif /* _LINUX_IO_URING_CMD_H */ diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index d787c16dc1c3..8554cdad8abc 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -1569,3 +1569,24 @@ int io_prep_reg_iovec(struct io_kiocb *req, struct iou_vec *iv, req->flags |= REQ_F_IMPORT_BUFFER; return 0; } + +int io_uring_cmd_get_buffer_info(struct io_uring_cmd *cmd, u64 *ubuf, + unsigned int *len) +{ + struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; + struct io_rsrc_data *data = &ctx->buf_table; + struct io_mapped_ubuf *imu; + unsigned int buf_index; + + if (!data->nr) + return -EINVAL; + + buf_index = cmd->sqe->buf_index; + imu = data->nodes[buf_index]->buf; + + *ubuf = imu->ubuf; + *len = imu->len; + + return 0; +} +EXPORT_SYMBOL_GPL(io_uring_cmd_get_buffer_info); -- 2.47.3 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() 2025-10-22 20:20 ` [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() Joanne Koong @ 2025-10-23 3:16 ` Caleb Sander Mateos 2025-10-23 22:20 ` Joanne Koong 0 siblings, 1 reply; 17+ messages in thread From: Caleb Sander Mateos @ 2025-10-23 3:16 UTC (permalink / raw) To: Joanne Koong Cc: miklos, axboe, linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li On Wed, Oct 22, 2025 at 1:23 PM Joanne Koong <joannelkoong@gmail.com> wrote: > > Add io_uring_cmd_get_buffer_info() to fetch buffer information that will > be necessary for constructing an iov iter for it. > > Signed-off-by: Joanne Koong <joannelkoong@gmail.com> > --- > include/linux/io_uring/cmd.h | 2 ++ > io_uring/rsrc.c | 21 +++++++++++++++++++++ > 2 files changed, 23 insertions(+) > > diff --git a/include/linux/io_uring/cmd.h b/include/linux/io_uring/cmd.h > index 7509025b4071..a92e810f37f9 100644 > --- a/include/linux/io_uring/cmd.h > +++ b/include/linux/io_uring/cmd.h > @@ -177,4 +177,6 @@ int io_buffer_register_bvec(struct io_uring_cmd *cmd, struct request *rq, > int io_buffer_unregister_bvec(struct io_uring_cmd *cmd, unsigned int index, > unsigned int issue_flags); > > +int io_uring_cmd_get_buffer_info(struct io_uring_cmd *cmd, u64 *ubuf, > + unsigned int *len); > #endif /* _LINUX_IO_URING_CMD_H */ > diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c > index d787c16dc1c3..8554cdad8abc 100644 > --- a/io_uring/rsrc.c > +++ b/io_uring/rsrc.c > @@ -1569,3 +1569,24 @@ int io_prep_reg_iovec(struct io_kiocb *req, struct iou_vec *iv, > req->flags |= REQ_F_IMPORT_BUFFER; > return 0; > } > + > +int io_uring_cmd_get_buffer_info(struct io_uring_cmd *cmd, u64 *ubuf, > + unsigned int *len) > +{ > + struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; > + struct io_rsrc_data *data = &ctx->buf_table; > + struct io_mapped_ubuf *imu; > + unsigned int buf_index; > + > + if (!data->nr) > + return -EINVAL; > + > + buf_index = cmd->sqe->buf_index; This is reading userspace-mapped memory, it should use READ_ONCE(). But why not just use cmd_to_io_kiocb(cmd)->buf_index? That's already sampled from the SQE in io_uring_cmd_prep() if the IORING_URING_CMD_FIXED flag is set. And it seems like the fuse uring_cmd implementation requires that flag to be set. > + imu = data->nodes[buf_index]->buf; Needs a bounds check? > + > + *ubuf = imu->ubuf; > + *len = imu->len; This wouldn't be valid for kernel registered buffers (those registered with io_buffer_register_bvec()). Either reject those or return a more general representation of the registered buffer memory (e.g. an iterator)? Best, Caleb > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(io_uring_cmd_get_buffer_info); > -- > 2.47.3 > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() 2025-10-23 3:16 ` Caleb Sander Mateos @ 2025-10-23 22:20 ` Joanne Koong 2025-10-24 0:00 ` Joanne Koong 0 siblings, 1 reply; 17+ messages in thread From: Joanne Koong @ 2025-10-23 22:20 UTC (permalink / raw) To: Caleb Sander Mateos Cc: miklos, axboe, linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li On Wed, Oct 22, 2025 at 8:17 PM Caleb Sander Mateos <csander@purestorage.com> wrote: > > On Wed, Oct 22, 2025 at 1:23 PM Joanne Koong <joannelkoong@gmail.com> wrote: > > diff --git a/include/linux/io_uring/cmd.h b/include/linux/io_uring/cmd.h > > index 7509025b4071..a92e810f37f9 100644 > > --- a/io_uring/rsrc.c > > +++ b/io_uring/rsrc.c > > @@ -1569,3 +1569,24 @@ int io_prep_reg_iovec(struct io_kiocb *req, struct iou_vec *iv, > > req->flags |= REQ_F_IMPORT_BUFFER; > > return 0; > > } > > + > > +int io_uring_cmd_get_buffer_info(struct io_uring_cmd *cmd, u64 *ubuf, > > + unsigned int *len) > > +{ > > + struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; > > + struct io_rsrc_data *data = &ctx->buf_table; > > + struct io_mapped_ubuf *imu; > > + unsigned int buf_index; > > + > > + if (!data->nr) > > + return -EINVAL; > > + > > + buf_index = cmd->sqe->buf_index; > > This is reading userspace-mapped memory, it should use READ_ONCE(). > But why not just use cmd_to_io_kiocb(cmd)->buf_index? That's already > sampled from the SQE in io_uring_cmd_prep() if the > IORING_URING_CMD_FIXED flag is set. And it seems like the fuse > uring_cmd implementation requires that flag to be set. Thanks, I didn't realize the cmd->sqe is userspace-mapped. I'll look at what io_uring_cmd_prep() does. For v2 I am going to drop this patch entirely and pass in the ubuf address and len through the 80B sqe command area when the server registers the ring. Thanks, Joanne ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() 2025-10-23 22:20 ` Joanne Koong @ 2025-10-24 0:00 ` Joanne Koong 0 siblings, 0 replies; 17+ messages in thread From: Joanne Koong @ 2025-10-24 0:00 UTC (permalink / raw) To: Caleb Sander Mateos Cc: miklos, axboe, linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li On Thu, Oct 23, 2025 at 3:20 PM Joanne Koong <joannelkoong@gmail.com> wrote: > > On Wed, Oct 22, 2025 at 8:17 PM Caleb Sander Mateos > <csander@purestorage.com> wrote: > > > > On Wed, Oct 22, 2025 at 1:23 PM Joanne Koong <joannelkoong@gmail.com> wrote: > > > diff --git a/include/linux/io_uring/cmd.h b/include/linux/io_uring/cmd.h > > > index 7509025b4071..a92e810f37f9 100644 > > > --- a/io_uring/rsrc.c > > > +++ b/io_uring/rsrc.c > > > @@ -1569,3 +1569,24 @@ int io_prep_reg_iovec(struct io_kiocb *req, struct iou_vec *iv, > > > req->flags |= REQ_F_IMPORT_BUFFER; > > > return 0; > > > } > > > + > > > +int io_uring_cmd_get_buffer_info(struct io_uring_cmd *cmd, u64 *ubuf, > > > + unsigned int *len) > > > +{ > > > + struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; > > > + struct io_rsrc_data *data = &ctx->buf_table; > > > + struct io_mapped_ubuf *imu; > > > + unsigned int buf_index; > > > + > > > + if (!data->nr) > > > + return -EINVAL; > > > + > > > + buf_index = cmd->sqe->buf_index; > > > > This is reading userspace-mapped memory, it should use READ_ONCE(). > > But why not just use cmd_to_io_kiocb(cmd)->buf_index? That's already > > sampled from the SQE in io_uring_cmd_prep() if the > > IORING_URING_CMD_FIXED flag is set. And it seems like the fuse > > uring_cmd implementation requires that flag to be set. > > Thanks, I didn't realize the cmd->sqe is userspace-mapped. I'll look > at what io_uring_cmd_prep() does. > > For v2 I am going to drop this patch entirely and pass in the ubuf > address and len through the 80B sqe command area when the server > registers the ring. Actually, after looking at this some more and realizing that we can't get away with recycling iters across requests, I like your io_uring_cmd_import_fixed_relative() helper idea. I think I'll just do it that way for v2 instead of having the libfuse server pass in the buffer address and length, since otherwise we would need to pass that info for every request (including commit/fetch, not just when registering) or have to stash it somewhere. Thanks, Joanne > > Thanks, > Joanne ^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v1 2/2] fuse: support io-uring registered buffers 2025-10-22 20:20 [PATCH v1 0/2] fuse io_uring: support registered buffers Joanne Koong 2025-10-22 20:20 ` [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() Joanne Koong @ 2025-10-22 20:20 ` Joanne Koong 2025-10-23 3:22 ` Caleb Sander Mateos ` (3 more replies) 2025-10-22 20:43 ` [PATCH v1 0/2] fuse io_uring: support " Bernd Schubert [not found] ` <CGME20251023114956epcas5p33a9384d06985dc5936fd355f1d580fb2@epcas5p3.samsung.com> 3 siblings, 4 replies; 17+ messages in thread From: Joanne Koong @ 2025-10-22 20:20 UTC (permalink / raw) To: miklos, axboe Cc: linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li Add support for io-uring registered buffers for fuse daemons communicating through the io-uring interface. Daemons may register buffers ahead of time, which will eliminate the overhead of pinning/unpinning user pages and translating virtual addresses for every server-kernel interaction. To support page-aligned payloads, the buffer is structured such that the payload is at the front of the buffer and the fuse_uring_req_header is offset from the end of the buffer. To be backwards compatible, fuse uring still needs to support non-registered buffers as well. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> --- fs/fuse/dev_uring.c | 216 ++++++++++++++++++++++++++++++++++++++---- fs/fuse/dev_uring_i.h | 17 +++- 2 files changed, 213 insertions(+), 20 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index f6b12aebb8bb..c4dd4d168b61 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -574,6 +574,37 @@ static int fuse_uring_out_header_has_err(struct fuse_out_header *oh, return err; } +static int fuse_uring_copy_from_ring_fixed_buf(struct fuse_ring *ring, + struct fuse_req *req, + struct fuse_ring_ent *ent) +{ + struct fuse_copy_state cs; + struct fuse_args *args = req->args; + struct iov_iter payload_iter; + struct iov_iter headers_iter; + struct fuse_uring_ent_in_out ring_in_out; + size_t copied; + + payload_iter = ent->fixed_buffer.payload_iter; + payload_iter.data_source = ITER_SOURCE; + headers_iter = ent->fixed_buffer.headers_iter; + headers_iter.data_source = ITER_SOURCE; + + iov_iter_advance(&headers_iter, offsetof(struct fuse_uring_req_header, + ring_ent_in_out)); + + copied = copy_from_iter(&ring_in_out, sizeof(ring_in_out), + &headers_iter); + if (copied != sizeof(ring_in_out)) + return -EFAULT; + + fuse_copy_init(&cs, false, &payload_iter); + cs.is_uring = true; + cs.req = req; + + return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); +} + static int fuse_uring_copy_from_ring(struct fuse_ring *ring, struct fuse_req *req, struct fuse_ring_ent *ent) @@ -584,12 +615,12 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, int err; struct fuse_uring_ent_in_out ring_in_out; - err = copy_from_user(&ring_in_out, &ent->headers->ring_ent_in_out, + err = copy_from_user(&ring_in_out, &ent->user.headers->ring_ent_in_out, sizeof(ring_in_out)); if (err) return -EFAULT; - err = import_ubuf(ITER_SOURCE, ent->payload, ring->max_payload_sz, + err = import_ubuf(ITER_SOURCE, ent->user.payload, ring->max_payload_sz, &iter); if (err) return err; @@ -601,6 +632,79 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); } +static int fuse_uring_args_to_ring_fixed_buf(struct fuse_ring *ring, + struct fuse_req *req, + struct fuse_ring_ent *ent) +{ + struct fuse_copy_state cs; + struct fuse_args *args = req->args; + struct fuse_in_arg *in_args = args->in_args; + int num_args = args->in_numargs; + struct iov_iter payload_iter; + struct iov_iter headers_iter; + struct fuse_uring_ent_in_out ent_in_out = { + .flags = 0, + .commit_id = req->in.h.unique, + }; + size_t copied; + bool advanced_headers = false; + int err; + + payload_iter = ent->fixed_buffer.payload_iter; + payload_iter.data_source = ITER_DEST; + + headers_iter = ent->fixed_buffer.headers_iter; + headers_iter.data_source = ITER_DEST; + + fuse_copy_init(&cs, true, &payload_iter); + cs.is_uring = true; + cs.req = req; + + if (num_args > 0) { + /* + * Expectation is that the first argument is the per op header. + * Some op code have that as zero size. + */ + if (args->in_args[0].size > 0) { + iov_iter_advance(&headers_iter, + offsetof(struct fuse_uring_req_header, + op_in)); + copied = copy_to_iter(in_args->value, in_args->size, + &headers_iter); + if (copied != in_args->size) { + pr_info_ratelimited( + "Copying the header failed.\n"); + return -EFAULT; + } + + iov_iter_advance(&headers_iter, + FUSE_URING_OP_IN_OUT_SZ - in_args->size); + advanced_headers = true; + } + in_args++; + num_args--; + } + if (!advanced_headers) + iov_iter_advance(&headers_iter, + offsetof(struct fuse_uring_req_header, + ring_ent_in_out)); + + /* copy the payload */ + err = fuse_copy_args(&cs, num_args, args->in_pages, + (struct fuse_arg *)in_args, 0); + if (err) { + pr_info_ratelimited("%s fuse_copy_args failed\n", __func__); + return err; + } + + ent_in_out.payload_sz = cs.ring.copied_sz; + copied = copy_to_iter(&ent_in_out, sizeof(ent_in_out), &headers_iter); + if (copied != sizeof(ent_in_out)) + return -EFAULT; + + return 0; +} + /* * Copy data from the req to the ring buffer */ @@ -618,7 +722,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, .commit_id = req->in.h.unique, }; - err = import_ubuf(ITER_DEST, ent->payload, ring->max_payload_sz, &iter); + err = import_ubuf(ITER_DEST, ent->user.payload, ring->max_payload_sz, &iter); if (err) { pr_info_ratelimited("fuse: Import of user buffer failed\n"); return err; @@ -634,7 +738,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, * Some op code have that as zero size. */ if (args->in_args[0].size > 0) { - err = copy_to_user(&ent->headers->op_in, in_args->value, + err = copy_to_user(&ent->user.headers->op_in, in_args->value, in_args->size); if (err) { pr_info_ratelimited( @@ -655,7 +759,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, } ent_in_out.payload_sz = cs.ring.copied_sz; - err = copy_to_user(&ent->headers->ring_ent_in_out, &ent_in_out, + err = copy_to_user(&ent->user.headers->ring_ent_in_out, &ent_in_out, sizeof(ent_in_out)); return err ? -EFAULT : 0; } @@ -679,18 +783,31 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, return err; /* copy the request */ - err = fuse_uring_args_to_ring(ring, req, ent); + if (ent->is_fixed_buffer) + err = fuse_uring_args_to_ring_fixed_buf(ring, req, ent); + else + err = fuse_uring_args_to_ring(ring, req, ent); if (unlikely(err)) { pr_info_ratelimited("Copy to ring failed: %d\n", err); return err; } /* copy fuse_in_header */ - err = copy_to_user(&ent->headers->in_out, &req->in.h, - sizeof(req->in.h)); - if (err) { - err = -EFAULT; - return err; + if (ent->is_fixed_buffer) { + struct iov_iter headers_iter = ent->fixed_buffer.headers_iter; + size_t copied; + + headers_iter.data_source = ITER_DEST; + copied = copy_to_iter(&req->in.h, sizeof(req->in.h), + &headers_iter); + + if (copied != sizeof(req->in.h)) + return -EFAULT; + } else { + err = copy_to_user(&ent->user.headers->in_out, &req->in.h, + sizeof(req->in.h)); + if (err) + return -EFAULT; } return 0; @@ -815,8 +932,18 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, struct fuse_conn *fc = ring->fc; ssize_t err = 0; - err = copy_from_user(&req->out.h, &ent->headers->in_out, - sizeof(req->out.h)); + if (ent->is_fixed_buffer) { + struct iov_iter headers_iter = ent->fixed_buffer.headers_iter; + size_t copied; + + headers_iter.data_source = ITER_SOURCE; + copied = copy_from_iter(&req->out.h, sizeof(req->out.h), &headers_iter); + if (copied != sizeof(req->out.h)) + err = -EFAULT; + } else { + err = copy_from_user(&req->out.h, &ent->user.headers->in_out, + sizeof(req->out.h)); + } if (err) { req->out.h.error = -EFAULT; goto out; @@ -828,7 +955,11 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, goto out; } - err = fuse_uring_copy_from_ring(ring, req, ent); + if (ent->is_fixed_buffer) + err = fuse_uring_copy_from_ring_fixed_buf(ring, req, ent); + else + err = fuse_uring_copy_from_ring(ring, req, ent); + out: fuse_uring_req_end(ent, req, err); } @@ -1027,6 +1158,52 @@ static int fuse_uring_get_iovec_from_sqe(const struct io_uring_sqe *sqe, return 0; } +static struct fuse_ring_ent * +fuse_uring_create_ring_ent_fixed_buf(struct io_uring_cmd *cmd, + struct fuse_ring_queue *queue) +{ + struct fuse_ring *ring = queue->ring; + struct fuse_ring_ent *ent; + unsigned payload_size, len; + u64 ubuf; + int err; + + err = -ENOMEM; + ent = kzalloc(sizeof(*ent), GFP_KERNEL_ACCOUNT); + if (!ent) + return ERR_PTR(err); + + INIT_LIST_HEAD(&ent->list); + + ent->queue = queue; + ent->is_fixed_buffer = true; + + err = io_uring_cmd_get_buffer_info(cmd, &ubuf, &len); + if (err) + goto error; + + payload_size = len - sizeof(struct fuse_uring_req_header); + err = io_uring_cmd_import_fixed(ubuf, payload_size, ITER_DEST, + &ent->fixed_buffer.payload_iter, cmd, 0); + if (err) + goto error; + + err = io_uring_cmd_import_fixed(ubuf + payload_size, + sizeof(struct fuse_uring_req_header), + ITER_DEST, + &ent->fixed_buffer.headers_iter, cmd, 0); + if (err) + goto error; + + atomic_inc(&ring->queue_refs); + + return ent; + +error: + kfree(ent); + return ERR_PTR(err); +} + static struct fuse_ring_ent * fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, struct fuse_ring_queue *queue) @@ -1065,8 +1242,8 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, INIT_LIST_HEAD(&ent->list); ent->queue = queue; - ent->headers = iov[0].iov_base; - ent->payload = iov[1].iov_base; + ent->user.headers = iov[0].iov_base; + ent->user.payload = iov[1].iov_base; atomic_inc(&ring->queue_refs); return ent; @@ -1085,6 +1262,8 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, struct fuse_ring_ent *ent; int err; unsigned int qid = READ_ONCE(cmd_req->qid); + bool is_fixed_buffer = + cmd->sqe->uring_cmd_flags & IORING_URING_CMD_FIXED; err = -ENOMEM; if (!ring) { @@ -1110,7 +1289,10 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, * case of entry errors below, will be done at ring destruction time. */ - ent = fuse_uring_create_ring_ent(cmd, queue); + if (is_fixed_buffer) + ent = fuse_uring_create_ring_ent_fixed_buf(cmd, queue); + else + ent = fuse_uring_create_ring_ent(cmd, queue); if (IS_ERR(ent)) return PTR_ERR(ent); diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h index 51a563922ce1..748c87e325f5 100644 --- a/fs/fuse/dev_uring_i.h +++ b/fs/fuse/dev_uring_i.h @@ -38,9 +38,20 @@ enum fuse_ring_req_state { /** A fuse ring entry, part of the ring queue */ struct fuse_ring_ent { - /* userspace buffer */ - struct fuse_uring_req_header __user *headers; - void __user *payload; + /* True if daemon has registered its buffers ahead of time */ + bool is_fixed_buffer; + union { + /* userspace buffer */ + struct { + struct fuse_uring_req_header __user *headers; + void __user *payload; + } user; + + struct { + struct iov_iter payload_iter; + struct iov_iter headers_iter; + } fixed_buffer; + }; /* the ring queue that owns the request */ struct fuse_ring_queue *queue; -- 2.47.3 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v1 2/2] fuse: support io-uring registered buffers 2025-10-22 20:20 ` [PATCH v1 2/2] fuse: support io-uring registered buffers Joanne Koong @ 2025-10-23 3:22 ` Caleb Sander Mateos 2025-10-23 12:39 ` kernel test robot ` (2 subsequent siblings) 3 siblings, 0 replies; 17+ messages in thread From: Caleb Sander Mateos @ 2025-10-23 3:22 UTC (permalink / raw) To: Joanne Koong Cc: miklos, axboe, linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li On Wed, Oct 22, 2025 at 1:23 PM Joanne Koong <joannelkoong@gmail.com> wrote: > > Add support for io-uring registered buffers for fuse daemons > communicating through the io-uring interface. Daemons may register > buffers ahead of time, which will eliminate the overhead of > pinning/unpinning user pages and translating virtual addresses for every > server-kernel interaction. > > To support page-aligned payloads, the buffer is structured such that the > payload is at the front of the buffer and the fuse_uring_req_header is > offset from the end of the buffer. > > To be backwards compatible, fuse uring still needs to support non-registered > buffers as well. > > Signed-off-by: Joanne Koong <joannelkoong@gmail.com> > --- > fs/fuse/dev_uring.c | 216 ++++++++++++++++++++++++++++++++++++++---- > fs/fuse/dev_uring_i.h | 17 +++- > 2 files changed, 213 insertions(+), 20 deletions(-) > > diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c > index f6b12aebb8bb..c4dd4d168b61 100644 > --- a/fs/fuse/dev_uring.c > +++ b/fs/fuse/dev_uring.c > @@ -574,6 +574,37 @@ static int fuse_uring_out_header_has_err(struct fuse_out_header *oh, > return err; > } > > +static int fuse_uring_copy_from_ring_fixed_buf(struct fuse_ring *ring, > + struct fuse_req *req, > + struct fuse_ring_ent *ent) > +{ > + struct fuse_copy_state cs; > + struct fuse_args *args = req->args; > + struct iov_iter payload_iter; > + struct iov_iter headers_iter; > + struct fuse_uring_ent_in_out ring_in_out; > + size_t copied; > + > + payload_iter = ent->fixed_buffer.payload_iter; > + payload_iter.data_source = ITER_SOURCE; > + headers_iter = ent->fixed_buffer.headers_iter; > + headers_iter.data_source = ITER_SOURCE; > + > + iov_iter_advance(&headers_iter, offsetof(struct fuse_uring_req_header, > + ring_ent_in_out)); > + > + copied = copy_from_iter(&ring_in_out, sizeof(ring_in_out), > + &headers_iter); > + if (copied != sizeof(ring_in_out)) > + return -EFAULT; > + > + fuse_copy_init(&cs, false, &payload_iter); > + cs.is_uring = true; > + cs.req = req; > + > + return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); > +} > + > static int fuse_uring_copy_from_ring(struct fuse_ring *ring, > struct fuse_req *req, > struct fuse_ring_ent *ent) > @@ -584,12 +615,12 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, > int err; > struct fuse_uring_ent_in_out ring_in_out; > > - err = copy_from_user(&ring_in_out, &ent->headers->ring_ent_in_out, > + err = copy_from_user(&ring_in_out, &ent->user.headers->ring_ent_in_out, > sizeof(ring_in_out)); > if (err) > return -EFAULT; > > - err = import_ubuf(ITER_SOURCE, ent->payload, ring->max_payload_sz, > + err = import_ubuf(ITER_SOURCE, ent->user.payload, ring->max_payload_sz, > &iter); > if (err) > return err; > @@ -601,6 +632,79 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, > return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); > } > > +static int fuse_uring_args_to_ring_fixed_buf(struct fuse_ring *ring, > + struct fuse_req *req, > + struct fuse_ring_ent *ent) > +{ > + struct fuse_copy_state cs; > + struct fuse_args *args = req->args; > + struct fuse_in_arg *in_args = args->in_args; > + int num_args = args->in_numargs; > + struct iov_iter payload_iter; > + struct iov_iter headers_iter; > + struct fuse_uring_ent_in_out ent_in_out = { > + .flags = 0, > + .commit_id = req->in.h.unique, > + }; > + size_t copied; > + bool advanced_headers = false; > + int err; > + > + payload_iter = ent->fixed_buffer.payload_iter; > + payload_iter.data_source = ITER_DEST; > + > + headers_iter = ent->fixed_buffer.headers_iter; > + headers_iter.data_source = ITER_DEST; > + > + fuse_copy_init(&cs, true, &payload_iter); > + cs.is_uring = true; > + cs.req = req; > + > + if (num_args > 0) { > + /* > + * Expectation is that the first argument is the per op header. > + * Some op code have that as zero size. > + */ > + if (args->in_args[0].size > 0) { > + iov_iter_advance(&headers_iter, > + offsetof(struct fuse_uring_req_header, > + op_in)); > + copied = copy_to_iter(in_args->value, in_args->size, > + &headers_iter); > + if (copied != in_args->size) { > + pr_info_ratelimited( > + "Copying the header failed.\n"); > + return -EFAULT; > + } > + > + iov_iter_advance(&headers_iter, > + FUSE_URING_OP_IN_OUT_SZ - in_args->size); > + advanced_headers = true; > + } > + in_args++; > + num_args--; > + } > + if (!advanced_headers) > + iov_iter_advance(&headers_iter, > + offsetof(struct fuse_uring_req_header, > + ring_ent_in_out)); > + > + /* copy the payload */ > + err = fuse_copy_args(&cs, num_args, args->in_pages, > + (struct fuse_arg *)in_args, 0); > + if (err) { > + pr_info_ratelimited("%s fuse_copy_args failed\n", __func__); > + return err; > + } > + > + ent_in_out.payload_sz = cs.ring.copied_sz; > + copied = copy_to_iter(&ent_in_out, sizeof(ent_in_out), &headers_iter); > + if (copied != sizeof(ent_in_out)) > + return -EFAULT; > + > + return 0; > +} > + > /* > * Copy data from the req to the ring buffer > */ > @@ -618,7 +722,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, > .commit_id = req->in.h.unique, > }; > > - err = import_ubuf(ITER_DEST, ent->payload, ring->max_payload_sz, &iter); > + err = import_ubuf(ITER_DEST, ent->user.payload, ring->max_payload_sz, &iter); > if (err) { > pr_info_ratelimited("fuse: Import of user buffer failed\n"); > return err; > @@ -634,7 +738,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, > * Some op code have that as zero size. > */ > if (args->in_args[0].size > 0) { > - err = copy_to_user(&ent->headers->op_in, in_args->value, > + err = copy_to_user(&ent->user.headers->op_in, in_args->value, > in_args->size); > if (err) { > pr_info_ratelimited( > @@ -655,7 +759,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, > } > > ent_in_out.payload_sz = cs.ring.copied_sz; > - err = copy_to_user(&ent->headers->ring_ent_in_out, &ent_in_out, > + err = copy_to_user(&ent->user.headers->ring_ent_in_out, &ent_in_out, > sizeof(ent_in_out)); > return err ? -EFAULT : 0; > } > @@ -679,18 +783,31 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, > return err; > > /* copy the request */ > - err = fuse_uring_args_to_ring(ring, req, ent); > + if (ent->is_fixed_buffer) > + err = fuse_uring_args_to_ring_fixed_buf(ring, req, ent); > + else > + err = fuse_uring_args_to_ring(ring, req, ent); > if (unlikely(err)) { > pr_info_ratelimited("Copy to ring failed: %d\n", err); > return err; > } > > /* copy fuse_in_header */ > - err = copy_to_user(&ent->headers->in_out, &req->in.h, > - sizeof(req->in.h)); > - if (err) { > - err = -EFAULT; > - return err; > + if (ent->is_fixed_buffer) { > + struct iov_iter headers_iter = ent->fixed_buffer.headers_iter; > + size_t copied; > + > + headers_iter.data_source = ITER_DEST; > + copied = copy_to_iter(&req->in.h, sizeof(req->in.h), > + &headers_iter); > + > + if (copied != sizeof(req->in.h)) > + return -EFAULT; > + } else { > + err = copy_to_user(&ent->user.headers->in_out, &req->in.h, > + sizeof(req->in.h)); > + if (err) > + return -EFAULT; > } > > return 0; > @@ -815,8 +932,18 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, > struct fuse_conn *fc = ring->fc; > ssize_t err = 0; > > - err = copy_from_user(&req->out.h, &ent->headers->in_out, > - sizeof(req->out.h)); > + if (ent->is_fixed_buffer) { > + struct iov_iter headers_iter = ent->fixed_buffer.headers_iter; > + size_t copied; > + > + headers_iter.data_source = ITER_SOURCE; > + copied = copy_from_iter(&req->out.h, sizeof(req->out.h), &headers_iter); > + if (copied != sizeof(req->out.h)) > + err = -EFAULT; > + } else { > + err = copy_from_user(&req->out.h, &ent->user.headers->in_out, > + sizeof(req->out.h)); > + } > if (err) { > req->out.h.error = -EFAULT; > goto out; > @@ -828,7 +955,11 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, > goto out; > } > > - err = fuse_uring_copy_from_ring(ring, req, ent); > + if (ent->is_fixed_buffer) > + err = fuse_uring_copy_from_ring_fixed_buf(ring, req, ent); > + else > + err = fuse_uring_copy_from_ring(ring, req, ent); > + > out: > fuse_uring_req_end(ent, req, err); > } > @@ -1027,6 +1158,52 @@ static int fuse_uring_get_iovec_from_sqe(const struct io_uring_sqe *sqe, > return 0; > } > > +static struct fuse_ring_ent * > +fuse_uring_create_ring_ent_fixed_buf(struct io_uring_cmd *cmd, > + struct fuse_ring_queue *queue) > +{ > + struct fuse_ring *ring = queue->ring; > + struct fuse_ring_ent *ent; > + unsigned payload_size, len; > + u64 ubuf; > + int err; > + > + err = -ENOMEM; > + ent = kzalloc(sizeof(*ent), GFP_KERNEL_ACCOUNT); > + if (!ent) > + return ERR_PTR(err); > + > + INIT_LIST_HEAD(&ent->list); > + > + ent->queue = queue; > + ent->is_fixed_buffer = true; > + > + err = io_uring_cmd_get_buffer_info(cmd, &ubuf, &len); > + if (err) > + goto error; > + > + payload_size = len - sizeof(struct fuse_uring_req_header); > + err = io_uring_cmd_import_fixed(ubuf, payload_size, ITER_DEST, > + &ent->fixed_buffer.payload_iter, cmd, 0); It feels a bit awkward to look up the userspace address from the registered buffer node just to pass it to io_uring_cmd_import_fixed(), which will subtract it off. Not to mention, there's a race here in the IO_URING_F_UNLOCKED case where the registered buffer can be updated between the io_uring_cmd_get_buffer_info() and io_uring_cmd_import_fixed() calls, so ubuf could be stale. Maybe it would make sense to introduce a helper for importing an iterator relative to the start of a registered buffer instead of using an absolute address? (Named "io_uring_cmd_import_fixed_relative()" or something?) Then we could get rid of io_uring_cmd_get_buffer_info() entirely. Best, Caleb > + if (err) > + goto error; > + > + err = io_uring_cmd_import_fixed(ubuf + payload_size, > + sizeof(struct fuse_uring_req_header), > + ITER_DEST, > + &ent->fixed_buffer.headers_iter, cmd, 0); > + if (err) > + goto error; > + > + atomic_inc(&ring->queue_refs); > + > + return ent; > + > +error: > + kfree(ent); > + return ERR_PTR(err); > +} > + > static struct fuse_ring_ent * > fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, > struct fuse_ring_queue *queue) > @@ -1065,8 +1242,8 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, > INIT_LIST_HEAD(&ent->list); > > ent->queue = queue; > - ent->headers = iov[0].iov_base; > - ent->payload = iov[1].iov_base; > + ent->user.headers = iov[0].iov_base; > + ent->user.payload = iov[1].iov_base; > > atomic_inc(&ring->queue_refs); > return ent; > @@ -1085,6 +1262,8 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, > struct fuse_ring_ent *ent; > int err; > unsigned int qid = READ_ONCE(cmd_req->qid); > + bool is_fixed_buffer = > + cmd->sqe->uring_cmd_flags & IORING_URING_CMD_FIXED; > > err = -ENOMEM; > if (!ring) { > @@ -1110,7 +1289,10 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, > * case of entry errors below, will be done at ring destruction time. > */ > > - ent = fuse_uring_create_ring_ent(cmd, queue); > + if (is_fixed_buffer) > + ent = fuse_uring_create_ring_ent_fixed_buf(cmd, queue); > + else > + ent = fuse_uring_create_ring_ent(cmd, queue); > if (IS_ERR(ent)) > return PTR_ERR(ent); > > diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h > index 51a563922ce1..748c87e325f5 100644 > --- a/fs/fuse/dev_uring_i.h > +++ b/fs/fuse/dev_uring_i.h > @@ -38,9 +38,20 @@ enum fuse_ring_req_state { > > /** A fuse ring entry, part of the ring queue */ > struct fuse_ring_ent { > - /* userspace buffer */ > - struct fuse_uring_req_header __user *headers; > - void __user *payload; > + /* True if daemon has registered its buffers ahead of time */ > + bool is_fixed_buffer; > + union { > + /* userspace buffer */ > + struct { > + struct fuse_uring_req_header __user *headers; > + void __user *payload; > + } user; > + > + struct { > + struct iov_iter payload_iter; > + struct iov_iter headers_iter; > + } fixed_buffer; > + }; > > /* the ring queue that owns the request */ > struct fuse_ring_queue *queue; > -- > 2.47.3 > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 2/2] fuse: support io-uring registered buffers 2025-10-22 20:20 ` [PATCH v1 2/2] fuse: support io-uring registered buffers Joanne Koong 2025-10-23 3:22 ` Caleb Sander Mateos @ 2025-10-23 12:39 ` kernel test robot 2025-10-23 13:22 ` kernel test robot 2025-10-23 23:44 ` Joanne Koong 3 siblings, 0 replies; 17+ messages in thread From: kernel test robot @ 2025-10-23 12:39 UTC (permalink / raw) To: Joanne Koong, miklos, axboe Cc: oe-kbuild-all, linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li Hi Joanne, kernel test robot noticed the following build errors: [auto build test ERROR on mszeredi-fuse/for-next] [also build test ERROR on linus/master v6.18-rc2 next-20251023] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/io-uring-add-io_uring_cmd_get_buffer_info/20251023-042601 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next patch link: https://lore.kernel.org/r/20251022202021.3649586-3-joannelkoong%40gmail.com patch subject: [PATCH v1 2/2] fuse: support io-uring registered buffers config: i386-buildonly-randconfig-003-20251023 (https://download.01.org/0day-ci/archive/20251023/202510232014.8BtM55jj-lkp@intel.com/config) compiler: gcc-14 (Debian 14.2.0-19) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251023/202510232014.8BtM55jj-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202510232014.8BtM55jj-lkp@intel.com/ All errors (new ones prefixed by >>): In file included from fs/fuse/inode.c:11: >> fs/fuse/dev_uring_i.h:51:41: error: field 'payload_iter' has incomplete type 51 | struct iov_iter payload_iter; | ^~~~~~~~~~~~ >> fs/fuse/dev_uring_i.h:52:41: error: field 'headers_iter' has incomplete type 52 | struct iov_iter headers_iter; | ^~~~~~~~~~~~ vim +/payload_iter +51 fs/fuse/dev_uring_i.h 38 39 /** A fuse ring entry, part of the ring queue */ 40 struct fuse_ring_ent { 41 /* True if daemon has registered its buffers ahead of time */ 42 bool is_fixed_buffer; 43 union { 44 /* userspace buffer */ 45 struct { 46 struct fuse_uring_req_header __user *headers; 47 void __user *payload; 48 } user; 49 50 struct { > 51 struct iov_iter payload_iter; > 52 struct iov_iter headers_iter; 53 } fixed_buffer; 54 }; 55 56 /* the ring queue that owns the request */ 57 struct fuse_ring_queue *queue; 58 59 /* fields below are protected by queue->lock */ 60 61 struct io_uring_cmd *cmd; 62 63 struct list_head list; 64 65 enum fuse_ring_req_state state; 66 67 struct fuse_req *fuse_req; 68 }; 69 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 2/2] fuse: support io-uring registered buffers 2025-10-22 20:20 ` [PATCH v1 2/2] fuse: support io-uring registered buffers Joanne Koong 2025-10-23 3:22 ` Caleb Sander Mateos 2025-10-23 12:39 ` kernel test robot @ 2025-10-23 13:22 ` kernel test robot 2025-10-23 17:42 ` Joanne Koong 2025-10-23 23:44 ` Joanne Koong 3 siblings, 1 reply; 17+ messages in thread From: kernel test robot @ 2025-10-23 13:22 UTC (permalink / raw) To: Joanne Koong, miklos, axboe Cc: llvm, oe-kbuild-all, linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li Hi Joanne, kernel test robot noticed the following build errors: [auto build test ERROR on mszeredi-fuse/for-next] [also build test ERROR on linus/master v6.18-rc2 next-20251023] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/io-uring-add-io_uring_cmd_get_buffer_info/20251023-042601 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next patch link: https://lore.kernel.org/r/20251022202021.3649586-3-joannelkoong%40gmail.com patch subject: [PATCH v1 2/2] fuse: support io-uring registered buffers config: i386-buildonly-randconfig-002-20251023 (https://download.01.org/0day-ci/archive/20251023/202510232038.LOpSOOQa-lkp@intel.com/config) compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251023/202510232038.LOpSOOQa-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202510232038.LOpSOOQa-lkp@intel.com/ All errors (new ones prefixed by >>): In file included from fs/fuse/dev.c:9: >> fs/fuse/dev_uring_i.h:51:20: error: field has incomplete type 'struct iov_iter' 51 | struct iov_iter payload_iter; | ^ include/linux/fs.h:74:8: note: forward declaration of 'struct iov_iter' 74 | struct iov_iter; | ^ In file included from fs/fuse/dev.c:9: fs/fuse/dev_uring_i.h:52:20: error: field has incomplete type 'struct iov_iter' 52 | struct iov_iter headers_iter; | ^ include/linux/fs.h:74:8: note: forward declaration of 'struct iov_iter' 74 | struct iov_iter; | ^ 2 errors generated. -- In file included from fs/fuse/dev_uring.c:8: >> fs/fuse/dev_uring_i.h:51:20: error: field has incomplete type 'struct iov_iter' 51 | struct iov_iter payload_iter; | ^ include/linux/fs.h:74:8: note: forward declaration of 'struct iov_iter' 74 | struct iov_iter; | ^ In file included from fs/fuse/dev_uring.c:8: fs/fuse/dev_uring_i.h:52:20: error: field has incomplete type 'struct iov_iter' 52 | struct iov_iter headers_iter; | ^ include/linux/fs.h:74:8: note: forward declaration of 'struct iov_iter' 74 | struct iov_iter; | ^ >> fs/fuse/dev_uring.c:588:22: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 588 | payload_iter = ent->fixed_buffer.payload_iter; | ~~~ ^ fs/fuse/dev_uring.c:590:22: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 590 | headers_iter = ent->fixed_buffer.headers_iter; | ~~~ ^ >> fs/fuse/dev_uring.c:618:43: error: no member named 'user' in 'struct fuse_ring_ent' 618 | err = copy_from_user(&ring_in_out, &ent->user.headers->ring_ent_in_out, | ~~~ ^ fs/fuse/dev_uring.c:623:38: error: no member named 'user' in 'struct fuse_ring_ent' 623 | err = import_ubuf(ITER_SOURCE, ent->user.payload, ring->max_payload_sz, | ~~~ ^ fs/fuse/dev_uring.c:653:22: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 653 | payload_iter = ent->fixed_buffer.payload_iter; | ~~~ ^ fs/fuse/dev_uring.c:656:22: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 656 | headers_iter = ent->fixed_buffer.headers_iter; | ~~~ ^ fs/fuse/dev_uring.c:725:36: error: no member named 'user' in 'struct fuse_ring_ent' 725 | err = import_ubuf(ITER_DEST, ent->user.payload, ring->max_payload_sz, &iter); | ~~~ ^ fs/fuse/dev_uring.c:741:29: error: no member named 'user' in 'struct fuse_ring_ent' 741 | err = copy_to_user(&ent->user.headers->op_in, in_args->value, | ~~~ ^ fs/fuse/dev_uring.c:762:27: error: no member named 'user' in 'struct fuse_ring_ent' 762 | err = copy_to_user(&ent->user.headers->ring_ent_in_out, &ent_in_out, | ~~~ ^ fs/fuse/dev_uring.c:797:39: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 797 | struct iov_iter headers_iter = ent->fixed_buffer.headers_iter; | ~~~ ^ fs/fuse/dev_uring.c:807:28: error: no member named 'user' in 'struct fuse_ring_ent' 807 | err = copy_to_user(&ent->user.headers->in_out, &req->in.h, | ~~~ ^ fs/fuse/dev_uring.c:936:39: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 936 | struct iov_iter headers_iter = ent->fixed_buffer.headers_iter; | ~~~ ^ fs/fuse/dev_uring.c:944:43: error: no member named 'user' in 'struct fuse_ring_ent' 944 | err = copy_from_user(&req->out.h, &ent->user.headers->in_out, | ~~~ ^ fs/fuse/dev_uring.c:1187:12: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 1187 | &ent->fixed_buffer.payload_iter, cmd, 0); | ~~~ ^ fs/fuse/dev_uring.c:1194:12: error: no member named 'fixed_buffer' in 'struct fuse_ring_ent' 1194 | &ent->fixed_buffer.headers_iter, cmd, 0); | ~~~ ^ fs/fuse/dev_uring.c:1245:7: error: no member named 'user' in 'struct fuse_ring_ent' 1245 | ent->user.headers = iov[0].iov_base; | ~~~ ^ fs/fuse/dev_uring.c:1246:7: error: no member named 'user' in 'struct fuse_ring_ent' 1246 | ent->user.payload = iov[1].iov_base; | ~~~ ^ 19 errors generated. vim +51 fs/fuse/dev_uring_i.h 38 39 /** A fuse ring entry, part of the ring queue */ 40 struct fuse_ring_ent { 41 /* True if daemon has registered its buffers ahead of time */ 42 bool is_fixed_buffer; 43 union { 44 /* userspace buffer */ 45 struct { 46 struct fuse_uring_req_header __user *headers; 47 void __user *payload; 48 } user; 49 50 struct { > 51 struct iov_iter payload_iter; 52 struct iov_iter headers_iter; 53 } fixed_buffer; 54 }; 55 56 /* the ring queue that owns the request */ 57 struct fuse_ring_queue *queue; 58 59 /* fields below are protected by queue->lock */ 60 61 struct io_uring_cmd *cmd; 62 63 struct list_head list; 64 65 enum fuse_ring_req_state state; 66 67 struct fuse_req *fuse_req; 68 }; 69 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 2/2] fuse: support io-uring registered buffers 2025-10-23 13:22 ` kernel test robot @ 2025-10-23 17:42 ` Joanne Koong 0 siblings, 0 replies; 17+ messages in thread From: Joanne Koong @ 2025-10-23 17:42 UTC (permalink / raw) To: kernel test robot Cc: miklos, axboe, llvm, oe-kbuild-all, linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li On Thu, Oct 23, 2025 at 6:23 AM kernel test robot <lkp@intel.com> wrote: > > Hi Joanne, > > kernel test robot noticed the following build errors: > > [auto build test ERROR on mszeredi-fuse/for-next] > [also build test ERROR on linus/master v6.18-rc2 next-20251023] > [If your patch is applied to the wrong git tree, kindly drop us a note. > And when submitting patch, we suggest to use '--base' as documented in > https://git-scm.com/docs/git-format-patch#_base_tree_information] > > url: https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/io-uring-add-io_uring_cmd_get_buffer_info/20251023-042601 > base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next > patch link: https://lore.kernel.org/r/20251022202021.3649586-3-joannelkoong%40gmail.com > patch subject: [PATCH v1 2/2] fuse: support io-uring registered buffers > config: i386-buildonly-randconfig-002-20251023 (https://download.01.org/0day-ci/archive/20251023/202510232038.LOpSOOQa-lkp@intel.com/config) > compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261) > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251023/202510232038.LOpSOOQa-lkp@intel.com/reproduce) > > If you fix the issue in a separate patch/commit (i.e. not just a new version of > the same patch/commit), kindly add following tags > | Reported-by: kernel test robot <lkp@intel.com> > | Closes: https://lore.kernel.org/oe-kbuild-all/202510232038.LOpSOOQa-lkp@intel.com/ > > All errors (new ones prefixed by >>): > > In file included from fs/fuse/dev.c:9: > >> fs/fuse/dev_uring_i.h:51:20: error: field has incomplete type 'struct iov_iter' > 51 | struct iov_iter payload_iter; > | ^ > include/linux/fs.h:74:8: note: forward declaration of 'struct iov_iter' > 74 | struct iov_iter; > | ^ > In file included from fs/fuse/dev.c:9: > fs/fuse/dev_uring_i.h:52:20: error: field has incomplete type 'struct iov_iter' > 52 | struct iov_iter headers_iter; Ahh okay, I tried building with the linked config (i386-buildonly-randconfig-002-20251023) and it indeed fails :( It needs a "#include <linux/uio.h>" added to the dev_uring_i.h file. I'll add that in for v2. Thanks, Joanne ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 2/2] fuse: support io-uring registered buffers 2025-10-22 20:20 ` [PATCH v1 2/2] fuse: support io-uring registered buffers Joanne Koong ` (2 preceding siblings ...) 2025-10-23 13:22 ` kernel test robot @ 2025-10-23 23:44 ` Joanne Koong 3 siblings, 0 replies; 17+ messages in thread From: Joanne Koong @ 2025-10-23 23:44 UTC (permalink / raw) To: miklos, axboe Cc: linux-fsdevel, bschubert, asml.silence, io-uring, xiaobing.li > diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h > index 51a563922ce1..748c87e325f5 100644 > --- a/fs/fuse/dev_uring_i.h > +++ b/fs/fuse/dev_uring_i.h > @@ -38,9 +38,20 @@ enum fuse_ring_req_state { > > /** A fuse ring entry, part of the ring queue */ > struct fuse_ring_ent { > - /* userspace buffer */ > - struct fuse_uring_req_header __user *headers; > - void __user *payload; > + /* True if daemon has registered its buffers ahead of time */ > + bool is_fixed_buffer; > + union { > + /* userspace buffer */ > + struct { > + struct fuse_uring_req_header __user *headers; > + void __user *payload; > + } user; > + > + struct { > + struct iov_iter payload_iter; > + struct iov_iter headers_iter; > + } fixed_buffer; > + }; The iters need to be reconstructed instead of recycling the same one since the buffer could be unregistered by userspace even while the server is running, even if libfuse doesn't do that. I'll account for this in v2. Thanks, Joanne > > /* the ring queue that owns the request */ > struct fuse_ring_queue *queue; > -- > 2.47.3 > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 0/2] fuse io_uring: support registered buffers 2025-10-22 20:20 [PATCH v1 0/2] fuse io_uring: support registered buffers Joanne Koong 2025-10-22 20:20 ` [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() Joanne Koong 2025-10-22 20:20 ` [PATCH v1 2/2] fuse: support io-uring registered buffers Joanne Koong @ 2025-10-22 20:43 ` Bernd Schubert 2025-10-23 22:27 ` Joanne Koong [not found] ` <CGME20251023114956epcas5p33a9384d06985dc5936fd355f1d580fb2@epcas5p3.samsung.com> 3 siblings, 1 reply; 17+ messages in thread From: Bernd Schubert @ 2025-10-22 20:43 UTC (permalink / raw) To: Joanne Koong, miklos, axboe Cc: linux-fsdevel, asml.silence, io-uring, xiaobing.li On 10/22/25 22:20, Joanne Koong wrote: > This adds support for daemons who preregister buffers to minimize the overhead > of pinning/unpinning user pages and translating virtual addresses. Registered > buffers pay the cost once during registration then reuse the pre-pinned pages, > which helps reduce the per-op overhead. > > This is on top of commit 211ddde0823f in the iouring tree. Interesting, on a first glance this looks like an alternative implementation of page pinning https://lore.kernel.org/all/20240901-b4-fuse-uring-rfcv3-without-mmap-v3-17-9207f7391444@ddn.com/ At DDN we are running with that patch (changed commit message) and another one that avoids io_uring_cmd_complete_in_task() - with pinned pages the IO submitting application can directly write into header and payload (note that the latter also required pinned headers) Going to look into your approach tomorrow. Thanks, Bernd ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 0/2] fuse io_uring: support registered buffers 2025-10-22 20:43 ` [PATCH v1 0/2] fuse io_uring: support " Bernd Schubert @ 2025-10-23 22:27 ` Joanne Koong 2025-10-24 18:12 ` Bernd Schubert 0 siblings, 1 reply; 17+ messages in thread From: Joanne Koong @ 2025-10-23 22:27 UTC (permalink / raw) To: Bernd Schubert Cc: miklos, axboe, linux-fsdevel, asml.silence, io-uring, xiaobing.li On Wed, Oct 22, 2025 at 1:43 PM Bernd Schubert <bschubert@ddn.com> wrote: > > On 10/22/25 22:20, Joanne Koong wrote: > > This adds support for daemons who preregister buffers to minimize the overhead > > of pinning/unpinning user pages and translating virtual addresses. Registered > > buffers pay the cost once during registration then reuse the pre-pinned pages, > > which helps reduce the per-op overhead. > > > > This is on top of commit 211ddde0823f in the iouring tree. > > Interesting, on a first glance this looks like an alternative > implementation of page pinning > > https://lore.kernel.org/all/20240901-b4-fuse-uring-rfcv3-without-mmap-v3-17-9207f7391444@ddn.com/ > > At DDN we are running with that patch (changed commit message) and another > one that avoids io_uring_cmd_complete_in_task() - with pinned pages > the IO submitting application can directly write into header and payload > (note that the latter also required pinned headers) > > Going to look into your approach tomorrow. Thanks for taking a look when you get the chance. The libfuse changes are in this branch https://github.com/joannekoong/libfuse/tree/registered_buffers btw. Thanks, Joanne > > > > Thanks, > Bernd ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 0/2] fuse io_uring: support registered buffers 2025-10-23 22:27 ` Joanne Koong @ 2025-10-24 18:12 ` Bernd Schubert 2025-10-24 19:37 ` Joanne Koong 0 siblings, 1 reply; 17+ messages in thread From: Bernd Schubert @ 2025-10-24 18:12 UTC (permalink / raw) To: Joanne Koong Cc: miklos, axboe, linux-fsdevel, asml.silence, io-uring, xiaobing.li On 10/24/25 00:27, Joanne Koong wrote: > On Wed, Oct 22, 2025 at 1:43 PM Bernd Schubert <bschubert@ddn.com> wrote: >> >> On 10/22/25 22:20, Joanne Koong wrote: >>> This adds support for daemons who preregister buffers to minimize the overhead >>> of pinning/unpinning user pages and translating virtual addresses. Registered >>> buffers pay the cost once during registration then reuse the pre-pinned pages, >>> which helps reduce the per-op overhead. >>> >>> This is on top of commit 211ddde0823f in the iouring tree. >> >> Interesting, on a first glance this looks like an alternative >> implementation of page pinning >> >> https://lore.kernel.org/all/20240901-b4-fuse-uring-rfcv3-without-mmap-v3-17-9207f7391444@ddn.com/ >> >> At DDN we are running with that patch (changed commit message) and another >> one that avoids io_uring_cmd_complete_in_task() - with pinned pages >> the IO submitting application can directly write into header and payload >> (note that the latter also required pinned headers) >> >> Going to look into your approach tomorrow. > > Thanks for taking a look when you get the chance. The libfuse changes > are in this branch > https://github.com/joannekoong/libfuse/tree/registered_buffers btw. Sorry, still didn't manage another task for tomorrow. Btw, the reason hadn't sent my patches is that I hadn't handled memory accounting yet. And then got busy with other tasks for much too long... I see in io_sqe_buffers_register() how that is done, although I'm confused why it first calls io_pin_pages() and only then accounts. I.e. it can temporarily go above the limit - I wonder what happens if the user opens another application that time that just needs a little locked memory... In general I think your solution more complex than mine - I think I'm going to update my patches (there are conflicts due to folio conversion) and then we can compare. Thanks, Bernd ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 0/2] fuse io_uring: support registered buffers 2025-10-24 18:12 ` Bernd Schubert @ 2025-10-24 19:37 ` Joanne Koong 0 siblings, 0 replies; 17+ messages in thread From: Joanne Koong @ 2025-10-24 19:37 UTC (permalink / raw) To: Bernd Schubert Cc: miklos, axboe, linux-fsdevel, asml.silence, io-uring, xiaobing.li On Fri, Oct 24, 2025 at 11:12 AM Bernd Schubert <bschubert@ddn.com> wrote: > > On 10/24/25 00:27, Joanne Koong wrote: > > On Wed, Oct 22, 2025 at 1:43 PM Bernd Schubert <bschubert@ddn.com> wrote: > >> > >> On 10/22/25 22:20, Joanne Koong wrote: > >>> This adds support for daemons who preregister buffers to minimize the overhead > >>> of pinning/unpinning user pages and translating virtual addresses. Registered > >>> buffers pay the cost once during registration then reuse the pre-pinned pages, > >>> which helps reduce the per-op overhead. > >>> > >>> This is on top of commit 211ddde0823f in the iouring tree. > >> > >> Interesting, on a first glance this looks like an alternative > >> implementation of page pinning > >> > >> https://lore.kernel.org/all/20240901-b4-fuse-uring-rfcv3-without-mmap-v3-17-9207f7391444@ddn.com/ > >> > >> At DDN we are running with that patch (changed commit message) and another > >> one that avoids io_uring_cmd_complete_in_task() - with pinned pages > >> the IO submitting application can directly write into header and payload > >> (note that the latter also required pinned headers) > >> > >> Going to look into your approach tomorrow. > > > > Thanks for taking a look when you get the chance. The libfuse changes > > are in this branch > > https://github.com/joannekoong/libfuse/tree/registered_buffers btw. > > Sorry, still didn't manage another task for tomorrow. Btw, the reason > hadn't sent my patches is that I hadn't handled memory accounting yet. > And then got busy with other tasks for much too long... > > I see in io_sqe_buffers_register() how that is done, although I'm > confused why it first calls io_pin_pages() and only then accounts. I.e. > it can temporarily go above the limit - I wonder what happens if the > user opens another application that time that just needs a little locked > memory... > > In general I think your solution more complex than mine - I think I'm > going to update my patches (there are conflicts due to folio conversion) > and then we can compare. Hi Bernd, I think this solution is actually less complex because all the pinning and accounting stuff is taken care of by io-uring and there's one pinned buffer instead of pinning the header and payload separately. I think the part that looks complex maybe is the integration of the bvec iter with header copying. For v2 I have it vastly simplified so that we just kmap the header, which makes it play more nicely with directly accessing the header struct members instead of going through the bvec iter. Thanks, Joanne > > > Thanks, > Bernd ^ permalink raw reply [flat|nested] 17+ messages in thread
[parent not found: <CGME20251023114956epcas5p33a9384d06985dc5936fd355f1d580fb2@epcas5p3.samsung.com>]
* Re: [PATCH v1 0/2] fuse io_uring: support registered buffers [not found] ` <CGME20251023114956epcas5p33a9384d06985dc5936fd355f1d580fb2@epcas5p3.samsung.com> @ 2025-10-23 11:45 ` Xiaobing Li 2025-10-24 18:02 ` Joanne Koong 0 siblings, 1 reply; 17+ messages in thread From: Xiaobing Li @ 2025-10-23 11:45 UTC (permalink / raw) To: joannelkoong Cc: asml.silence, axboe, bschubert, io-uring, linux-fsdevel, miklos, joshi.k, kun.dou, peiwei.li, xue01.he On 10/22/25 22:20, Joanne Koong wrote: > This adds support for daemons who preregister buffers to minimize the overhead > of pinning/unpinning user pages and translating virtual addresses. Registered > buffers pay the cost once during registration then reuse the pre-pinned pages, > which helps reduce the per-op overhead. > > This is on top of commit 211ddde0823f in the iouring tree. Do you have any test data? How does the benefit compare? By the way, how were the changes made to libfuse? Thanks, Xiaobing Li ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v1 0/2] fuse io_uring: support registered buffers 2025-10-23 11:45 ` Xiaobing Li @ 2025-10-24 18:02 ` Joanne Koong 0 siblings, 0 replies; 17+ messages in thread From: Joanne Koong @ 2025-10-24 18:02 UTC (permalink / raw) To: Xiaobing Li Cc: asml.silence, axboe, bschubert, io-uring, linux-fsdevel, miklos, joshi.k, kun.dou, peiwei.li, xue01.he On Thu, Oct 23, 2025 at 11:17 PM Xiaobing Li <xiaobing.li@samsung.com> wrote: > > On 10/22/25 22:20, Joanne Koong wrote: > > This adds support for daemons who preregister buffers to minimize the overhead > > of pinning/unpinning user pages and translating virtual addresses. Registered > > buffers pay the cost once during registration then reuse the pre-pinned pages, > > which helps reduce the per-op overhead. > > > > This is on top of commit 211ddde0823f in the iouring tree. > > Do you have any test data? How does the benefit compare? > By the way, how were the changes made to libfuse? Hi Xiaobing, I am going to run more rigorous benchmarks on this next week after making the changes for v2 and will report back what I see. The libfuse-side changes are in this branch [1]. Thanks, Joanne [1] https://github.com/joannekoong/libfuse/tree/registered_buffers > > Thanks, > Xiaobing Li ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2025-10-24 19:37 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-22 20:20 [PATCH v1 0/2] fuse io_uring: support registered buffers Joanne Koong
2025-10-22 20:20 ` [PATCH v1 1/2] io-uring: add io_uring_cmd_get_buffer_info() Joanne Koong
2025-10-23 3:16 ` Caleb Sander Mateos
2025-10-23 22:20 ` Joanne Koong
2025-10-24 0:00 ` Joanne Koong
2025-10-22 20:20 ` [PATCH v1 2/2] fuse: support io-uring registered buffers Joanne Koong
2025-10-23 3:22 ` Caleb Sander Mateos
2025-10-23 12:39 ` kernel test robot
2025-10-23 13:22 ` kernel test robot
2025-10-23 17:42 ` Joanne Koong
2025-10-23 23:44 ` Joanne Koong
2025-10-22 20:43 ` [PATCH v1 0/2] fuse io_uring: support " Bernd Schubert
2025-10-23 22:27 ` Joanne Koong
2025-10-24 18:12 ` Bernd Schubert
2025-10-24 19:37 ` Joanne Koong
[not found] ` <CGME20251023114956epcas5p33a9384d06985dc5936fd355f1d580fb2@epcas5p3.samsung.com>
2025-10-23 11:45 ` Xiaobing Li
2025-10-24 18:02 ` Joanne Koong
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox