On Fri, Sep 12, 2025 at 5:27 PM Thomas Bertschinger <tahbertschinger@xxxxxxxxx> wrote: > > This defines a new flag FILEID_CACHED that the VFS can set in the > handle_type field of struct file_handle to request that the FS > implementations of fh_to_{dentry,parent}() only complete if they can > satisfy the request with cached data. > > Because not every FS implementation will recognize this new flag, those > that do recognize the flag can indicate their support using a new > export flag, EXPORT_OP_NONBLOCK. > > If FILEID_CACHED is set in a file handle, but the filesystem does not > set EXPORT_OP_NONBLOCK, then the VFS will return -EAGAIN without > attempting to call into the filesystem code. > > exportfs_decode_fh_raw() is updated to respect the new flag by returning > -EAGAIN when it would need to do an operation that may not be possible > with only cached data. > > Suggested-by: Amir Goldstein <amir73il@xxxxxxxxx> > Signed-off-by: Thomas Bertschinger <tahbertschinger@xxxxxxxxx> > --- > I didn't apply Amir's Reviewed-by for this patch because I added the > Documenation section, which was not reviewed in v2. Documentation looks good. Reviewed-by: Amir Goldstein <amir73il@xxxxxxxxx> Thanks, Amir. > > Documentation/filesystems/nfs/exporting.rst | 6 ++++++ > fs/exportfs/expfs.c | 12 ++++++++++++ > fs/fhandle.c | 2 ++ > include/linux/exportfs.h | 5 +++++ > 4 files changed, 25 insertions(+) > > diff --git a/Documentation/filesystems/nfs/exporting.rst b/Documentation/filesystems/nfs/exporting.rst > index de64d2d002a2..70f46eaeb0d4 100644 > --- a/Documentation/filesystems/nfs/exporting.rst > +++ b/Documentation/filesystems/nfs/exporting.rst > @@ -238,3 +238,9 @@ following flags are defined: > all of an inode's dirty data on last close. Exports that behave this > way should set EXPORT_OP_FLUSH_ON_CLOSE so that NFSD knows to skip > waiting for writeback when closing such files. > + > + EXPORT_OP_NONBLOCK - FS supports fh_to_{dentry,parent}() using cached data > + When performing open_by_handle_at(2) using io_uring, it is useful to > + complete the file open using only cached data when possible, otherwise > + failing with -EAGAIN. This flag indicates that the filesystem supports this > + mode of operation. > diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c > index 949ce6ef6c4e..e2cfdd9d6392 100644 > --- a/fs/exportfs/expfs.c > +++ b/fs/exportfs/expfs.c > @@ -441,6 +441,7 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, > void *context) > { > const struct export_operations *nop = mnt->mnt_sb->s_export_op; > + bool decode_cached = fileid_type & FILEID_CACHED; > struct dentry *result, *alias; > char nbuf[NAME_MAX+1]; > int err; > @@ -453,6 +454,10 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, > */ > if (!exportfs_can_decode_fh(nop)) > return ERR_PTR(-ESTALE); > + > + if (decode_cached && !(nop->flags & EXPORT_OP_NONBLOCK)) > + return ERR_PTR(-EAGAIN); > + > result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); > if (IS_ERR_OR_NULL(result)) > return result; > @@ -481,6 +486,10 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, > * filesystem root. > */ > if (result->d_flags & DCACHE_DISCONNECTED) { > + err = -EAGAIN; > + if (decode_cached) > + goto err_result; > + > err = reconnect_path(mnt, result, nbuf); > if (err) > goto err_result; > @@ -526,6 +535,9 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, > err = PTR_ERR(target_dir); > if (IS_ERR(target_dir)) > goto err_result; > + err = -EAGAIN; > + if (decode_cached && (target_dir->d_flags & DCACHE_DISCONNECTED)) > + goto err_result; > > /* > * And as usual we need to make sure the parent directory is > diff --git a/fs/fhandle.c b/fs/fhandle.c > index 2dc669aeb520..509ff8983f94 100644 > --- a/fs/fhandle.c > +++ b/fs/fhandle.c > @@ -273,6 +273,8 @@ static int do_handle_to_path(struct file_handle *handle, struct path *path, > if (IS_ERR_OR_NULL(dentry)) { > if (dentry == ERR_PTR(-ENOMEM)) > return -ENOMEM; > + if (dentry == ERR_PTR(-EAGAIN)) > + return -EAGAIN; > return -ESTALE; > } > path->dentry = dentry; > diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h > index 30a9791d88e0..8238b6f67956 100644 > --- a/include/linux/exportfs.h > +++ b/include/linux/exportfs.h > @@ -199,6 +199,8 @@ struct handle_to_path_ctx { > #define FILEID_FS_FLAGS_MASK 0xff00 > #define FILEID_FS_FLAGS(flags) ((flags) & FILEID_FS_FLAGS_MASK) > > +#define FILEID_CACHED 0x100 /* Use only cached data when decoding handle */ > + > /* User flags: */ > #define FILEID_USER_FLAGS_MASK 0xffff0000 > #define FILEID_USER_FLAGS(type) ((type) & FILEID_USER_FLAGS_MASK) > @@ -303,6 +305,9 @@ struct export_operations { > */ > #define EXPORT_OP_FLUSH_ON_CLOSE (0x20) /* fs flushes file data on close */ > #define EXPORT_OP_NOLOCKS (0x40) /* no file locking support */ > +#define EXPORT_OP_NONBLOCK (0x80) /* Filesystem supports non- > + blocking fh_to_dentry() > + */ > unsigned long flags; > }; > > -- > 2.51.0 >