Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 12 Feb 2021 01:07:01 GMT
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: bf0db19339e7 - main - buf SU hooks: track buf_start() calls with B_IOSTARTED flag
Message-ID:  <202102120107.11C171c7070389@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by kib:

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

commit bf0db19339e770a82236b74f523be4b572bde15d
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-01-30 02:10:34 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2021-02-12 01:02:19 +0000

    buf SU hooks: track buf_start() calls with B_IOSTARTED flag
    
    and only call buf_complete() if previously started.  Some error paths,
    like CoW failire, might skip buf_start() and do bufdone(), which itself
    call buf_complete().
    
    Various SU handle_written_XXX() functions check that io was started
    and incomplete parts of the buffer data reverted before restoring them.
    This is a useful invariant that B_IO_STARTED on buffer layer allows to
    keep instead of changing check and panic into check and return.
    
    Reported by:    pho
    Reviewed by:    chs, mckusick
    Tested by:      pho
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundations
---
 sys/kern/vfs_bio.c       | 14 ++++++++++++++
 sys/sys/buf.h            | 16 +++++++++++-----
 sys/ufs/ffs/ffs_vfsops.c |  4 ++--
 3 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index ff25e5b0043c..45be14b47207 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -2639,6 +2639,13 @@ brelse(struct buf *bp)
 		return;
 	}
 
+	if (LIST_EMPTY(&bp->b_dep)) {
+		bp->b_flags &= ~B_IOSTARTED;
+	} else {
+		KASSERT((bp->b_flags & B_IOSTARTED) == 0,
+		    ("brelse: SU io not finished bp %p", bp));
+	}
+
 	if ((bp->b_vflags & (BV_BKGRDINPROG | BV_BKGRDERR)) == BV_BKGRDERR) {
 		BO_LOCK(bp->b_bufobj);
 		bp->b_vflags &= ~BV_BKGRDERR;
@@ -2826,6 +2833,13 @@ bqrelse(struct buf *bp)
 	bp->b_flags &= ~(B_ASYNC | B_NOCACHE | B_AGE | B_RELBUF);
 	bp->b_xflags &= ~(BX_CVTENXIO);
 
+	if (LIST_EMPTY(&bp->b_dep)) {
+		bp->b_flags &= ~B_IOSTARTED;
+	} else {
+		KASSERT((bp->b_flags & B_IOSTARTED) == 0,
+		    ("bqrelse: SU io not finished bp %p", bp));
+	}
+
 	if (bp->b_flags & B_MANAGED) {
 		if (bp->b_flags & B_REMFREE)
 			bremfreef(bp);
diff --git a/sys/sys/buf.h b/sys/sys/buf.h
index 50fa0f35491e..2997560b9ab3 100644
--- a/sys/sys/buf.h
+++ b/sys/sys/buf.h
@@ -232,7 +232,7 @@ struct buf {
 #define	B_MALLOC	0x00010000	/* malloced b_data */
 #define	B_CLUSTEROK	0x00020000	/* Pagein op, so swap() can count it. */
 #define	B_INVALONERR	0x00040000	/* Invalidate on write error. */
-#define	B_00080000	0x00080000	/* Available flag. */
+#define	B_IOSTARTED	0x00080000	/* buf_start() called */
 #define	B_00100000	0x00100000	/* Available flag. */
 #define	B_MAXPHYS	0x00200000	/* nitems(b_pages[]) = atop(MAXPHYS). */
 #define	B_RELBUF	0x00400000	/* Release VMIO buffer. */
@@ -248,8 +248,8 @@ struct buf {
 
 #define PRINT_BUF_FLAGS "\20\40remfree\37cluster\36vmio\35ram\34managed" \
 	"\33paging\32infreecnt\31nocopy\30b23\27relbuf\26maxphys\25b20" \
-	"\24b19\23invalonerr\22clusterok\21malloc\20nocache\17b14\16inval" \
-	"\15reuse\14noreuse\13eintr\12done\11b8\10delwri" \
+	"\24iostarted\23invalonerr\22clusterok\21malloc\20nocache\17b14" \
+	"\16inval\15reuse\14noreuse\13eintr\12done\11b8\10delwri" \
 	"\7validsuspwrt\6cache\5deferred\4direct\3async\2needcommit\1age"
 
 /*
@@ -434,6 +434,9 @@ bstrategy(struct buf *bp)
 static __inline void
 buf_start(struct buf *bp)
 {
+	KASSERT((bp->b_flags & B_IOSTARTED) == 0,
+	    ("recursed buf_start %p", bp));
+	bp->b_flags |= B_IOSTARTED;
 	if (bioops.io_start)
 		(*bioops.io_start)(bp);
 }
@@ -441,8 +444,11 @@ buf_start(struct buf *bp)
 static __inline void
 buf_complete(struct buf *bp)
 {
-	if (bioops.io_complete)
-		(*bioops.io_complete)(bp);
+	if ((bp->b_flags & B_IOSTARTED) != 0) {
+		bp->b_flags &= ~B_IOSTARTED;
+		if (bioops.io_complete)
+			(*bioops.io_complete)(bp);
+	}
 }
 
 static __inline void
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 60d4dad57d03..04afbfd4d6e4 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -2393,10 +2393,10 @@ ffs_backgroundwritedone(struct buf *bp)
 #endif
 	/*
 	 * This buffer is marked B_NOCACHE so when it is released
-	 * by biodone it will be tossed.
+	 * by biodone it will be tossed.  Clear B_IOSTARTED in case of error.
 	 */
 	bp->b_flags |= B_NOCACHE;
-	bp->b_flags &= ~B_CACHE;
+	bp->b_flags &= ~(B_CACHE | B_IOSTARTED);
 	pbrelvp(bp);
 
 	/*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202102120107.11C171c7070389>