git: cb85c2e2e995 - stable/15 - exterror(9): add infra for bufs and bios

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Mon, 01 Dec 2025 03:48:24 UTC
The branch stable/15 has been updated by kib:

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

commit cb85c2e2e99551fb067ef8a20400e419069a871e
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2025-10-25 09:18:28 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2025-12-01 03:39:09 +0000

    exterror(9): add infra for bufs and bios
    
    MFC note: this commit changes the layout for both struct buf and bio.
    As such, it breaks KBI for VFS, which by itself often happens after X.0,
    so this is not a precedent.
    
    Discussed with: mckusick
    Approved by:    re (cperciva)
    
    (cherry picked from commit 6c406b5b93125d030f0e63716ff389ce1a6ec4c5)
---
 sys/geom/geom_dev.c  |  4 ++++
 sys/geom/geom_disk.c | 10 ++++++++--
 sys/geom/geom_subr.c | 10 ++++++++--
 sys/geom/geom_vfs.c  |  7 ++++++-
 sys/kern/vfs_bio.c   | 15 +++++++++++----
 sys/sys/bio.h        |  7 ++++++-
 sys/sys/buf.h        | 10 +++++++++-
 sys/sys/exterr_cat.h |  2 ++
 8 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c
index 46e2695293b8..e47be2768090 100644
--- a/sys/geom/geom_dev.c
+++ b/sys/geom/geom_dev.c
@@ -734,6 +734,10 @@ g_dev_done(struct bio *bp2)
 		g_trace(G_T_BIO, "g_dev_done(%p) had error %d",
 		    bp2, bp2->bio_error);
 		bp->bio_flags |= BIO_ERROR;
+		if ((bp2->bio_flags & BIO_EXTERR) != 0) {
+			bp->bio_flags |= BIO_EXTERR;
+			bp->bio_exterr = bp2->bio_exterr;
+		}
 	} else {
 		if (bp->bio_cmd == BIO_READ)
 			KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, NOTE_READ);
diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c
index 9dbf00371dba..b267130d1e0c 100644
--- a/sys/geom/geom_disk.c
+++ b/sys/geom/geom_disk.c
@@ -235,8 +235,14 @@ g_disk_done(struct bio *bp)
 	bp2 = bp->bio_parent;
 	binuptime(&now);
 	mtx_lock(&sc->done_mtx);
-	if (bp2->bio_error == 0)
-		bp2->bio_error = bp->bio_error;
+	if (bp2->bio_error == 0) {
+		if ((bp->bio_flags & BIO_EXTERR) != 0) {
+			bp2->bio_flags |= BIO_EXTERR;
+			bp2->bio_exterr = bp->bio_exterr;
+		} else {
+			bp2->bio_error = bp->bio_error;
+		}
+	}
 	bp2->bio_completed += bp->bio_length - bp->bio_resid;
 
 	if (bp->bio_cmd == BIO_READ)
diff --git a/sys/geom/geom_subr.c b/sys/geom/geom_subr.c
index 1429c84942ed..c002a5d9acfe 100644
--- a/sys/geom/geom_subr.c
+++ b/sys/geom/geom_subr.c
@@ -1151,8 +1151,14 @@ g_std_done(struct bio *bp)
 	struct bio *bp2;
 
 	bp2 = bp->bio_parent;
-	if (bp2->bio_error == 0)
-		bp2->bio_error = bp->bio_error;
+	if (bp2->bio_error == 0) {
+		if ((bp->bio_flags & BIO_EXTERR) != 0) {
+			bp2->bio_flags |= BIO_EXTERR;
+			bp2->bio_exterr = bp->bio_exterr;
+		} else {
+			bp2->bio_error = bp->bio_error;
+		}
+	}
 	bp2->bio_completed += bp->bio_completed;
 	g_destroy_bio(bp);
 	bp2->bio_inbed++;
diff --git a/sys/geom/geom_vfs.c b/sys/geom/geom_vfs.c
index 9b5e5a84191f..f074ac43d245 100644
--- a/sys/geom/geom_vfs.c
+++ b/sys/geom/geom_vfs.c
@@ -26,9 +26,11 @@
  * SUCH DAMAGE.
  */
 
+#define	EXTERR_CATEGORY	EXTERR_CAT_GEOMVFS
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bio.h>
+#include <sys/exterrvar.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
@@ -156,10 +158,13 @@ g_vfs_done(struct bio *bip)
 			    " suppressing further ENXIO");
 		}
 	}
-	bp->b_error = bip->bio_error;
 	bp->b_ioflags = bip->bio_flags;
 	if (bip->bio_error)
 		bp->b_ioflags |= BIO_ERROR;
+	if ((bp->b_ioflags & BIO_EXTERR) != 0)
+		bp->b_exterr = bip->bio_exterr;
+	else
+		bp->b_error = bip->bio_error;
 	bp->b_resid = bp->b_bcount - bip->bio_completed;
 	g_destroy_bio(bip);
 
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index 19c39e42bafa..22b7fe8d059a 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -44,6 +44,7 @@
  * see man buf(9) for more info.
  */
 
+#define	EXTERR_CATEGORY	EXTERR_CAT_VFSBIO
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/asan.h>
@@ -55,6 +56,7 @@
 #include <sys/counter.h>
 #include <sys/devicestat.h>
 #include <sys/eventhandler.h>
+#include <sys/exterrvar.h>
 #include <sys/fail.h>
 #include <sys/ktr.h>
 #include <sys/limits.h>
@@ -1775,7 +1777,6 @@ buf_alloc(struct bufdomain *bd)
 	bp->b_blkno = bp->b_lblkno = 0;
 	bp->b_offset = NOOFFSET;
 	bp->b_iodone = 0;
-	bp->b_error = 0;
 	bp->b_resid = 0;
 	bp->b_bcount = 0;
 	bp->b_npages = 0;
@@ -1785,6 +1786,7 @@ buf_alloc(struct bufdomain *bd)
 	bp->b_fsprivate1 = NULL;
 	bp->b_fsprivate2 = NULL;
 	bp->b_fsprivate3 = NULL;
+	exterr_clear(&bp->b_exterr);
 	LIST_INIT(&bp->b_dep);
 
 	return (bp);
@@ -2276,7 +2278,7 @@ breadn_flags(struct vnode *vp, daddr_t blkno, daddr_t dblkno, int size,
 		}
 		if ((flags & GB_CVTENXIO) != 0)
 			bp->b_xflags |= BX_CVTENXIO;
-		bp->b_ioflags &= ~BIO_ERROR;
+		bp->b_ioflags &= ~(BIO_ERROR | BIO_EXTERR);
 		if (bp->b_rcred == NOCRED && cred != NOCRED)
 			bp->b_rcred = crhold(cred);
 		vfs_busy_pages(bp, 0);
@@ -2353,7 +2355,7 @@ bufwrite(struct buf *bp)
 	bundirty(bp);
 
 	bp->b_flags &= ~B_DONE;
-	bp->b_ioflags &= ~BIO_ERROR;
+	bp->b_ioflags &= ~(BIO_ERROR | BIO_EXTERR);
 	bp->b_flags |= B_CACHE;
 	bp->b_iocmd = BIO_WRITE;
 
@@ -4520,8 +4522,11 @@ biowait(struct bio *bp, const char *wmesg)
 	while ((bp->bio_flags & BIO_DONE) == 0)
 		msleep(bp, mtxp, PRIBIO, wmesg, 0);
 	mtx_unlock(mtxp);
-	if (bp->bio_error != 0)
+	if (bp->bio_error != 0) {
+		if ((bp->bio_flags & BIO_EXTERR) != 0)
+			return (exterr_set_from(&bp->bio_exterr));
 		return (bp->bio_error);
+	}
 	if (!(bp->bio_flags & BIO_ERROR))
 		return (0);
 	return (EIO);
@@ -4568,6 +4573,8 @@ bufwait(struct buf *bp)
 		return (EINTR);
 	}
 	if (bp->b_ioflags & BIO_ERROR) {
+		if ((bp->b_ioflags & BIO_EXTERR) != 0)
+			exterr_set_from(&bp->b_exterr);
 		return (bp->b_error ? bp->b_error : EIO);
 	} else {
 		return (0);
diff --git a/sys/sys/bio.h b/sys/sys/bio.h
index 74d2b03bd180..fa7f19961ebd 100644
--- a/sys/sys/bio.h
+++ b/sys/sys/bio.h
@@ -37,6 +37,7 @@
 #ifndef _SYS_BIO_H_
 #define	_SYS_BIO_H_
 
+#include <sys/_exterr.h>
 #include <sys/queue.h>
 #include <sys/disk_zone.h>
 
@@ -65,6 +66,7 @@
 #define	BIO_TRANSIENT_MAPPING	0x20
 #define	BIO_VLIST	0x40
 #define	BIO_SWAP	0x200	/* Swap-related I/O */
+#define	BIO_EXTERR	0x2000
 #define BIO_SPEEDUP_WRITE	0x4000	/* Resource shortage at upper layers */
 #define BIO_SPEEDUP_TRIM	0x8000	/* Resource shortage at upper layers */
 
@@ -94,7 +96,6 @@ struct bio {
 	struct vm_page **bio_ma;	/* Or unmapped. */
 	int	bio_ma_offset;		/* Offset in the first page of bio_ma. */
 	int	bio_ma_n;		/* Number of pages in bio_ma. */
-	int	bio_error;		/* Errno for BIO_ERROR. */
 	long	bio_resid;		/* Remaining I/O in bytes. */
 	void	(*bio_done)(struct bio *);
 	void	*bio_driver1;		/* Private use by the provider. */
@@ -130,8 +131,12 @@ struct bio {
 
 	/* XXX: these go away when bio chaining is introduced */
 	daddr_t bio_pblkno;               /* physical block number */
+	struct kexterr bio_exterr;
 };
 
+/* Errno for BIO_ERROR. */
+#define	bio_error	bio_exterr.error
+
 struct uio;
 struct devstat;
 
diff --git a/sys/sys/buf.h b/sys/sys/buf.h
index 064d5cb05214..f08f05e6d50f 100644
--- a/sys/sys/buf.h
+++ b/sys/sys/buf.h
@@ -37,6 +37,7 @@
 #ifndef _SYS_BUF_H_
 #define	_SYS_BUF_H_
 
+#include <sys/_exterr.h>
 #include <sys/bufobj.h>
 #include <sys/queue.h>
 #include <sys/lock.h>
@@ -98,7 +99,6 @@ struct buf {
 	long		b_bcount;
 	void		*b_caller1;
 	caddr_t		b_data;
-	int		b_error;
 	uint16_t	b_iocmd;	/* BIO_* bio_cmd from bio.h */
 	uint16_t	b_ioflags;	/* BIO_* bio_flags from bio.h */
 	off_t		b_iooffset;
@@ -153,10 +153,12 @@ struct buf {
 #elif defined(BUF_TRACKING)
 	const char	*b_io_tracking;
 #endif
+	struct	kexterr b_exterr;
 	struct	vm_page *b_pages[];
 };
 
 #define b_object	b_bufobj->bo_object
+#define	b_error		b_exterr.error
 
 /*
  * These flags are kept in b_flags.
@@ -390,6 +392,12 @@ struct buf {
 	_lockmgr_disown(&(bp)->b_lock, LOCK_FILE, LOCK_LINE)
 #endif
 
+#define	BUF_EXTERR_FROM_CURTHR(bp)					\
+	bp->b_exterr = curthread->td_kexterr
+
+#define	BUF_EXTERR_TO_CURTHR(bp)					\
+	curthread->td_kexterr = bp->b_exterr
+
 #endif /* _KERNEL */
 
 struct buf_queue_head {
diff --git a/sys/sys/exterr_cat.h b/sys/sys/exterr_cat.h
index 43f31e1d5dd6..34a4b9f86694 100644
--- a/sys/sys/exterr_cat.h
+++ b/sys/sys/exterr_cat.h
@@ -21,6 +21,8 @@
 #define	EXTERR_CAT_BRIDGE	7
 #define	EXTERR_CAT_SWAP		8
 #define	EXTERR_CAT_VFSSYSCALL	9
+#define	EXTERR_CAT_VFSBIO	10
+#define	EXTERR_CAT_GEOMVFS	11
 
 #endif