git: d056bc6f401a - stable/13 - fusefs: inline fuse_io_dispatch

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Mon, 03 Jan 2022 03:03:35 UTC
The branch stable/13 has been updated by asomers:

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

commit d056bc6f401a01115b6f5fed532c5ce58c0b0414
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2021-12-05 21:25:17 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2022-01-03 02:54:39 +0000

    fusefs: inline fuse_io_dispatch
    
    This function was always confusing, because it created an H-shaped
    callgraph: two functions called in and left via different paths based on
    which which called.
    
    (cherry picked from commit dc433e1530af26b0430d66c06c342889e9609590)
---
 sys/fs/fuse/fuse_io.c    | 181 ++---------------------------------------------
 sys/fs/fuse/fuse_io.h    |  11 ++-
 sys/fs/fuse/fuse_vnops.c | 167 ++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 178 insertions(+), 181 deletions(-)

diff --git a/sys/fs/fuse/fuse_io.c b/sys/fs/fuse/fuse_io.c
index f85d17517ee0..609d08f9e3d5 100644
--- a/sys/fs/fuse/fuse_io.c
+++ b/sys/fs/fuse/fuse_io.c
@@ -120,184 +120,11 @@ SDT_PROVIDER_DECLARE(fusefs);
  */
 SDT_PROBE_DEFINE2(fusefs, , io, trace, "int", "char*");
 
-static int
-fuse_inval_buf_range(struct vnode *vp, off_t filesize, off_t start, off_t end);
-static int 
-fuse_read_directbackend(struct vnode *vp, struct uio *uio,
-    struct ucred *cred, struct fuse_filehandle *fufh);
-static int 
-fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
-    struct ucred *cred, struct fuse_filehandle *fufh, pid_t pid);
-static int 
-fuse_write_directbackend(struct vnode *vp, struct uio *uio,
-    struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
-    int ioflag, bool pages);
-static int 
-fuse_write_biobackend(struct vnode *vp, struct uio *uio,
-    struct ucred *cred, struct fuse_filehandle *fufh, int ioflag, pid_t pid);
-
-/* Invalidate a range of cached data, whether dirty of not */
-static int
-fuse_inval_buf_range(struct vnode *vp, off_t filesize, off_t start, off_t end)
-{
-	struct buf *bp;
-	daddr_t left_lbn, end_lbn, right_lbn;
-	off_t new_filesize;
-	int iosize, left_on, right_on, right_blksize;
-
-	iosize = fuse_iosize(vp);
-	left_lbn = start / iosize;
-	end_lbn = howmany(end, iosize);
-	left_on = start & (iosize - 1);
-	if (left_on != 0) {
-		bp = getblk(vp, left_lbn, iosize, PCATCH, 0, 0);
-		if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyend >= left_on) {
-			/* 
-			 * Flush the dirty buffer, because we don't have a
-			 * byte-granular way to record which parts of the
-			 * buffer are valid.
-			 */
-			bwrite(bp);
-			if (bp->b_error)
-				return (bp->b_error);
-		} else {
-			brelse(bp);
-		}
-	}
-	right_on = end & (iosize - 1);
-	if (right_on != 0) {
-		right_lbn = end / iosize;
-		new_filesize = MAX(filesize, end);
-		right_blksize = MIN(iosize, new_filesize - iosize * right_lbn);
-		bp = getblk(vp, right_lbn, right_blksize, PCATCH, 0, 0);
-		if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyoff < right_on) {
-			/* 
-			 * Flush the dirty buffer, because we don't have a
-			 * byte-granular way to record which parts of the
-			 * buffer are valid.
-			 */
-			bwrite(bp);
-			if (bp->b_error)
-				return (bp->b_error);
-		} else {
-			brelse(bp);
-		}
-	}
-
-	v_inval_buf_range(vp, left_lbn, end_lbn, iosize);
-	return (0);
-}
-
-SDT_PROBE_DEFINE5(fusefs, , io, io_dispatch, "struct vnode*", "struct uio*",
-		"int", "struct ucred*", "struct fuse_filehandle*");
-SDT_PROBE_DEFINE4(fusefs, , io, io_dispatch_filehandles_closed, "struct vnode*",
-    "struct uio*", "int", "struct ucred*");
-int
-fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
-    struct ucred *cred, pid_t pid)
-{
-	struct fuse_filehandle *fufh;
-	int err, directio;
-	int fflag;
-	bool closefufh = false;
-
-	MPASS(vp->v_type == VREG || vp->v_type == VDIR);
-
-	fflag = (uio->uio_rw == UIO_READ) ? FREAD : FWRITE;
-	err = fuse_filehandle_getrw(vp, fflag, &fufh, cred, pid);
-	if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
-		/* 
-		 * nfsd will do I/O without first doing VOP_OPEN.  We
-		 * must implicitly open the file here
-		 */
-		err = fuse_filehandle_open(vp, fflag, &fufh, curthread, cred);
-		closefufh = true;
-	}
-	else if (err) {
-		SDT_PROBE4(fusefs, , io, io_dispatch_filehandles_closed,
-			vp, uio, ioflag, cred);
-		printf("FUSE: io dispatch: filehandles are closed\n");
-		return err;
-	}
-	if (err)
-		goto out;
-	SDT_PROBE5(fusefs, , io, io_dispatch, vp, uio, ioflag, cred, fufh);
-
-	/*
-         * Ideally, when the daemon asks for direct io at open time, the
-         * standard file flag should be set according to this, so that would
-         * just change the default mode, which later on could be changed via
-         * fcntl(2).
-         * But this doesn't work, the O_DIRECT flag gets cleared at some point
-         * (don't know where). So to make any use of the Fuse direct_io option,
-         * we hardwire it into the file's private data (similarly to Linux,
-         * btw.).
-         */
-	directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));
-
-	switch (uio->uio_rw) {
-	case UIO_READ:
-		fuse_vnode_update(vp, FN_ATIMECHANGE);
-		if (directio) {
-			SDT_PROBE2(fusefs, , io, trace, 1,
-				"direct read of vnode");
-			err = fuse_read_directbackend(vp, uio, cred, fufh);
-		} else {
-			SDT_PROBE2(fusefs, , io, trace, 1,
-				"buffered read of vnode");
-			err = fuse_read_biobackend(vp, uio, ioflag, cred, fufh,
-				pid);
-		}
-		break;
-	case UIO_WRITE:
-		fuse_vnode_update(vp, FN_MTIMECHANGE | FN_CTIMECHANGE);
-		if (directio) {
-			off_t start, end, filesize;
-			bool pages = (ioflag & IO_VMIO) != 0;
-
-			SDT_PROBE2(fusefs, , io, trace, 1,
-				"direct write of vnode");
-
-			err = fuse_vnode_size(vp, &filesize, cred, curthread);
-			if (err)
-				goto out;
-
-			start = uio->uio_offset;
-			end = start + uio->uio_resid;
-			if (!pages) {
-				err = fuse_inval_buf_range(vp, filesize, start,
-				    end);
-				if (err)
-					return (err);
-			}
-			err = fuse_write_directbackend(vp, uio, cred, fufh,
-				filesize, ioflag, pages);
-		} else {
-			SDT_PROBE2(fusefs, , io, trace, 1,
-				"buffered write of vnode");
-			if (!fsess_opt_writeback(vnode_mount(vp)))
-				ioflag |= IO_SYNC;
-			err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag,
-				pid);
-		}
-		fuse_internal_clear_suid_on_write(vp, cred, uio->uio_td);
-		break;
-	default:
-		panic("uninterpreted mode passed to fuse_io_dispatch");
-	}
-
-out:
-	if (closefufh)
-		fuse_filehandle_close(vp, fufh, curthread, cred);
-
-	return (err);
-}
-
 SDT_PROBE_DEFINE4(fusefs, , io, read_bio_backend_start, "int", "int", "int", "int");
 SDT_PROBE_DEFINE2(fusefs, , io, read_bio_backend_feed, "int", "struct buf*");
 SDT_PROBE_DEFINE4(fusefs, , io, read_bio_backend_end, "int", "ssize_t", "int",
 		"struct buf*");
-static int
+int
 fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
     struct ucred *cred, struct fuse_filehandle *fufh, pid_t pid)
 {
@@ -403,7 +230,7 @@ SDT_PROBE_DEFINE1(fusefs, , io, read_directbackend_start,
 SDT_PROBE_DEFINE3(fusefs, , io, read_directbackend_complete,
 	"struct fuse_dispatcher*", "struct fuse_read_in*", "struct uio*");
 
-static int
+int
 fuse_read_directbackend(struct vnode *vp, struct uio *uio,
     struct ucred *cred, struct fuse_filehandle *fufh)
 {
@@ -465,7 +292,7 @@ out:
 	return (err);
 }
 
-static int
+int
 fuse_write_directbackend(struct vnode *vp, struct uio *uio,
     struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
     int ioflag, bool pages)
@@ -628,7 +455,7 @@ SDT_PROBE_DEFINE6(fusefs, , io, write_biobackend_start, "int64_t", "int", "int",
 SDT_PROBE_DEFINE2(fusefs, , io, write_biobackend_append_race, "long", "int");
 SDT_PROBE_DEFINE2(fusefs, , io, write_biobackend_issue, "int", "struct buf*");
 
-static int
+int
 fuse_write_biobackend(struct vnode *vp, struct uio *uio,
     struct ucred *cred, struct fuse_filehandle *fufh, int ioflag, pid_t pid)
 {
diff --git a/sys/fs/fuse/fuse_io.h b/sys/fs/fuse/fuse_io.h
index 6240b6e89775..8305171fc941 100644
--- a/sys/fs/fuse/fuse_io.h
+++ b/sys/fs/fuse/fuse_io.h
@@ -65,10 +65,17 @@
 #ifndef _FUSE_IO_H_
 #define _FUSE_IO_H_
 
-int fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
-    struct ucred *cred, pid_t pid);
 int fuse_io_strategy(struct vnode *vp, struct buf *bp);
 int fuse_io_flushbuf(struct vnode *vp, int waitfor, struct thread *td);
 int fuse_io_invalbuf(struct vnode *vp, struct thread *td);
+int fuse_read_directbackend(struct vnode *vp, struct uio *uio,
+    struct ucred *cred, struct fuse_filehandle *fufh);
+int fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
+    struct ucred *cred, struct fuse_filehandle *fufh, pid_t pid);
+int fuse_write_directbackend(struct vnode *vp, struct uio *uio,
+    struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
+    int ioflag, bool pages);
+int fuse_write_biobackend(struct vnode *vp, struct uio *uio,
+    struct ucred *cred, struct fuse_filehandle *fufh, int ioflag, pid_t pid);
 
 #endif /* _FUSE_IO_H_ */
diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
index d0f4cae302c1..36507fae4d59 100644
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -318,6 +318,59 @@ fuse_fifo_close(struct vop_close_args *ap)
 	return (fifo_specops.vop_close(ap));
 }
 
+/* Invalidate a range of cached data, whether dirty of not */
+static int
+fuse_inval_buf_range(struct vnode *vp, off_t filesize, off_t start, off_t end)
+{
+	struct buf *bp;
+	daddr_t left_lbn, end_lbn, right_lbn;
+	off_t new_filesize;
+	int iosize, left_on, right_on, right_blksize;
+
+	iosize = fuse_iosize(vp);
+	left_lbn = start / iosize;
+	end_lbn = howmany(end, iosize);
+	left_on = start & (iosize - 1);
+	if (left_on != 0) {
+		bp = getblk(vp, left_lbn, iosize, PCATCH, 0, 0);
+		if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyend >= left_on) {
+			/*
+			 * Flush the dirty buffer, because we don't have a
+			 * byte-granular way to record which parts of the
+			 * buffer are valid.
+			 */
+			bwrite(bp);
+			if (bp->b_error)
+				return (bp->b_error);
+		} else {
+			brelse(bp);
+		}
+	}
+	right_on = end & (iosize - 1);
+	if (right_on != 0) {
+		right_lbn = end / iosize;
+		new_filesize = MAX(filesize, end);
+		right_blksize = MIN(iosize, new_filesize - iosize * right_lbn);
+		bp = getblk(vp, right_lbn, right_blksize, PCATCH, 0, 0);
+		if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyoff < right_on) {
+			/*
+			 * Flush the dirty buffer, because we don't have a
+			 * byte-granular way to record which parts of the
+			 * buffer are valid.
+			 */
+			bwrite(bp);
+			if (bp->b_error)
+				return (bp->b_error);
+		} else {
+			brelse(bp);
+		}
+	}
+
+	v_inval_buf_range(vp, left_lbn, end_lbn, iosize);
+	return (0);
+}
+
+
 /* Send FUSE_LSEEK for this node */
 static int
 fuse_vnop_do_lseek(struct vnode *vp, struct thread *td, struct ucred *cred,
@@ -1587,6 +1640,8 @@ fuse_vnop_pathconf(struct vop_pathconf_args *ap)
 	}
 }
 
+SDT_PROBE_DEFINE3(fusefs, , vnops, filehandles_closed, "struct vnode*",
+    "struct uio*", "struct ucred*");
 /*
     struct vnop_read_args {
 	struct vnode *a_vp;
@@ -1603,6 +1658,11 @@ fuse_vnop_read(struct vop_read_args *ap)
 	int ioflag = ap->a_ioflag;
 	struct ucred *cred = ap->a_cred;
 	pid_t pid = curthread->td_proc->p_pid;
+	struct fuse_filehandle *fufh;
+	int err;
+	bool closefufh = false, directio;
+
+	MPASS(vp->v_type == VREG || vp->v_type == VDIR);
 
 	if (fuse_isdeadfs(vp)) {
 		return ENXIO;
@@ -1612,7 +1672,45 @@ fuse_vnop_read(struct vop_read_args *ap)
 		ioflag |= IO_DIRECT;
 	}
 
-	return fuse_io_dispatch(vp, uio, ioflag, cred, pid);
+	err = fuse_filehandle_getrw(vp, FREAD, &fufh, cred, pid);
+	if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
+		/*
+		 * nfsd will do I/O without first doing VOP_OPEN.  We
+		 * must implicitly open the file here
+		 */
+		err = fuse_filehandle_open(vp, FREAD, &fufh, curthread, cred);
+		closefufh = true;
+	}
+	if (err) {
+		SDT_PROBE3(fusefs, , vnops, filehandles_closed, vp, uio, cred);
+		return err;
+	}
+
+	/*
+         * Ideally, when the daemon asks for direct io at open time, the
+         * standard file flag should be set according to this, so that would
+         * just change the default mode, which later on could be changed via
+         * fcntl(2).
+         * But this doesn't work, the O_DIRECT flag gets cleared at some point
+         * (don't know where). So to make any use of the Fuse direct_io option,
+         * we hardwire it into the file's private data (similarly to Linux,
+         * btw.).
+         */
+	directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));
+
+	fuse_vnode_update(vp, FN_ATIMECHANGE);
+	if (directio) {
+		SDT_PROBE2(fusefs, , vnops, trace, 1, "direct read of vnode");
+		err = fuse_read_directbackend(vp, uio, cred, fufh);
+	} else {
+		SDT_PROBE2(fusefs, , vnops, trace, 1, "buffered read of vnode");
+		err = fuse_read_biobackend(vp, uio, ioflag, cred, fufh, pid);
+	}
+
+	if (closefufh)
+		fuse_filehandle_close(vp, fufh, curthread, cred);
+
+	return (err);
 }
 
 /*
@@ -2165,6 +2263,11 @@ fuse_vnop_write(struct vop_write_args *ap)
 	int ioflag = ap->a_ioflag;
 	struct ucred *cred = ap->a_cred;
 	pid_t pid = curthread->td_proc->p_pid;
+	struct fuse_filehandle *fufh;
+	int err;
+	bool closefufh = false, directio;
+
+	MPASS(vp->v_type == VREG || vp->v_type == VDIR);
 
 	if (fuse_isdeadfs(vp)) {
 		return ENXIO;
@@ -2174,7 +2277,67 @@ fuse_vnop_write(struct vop_write_args *ap)
 		ioflag |= IO_DIRECT;
 	}
 
-	return fuse_io_dispatch(vp, uio, ioflag, cred, pid);
+	err = fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
+	if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
+		/*
+		 * nfsd will do I/O without first doing VOP_OPEN.  We
+		 * must implicitly open the file here
+		 */
+		err = fuse_filehandle_open(vp, FWRITE, &fufh, curthread, cred);
+		closefufh = true;
+	}
+	if (err) {
+		SDT_PROBE3(fusefs, , vnops, filehandles_closed, vp, uio, cred);
+		return err;
+	}
+
+	/*
+         * Ideally, when the daemon asks for direct io at open time, the
+         * standard file flag should be set according to this, so that would
+         * just change the default mode, which later on could be changed via
+         * fcntl(2).
+         * But this doesn't work, the O_DIRECT flag gets cleared at some point
+         * (don't know where). So to make any use of the Fuse direct_io option,
+         * we hardwire it into the file's private data (similarly to Linux,
+         * btw.).
+         */
+	directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));
+
+	fuse_vnode_update(vp, FN_MTIMECHANGE | FN_CTIMECHANGE);
+	if (directio) {
+		off_t start, end, filesize;
+		bool pages = (ioflag & IO_VMIO) != 0;
+
+		SDT_PROBE2(fusefs, , vnops, trace, 1, "direct write of vnode");
+
+		err = fuse_vnode_size(vp, &filesize, cred, curthread);
+		if (err)
+			goto out;
+
+		start = uio->uio_offset;
+		end = start + uio->uio_resid;
+		if (!pages) {
+			err = fuse_inval_buf_range(vp, filesize, start,
+			    end);
+			if (err)
+				goto out;
+		}
+		err = fuse_write_directbackend(vp, uio, cred, fufh,
+			filesize, ioflag, pages);
+	} else {
+		SDT_PROBE2(fusefs, , vnops, trace, 1,
+			"buffered write of vnode");
+		if (!fsess_opt_writeback(vnode_mount(vp)))
+			ioflag |= IO_SYNC;
+		err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag, pid);
+	}
+	fuse_internal_clear_suid_on_write(vp, cred, uio->uio_td);
+
+out:
+	if (closefufh)
+		fuse_filehandle_close(vp, fufh, curthread, cred);
+
+	return (err);
 }
 
 static daddr_t