git: 4a6526d84a56 - main - fusefs: optimize NFS readdir for FUSE_NO_OPENDIR_SUPPORT

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Fri, 04 Feb 2022 23:31:42 UTC
The branch main has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=4a6526d84a56f398732bff491e63aa42f796a27d

commit 4a6526d84a56f398732bff491e63aa42f796a27d
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2022-01-02 22:29:50 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2022-02-04 23:30:58 +0000

    fusefs: optimize NFS readdir for FUSE_NO_OPENDIR_SUPPORT
    
    In its lowest common denominator, FUSE does not require that a directory
    entry's d_off field is valid outside of the lifetime of the directory's
    FUSE file handle.  But since NFS is stateless, it must reopen the
    directory on every call to VOP_READDIR.  That means reading the
    directory all the way from the first entry.  Not only does this create
    an O(n^2) condition for large directories, but it can also result in
    incorrect behavior if either:
    
    * The file system _does_ change the d_off field for the last directory
      entry previously seen by NFS, or
    * The file system deletes the last directory entry previously seen by
      NFS.
    
    Handily, for file systems that set FUSE_NO_OPENDIR_SUPPORT d_off is
    guaranteed to be valid for the lifetime of the directory entry, there is
    no need to read the directory from the start.
    
    MFC after:      3 weeks
    Reviewed by:    rmacklem
---
 sys/fs/fuse/fuse_vnops.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
index 259925f54d9f..da925b5dcbb5 100644
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -1846,6 +1846,7 @@ fuse_vnop_readdir(struct vop_readdir_args *ap)
 	struct uio *uio = ap->a_uio;
 	struct ucred *cred = ap->a_cred;
 	struct fuse_filehandle *fufh = NULL;
+	struct fuse_data *mpdata = fuse_get_mpdata(vnode_mount(vp));
 	struct fuse_iov cookediov;
 	int err = 0;
 	uint64_t *cookies;
@@ -1874,13 +1875,14 @@ fuse_vnop_readdir(struct vop_readdir_args *ap)
 		 * must implicitly open the directory here
 		 */
 		err = fuse_filehandle_open(vp, FREAD, &fufh, curthread, cred);
-		if (err == 0) {
+		if (err == 0 && !(mpdata->dataflags & FSESS_NO_OPEN_SUPPORT)) {
 			/*
-			 * When a directory is opened, it must be read from
-			 * the beginning.  Hopefully, the "startoff" still
-			 * exists as an offset cookie for the directory.
-			 * If not, it will read the entire directory without
-			 * returning any entries and just return eof.
+			 * FUSE does not require a directory entry's d_off
+			 * field to be valid outside of the lifetime of the
+			 * directory's FUSE file handle.  So we must read the
+			 * directory from the beginning.  However, if the file
+			 * system sets FUSE_NO_OPENDIR_SUPPORT, then the d_off
+			 * field will be valid for the lifetime of the dirent.
 			 */
 			uio->uio_offset = 0;
 		}