<feed xmlns='http://www.w3.org/2005/Atom'>
<title>kernel/linux.git/include/uapi/linux/fuse.h, branch v7.3-rc1</title>
<subtitle>Linux kernel stable tree (mirror)</subtitle>
<id>https://git.radix-linux.su/kernel/linux.git/atom?h=v7.3-rc1</id>
<link rel='self' href='https://git.radix-linux.su/kernel/linux.git/atom?h=v7.3-rc1'/>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/'/>
<updated>2026-08-17T15:02:37+00:00</updated>
<entry>
<title>fuse: add zero-copy over io-uring</title>
<updated>2026-08-17T15:02:37+00:00</updated>
<author>
<name>Joanne Koong</name>
<email>joannelkoong@gmail.com</email>
</author>
<published>2026-08-14T18:59:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=43f8343858eb942d7f7c49964b31c54dcc314890'/>
<id>urn:sha1:43f8343858eb942d7f7c49964b31c54dcc314890</id>
<content type='text'>
Implement zero-copy in fuse io-uring to eliminate memory copies between
the application, kernel, and server for read/write operations. The
server can directly access client pages or page cache folios without
copying data through an intermediary buffer. When a fuse request arrives,
the kernel registers the relevant pages into a sparse slot in the
server's io_uring registered buffer table. The server can then operate
on these pages directly using io-uring fixed buffer operations (eg
read_fixed/write_fixed) and the kernel unregisters these pages when the
request completes. Non-page-backed args (eg op out headers) will go
through the payload buffer as normal. The server can specify which open
files should have their reads/writes go through zero-copy, by setting
the FOPEN_IO_URING_ZERO_COPY flag when servicing opens.

This requires CAP_SYS_ADMIN and bufpools. This is gated behind
CAP_SYS_ADMIN because zero-copy allows the server direct access to the
client's underlying pages, rather than operating on an intermediary
buffer that the contents of the client's pages were copied into or on
page cache folios.

The request flow for the zero-copy direct-io write path (client writes
data, server reads it) is as follows:
=======================================================================
|  Kernel                                   |  FUSE server
|                                           |
|  "write(fd, buf, 1MB)"                    |
|                                           |
|  &gt;sys_write()                             |
|    &gt;fuse_file_write_iter()                |
|      &gt;fuse_send_one()                     |
|        [req-&gt;args-&gt;in_pages = true]       |
|        [folios hold client write data]    |
|                                           |
|  &gt;fuse_uring_copy_to_ring()               |
|    &gt;copy_header_to_ring(IN_OUT)           |
|      [memcpy fuse_in_header]              |
|    &gt;copy_header_to_ring(OP)               |
|      [memcpy write_in header]             |
|                                           |
|    &gt;fuse_uring_args_to_ring()             |
|      &gt;setup_fuse_copy_state()             |
|        [skip_folio_copy = true]           |
|                                           |
|      &gt;fuse_uring_set_up_zero_copy()       |
|        [folio_get for each client folio]  |
|        [build bio_vec array from folios]  |
|        &gt;io_buffer_register_bvec()         |
|          [register pages at
                 ent-&gt;zero_copy_index]      |
|        [ent-&gt;zero_copied = true]          |
|                                           |
|      &gt;fuse_copy_args()                    |
|        [skip_folio_copy =&gt; return 0       |
|         for page arg, skip data copy]     |
|                                           |
|    &gt;copy_header_to_ring(RING_ENT)         |
|      [memcpy ent_in_out]                  |
|    &gt;io_uring_cmd_done()                   |
|                                           |
|                                           | [CQE received]
|                                           |
|                                           | [issue io_uring READ at
|                                           |  ent-&gt;zero_copy_index]
|                                           | [reads directly from
|                                           |client's pages (ZERO_COPY)]
|                                           |
|                                           | [write data to backing
|                                           | store]
|                                           |  [submit COMMIT AND FETCH]
|                                           |
|  &gt;fuse_uring_commit_fetch()               |
|    &gt;fuse_uring_commit()                   |
|      &gt;fuse_uring_copy_from_ring()         |
|    &gt;fuse_uring_req_end()                  |
|      &gt;io_buffer_unregister(ent-&gt;zero_copy_index) |
|        [unregister pages from index]      |
|      &gt;fuse_zero_copy_release()            |
|        [folio_put for each folio]         |
|      [ent-&gt;zero_copied = false]           |
|      &gt;fuse_request_end()                  |
|        [wake up client]                   |

The zero-copy read path is analogous.

Some requests may have both page-backed args and non-page-backed args.
For these requests, the page-backed args are zero-copied while the
non-page-backed args are copied to the buffer selected from the buffer
pool:
    zero-copy: pages registered via io_buffer_register_bvec()
    non-page-backed: copied to payload buffer via fuse_copy_args()

For a request whose payload is zero-copied, the
registration/unregistration path looks like:

    register:  fuse_uring_set_up_zero_copy()
                 folio_get() for each folio
                 io_buffer_register_bvec(ent-&gt;zero_copy_index)

    unregister: fuse_uring_req_end()
                  io_buffer_unregister(ent-&gt;zero_copy_index)
                  -&gt; fuse_zero_copy_release() callback
                     folio_put() for each folio

Please note that on abort for in-flight zero-copied requests that have
been sent to userspace, the registered bvec slot remains occupied and
its folios remain pinned until the io-uring ring is destroyed, at which
point io-uring unregisters all buffers and the fuse_zero_copy_release()
callback drops the folio references. Unregistering at teardown would
require operating on the ring context directly, whose validity is hard
to ascertain; this is deemed not worth the complexity for the abort
race, since everything is freed when the ring is torn down.

The throughput improvement from zero-copy depends on how much of the
per-request latency is spent on data copying vs backing I/O. The gain
comes from eliminating the payload-buffer memcpy,  but accessing the
zero-copied pages requires the server to issue the read/write as an
IORING_OP_READ/WRITE_FIXED operation. The benefit is largest when the
mempcy is a meaningful fraction of per-request latency while backing i/o
is still noticable enough that the extra io-uring op's overhead doesn't
dominate.

Benchmarked with passthrough_hp (--nopassthrough, q_depth=8) on a
2-socket Intel Xeon Gold 6138 (40 cores / 80 threads), using fio (sync
engine, bs=1M, O_DIRECT, numjobs=2, 30s run + 10s ramp, 3 runs) where
direct-I/O throughput is against a RAM-backed (tmpfs) source (backing
I/O is not the bottleneck):

		baseline   registered-buf   zero-copy   (zc vs base)
direct read     ~5.1 GB/s  ~5.4 GB/s        ~8.9 GB/s   (+75%)
direct write    ~3.4 GB/s  ~4.8 GB/s        ~5.1 GB/s   (+50%)

Reads end up higher than writes because the backing store reads faster
than it writes (the baseline shows the same read&gt;write gap, and the raw
device does too). On a device-bound NVMe (~2 GB/s reads) the read gain
shrinks to ~10-16% (and no measurable gains for writes), as backing I/O
rather than the eliminated copy dominates latency.

The benefit overall scales with how much of the
per-request latency is the data copy versus backing I/O.

Signed-off-by: Joanne Koong &lt;joannelkoong@gmail.com&gt;
Reviewed-by: Bernd Schubert &lt;bernd@bsbernd.com&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: add io-uring buffer pools</title>
<updated>2026-08-17T15:02:37+00:00</updated>
<author>
<name>Joanne Koong</name>
<email>joannelkoong@gmail.com</email>
</author>
<published>2026-08-14T18:59:43+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=b45aaabc628bc7356e21bd2eb0c2ae9bdfa13894'/>
<id>urn:sha1:b45aaabc628bc7356e21bd2eb0c2ae9bdfa13894</id>
<content type='text'>
Right now, ents and buffers are tightly coupled in fuse io-uring where
each entry has its own dedicated payload buffer, requiring N buffers for
N entries where each buffer must be large enough to accomodate the
maximum payload size. This is suboptimal as most request types (lookup,
open, release, getattr, etc) require vastly less bytes than the maximum
payload size and some requests (unlink, rmdir, fsync, flush, etc) do not
require payload buffers at all.

Instead of requiring a 1:1 coupling between ents and payload buffers,
allow the server to pass in a buffer pool (a contiguous chunk of memory)
that the kernel will use as it wishes for servicing ents/requests.
Entries only reserve a "buffer" from the pool while actively processing
a request that requires a payload buffer. This decoupling and letting
the kernel delegate memory from the pool for requests allows the kernel to
optimize memory usage and reduces the memory usage requirements needed
to use fuse-over-io-uring.

A pool is registered per queue with the new
FUSE_IO_URING_CMD_ADD_BUFPOOL command. The server passes the pool's base
address and length in fuse_uring_cmd_req.bufpool.{uaddr,len}.

Internally, the kernel splits the region into buffers of
ring-&gt;max_payload_sz bytes each (nr_bufs = pool len / max_payload_sz). A
queue commits to a payload mode on first use: registering an entry that
carries its own payload selects the legacy per-entry mode, while
ADD_BUFPOOL selects pool mode. The two are mutually exclusive, so
ADD_BUFPOOL must be issued before any payload-carrying entries are
registered on that queue. The queue must have been created before the
bufpool is added, through the FUSE_IO_URING_CMD_ADD_QUEUE command.

The kernel tracks free buffers with a bitmap (a set bit marks a free
buffer). On dispatch, a request that needs a payload claims a free
buffer (find_first_bit + clear). A request that needs none claims
nothing. The buffer's byte offset within the pool is reported to the
server in the new fuse_uring_ent_in_out.offset field so that the server
can locate the payload. On completion the buffer is returned to the pool
or reused directly if the next request on that entry also has a payload.

The FUSE_HAS_IO_URING_BUFPOOL flag advertises kernel support to the
server for bufpools.

Buffer pool request flow
~~~~~~~~~~~~~~~~~~~~~~~~
|  Kernel                                  |  FUSE daemon
|                                          |
|  [request arrives]                       |
|    [claim a free pool buffer]            |
|    &gt;fuse_uring_select_buffer()           |
|    [copy headers to ring]                |
|    [copy payload to buffer]              |
|    [report buffer offset in ent_in_out]  |
|    &gt;io_uring_cmd_done()                  |
|                                          |  [read headers]
|                                          |  [read/write payload at offset]
|                                          |  [process request]
|                                          |  &gt;io_uring_submit()
|                                          |   COMMIT_AND_FETCH
|  &gt;fuse_uring_commit_fetch()              |
|    [copy reply from ring]                |
|    [return buffer to the pool]           |
|    &gt;fuse_uring_recycle_buffer()          |

Reviewed-by: Bernd Schubert &lt;bernd@bsbernd.com&gt;
Signed-off-by: Joanne Koong &lt;joannelkoong@gmail.com&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: add FUSE_IO_URING_CMD_ADD_QUEUE</title>
<updated>2026-08-17T15:02:37+00:00</updated>
<author>
<name>Joanne Koong</name>
<email>joannelkoong@gmail.com</email>
</author>
<published>2026-08-14T18:59:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=ebed9ea5b469588c6074f3ed5b8d8ec63c4ccf48'/>
<id>urn:sha1:ebed9ea5b469588c6074f3ed5b8d8ec63c4ccf48</id>
<content type='text'>
fuse-over-io-uring queues are currently created lazily, as a side effect
of the first FUSE_IO_URING_CMD_REGISTER command for a given qid. This
ties queue creation to entry registration.

Add a FUSE_IO_URING_CMD_ADD_QUEUE command so a server can create a queue
explicitly, decoupling queue setup from entry registration. This is
additionally a prerequisite for FUSE_IO_URING_CMD_ADD_BUFPOOL, which
attaches a buffer pool to an existing queue and therefore needs the
queue to have been created first.

Reviewed-by: Bernd Schubert &lt;bernd@bsbernd.com&gt;
Signed-off-by: Joanne Koong &lt;joannelkoong@gmail.com&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: add prune notification</title>
<updated>2025-09-05T07:11:28+00:00</updated>
<author>
<name>Miklos Szeredi</name>
<email>mszeredi@redhat.com</email>
</author>
<published>2025-09-02T08:22:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=3f29d59e92a96d843c2ff10ebfed92ac26878658'/>
<id>urn:sha1:3f29d59e92a96d843c2ff10ebfed92ac26878658</id>
<content type='text'>
Some fuse servers need to prune their caches, which can only be done if the
kernel's own dentry/inode caches are pruned first to avoid dangling
references.

Add FUSE_NOTIFY_PRUNE, which takes an array of node ID's to try and get rid
of.  Inodes with active references are skipped.

A similar functionality is already provided by FUSE_NOTIFY_INVAL_ENTRY with
the FUSE_EXPIRE_ONLY flag.  Differences in the interface are

FUSE_NOTIFY_INVAL_ENTRY:

  - can only prune one dentry

  - dentry is determined by parent ID and name

  - if inode has multiple aliases (cached hard links), then they would have
    to be invalidated individually to be able to get rid of the inode

FUSE_NOTIFY_PRUNE:

  - can prune multiple inodes

  - inodes determined by their node ID

  - aliases are taken care of automatically

Reviewed-by: Joanne Koong &lt;joannelkoong@gmail.com&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: remove FUSE_NOTIFY_CODE_MAX from &lt;uapi/linux/fuse.h&gt;</title>
<updated>2025-09-05T07:10:44+00:00</updated>
<author>
<name>Miklos Szeredi</name>
<email>mszeredi@redhat.com</email>
</author>
<published>2025-08-26T15:08:19+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=0a0fdb98d16e334e259352893462030f15fb887f'/>
<id>urn:sha1:0a0fdb98d16e334e259352893462030f15fb887f</id>
<content type='text'>
Constants that change value from version to version have no place in an
interface definition.

Hopefully this won't break anything.

Reviewed-by: Joanne Koong &lt;joannelkoong@gmail.com&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: allow synchronous FUSE_INIT</title>
<updated>2025-09-02T09:14:15+00:00</updated>
<author>
<name>Miklos Szeredi</name>
<email>mszeredi@redhat.com</email>
</author>
<published>2025-08-22T11:10:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=dfb84c33079497bf27058b15780e1c7bba4c371b'/>
<id>urn:sha1:dfb84c33079497bf27058b15780e1c7bba4c371b</id>
<content type='text'>
FUSE_INIT has always been asynchronous with mount.  That means that the
server processed this request after the mount syscall returned.

This means that FUSE_INIT can't supply the root inode's ID, hence it
currently has a hardcoded value.  There are other limitations such as not
being able to perform getxattr during mount, which is needed by selinux.

To remove these limitations allow server to process FUSE_INIT while
initializing the in-core super block for the fuse filesystem.  This can
only be done if the server is prepared to handle this, so add
FUSE_DEV_IOC_SYNC_INIT ioctl, which

 a) lets the server know whether this feature is supported, returning
 ENOTTY othewrwise.

 b) lets the kernel know to perform a synchronous initialization

The implementation is slightly tricky, since fuse_dev/fuse_conn are set up
only during super block creation.  This is solved by setting the private
data of the fuse device file to a special value ((struct fuse_dev *) 1) and
waiting for this to be turned into a proper fuse_dev before commecing with
operations on the device file.

Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: add COPY_FILE_RANGE_64 that allows large copies</title>
<updated>2025-08-27T12:29:43+00:00</updated>
<author>
<name>Miklos Szeredi</name>
<email>mszeredi@redhat.com</email>
</author>
<published>2025-08-05T15:10:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=7a37f55af7af868119b4fb69285f5fa03ba8cf35'/>
<id>urn:sha1:7a37f55af7af868119b4fb69285f5fa03ba8cf35</id>
<content type='text'>
The FUSE protocol uses struct fuse_write_out to convey the return value of
copy_file_range, which is restricted to uint32_t.  But the COPY_FILE_RANGE
interface supports a 64-bit size copies and there's no reason why copies
should be limited to 32-bit.

Introduce a new op COPY_FILE_RANGE_64, which is identical, except the
number of bytes copied is returned in a 64-bit value.

If the fuse server does not support COPY_FILE_RANGE_64, fall back to
COPY_FILE_RANGE.

Reported-by: Florian Weimer &lt;fweimer@redhat.com&gt;
Closes: https://lore.kernel.org/all/lhuh5ynl8z5.fsf@oldenburg.str.redhat.com/
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: add more control over cache invalidation behaviour</title>
<updated>2025-04-15T10:56:40+00:00</updated>
<author>
<name>Luis Henriques</name>
<email>luis@igalia.com</email>
</author>
<published>2025-02-26T09:14:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=2396356a945bb022aff02656f59c2a45d457043f'/>
<id>urn:sha1:2396356a945bb022aff02656f59c2a45d457043f</id>
<content type='text'>
Currently userspace is able to notify the kernel to invalidate the cache
for an inode.  This means that, if all the inodes in a filesystem need to
be invalidated, then userspace needs to iterate through all of them and do
this kernel notification separately.

This patch adds the concept of 'epoch': each fuse connection will have the
current epoch initialized and every new dentry will have it's d_time set to
the current epoch value.  A new operation will then allow userspace to
increment the epoch value.  Every time a dentry is d_revalidate()'ed, it's
epoch is compared with the current connection epoch and invalidated if it's
value is different.

Signed-off-by: Luis Henriques &lt;luis@igalia.com&gt;
Tested-by: Laura Promberger &lt;laura.promberger@cern.ch&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: add kernel-enforced timeout option for requests</title>
<updated>2025-03-31T12:59:25+00:00</updated>
<author>
<name>Joanne Koong</name>
<email>joannelkoong@gmail.com</email>
</author>
<published>2025-01-22T21:55:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=0f6439f61a6e2ddc92b98362c6d1afc210f56a90'/>
<id>urn:sha1:0f6439f61a6e2ddc92b98362c6d1afc210f56a90</id>
<content type='text'>
There are situations where fuse servers can become unresponsive or
stuck, for example if the server is deadlocked. Currently, there's no
good way to detect if a server is stuck and needs to be killed manually.

This commit adds an option for enforcing a timeout (in seconds) for
requests where if the timeout elapses without the server responding to
the request, the connection will be automatically aborted.

Please note that these timeouts are not 100% precise. For example, the
request may take roughly an extra FUSE_TIMEOUT_TIMER_FREQ seconds beyond
the requested timeout due to internal implementation, in order to
mitigate overhead.

[SzM: Bump the API version number]

Signed-off-by: Joanne Koong &lt;joannelkoong@gmail.com&gt;
Reviewed-by: Jeff Layton &lt;jlayton@kernel.org&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
<entry>
<title>fuse: {io-uring} Handle SQEs - register commands</title>
<updated>2025-01-24T10:54:08+00:00</updated>
<author>
<name>Bernd Schubert</name>
<email>bschubert@ddn.com</email>
</author>
<published>2025-01-20T01:28:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.radix-linux.su/kernel/linux.git/commit/?id=24fe962c86f55347385933a1b06ca71b60854690'/>
<id>urn:sha1:24fe962c86f55347385933a1b06ca71b60854690</id>
<content type='text'>
This adds basic support for ring SQEs (with opcode=IORING_OP_URING_CMD).
For now only FUSE_IO_URING_CMD_REGISTER is handled to register queue
entries.

Signed-off-by: Bernd Schubert &lt;bschubert@ddn.com&gt;
Reviewed-by: Pavel Begunkov &lt;asml.silence@gmail.com&gt; # io_uring
Reviewed-by: Luis Henriques &lt;luis@igalia.com&gt;
Signed-off-by: Miklos Szeredi &lt;mszeredi@redhat.com&gt;
</content>
</entry>
</feed>
