Re: [PATCH v2 21/21] docs: fuse: document extended passthrough (FUSE_PASSTHROUGH_INO)
Amir Goldstein <[email protected]> Sat, 16 May 2026 22:59:30 +0200
| Newsgroups | org.kernel.vger.linux-unionfs,dev.linux.lists.fuse-devel |
|---|---|
| Message-ID | <CAOQ4uxg4+6s094=ka7xG+Rjz2A6amyrSOr6C0QpYQ_iyrR4f+Q@mail.gmail.com> |
On Sat, May 16, 2026 at 8:23 PM Amir Goldstein <[email protected]> wrote: > > On Sat, May 16, 2026 at 2:53 AM Joanne Koong <[email protected]> wrote: > > > > Add section about extended passthrough (FUSE_PASSTHROUGH_INO) mode. > > > > Signed-off-by: Joanne Koong <[email protected]> > > --- > > .../filesystems/fuse/fuse-passthrough.rst | 126 ++++++++++++++++++ > > 1 file changed, 126 insertions(+) > > > > diff --git a/Documentation/filesystems/fuse/fuse-passthrough.rst b/Documentation/filesystems/fuse/fuse-passthrough.rst > > index 2b0e7c2da54a..751d27c6fc5c 100644 > > --- a/Documentation/filesystems/fuse/fuse-passthrough.rst > > +++ b/Documentation/filesystems/fuse/fuse-passthrough.rst > > @@ -25,6 +25,11 @@ operations. > > Currently, passthrough is supported for operations like ``read(2)``/``write(2)`` > > (via ``read_iter``/``write_iter``), ``splice(2)``, and ``mmap(2)``. > > > > +With the extended ``FUSE_PASSTHROUGH_INO`` mode, passthrough is also supported > > +for inode operations (getattr, setattr) and directory operations (readdir). > > +In this mode, a backing file is attached to a fuse inode for its entire > > +lifetime. > > + > > Enabling Passthrough > > ==================== > > > > @@ -46,6 +51,127 @@ To use FUSE passthrough: > > the ``backing_id`` to release the kernel's reference to the backing file > > when it's no longer needed for passthrough setups. > > > > +Extended Passthrough (FUSE_PASSTHROUGH_INO) > > +============================================ > > + > > +``FUSE_PASSTHROUGH_INO`` is a stricter variant of ``FUSE_PASSTHROUGH`` in > > +which the backing file inode number must match the fuse inode number, enforcing > > +a one-to-one mapping. The kernel offers this flag during ``FUSE_INIT`` if > > +``CONFIG_FUSE_PASSTHROUGH`` is enabled and the architecture has 64-bit > > +``ino_t``. The daemon accepts by returning it back in the init reply. > > IIUC, ino_t is now always 64-bit. > > > + > > +Enabling Extended Passthrough > > +----------------------------- > > + > > +To use extended passthrough: > > + > > + 1. Follow steps 1-2 from `Enabling Passthrough`_ above. The daemon must > > + also negotiate the ``FUSE_PASSTHROUGH_INO`` capability during > > + ``FUSE_INIT``. > > + 2. When registering a backing file via ``FUSE_DEV_IOC_BACKING_OPEN``, set > > + the ``ops_mask`` field in ``struct fuse_backing_map`` to declare which > > + operations should be passed through. At minimum, > > + ``FUSE_PASSTHROUGH_OP_GETATTR`` must be set for any inode-level > > + passthrough. > > + 3. When handling a ``LOOKUP``, ``CREATE``, ``MKNOD``, ``MKDIR``, > > + ``SYMLINK``, or ``LINK`` request, the daemon responds with a > > + ``fuse_entry2_out`` (instead of ``fuse_entry_out``). To enable > > + passthrough on the inode, set ``backing_id`` to the id returned by > > + ``FUSE_DEV_IOC_BACKING_OPEN``. Set ``backing_id`` to 0 for inodes > > + that should not use passthrough. The ``nodeid`` in the response must > > + be the backing file's inode number (``i_ino``). If they don't match, > > + the kernel rejects the passthrough setup with ``-EIO``. > > + 4. When handling an ``OPEN`` 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`` or leaves ``fuse_open_out::backing_id`` > > + blank. If the daemon would like to opt out of passthrough when the inode > > + is already in passthrough mode, it may additionally set > > + ``FOPEN_DIRECT_IO``, which will forward read/write operations directly to > > + the daemon. > > + 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. > > + > > +Passthrough Operations Mask > > +--------------------------- > > + > > +When registering a backing file via ``FUSE_DEV_IOC_BACKING_OPEN``, the daemon > > +sets ``ops_mask`` in ``struct fuse_backing_map`` to declare which operations > > +should be passed through:: > > + > > + FUSE_PASSTHROUGH_OP_READ > > + FUSE_PASSTHROUGH_OP_WRITE > > + FUSE_PASSTHROUGH_OP_READDIR > > + FUSE_PASSTHROUGH_OP_GETATTR > > + FUSE_PASSTHROUGH_OP_SETATTR > > + > > +Operations fall into two categories, which can be combined: > > + > > +**Inode operations** (getattr, setattr): Activated on lookup when the daemon > > +returns a ``backing_id`` in the ``fuse_entry2_out`` response. The backing > > +file reference persists for the lifetime of the fuse inode. Getattr is the > > +minimum required inode operation. > > + > > +**File operations** (read, write, readdir): Require the file to be opened with > > +``FOPEN_PASSTHROUGH`` in the daemon's open response. Read and write can be set > > +independently for partial passthrough. If only one direction is set, the other > > +falls back to direct IO and mmap is disabled. > > + > > +Extended Entry Reply > > +-------------------- > > + > > +When ``FUSE_PASSTHROUGH_INO`` is negotiated, the kernel uses > > +``fuse_entry2_out`` instead of ``fuse_entry_out`` for entry responses. > > +This struct carries a ``backing_id`` and ``fuse_statx`` attributes instead > > +of ``fuse_attr``. > > + > > +When ``backing_id > 0``, the kernel associates the inode with the backing file > > +for passthrough inode operations. Statx attributes are not cached because > > +passthrough getattr fetches them directly from the backing inode. > > + > > +When ``backing_id == 0`` (no passthrough), the statx attributes from the reply > > +are cached normally. > > + > > +A negative ``backing_id`` is treated as an error. The kernel sends > > +``FUSE_FORGET`` for the returned nodeid and fails the operation. > > + > > +IO Mode State Machine > > +--------------------- > > + > > +The ``iocachectr`` field in ``struct fuse_inode`` prevents conflicting access > > +modes on the same inode (page-cache I/O and passthrough I/O cannot coexist):: > > + > > + iocachectr > 0 Cached mode > > + iocachectr == 0 Idle. No files open, no passthrough > > More accurately, no open files in caching or passthough. > files open with FOPEN_DIRECT_IO may exist and they > do not deny entering either caching or passthrough iomode. > > > + iocachectr < 0 Uncached/passthrough mode > > + > > +Each open file in passthrough mode holds one reference (``iocachectr--``). > > +The inode-level passthrough setup holds one additional long-lived reference > > +if the backing has inode ops (getattr/setattr). This long-lived reference is > > +released on inode eviction. > > + > > +Cached mode and passthrough mode are mutually exclusive. Attempting either > > +while the other is active returns ``-ETXTBSY``. > > + > > +For directories, the same mechanism arbitrates between cached readdir > > +(``FOPEN_CACHE_DIR``) and passthrough readdir. A directory opened without > > +``FOPEN_CACHE_DIR`` and without ``FOPEN_PASSTHROUGH`` is treated as direct I/O > > +and does not affect io mode. > > + > > +Things to note > > +-------------- > > + > > +- ``FUSE_PASSTHROUGH_INO`` requires 64-bit ``ino_t``. > > Not relevant I think. > > > +- Readdirplus does not set up inode passthrough. Inodes created via readdirplus > > + use normal FUSE operations until a fresh lookup occurs. > > +- An inode's backing association is set once and cannot be changed. > > +- Passthrough and cached I/O cannot coexist on the same inode. > > +- If any inode operations are passed through, this means all opened files need > > + to set the ``FOPEN_PASSTHROUGH`` flag in the open response, even if reads > > + and writes are not passed through. If reads and writes are not passed > > + through, they will go directly to the daemon. > > I had not considered this. > Setting OP_GETATTR/SETATTR in LOOKUP without any OP_RW bits > and then requiring that the server open files with FOPEN_PASSTHROUGH > with the backing_id sounds strange - > I understand why it was done this way, but for users this could be perplexing. > > The thing is that if the server is only doing GETATTR passthrough there is not > really a reason to deny open file in caching mode (I think?). > > The limitation that requires FOPEN_PASSTHROUGH is encoded in > fuse_file_cached_io_open() > fuse_is_io_cache_wait() > !fuse_inode_backing() > > We have two options. > > Either we decide that server passing through GETATTR and opening > files in caching mode is not interesting to support (for now) and then just > maybe we need to better document the reason that server needs to > use FOPEN_PASSTHROUGH despite not doing io passthrough. > > Or we relax the limitation w.r.t cached io mode, something like this > (completely untested most likely broken based or earlier review > suggestion regarding fuse_passthrough_op): > > Thanks, > Amir. > > diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c > index c5f3e2201c6ab..7c33e7a974f6c 100644 > --- a/fs/fuse/iomode.c > +++ b/fs/fuse/iomode.c > @@ -20,7 +20,8 @@ static inline bool fuse_is_io_cache_wait(struct inode *inode) > struct fuse_inode *fi = get_fuse_inode(inode); > > return S_ISREG(inode->i_mode) && > - READ_ONCE(fi->iocachectr) < 0 && !fuse_inode_backing(fi); > + READ_ONCE(fi->iocachectr) < 0 && > + !fuse_passthrough_op(inode, FUSE_PASSTHROUGH_RW_OPS); > } Obviously, this was naive and not close to enough, because fuse_inode_set_passthrough() still takes the negative iocachectr refcount. Please ignore this. Supporting caching iomode and GETATTR passthrough is left for a future exercise, if anyone is ever interested. Thanks, Amir.