* [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
* [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 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 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 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 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 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 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 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 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 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
* 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
* 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
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