Commit 2619a6d4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull fuse updates from Miklos Szeredi:

 - Remove tmp page copying in writeback path (Joanne).

   This removes ~300 lines and with that a lot of complexity related to
   avoiding reclaim related deadlock. The old mechanism is replaced with
   a mapping flag that tells the MM not to block reclaim waiting for
   writeback to complete. The MM parts have been reviewed/acked by
   respective maintainers.

 - Convert more code to handle large folios (Joanne). This still just
   adds the code to deal with large folios and does not enable them yet.

 - Allow invalidating all cached lookups atomically (Luis Henriques).
   This feature is useful for CernVMFS, which currently does this
   iteratively.

 - Align write prefaulting in fuse with generic one (Dave Hansen)

 - Fix race causing invalid data to be cached when setting attributes on
   different nodes of a distributed fs (Guang Yuan Wu)

 - Update documentation for passthrough (Chen Linxuan)

 - Add fdinfo about the device number associated with an opened
   /dev/fuse instance (Chen Linxuan)

 - Increase readdir buffer size (Miklos). This depends on a patch to VFS
   readdir code that was already merged through Christians tree.

 - Optimize io-uring request expiration (Joanne)

 - Misc cleanups

* tag 'fuse-update-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: (25 commits)
  fuse: increase readdir buffer size
  readdir: supply dir_context.count as readdir buffer size hint
  fuse: don't allow signals to interrupt getdents copying
  fuse: support large folios for writeback
  fuse: support large folios for readahead
  fuse: support large folios for queued writes
  fuse: support large folios for stores
  fuse: support large folios for symlinks
  fuse: support large folios for folio reads
  fuse: support large folios for writethrough writes
  fuse: refactor fuse_fill_write_pages()
  fuse: support large folios for retrieves
  fuse: support copying large folios
  fs: fuse: add dev id to /dev/fuse fdinfo
  docs: filesystems: add fuse-passthrough.rst
  MAINTAINERS: update filter of FUSE documentation
  fuse: fix race between concurrent setattrs from multiple nodes
  fuse: remove tmp folio for writebacks and internal rb tree
  mm: skip folio reclaim in legacy memcg contexts for deadlockable mappings
  fuse: optimize over-io-uring request expiration check
  ...
parents 0fb34422 dabb9039
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
.. SPDX-License-Identifier: GPL-2.0

================
FUSE Passthrough
================

Introduction
============

FUSE (Filesystem in Userspace) passthrough is a feature designed to improve the
performance of FUSE filesystems for I/O operations. Typically, FUSE operations
involve communication between the kernel and a userspace FUSE daemon, which can
incur overhead. Passthrough allows certain operations on a FUSE file to bypass
the userspace daemon and be executed directly by the kernel on an underlying
"backing file".

This is achieved by the FUSE daemon registering a file descriptor (pointing to
the backing file on a lower filesystem) with the FUSE kernel module. The kernel
then receives an identifier (``backing_id``) for this registered backing file.
When a FUSE file is subsequently opened, the FUSE daemon can, in its response to
the ``OPEN`` request, include this ``backing_id`` and set the
``FOPEN_PASSTHROUGH`` flag. This establishes a direct link for specific
operations.

Currently, passthrough is supported for operations like ``read(2)``/``write(2)``
(via ``read_iter``/``write_iter``), ``splice(2)``, and ``mmap(2)``.

Enabling Passthrough
====================

To use FUSE passthrough:

  1. The FUSE filesystem must be compiled with ``CONFIG_FUSE_PASSTHROUGH``
     enabled.
  2. The FUSE daemon, during the ``FUSE_INIT`` handshake, must negotiate the
     ``FUSE_PASSTHROUGH`` capability and specify its desired
     ``max_stack_depth``.
  3. The (privileged) FUSE daemon uses the ``FUSE_DEV_IOC_BACKING_OPEN`` ioctl
     on its connection file descriptor (e.g., ``/dev/fuse``) to register a
     backing file descriptor and obtain a ``backing_id``.
  4. When handling an ``OPEN`` or ``CREATE`` request for a FUSE file, the daemon
     replies with the ``FOPEN_PASSTHROUGH`` flag set in
     ``fuse_open_out::open_flags`` and provides the corresponding ``backing_id``
     in ``fuse_open_out::backing_id``.
  5. The FUSE daemon should eventually call ``FUSE_DEV_IOC_BACKING_CLOSE`` with
     the ``backing_id`` to release the kernel's reference to the backing file
     when it's no longer needed for passthrough setups.

Privilege Requirements
======================

Setting up passthrough functionality currently requires the FUSE daemon to
possess the ``CAP_SYS_ADMIN`` capability. This requirement stems from several
security and resource management considerations that are actively being
discussed and worked on. The primary reasons for this restriction are detailed
below.

Resource Accounting and Visibility
----------------------------------

The core mechanism for passthrough involves the FUSE daemon opening a file
descriptor to a backing file and registering it with the FUSE kernel module via
the ``FUSE_DEV_IOC_BACKING_OPEN`` ioctl. This ioctl returns a ``backing_id``
associated with a kernel-internal ``struct fuse_backing`` object, which holds a
reference to the backing ``struct file``.

A significant concern arises because the FUSE daemon can close its own file
descriptor to the backing file after registration. The kernel, however, will
still hold a reference to the ``struct file`` via the ``struct fuse_backing``
object as long as it's associated with a ``backing_id`` (or subsequently, with
an open FUSE file in passthrough mode).

This behavior leads to two main issues for unprivileged FUSE daemons:

  1. **Invisibility to lsof and other inspection tools**: Once the FUSE
     daemon closes its file descriptor, the open backing file held by the kernel
     becomes "hidden." Standard tools like ``lsof``, which typically inspect
     process file descriptor tables, would not be able to identify that this
     file is still open by the system on behalf of the FUSE filesystem. This
     makes it difficult for system administrators to track resource usage or
     debug issues related to open files (e.g., preventing unmounts).

  2. **Bypassing RLIMIT_NOFILE**: The FUSE daemon process is subject to
     resource limits, including the maximum number of open file descriptors
     (``RLIMIT_NOFILE``). If an unprivileged daemon could register backing files
     and then close its own FDs, it could potentially cause the kernel to hold
     an unlimited number of open ``struct file`` references without these being
     accounted against the daemon's ``RLIMIT_NOFILE``. This could lead to a
     denial-of-service (DoS) by exhausting system-wide file resources.

The ``CAP_SYS_ADMIN`` requirement acts as a safeguard against these issues,
restricting this powerful capability to trusted processes.

**NOTE**: ``io_uring`` solves this similar issue by exposing its "fixed files",
which are visible via ``fdinfo`` and accounted under the registering user's
``RLIMIT_NOFILE``.

Filesystem Stacking and Shutdown Loops
--------------------------------------

Another concern relates to the potential for creating complex and problematic
filesystem stacking scenarios if unprivileged users could set up passthrough.
A FUSE passthrough filesystem might use a backing file that resides:

  * On the *same* FUSE filesystem.
  * On another filesystem (like OverlayFS) which itself might have an upper or
    lower layer that is a FUSE filesystem.

These configurations could create dependency loops, particularly during
filesystem shutdown or unmount sequences, leading to deadlocks or system
instability. This is conceptually similar to the risks associated with the
``LOOP_SET_FD`` ioctl, which also requires ``CAP_SYS_ADMIN``.

To mitigate this, FUSE passthrough already incorporates checks based on
filesystem stacking depth (``sb->s_stack_depth`` and ``fc->max_stack_depth``).
For example, during the ``FUSE_INIT`` handshake, the FUSE daemon can negotiate
the ``max_stack_depth`` it supports. When a backing file is registered via
``FUSE_DEV_IOC_BACKING_OPEN``, the kernel checks if the backing file's
filesystem stack depth is within the allowed limit.

The ``CAP_SYS_ADMIN`` requirement provides an additional layer of security,
ensuring that only privileged users can create these potentially complex
stacking arrangements.

General Security Posture
------------------------

As a general principle for new kernel features that allow userspace to instruct
the kernel to perform direct operations on its behalf based on user-provided
file descriptors, starting with a higher privilege requirement (like
``CAP_SYS_ADMIN``) is a conservative and common security practice. This allows
the feature to be used and tested while further security implications are
evaluated and addressed.
+1 −0
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ Documentation for filesystem implementations.
   fuse
   fuse-io
   fuse-io-uring
   fuse-passthrough
   inotify
   isofs
   nilfs2
+1 −1
Original line number Diff line number Diff line
@@ -9846,7 +9846,7 @@ L: linux-fsdevel@vger.kernel.org
S:	Maintained
W:	https://github.com/libfuse/
T:	git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git
F:	Documentation/filesystems/fuse.rst
F:	Documentation/filesystems/fuse*
F:	fs/fuse/
F:	include/uapi/linux/fuse.h
+111 −71
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/swap.h>
#include <linux/splice.h>
#include <linux/sched.h>
#include <linux/seq_file.h>

#define CREATE_TRACE_POINTS
#include "fuse_trace.h"
@@ -45,7 +46,7 @@ bool fuse_request_expired(struct fuse_conn *fc, struct list_head *list)
	return time_is_before_jiffies(req->create_time + fc->timeout.req_timeout);
}

bool fuse_fpq_processing_expired(struct fuse_conn *fc, struct list_head *processing)
static bool fuse_fpq_processing_expired(struct fuse_conn *fc, struct list_head *processing)
{
	int i;

@@ -816,7 +817,7 @@ static int unlock_request(struct fuse_req *req)
	return err;
}

void fuse_copy_init(struct fuse_copy_state *cs, int write,
void fuse_copy_init(struct fuse_copy_state *cs, bool write,
		    struct iov_iter *iter)
{
	memset(cs, 0, sizeof(*cs));
@@ -955,10 +956,10 @@ static int fuse_check_folio(struct folio *folio)
 * folio that was originally in @pagep will lose a reference and the new
 * folio returned in @pagep will carry a reference.
 */
static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
static int fuse_try_move_folio(struct fuse_copy_state *cs, struct folio **foliop)
{
	int err;
	struct folio *oldfolio = page_folio(*pagep);
	struct folio *oldfolio = *foliop;
	struct folio *newfolio;
	struct pipe_buffer *buf = cs->pipebufs;

@@ -979,7 +980,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
	cs->pipebufs++;
	cs->nr_segs--;

	if (cs->len != PAGE_SIZE)
	if (cs->len != folio_size(oldfolio))
		goto out_fallback;

	if (!pipe_buf_try_steal(cs->pipe, buf))
@@ -1025,7 +1026,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
	if (test_bit(FR_ABORTED, &cs->req->flags))
		err = -ENOENT;
	else
		*pagep = &newfolio->page;
		*foliop = newfolio;
	spin_unlock(&cs->req->waitq.lock);

	if (err) {
@@ -1058,7 +1059,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
	goto out_put_old;
}

static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio,
			  unsigned offset, unsigned count)
{
	struct pipe_buffer *buf;
@@ -1067,17 +1068,17 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
	if (cs->nr_segs >= cs->pipe->max_usage)
		return -EIO;

	get_page(page);
	folio_get(folio);
	err = unlock_request(cs->req);
	if (err) {
		put_page(page);
		folio_put(folio);
		return err;
	}

	fuse_copy_finish(cs);

	buf = cs->pipebufs;
	buf->page = page;
	buf->page = &folio->page;
	buf->offset = offset;
	buf->len = count;

@@ -1089,20 +1090,24 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
}

/*
 * Copy a page in the request to/from the userspace buffer.  Must be
 * Copy a folio in the request to/from the userspace buffer.  Must be
 * done atomically
 */
static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
static int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop,
			   unsigned offset, unsigned count, int zeroing)
{
	int err;
	struct page *page = *pagep;
	struct folio *folio = *foliop;
	size_t size;

	if (page && zeroing && count < PAGE_SIZE)
		clear_highpage(page);
	if (folio) {
		size = folio_size(folio);
		if (zeroing && count < size)
			folio_zero_range(folio, 0, size);
	}

	while (count) {
		if (cs->write && cs->pipebufs && page) {
		if (cs->write && cs->pipebufs && folio) {
			/*
			 * Can't control lifetime of pipe buffers, so always
			 * copy user pages.
@@ -1112,12 +1117,12 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
				if (err)
					return err;
			} else {
				return fuse_ref_page(cs, page, offset, count);
				return fuse_ref_folio(cs, folio, offset, count);
			}
		} else if (!cs->len) {
			if (cs->move_pages && page &&
			    offset == 0 && count == PAGE_SIZE) {
				err = fuse_try_move_page(cs, pagep);
			if (cs->move_folios && folio &&
			    offset == 0 && count == size) {
				err = fuse_try_move_folio(cs, foliop);
				if (err <= 0)
					return err;
			} else {
@@ -1126,21 +1131,29 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
					return err;
			}
		}
		if (page) {
			void *mapaddr = kmap_local_page(page);
			void *buf = mapaddr + offset;
			offset += fuse_copy_do(cs, &buf, &count);
		if (folio) {
			void *mapaddr = kmap_local_folio(folio, offset);
			void *buf = mapaddr;
			unsigned int copy = count;
			unsigned int bytes_copied;

			if (folio_test_highmem(folio) && count > PAGE_SIZE - offset_in_page(offset))
				copy = PAGE_SIZE - offset_in_page(offset);

			bytes_copied = fuse_copy_do(cs, &buf, &copy);
			kunmap_local(mapaddr);
			offset += bytes_copied;
			count -= bytes_copied;
		} else
			offset += fuse_copy_do(cs, NULL, &count);
	}
	if (page && !cs->write)
		flush_dcache_page(page);
	if (folio && !cs->write)
		flush_dcache_folio(folio);
	return 0;
}

/* Copy pages in the request to/from userspace buffer */
static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
/* Copy folios in the request to/from userspace buffer */
static int fuse_copy_folios(struct fuse_copy_state *cs, unsigned nbytes,
			    int zeroing)
{
	unsigned i;
@@ -1151,23 +1164,12 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
		int err;
		unsigned int offset = ap->descs[i].offset;
		unsigned int count = min(nbytes, ap->descs[i].length);
		struct page *orig, *pagep;

		orig = pagep = &ap->folios[i]->page;

		err = fuse_copy_page(cs, &pagep, offset, count, zeroing);
		err = fuse_copy_folio(cs, &ap->folios[i], offset, count, zeroing);
		if (err)
			return err;

		nbytes -= count;

		/*
		 *  fuse_copy_page may have moved a page from a pipe instead of
		 *  copying into our given page, so update the folios if it was
		 *  replaced.
		 */
		if (pagep != orig)
			ap->folios[i] = page_folio(pagep);
	}
	return 0;
}
@@ -1197,7 +1199,7 @@ int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
	for (i = 0; !err && i < numargs; i++)  {
		struct fuse_arg *arg = &args[i];
		if (i == numargs - 1 && argpages)
			err = fuse_copy_pages(cs, arg->size, zeroing);
			err = fuse_copy_folios(cs, arg->size, zeroing);
		else
			err = fuse_copy_one(cs, arg->value, arg->size);
	}
@@ -1538,7 +1540,7 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to)
	if (!user_backed_iter(to))
		return -EINVAL;

	fuse_copy_init(&cs, 1, to);
	fuse_copy_init(&cs, true, to);

	return fuse_dev_do_read(fud, file, &cs, iov_iter_count(to));
}
@@ -1561,7 +1563,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
	if (!bufs)
		return -ENOMEM;

	fuse_copy_init(&cs, 1, NULL);
	fuse_copy_init(&cs, true, NULL);
	cs.pipebufs = bufs;
	cs.pipe = pipe;
	ret = fuse_dev_do_read(fud, in, &cs, len);
@@ -1786,20 +1788,23 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
	num = outarg.size;
	while (num) {
		struct folio *folio;
		struct page *page;
		unsigned int this_num;
		unsigned int folio_offset;
		unsigned int nr_bytes;
		unsigned int nr_pages;

		folio = filemap_grab_folio(mapping, index);
		err = PTR_ERR(folio);
		if (IS_ERR(folio))
			goto out_iput;

		page = &folio->page;
		this_num = min_t(unsigned, num, folio_size(folio) - offset);
		err = fuse_copy_page(cs, &page, offset, this_num, 0);
		folio_offset = ((index - folio->index) << PAGE_SHIFT) + offset;
		nr_bytes = min_t(unsigned, num, folio_size(folio) - folio_offset);
		nr_pages = (offset + nr_bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;

		err = fuse_copy_folio(cs, &folio, folio_offset, nr_bytes, 0);
		if (!folio_test_uptodate(folio) && !err && offset == 0 &&
		    (this_num == folio_size(folio) || file_size == end)) {
			folio_zero_segment(folio, this_num, folio_size(folio));
		    (nr_bytes == folio_size(folio) || file_size == end)) {
			folio_zero_segment(folio, nr_bytes, folio_size(folio));
			folio_mark_uptodate(folio);
		}
		folio_unlock(folio);
@@ -1808,9 +1813,9 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
		if (err)
			goto out_iput;

		num -= this_num;
		num -= nr_bytes;
		offset = 0;
		index++;
		index += nr_pages;
	}

	err = 0;
@@ -1849,7 +1854,7 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
	unsigned int num;
	unsigned int offset;
	size_t total_len = 0;
	unsigned int num_pages, cur_pages = 0;
	unsigned int num_pages;
	struct fuse_conn *fc = fm->fc;
	struct fuse_retrieve_args *ra;
	size_t args_size = sizeof(*ra);
@@ -1867,6 +1872,7 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,

	num_pages = (num + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
	num_pages = min(num_pages, fc->max_pages);
	num = min(num, num_pages << PAGE_SHIFT);

	args_size += num_pages * (sizeof(ap->folios[0]) + sizeof(ap->descs[0]));

@@ -1887,25 +1893,29 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,

	index = outarg->offset >> PAGE_SHIFT;

	while (num && cur_pages < num_pages) {
	while (num) {
		struct folio *folio;
		unsigned int this_num;
		unsigned int folio_offset;
		unsigned int nr_bytes;
		unsigned int nr_pages;

		folio = filemap_get_folio(mapping, index);
		if (IS_ERR(folio))
			break;

		this_num = min_t(unsigned, num, PAGE_SIZE - offset);
		folio_offset = ((index - folio->index) << PAGE_SHIFT) + offset;
		nr_bytes = min(folio_size(folio) - folio_offset, num);
		nr_pages = (offset + nr_bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;

		ap->folios[ap->num_folios] = folio;
		ap->descs[ap->num_folios].offset = offset;
		ap->descs[ap->num_folios].length = this_num;
		ap->descs[ap->num_folios].offset = folio_offset;
		ap->descs[ap->num_folios].length = nr_bytes;
		ap->num_folios++;
		cur_pages++;

		offset = 0;
		num -= this_num;
		total_len += this_num;
		index++;
		num -= nr_bytes;
		total_len += nr_bytes;
		index += nr_pages;
	}
	ra->inarg.offset = outarg->offset;
	ra->inarg.size = total_len;
@@ -2021,11 +2031,24 @@ static int fuse_notify_resend(struct fuse_conn *fc)
	return 0;
}

/*
 * Increments the fuse connection epoch.  This will result of dentries from
 * previous epochs to be invalidated.
 *
 * XXX optimization: add call to shrink_dcache_sb()?
 */
static int fuse_notify_inc_epoch(struct fuse_conn *fc)
{
	atomic_inc(&fc->epoch);

	return 0;
}

static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
		       unsigned int size, struct fuse_copy_state *cs)
{
	/* Don't try to move pages (yet) */
	cs->move_pages = 0;
	/* Don't try to move folios (yet) */
	cs->move_folios = false;

	switch (code) {
	case FUSE_NOTIFY_POLL:
@@ -2049,6 +2072,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
	case FUSE_NOTIFY_RESEND:
		return fuse_notify_resend(fc);

	case FUSE_NOTIFY_INC_EPOCH:
		return fuse_notify_inc_epoch(fc);

	default:
		fuse_copy_finish(cs);
		return -EINVAL;
@@ -2173,7 +2199,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
	spin_unlock(&fpq->lock);
	cs->req = req;
	if (!req->args->page_replace)
		cs->move_pages = 0;
		cs->move_folios = false;

	if (oh.error)
		err = nbytes != sizeof(oh) ? -EINVAL : 0;
@@ -2211,7 +2237,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from)
	if (!user_backed_iter(from))
		return -EINVAL;

	fuse_copy_init(&cs, 0, from);
	fuse_copy_init(&cs, false, from);

	return fuse_dev_do_write(fud, &cs, iov_iter_count(from));
}
@@ -2285,13 +2311,13 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
	}
	pipe_unlock(pipe);

	fuse_copy_init(&cs, 0, NULL);
	fuse_copy_init(&cs, false, NULL);
	cs.pipebufs = bufs;
	cs.nr_segs = nbuf;
	cs.pipe = pipe;

	if (flags & SPLICE_F_MOVE)
		cs.move_pages = 1;
		cs.move_folios = true;

	ret = fuse_dev_do_write(fud, &cs, len);

@@ -2602,6 +2628,17 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
	}
}

#ifdef CONFIG_PROC_FS
static void fuse_dev_show_fdinfo(struct seq_file *seq, struct file *file)
{
	struct fuse_dev *fud = fuse_get_dev(file);
	if (!fud)
		return;

	seq_printf(seq, "fuse_connection:\t%u\n", fud->fc->dev);
}
#endif

const struct file_operations fuse_dev_operations = {
	.owner		= THIS_MODULE,
	.open		= fuse_dev_open,
@@ -2617,6 +2654,9 @@ const struct file_operations fuse_dev_operations = {
#ifdef CONFIG_FUSE_IO_URING
	.uring_cmd	= fuse_uring_cmd,
#endif
#ifdef CONFIG_PROC_FS
	.show_fdinfo	= fuse_dev_show_fdinfo,
#endif
};
EXPORT_SYMBOL_GPL(fuse_dev_operations);

+25 −9
Original line number Diff line number Diff line
@@ -140,6 +140,21 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring)
	}
}

static bool ent_list_request_expired(struct fuse_conn *fc, struct list_head *list)
{
	struct fuse_ring_ent *ent;
	struct fuse_req *req;

	ent = list_first_entry_or_null(list, struct fuse_ring_ent, list);
	if (!ent)
		return false;

	req = ent->fuse_req;

	return time_is_before_jiffies(req->create_time +
				      fc->timeout.req_timeout);
}

bool fuse_uring_request_expired(struct fuse_conn *fc)
{
	struct fuse_ring *ring = fc->ring;
@@ -157,7 +172,8 @@ bool fuse_uring_request_expired(struct fuse_conn *fc)
		spin_lock(&queue->lock);
		if (fuse_request_expired(fc, &queue->fuse_req_queue) ||
		    fuse_request_expired(fc, &queue->fuse_req_bg_queue) ||
		    fuse_fpq_processing_expired(fc, queue->fpq.processing)) {
		    ent_list_request_expired(fc, &queue->ent_w_req_queue) ||
		    ent_list_request_expired(fc, &queue->ent_in_userspace)) {
			spin_unlock(&queue->lock);
			return true;
		}
@@ -494,7 +510,7 @@ static void fuse_uring_cancel(struct io_uring_cmd *cmd,
	spin_lock(&queue->lock);
	if (ent->state == FRRS_AVAILABLE) {
		ent->state = FRRS_USERSPACE;
		list_move(&ent->list, &queue->ent_in_userspace);
		list_move_tail(&ent->list, &queue->ent_in_userspace);
		need_cmd_done = true;
		ent->cmd = NULL;
	}
@@ -577,8 +593,8 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring,
	if (err)
		return err;

	fuse_copy_init(&cs, 0, &iter);
	cs.is_uring = 1;
	fuse_copy_init(&cs, false, &iter);
	cs.is_uring = true;
	cs.req = req;

	return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz);
@@ -607,8 +623,8 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req,
		return err;
	}

	fuse_copy_init(&cs, 1, &iter);
	cs.is_uring = 1;
	fuse_copy_init(&cs, true, &iter);
	cs.is_uring = true;
	cs.req = req;

	if (num_args > 0) {
@@ -714,7 +730,7 @@ static int fuse_uring_send_next_to_ring(struct fuse_ring_ent *ent,
	cmd = ent->cmd;
	ent->cmd = NULL;
	ent->state = FRRS_USERSPACE;
	list_move(&ent->list, &queue->ent_in_userspace);
	list_move_tail(&ent->list, &queue->ent_in_userspace);
	spin_unlock(&queue->lock);

	io_uring_cmd_done(cmd, 0, 0, issue_flags);
@@ -764,7 +780,7 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
	clear_bit(FR_PENDING, &req->flags);
	ent->fuse_req = req;
	ent->state = FRRS_FUSE_REQ;
	list_move(&ent->list, &queue->ent_w_req_queue);
	list_move_tail(&ent->list, &queue->ent_w_req_queue);
	fuse_uring_add_to_pq(ent, req);
}

@@ -1180,7 +1196,7 @@ static void fuse_uring_send(struct fuse_ring_ent *ent, struct io_uring_cmd *cmd,

	spin_lock(&queue->lock);
	ent->state = FRRS_USERSPACE;
	list_move(&ent->list, &queue->ent_in_userspace);
	list_move_tail(&ent->list, &queue->ent_in_userspace);
	ent->cmd = NULL;
	spin_unlock(&queue->lock);

Loading