summaryrefslogtreecommitdiff
path: root/fs/fuse/dev.c
AgeCommit message (Collapse)Author
2026-08-18fuse: wake one waiter per freed slot when raising max_backgroundBaokun Li
fuse_get_req() parks background allocations on fch->blocked_waitq via wait_event_state_exclusive(), so each wakeup releases exactly one waiter. fuse_chan_max_background_set() clears fch->blocked when the new limit exceeds num_background, but the accompanying wake_up() releases a single waiter regardless of how many slots just became available. Raising max_background from 10 to 100 therefore admits one request instead of ninety. The remaining waiters are not permanently stranded — the "else if (!fch->blocked)" branch in fuse_request_end() wakes one more per completion — but that only helps while requests keep completing. Consider a fixed pool of threads doing readahead or async direct I/O with the quota exhausted: every thread is either in flight or parked, and each completion wakes one waiter while freeing one slot, a net change of zero. num_background oscillates around the old limit and the added quota is never taken up. Waking one waiter per freed slot also preserves submission order: once fch->blocked is clear, new callers of fuse_get_req() skip the waitqueue entirely, overtaking waiters that parked before the limit was raised. Use wake_up_nr() with the number of slots that just became available. Since the wakeup is guarded by !fch->blocked, num_background is strictly below max_background, so the count is at least 1 and never degenerates into wake_up_all(). Signed-off-by: Baokun Li <libaokun@linux.alibaba.com> Reviewed-By: Horst Birthelmer <hbirthelmer@ddn.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-08-18fuse: give wakeup hints to the scheduler for synchronous requestsXuewen Yan
When a synchronous FUSE request is sent, the in-kernel client queues it on fiq->pending and wakes the userspace daemon sleeping in fuse_dev_do_read()->wait_event_interruptible_exclusive(fiq->waitq, ...). The client then blocks in request_wait_answer() waiting for the reply, so the waker is about to go to sleep: this is exactly the pattern that WF_SYNC is meant to optimise. As Peter Zijlstra explained in the earlier discussion [1], WF_SYNC is a hint that the waker is about to sleep and the waker and wakee share data, so stacking the woken thread on the current CPU is beneficial for cache locality instead of searching for an idle one. Add a wake_up_sync() wrapper for task on the synchronous request path. Performance: On an Android big.LITTLE device where the FUSE daemon (MediaProvider) runs as a background service on the little cores while foreground applications run on the big cores, the synchronous wakeup hint lets the scheduler pull the daemon thread onto the big core that is issuing the request, where the request data is cache-hot. Measured by qixiaoyu [2] on a 2000-picture zip decompression to /sdcard: ------------------------------------------ | Default | patched | Improvement | ------------------------------------------ | 13.0 s | 7.0 s | 46% | ------------------------------------------ Server thread wall duration: 3583 ms -> 1276 ms Server runs on big core: 5% -> 79% The original 4K-file copy/compress/decompress workload [1] on the same kind of device showed a ~28% improvement (13.8s -> 9.9s). Note: Miklos reported [2] that on his test box he could not observe an actual migration from wake_up_interruptible_sync(); the benefit appears to be most visible on asymmetric topologies (big.LITTLE, where the daemon normally lives on a little core) and on workloads dominated by small synchronous requests. No regression was reported on the symmetric- SMP test setups tried. The earlier version of this change [1] added a `bool sync` argument to all three hooks of `struct fuse_iqueue_ops` and threaded it through virtio_fs as well. Miklos questioned the interface churn, and the patch has been stalled since. Re-work it so the exported interface is left alone. The hint is carried in a new FR_SYNC_WAKEUP bit of the existing `fuse_req->flags` bitfield (an `unsigned long`, so no layout change): - __fuse_request_send() sets the flag before fuse_send_one(). - fuse_dev_queue_req() consumes it with test_and_clear_bit() and forwards the result to fuse_dev_wake_and_unlock(), which then picks wake_up_sync() or wake_up(). - The forget, interrupt and resend paths pass `false` explicitly, preserving their original wake_up() behaviour. Only /dev/fuse ever wakes fiq->waitq; virtio_fs and fuse_uring dispatch through their own transport and never call wake_up(), so threading `sync` through their ops would just add an unused argument. test_and_clear_bit() makes the flag a one-shot hint that cannot leak into a future requeue, and no extra cleanup is needed in fuse_request_end()/fuse_put_request(). [1] https://lore.kernel.org/lkml/1638780405-38026-1-git-send-email-quic_pragalla@quicinc.com/ [2] https://lore.kernel.org/lkml/20221222093407.GA1141@mi-HP-ProDesk-680-G4-MT/ This work is based on "Pradeep P V K <quic_pragalla@quicinc.com>" and "Pavankumar Kondeti <quic_pkondeti@quicinc.com>" Assisted-by: TRAE:GLM-5.2 Signed-off-by: Xuewen Yan <xuewen.yan@unisoc.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-08-17fuse: wait for FR_FINISHED on abort_on_kill to prevent use-after-freeRochan Avlur
The abort_on_kill path in request_wait_answer() calls fuse_abort_conn() and returns without waiting for FR_FINISHED. If fuse_dev_do_write() is concurrently processing the same request (FR_LOCKED set), the caller frees req->args while it is still being accessed, causing a use-after-free. Fix this by jumping to the existing wait_event(FR_FINISHED) instead of returning early. The wait will not hang because fuse_abort_conn() ensures all requests are ended. Reported-by: syzbot+d6540a3fa1626e11360d@syzkaller.appspotmail.com Fixes: 204aa22a686b ("fuse: abort on fatal signal during sync init") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Rochan Avlur <rochan.avlur@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-08-17fuse: add zero-copy over io-uringJoanne Koong
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)" | | | | >sys_write() | | >fuse_file_write_iter() | | >fuse_send_one() | | [req->args->in_pages = true] | | [folios hold client write data] | | | | >fuse_uring_copy_to_ring() | | >copy_header_to_ring(IN_OUT) | | [memcpy fuse_in_header] | | >copy_header_to_ring(OP) | | [memcpy write_in header] | | | | >fuse_uring_args_to_ring() | | >setup_fuse_copy_state() | | [skip_folio_copy = true] | | | | >fuse_uring_set_up_zero_copy() | | [folio_get for each client folio] | | [build bio_vec array from folios] | | >io_buffer_register_bvec() | | [register pages at ent->zero_copy_index] | | [ent->zero_copied = true] | | | | >fuse_copy_args() | | [skip_folio_copy => return 0 | | for page arg, skip data copy] | | | | >copy_header_to_ring(RING_ENT) | | [memcpy ent_in_out] | | >io_uring_cmd_done() | | | | | [CQE received] | | | | [issue io_uring READ at | | ent->zero_copy_index] | | [reads directly from | |client's pages (ZERO_COPY)] | | | | [write data to backing | | store] | | [submit COMMIT AND FETCH] | | | >fuse_uring_commit_fetch() | | >fuse_uring_commit() | | >fuse_uring_copy_from_ring() | | >fuse_uring_req_end() | | >io_buffer_unregister(ent->zero_copy_index) | | [unregister pages from index] | | >fuse_zero_copy_release() | | [folio_put for each folio] | | [ent->zero_copied = false] | | >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->zero_copy_index) unregister: fuse_uring_req_end() io_buffer_unregister(ent->zero_copy_index) -> 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>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 <joannelkoong@gmail.com> Reviewed-by: Bernd Schubert <bernd@bsbernd.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-08-17fuse: decouple fuse_ring creation from ent registrationJoanne Koong
Currently, the connection's fuse_ring is created lazily on the first FUSE_IO_URING_CMD_REGISTER command. A server registers entries from one thread per queue (one per CPU) and those threads issue their first REGISTER command concurrently. They then race to create the single per-connection fuse_ring, which required open-coded handling in fuse_uring_create() to detect and protect against concurrent creations. Decouple fuse_ring creation from ent registration and move it to FUSE_INIT reply processing after a server has negotiated and set FUSE_OVER_IO_URING. The ring is published before the connection is marked initialized. fuse_uring_register() no longer creates the ring and it instead uses the ring set up at init time. Reviewed-by: Bernd Schubert <bernd@bsbernd.com> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-07-17fuse: use release/acquire for fch->initializedJoanne Koong
fuse_chan_set_initialized() sets values for the connection state and then sets fch->initialized to true, but lockless readers read fch->initialized and if true, go to read the connection state values, without using any barriers. There are a few instances where this happens (fuse_uring_cmd() before dispatching register / commit-and-fetch cmds, fuse_dev_do_wriite() for handling notify retrieves, etc). To make this as simple as possible, use release/acquire semantics for writing/reading fch->initialized. Add the missing read barriers. This is not marked for stable as these are not realistically reachable on a well-behaved server, and buggy/malicious servers who trigger this path fail benignly rather than crash or deadlock the kernel. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-07-17fuse: fix missing barrier when checking io-uring readinessJoanne Koong
fuse_block_alloc() reads fch->initialized and then fch->io_uring. fch->io_uring is set before fch->initialized, ordered by the smp_wmb() in fuse_chan_set_intialized(), but fuse_block_alloc() has no matching read barrier between the two loads. This may lead a CPU to observe fch->initialized=1 but fch->io_uring=0, and skip the check that blocks request allocation until the io-uring queues are ready. This can reintroduce the lock-order inversion deadlock that commit 3393ff964e0f prevents. Add an smp_rmb() barrier to pair with the smp_wmb() in fuse_chan_set_initialized() to prevent this. Fixes: 3393ff964e0f ("fuse: block request allocation until io-uring init is complete") Cc: stable@vger.kernel.org Reviewed-by: Bernd Schubert <bernd@bsbernd.com> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-07-09fuse: fix race between interrupt and resendMiklos Szeredi
After commit f8fce75fedf7 ("fuse: clear intr_entry in fuse_resend and fuse_remove_pending_req") the WARN_ON(!list_empty(&req->intr_entry)) in fuse_request_free() still triggers due to the following race: In request_wait_answer() if (test_bit(FR_SENT, &req->flags)) -> returns true In fuse_chan_resend() clear_bit(FR_SENT, &req->flags) In request_wait_answer() queue_interrupt(req) Fix by: - move clearing FR_SENT inside fpq->lock - move setting FR_PENDING inside fiq->lock - recheck FR_SENT after acquiring fiq->lock in fuse_dev_queue_interrupt() Reported-by: zdi-disclosures@trendmicro.com Fixes: f8fce75fedf7 ("fuse: clear intr_entry in fuse_resend and fuse_remove_pending_req") Cc: stable@vger.kernel.org # 6.9 Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: clean up interrupt readingJoanne Koong
Clean up interrupt reading logic. Remove passing the pointer to the fuse request as an arg and make the header initializations more readable. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: remove stray newline in fuse_dev_do_read()Joanne Koong
Remove stray newline that shouldn't be there. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: use READ_ONCE in fuse_chan_num_background()Li Wang
fuse_chan_num_background() is called without holding fch->bg_lock (for example from fuse_writepages() to compare against fc->congestion_threshold), while fch->num_background is updated under bg_lock in dev.c and dev_uring.c. This is the same locked-write/lockless-read pattern already used for max_background in fuse_chan_max_background(). Use READ_ONCE() on the read side so that: - The compiler does not cache or coalesce loads of a value that may change concurrently on another CPU. - Prevent KCSAN from reporting an unexpected race. Signed-off-by: Li Wang <liwang@kylinos.cn> Fixes: 670d21c6e17f ("fuse: remove reliance on bdi congestion") Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: add fuse_request_sent tracepointAmir Goldstein
This new tracepoint complements fuse_request_send (enqueue) and fuse_request_end (completion). It fires after the request has been successfully copied to the daemon's buffer, just before the daemon can start to process it. fuse_request_sent does not fire if the copy of the request fails. It also does not fire for NOTIFY_REPLY, which fires the _end tracepoint at the end of copy. This is needed for tools tracking the in-flight state of user initiated fuse requests. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: Add SPDX ID lines to some filesTim Bird
Some fuse source files are missing SPDX-License-Identifier lines. Add appropriate IDs to these files, and remove old license references from the headers. Signed-off-by: Tim Bird <tim.bird@sony.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: remove redundant buffer size checks for interrupt and forget requestsJoanne Koong
In fuse_dev_do_read(), there is already logic that ensures the buffer is a minimum of at least FUSE_MIN_READ_BUFFER (8k) bytes. This makes the buffer size checks for interrupt and forget requests redundant as sizeof(struct fuse_in_header) + sizeof(struct fuse_interrupt_in) and sizeof(struct fuse_in_header) + sizeof(struct fuse_forget_in) are both less than FUSE_MIN_READ_BUFFER. We can get rid of these checks. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: simplify fuse_dev_ioctl_clone()Miklos Szeredi
Don't need to check if the new device file is already initialized, since fuse_dev_install_with_pq() will do that anyway. Make fuse_dev_install_with_pq() return a boolean value indicating success so that fuse_dev_ioctl_clone() can return an error in case of failure. Move aborting the connection (setting fc->connected to zero) to fuse_dev_install(), because it is not needed when the clone ioctl fails. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: alloc pqueue before installing fch in fuse_devMiklos Szeredi
Prior to this patchset, fuse_dev (containing fuse_pqueue) was allocated on mount. But now fuse_dev is allocated when opening /dev/fuse, even though the queues are not needed at that time. Delay allocation of the pqueue (4k worth of list_head) just before mounting or cloning a device. Various distributions (e.g. Debian/Fedora) configure /dev/fuse as world writable, so the pqueue allocation should be deferred to a privileged operation (mount) to prevent unprivileged userspace from consuming pinned kernel memory. [Li Wang: fix kernel NULL pointer dereference in fuse_uring_add_to_pq()] [Fix race in fuse_dev_release()] Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: remove #include "fuse_i.h" from dev.c and dev_uring.cMiklos Szeredi
Move a couple of function declarations from fuse_i.h to dev.h and fuse_dev_i.h. Add fuse_conn_get_id() helper that retrieves the connection ID (s_dev) from fuse_conn. With the exception of cuse.c, virtio_fs.c and trace.c source files now either include fuse_i.h or fuse_dev_i/dev_uring_i.h but not both. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: remove fuse_mutex protection from fuse_dev_ioctl_sync_init()Miklos Szeredi
In normal use ioctl(FUSE_DEV_IOC_SYNC_INIT) comes before the mount() or fsconfig() syscalls, they are executed strictly serially. If ioctl and mount are performed in parallel, the behavior is nondeterministic. Removing the mutex does not change this. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: set params in fuse_chan_set_initialized()Miklos Szeredi
Set minor, max_write and max_pages in the fuse_chan. These match the same fields in fuse_conn but are needed in both layers. [Dongyang Jin: Pointers should use NULL instead of explicit '0'] Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: create notify.cMiklos Szeredi
Move FUSE_NOTIFY_* handling into a separate source file. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: create poll.cMiklos Szeredi
Move f_op->poll related functions to the new source file. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: change fud->fc to fud->chanMiklos Szeredi
Store pointer to struct fuse_chan instead of struct fuse_conn in fuse_dev. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: split out filesystem part of request sendingMiklos Szeredi
Create a new source file: req.c and add the request sending entry functions: __fuse_simple_request() fuse_simple_background() fuse_simple_notify_reply() Introduce transport layer sending functions that are called by the respective fs layer function: fuse_chan_send() fuse_chan_send_bg() fuse_chan_send_notify_reply() Move calculation of request header fields uid, gid and pid from fuse_get_req() and fuse_force_creads() to a new helper: fuse_fill_creds(). These fileds are now passed to the transport layer via struct fuse_args. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: change req->fm to req->chanMiklos Szeredi
Store a struct fuse_chan pointer in fuse_req instead of a struct fuse_mount pointer. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: remove fm arg of args->end callbackMiklos Szeredi
Only used by FUSE_INIT and CUSE_INIT, these can store the relevant pointer in their structs derived from fuse_args. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: abort related layering cleanupMiklos Szeredi
- rename fuse_abort_conn() to fuse_chan_abort(), pass fuse_chan pointer instead of fuse_conn - pass an abort_with_err argument that tells fuse_dev_(read|write) to return with ECONNABORTED instead of ENODEV - move fc->aborted to fch->abort_with_err - rename fuse_wait_aborted() to fuse_chan_wait_aborted() Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: remove #include "fuse_i.h" from "dev_uring_i.h"Miklos Szeredi
Start getting rid of fs layer stuff from transport layer files. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move fuse_dev_waitq to dev.cMiklos Szeredi
Move wake_up_all(&fuse_dev_waitq) into fuse_dev_install() where it logically belongs. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move forget related struct and helpersMiklos Szeredi
Move: - struct fuse_forget_link to fuse_dev_i.h - fuse_alloc_forget() to dev.c/dev.h Rename: - fuse_queue_forget -> fuse_chan_queue_forget Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: don't access transport layer structs directly from the fs layerMiklos Szeredi
Add helpers (get and set functions mainly) that cleanly separate the layers. Remove #include "fuse_dev_i.h" from: - inode.c - file.c - control.c Remove #include "dev_uring_i.h" from inode.c. [Li Wang: drop redundant initializer in process_init_limits()] Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move request timeout to fuse_chanMiklos Szeredi
Move: - timeout Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: split off fch->lock from fc->lockMiklos Szeredi
And document which members they protect. end_polls() is called with both, outer fch->lock is probably unnecessary, but doesn't hurt for now. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move interrupt related members to fuse_chanMiklos Szeredi
Move: - no_interrupt Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move io_uring related members to fuse_chanMiklos Szeredi
Move: - io_uring - ring Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move request blocking related members to fuse_chanMiklos Szeredi
Move: - initialized - blocked - blocked_waitq - connected - num_waiting Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move background queuing related members to fuse_chanMiklos Szeredi
Move: - max_background - num_background - active_background - bg_queue - bg_lock Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move 'devices' member from fuse_conn to fuse_chanMiklos Szeredi
This belongs in the transport layer. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move fuse_dev and fuse_pqueue to dev.cMiklos Szeredi
Move function definitions to dev.c, struct definitions to fuse_dev_i.h. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move fuse_iqueue to fuse_chanMiklos Szeredi
Move the 'fiq' member from fuse_conn to fuse_chan. Move iqueue related structure definitions and function declarations from "fuse_i.h" to "fuse_dev_i.h". Add a fuse_dev_chan_new() helper, that returns a fuse_chan initialized with the fuse_dev_fiq_ops. Add a fuse_chan_release() function, that calls fiq->ops->release(). Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: add struct fuse_chanMiklos Szeredi
The goal is to separate transport layer stuff out from struct fuse_conn, leaving just the filesystem related members. Add a new object referenced from fuse_conn. This patch just implements the allocation and freeing of this object. Following patches will move transport related members from fuse_conn to fuse_chan. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: move request timeout code to a new source fileMiklos Szeredi
This marks the first step in cleanly separating the transport layer from the filesystem layer. Add "dev.h", which will contain the interface definition for the transport layer. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: fix io-uring background queue dispatch on request completionJoanne Koong
When a background request completes via the io_uring path, the background queue gets flushed to dispatch pending background requests, but this is done before the connection-level background counters (fc->num_background, fc->active_background) are properly accounted, which may reduce effective queue depth to one. The connection-level counters are decremented in fuse_request_end(), but flush_bg_queue() flushes the /dev/fuse path queue (fc->bg_queue), not the io_uring per-queue bg one, which means pending uring background requests on the queue are never dispatched in this path. Fix this by accounting the connection-level background counters first before flushing the queue's background queue. Since fuse_request_bg_finish() clears FR_BACKGROUND, fuse_request_end() will skip the background cleanup branch entirely, which avoids any double-decrements; it will call the wake_up(&req->waitq) branch but this is effectively a no-op as background requests have no waiters on req->waitq. Reviewed-by: Bernd Schubert <bernd@bsbernd.com> Fixes: 857b0263f30e ("fuse: Allow to queue bg requests through io-uring") Cc: stable@vger.kernel.org Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: avoid 32-bit prune notification count wrapSamuel Moelius
FUSE_NOTIFY_PRUNE validates the nodeid payload length with: size - sizeof(outarg) != outarg.count * sizeof(u64) On 32-bit kernels, size_t is also 32 bits, so the daemon-controlled count multiplication can wrap. A prune notification with count 0x20000000 and no nodeid payload passes the check, enters the copy loop, and asks the device copy path to read nodeids that are not present in the userspace write buffer. In QEMU this reaches the fuse_copy_fill() BUG_ON(!err) path. Validate the payload length with array_size() instead. That accepts exactly the same valid messages, but avoids wrapping arithmetic before the copy loop consumes the count. Assisted-by: Codex:gpt-5.5-cyber-preview Fixes: 3f29d59e92a9 ("fuse: add prune notification") Cc: stable@vger.kernel.org Signed-off-by: Samuel Moelius <sam.moelius@trailofbits.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-15fuse: clear intr_entry in fuse_resend and fuse_remove_pending_reqJi'an Zhou
When fuse_resend() moves a request from fpq->processing back to fiq->pending, it sets FR_PENDING and clears FR_SENT but does not remove the requests intr_entry from fiq->interrupts. If the request had FR_INTERRUPTED set from a prior signal, intr_entry remains dangling on fiq->interrupts. When the requesting task then receives a fatal signal, fuse_remove_pending_req() sees FR_PENDING=1, removes the request from fiq->pending and frees it via the refcount path, also without cleaning intr_entry. The stale intr_entry causes use-after-free when fuse_read_interrupt() iterates fiq->interrupts: - list_del_init(&req->intr_entry) -> UAF write on freed slab - req->in.h.unique -> UAF read, data leaked to userspace Remove intr_entry from fiq->interrupts in fuse_resend() for interrupted requests before they are placed back on fiq->pending. Add a WARN_ON if the intr_entry is not empty on request destruction. Fixes: 760eac73f9f6 ("fuse: Introduce a new notification type for resend pending requests") Cc: stable@vger.kernel.org # 6.9 Signed-off-by: Ji'an Zhou <eilaimemedsnaimel@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-09fuse: re-lock request before returning from fuse_ref_folio()Joanne Koong
fuse_ref_folio() unlocks the request but does not re-lock it before returning. fuse_chan_abort() can end the request and the async end callback (eg fuse_writepage_free()) can free the args while the subsequent copy chain logic after fuse_ref_folio() accesses them, leading to use-after-free issues. Fix this by locking the request in fuse_ref_folio() before returning. Fixes: c3021629a0d8 ("fuse: support splice() reading from fuse device") Cc: stable@vger.kernel.org Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-06-09fuse: re-lock request before replacing page cache folioJoanne Koong
fuse_try_move_folio() unlocks the request on entry but does not re-lock it on the success path. This means fuse_chan_abort() can end the request and free the fuse_io_args (eg fuse_readpages_end()) while the subsequent copy chain logic after fuse_try_move_folio() accesses the fuse_io_args, leading to use-after-free issues. Fix this by calling lock_request() before replace_page_cache_folio(). This ensures the request is locked on the success path which will prevent the fuse_io_args from being freed while the later copying logic runs, and also ensures that the ap->folios[i]->mapping is never null since ap->folios[i] will always point to the newfolio after replace_page_cache_folio(). Fixes: ce534fb05292 ("fuse: allow splice to move pages") Cc: stable@vger.kernel.org Reported-by: Lei Lu <llfamsec@gmail.com> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-04-27fuse: don't block in fuse_get_dev() for non-sync_init caseJoanne Koong
Commit a8dd5f1b73bc ("fuse: create fuse_dev on /dev/fuse open instead of mount") changed behavior so that fuse_get_dev() now unconditionally blocks waiting for a connection, even in the case where sync_init was not set. Previously, non-sync_init opens returned -EPERM immediately. Restore the previous behavior of returning -EPERM. Fixes: a8dd5f1b73bc ("fuse: create fuse_dev on /dev/fuse open instead of mount") Reported-by: Mark Brown <broonie@kernel.org> Closes: https://lore.kernel.org/all/3c9f8396-41f4-4c88-b883-34bede72b427@sirena.org.uk/ Cc: <stable@vger.kernel.org> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Tested-by: Mark Brown <broonie@kernel.org> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-04-02fuse: clean up device cloningMiklos Szeredi
- fuse_mutex is not needed for device cloning, because fuse_dev_install() uses cmpxcg() to set fud->fc, which prevents races between clone/mount or clone/clone. This makes the logic simpler - Drop fc->dev_count. This is only used to check in release if the device is the last clone, but checking list_empty(&fc->devices) is equivalent after removing the released device from the list. Removing the fuse_dev before calling fuse_abort_conn() is okay, since the processing and io lists are now empty for this device. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-04-02fuse: add refcount to fuse_devMiklos Szeredi
This will make it possible to grab the fuse_dev and subsequently release the file that it came from. In the above case, fud->fc will be set to FUSE_DEV_FC_DISCONNECTED to indicate that this is no longer a functional device. When trying to assign an fc to such a disconnected fuse_dev, the fc is set to the disconnected state. Use atomic operations xchg() and cmpxchg() to prevent races. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2026-04-02fuse: create fuse_dev on /dev/fuse open instead of mountMiklos Szeredi
Allocate struct fuse_dev when opening the device. This means that unlike before, ->private_data is always set to a valid pointer. The use of USE_DEV_SYNC_INIT magic pointer for the private_data is now replaced with a simple bool sync_init member. If sync INIT is not set, I/O on the device returns error before mount. Keep this behavior by checking for the ->fc member. If fud->fc is set, the mount has succeeded. Testing this used READ_ONCE(file->private_data) and smp_mb() to try and provide the necessary semantics. Switch this to smp_store_release() and smp_load_acquire(). Setting fud->fc is protected by fuse_mutex, this is unchanged. Will need this later so the /dev/fuse open file reference is not held during FSCONFIG_CMD_CREATE. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>