Index: sys/kern/vfs_wapbl.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_wapbl.c,v
retrieving revision 1.112
diff -u -r1.112 vfs_wapbl.c
--- sys/kern/vfs_wapbl.c	9 Apr 2022 23:38:33 -0000	1.112
+++ sys/kern/vfs_wapbl.c	26 Jul 2025 16:06:23 -0000
@@ -241,6 +241,9 @@
 #define WAPBL_JDATA_FLAGS(wl)	\
 		(WAPBL_JFLAGS(wl) & B_MEDIA_DPO)	/* only DPO */
 	int wl_jwrite_flags;	/* r: 	journal write flags */
+
+	size_t wl_ext_size;	/* lm: size of externally managed metadata */
+	void *wl_ext;		/* u: handle for externally managed metadata */
 };
 
 #ifdef WAPBL_DEBUG_PRINT
@@ -313,8 +316,12 @@
 	.wo_wapbl_junlock_assert= wapbl_junlock_assert,
 	.wo_wapbl_jlock_assert	= wapbl_jlock_assert,
 
-	/* XXX: the following is only used to say "this is a wapbl buf" */
+	/* XXX: wo_wapbl_biodone is only used to say "this is a wapbl buf" */
 	.wo_wapbl_biodone	= wapbl_biodone,
+	.wo_wapbl_usage		= wapbl_usage,
+	.wo_wapbl_get_ext	= wapbl_get_ext,
+	.wo_wapbl_set_ext	= wapbl_set_ext,
+	.wo_wapbl_add_ext	= wapbl_add_ext,
 };
 
 SYSCTL_SETUP(wapbl_sysctl_init, "wapbl sysctl")
@@ -649,6 +656,9 @@
 		TAILQ_INSERT_TAIL(&wl->wl_iobufs, bp, b_wapbllist);
 	}
 
+	wl->wl_ext_size = 0;
+	wl->wl_ext = NULL;
+
 	/*
 	 * if there was an existing set of unlinked but
 	 * allocated inodes, preserve it in the new
@@ -707,6 +717,7 @@
 	 */
 	rw_enter(&wl->wl_rwlock, RW_WRITER);
 	wl->wl_flush(wl->wl_mount, TAILQ_FIRST(&wl->wl_dealloclist));
+	wl->wl_ext_size = 0;
 
 #ifdef WAPBL_DEBUG_PRINT
 	{
@@ -1208,7 +1219,8 @@
 		   wl->wl_bufbytes_max / 2) ||
 		  ((wl->wl_bufcount + (lockcount * 10)) >
 		   wl->wl_bufcount_max / 2) ||
-		  (wapbl_transaction_len(wl) > wl->wl_circ_size / 2) ||
+		  (wapbl_transaction_len(wl) + wl->wl_ext_size
+			> wl->wl_circ_size / 2) ||
 		  (wl->wl_dealloccnt >= (wl->wl_dealloclim / 2));
 	mutex_exit(&wl->wl_mtx);
 
@@ -1783,6 +1795,7 @@
 	 */
 	rw_enter(&wl->wl_rwlock, RW_WRITER);
 	wl->wl_flush(wl->wl_mount, TAILQ_FIRST(&wl->wl_dealloclist));
+	wl->wl_ext_size = 0;
 
 	/*
 	 * Now that we are exclusively locked and the file system has
@@ -1960,6 +1973,7 @@
 	if (error) {
 		wl->wl_flush_abort(wl->wl_mount,
 		    TAILQ_FIRST(&wl->wl_dealloclist));
+		wl->wl_ext_size = 0;
 	}
 
 #ifdef WAPBL_DEBUG_PRINT
@@ -3426,6 +3440,54 @@
 
 #ifdef _KERNEL
 
+ssize_t
+wapbl_usage(struct wapbl *wl, size_t *capp, size_t *usagep, size_t *extp)
+{
+	size_t capacity, usage, ext;
+
+	mutex_enter(&wl->wl_mtx);
+	capacity = wl->wl_circ_size - wl->wl_reserved_bytes;
+	usage = wapbl_transaction_len(wl);
+	ext = wl->wl_ext_size;
+	mutex_exit(&wl->wl_mtx);
+
+	if (capp != NULL)
+		*capp = capacity;
+	if (usagep != NULL)
+		*usagep = usage;
+	if (extp != NULL)
+		*extp = ext;
+
+	return (ssize_t)(capacity - (usage + ext));
+}
+
+void
+wapbl_add_ext(struct wapbl *wl, size_t bsize)
+{
+	mutex_enter(&wl->wl_mtx);
+	wl->wl_ext_size += bsize;
+	mutex_exit(&wl->wl_mtx);
+}
+
+void *
+wapbl_get_ext(struct wapbl *wl)
+{
+	void *vp;
+
+	mutex_enter(&wl->wl_mtx);
+	vp = wl->wl_ext;
+	mutex_exit(&wl->wl_mtx);
+	return vp;
+}
+
+void
+wapbl_set_ext(struct wapbl *wl, void *v)
+{
+	mutex_enter(&wl->wl_mtx);
+	wl->wl_ext = v;
+	mutex_exit(&wl->wl_mtx);
+}
+
 MODULE(MODULE_CLASS_VFS, wapbl, NULL);
 
 static int
Index: sys/sys/mount.h
===================================================================
RCS file: /cvsroot/src/sys/sys/mount.h,v
retrieving revision 1.240
diff -u -r1.240 mount.h
--- sys/sys/mount.h	4 Nov 2022 11:20:40 -0000	1.240
+++ sys/sys/mount.h	26 Jul 2025 16:06:23 -0000
@@ -296,6 +297,10 @@
 	void (*wo_wapbl_junlock_assert)(struct wapbl *);
 	void (*wo_wapbl_jlock_assert)(struct wapbl *);
 	void (*wo_wapbl_biodone)(struct buf *);
+	ssize_t (*wo_wapbl_usage)(struct wapbl *, size_t *, size_t *, size_t *);
+	void (*wo_wapbl_add_ext)(struct wapbl *, size_t);
+	void (*wo_wapbl_set_ext)(struct wapbl *, void *);
+	void *(*wo_wapbl_get_ext)(struct wapbl *);
 };
 #define WAPBL_DISCARD(MP)						\
     (*(MP)->mnt_wapbl_op->wo_wapbl_discard)((MP)->mnt_wapbl)
Index: sys/sys/wapbl.h
===================================================================
RCS file: /cvsroot/src/sys/sys/wapbl.h,v
retrieving revision 1.21
diff -u -r1.21 wapbl.h
--- sys/sys/wapbl.h	10 Dec 2018 21:19:33 -0000	1.21
+++ sys/sys/wapbl.h	26 Jul 2025 16:06:23 -0000
@@ -152,6 +161,18 @@
 
 void	wapbl_resize_buf(struct wapbl *, struct buf *, long, long);
 
+/* Return the amount of space left in the transaction buffer */
+ssize_t	wapbl_usage(struct wapbl *, size_t *, size_t *, size_t *);
+
+/* Reserve space for external metadata to be written later */
+void	wapbl_add_ext(struct wapbl *, size_t);
+
+/* Register handle for external metadata management */
+void	wapbl_set_ext(struct wapbl *, void *);
+
+/* Retrieve handle for external metadata management */
+void *wapbl_get_ext(struct wapbl *);
+
 /*
  * This will flush all completed transactions to disk and
  * start asynchronous writes on the associated buffers
Index: sys/ufs/ffs/ffs_alloc.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_alloc.c,v
retrieving revision 1.171.4.1
diff -u -r1.171.4.1 ffs_alloc.c
--- sys/ufs/ffs/ffs_alloc.c	13 May 2023 11:51:14 -0000	1.171.4.1
+++ sys/ufs/ffs/ffs_alloc.c	26 Jul 2025 16:06:23 -0000
@@ -257,6 +257,7 @@
 		cg = dtog(fs, bpref);
 	bno = ffs_hashalloc(ip, cg, bpref, size, 0, flags, ffs_alloccg);
 	if (bno > 0) {
+		FFS_WAPBL_REGISTER_CG(ump->um_mountp, bno);
 		DIP_ADD(ip, blocks, btodb(size));
 		if (flags & IO_EXT)
 			ip->i_flag |= IN_CHANGE;
@@ -507,6 +508,7 @@
 			ffs_blkfree(fs, ip->i_devvp, bprev, (long)osize,
 			    ip->i_number);
 		}
+		FFS_WAPBL_REGISTER_CG(ump->um_mountp, bno);
 		DIP_ADD(ip, blocks, btodb(nsize - osize));
 		if (flags & IO_EXT)
 			ip->i_flag |= IN_CHANGE;
@@ -1748,6 +1763,7 @@
 	if (error)
 		return;
 
+	FFS_WAPBL_REGISTER_CG(ump->um_mountp, bno);
 	if (!ump->um_discarddata) {
 		ffs_blkfree_cg(fs, devvp, bno, size);
 		return;
Index: sys/ufs/ffs/ffs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_extern.h,v
retrieving revision 1.87.2.1
diff -u -r1.87.2.1 ffs_extern.h
--- sys/ufs/ffs/ffs_extern.h	13 May 2023 11:51:14 -0000	1.87.2.1
+++ sys/ufs/ffs/ffs_extern.h	26 Jul 2025 16:06:23 -0000
@@ -169,6 +169,7 @@
 int	ffs_wapbl_stop(struct mount *, int);
 int	ffs_wapbl_replay_start(struct mount *, struct fs *, struct vnode *);
 void	ffs_wapbl_blkalloc(struct fs *, struct vnode *, daddr_t, int);
+void	ffs_wapbl_register_cg(struct mount *, daddr_t, const char *, int);
 
 void	ffs_wapbl_sync_metadata(struct mount *, struct wapbl_dealloc *);
 void	ffs_wapbl_abort_sync_metadata(struct mount *, struct wapbl_dealloc *);
@@ -177,6 +178,13 @@
 extern int (**ffs_specop_p)(void *);
 extern int (**ffs_fifoop_p)(void *);
 
+#ifdef WAPBL
+#define	FFS_WAPBL_REGISTER_CG(mp, daddr)				\
+	ffs_wapbl_register_cg(mp, daddr, __func__, __LINE__)
+#else /* ! WAPBL */
+#define	FFS_WAPBL_REGISTER_CG(mp, daddr) do { } while (0)
+#endif /* ! WAPBL */
+
 #endif /* defined(_KERNEL) */
 
 /* ffs_appleufs.c */
Index: sys/ufs/ffs/ffs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_inode.c,v
retrieving revision 1.131
diff -u -r1.131 ffs_inode.c
--- sys/ufs/ffs/ffs_inode.c	31 Jul 2020 04:07:30 -0000	1.131
+++ sys/ufs/ffs/ffs_inode.c	26 Jul 2025 16:06:23 -0000
@@ -274,6 +274,9 @@
 					continue;
 				bsize = ffs_sblksize(fs, osize, i);
 				if (wapbl) {
+					error = UFS_WAPBL_CHECK(omp);
+					if (error)
+						return error;
 					error = UFS_WAPBL_REGISTER_DEALLOCATION(omp,
 					    FFS_FSBTODB(fs, oldblks[i]), bsize, NULL);
 					if (error)
@@ -474,6 +477,9 @@
 		if (bn != 0) {
 			if (lastiblock[level] < 0 &&
 			    oip->i_ump->um_mountp->mnt_wapbl) {
+				error = UFS_WAPBL_CHECK(omp);
+				if (error)
+					goto out;
 				error = UFS_WAPBL_REGISTER_DEALLOCATION(
 				    oip->i_ump->um_mountp,
 				    FFS_FSBTODB(fs, bn), fs->fs_bsize,
@@ -515,6 +521,9 @@
 		if (bn == 0)
 			continue;
 
+		error = UFS_WAPBL_CHECK(omp);
+		if (error)
+			goto out;
 		bsize = ffs_blksize(fs, oip, i);
 		if ((oip->i_ump->um_mountp->mnt_wapbl) &&
 		    (ovp->v_type != VREG)) {
@@ -558,6 +567,9 @@
 			bn += ffs_numfrags(fs, newspace);
 			if ((oip->i_ump->um_mountp->mnt_wapbl) &&
 			    (ovp->v_type != VREG)) {
+				error = UFS_WAPBL_CHECK(omp);
+				if (error)
+					goto out;
 				error = UFS_WAPBL_REGISTER_DEALLOCATION(
 				    oip->i_ump->um_mountp, FFS_FSBTODB(fs, bn),
 				    oldspace - newspace, NULL);
@@ -733,6 +745,9 @@
 		if (nb == 0)
 			continue;
 
+		error = UFS_WAPBL_CHECK(ip->i_ump->um_mountp);
+		if (error)
+			goto out;
 		if ((ip->i_ump->um_mountp->mnt_wapbl) &&
 		    ((level > SINGLE) || (ITOV(ip)->v_type != VREG))) {
 			error = UFS_WAPBL_REGISTER_DEALLOCATION(
Index: sys/ufs/ffs/ffs_wapbl.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_wapbl.c,v
retrieving revision 1.48
diff -u -r1.48 ffs_wapbl.c
--- sys/ufs/ffs/ffs_wapbl.c	22 May 2022 11:27:36 -0000	1.48
+++ sys/ufs/ffs/ffs_wapbl.c	26 Jul 2025 16:06:23 -0000
@@ -176,6 +176,7 @@
 	struct fs *fs = ump->um_fs;
 	int error __diagused;
 	struct wapbl_dealloc *wd;
+	char *bitmap;
 
 	UFS_WAPBL_JLOCK_ASSERT(ump->um_mountp);
 
@@ -194,6 +195,12 @@
 		error = ffs_cgupdate(ump, 0);
 		KASSERT(error == 0);
 	}
+
+	mutex_enter(&ump->um_lock);
+	bitmap = (char *)wapbl_get_ext(mp->mnt_wapbl);
+	if (bitmap != NULL)
+		memset(bitmap, 0, howmany(fs->fs_ncg, NBBY));
+	mutex_exit(&ump->um_lock);
 }
 
 void
@@ -202,6 +209,7 @@
 	struct ufsmount *ump = VFSTOUFS(mp);
 	struct fs *fs = ump->um_fs;
 	struct wapbl_dealloc *wd;
+	char *bitmap;
 
 	for (wd = fdealloc; wd != NULL; wd = TAILQ_NEXT(wd, wd_entries)) {
 		/*
@@ -213,6 +221,12 @@
 		ffs_blkalloc_ump(ump, FFS_DBTOFSB(fs, wd->wd_blkno),
 		    wd->wd_len);
 	}
+
+	mutex_enter(&ump->um_lock);
+	bitmap = (char *)wapbl_get_ext(mp->mnt_wapbl);
+	if (bitmap != NULL)
+		memset(bitmap, 0, howmany(fs->fs_ncg, NBBY));
+	mutex_exit(&ump->um_lock);
 }
 
 static int
@@ -361,6 +375,8 @@
 				return error;
 
 			mp->mnt_wapbl_op = &wapbl_ops;
+			wapbl_set_ext(mp->mnt_wapbl, kmem_alloc(
+				howmany(fs->fs_ncg, NBBY), KM_SLEEP));
 
 #ifdef WAPBL_DEBUG
 			printf("%s: %s: enabling logging\n", __func__,
@@ -433,6 +449,7 @@
 {
 	struct ufsmount *ump = VFSTOUFS(mp);
 	struct fs *fs = ump->um_fs;
+	char *extp;
 	int error;
 
 	if (mp->mnt_wapbl) {
@@ -460,13 +477,17 @@
 		KASSERT(error == 0);	/* XXX a bit drastic! */
 		UFS_WAPBL_END(mp);
 	forceout:
+		extp = wapbl_get_ext(mp->mnt_wapbl);
+		wapbl_set_ext(mp->mnt_wapbl, NULL);
 		error = wapbl_stop(mp->mnt_wapbl, force);
 		if (error) {
 			KASSERT(!force);
+			wapbl_set_ext(mp->mnt_wapbl, extp);
 			fs->fs_flags |= FS_DOWAPBL;
 			return error;
 		}
 		fs->fs_flags &= ~FS_DOWAPBL; /* Repeat in case of forced error */
+		kmem_free(extp, howmany(fs->fs_ncg, NBBY));
 		mp->mnt_wapbl = NULL;
 
 #ifdef WAPBL_DEBUG
@@ -926,3 +947,25 @@
 	*size = 0;
 	return;
 }
+
+void
+ffs_wapbl_register_cg(struct mount *mp, daddr_t bno, const char *func, int line)
+{
+	struct fs *fs;
+	u_int cg;
+	char *bitmap;
+	struct wapbl *wl = mp->mnt_wapbl;
+	
+        if (wl != NULL) {
+		fs = VFSTOUFS(mp)->um_fs;
+		cg = dtog(fs, bno);
+		bitmap = (char *)wapbl_get_ext(wl);
+		mutex_enter(&VFSTOUFS(mp)->um_lock);
+		if (!isset(bitmap, cg)) {
+			setbit(bitmap, cg);
+			wapbl_add_ext(wl, mp->mnt_stat.f_bsize);
+		}
+		mutex_exit(&VFSTOUFS(mp)->um_lock);
+        }
+}
+
Index: sys/ufs/ufs/ufs_readwrite.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_readwrite.c,v
retrieving revision 1.128
diff -u -r1.128 ufs_readwrite.c
--- sys/ufs/ufs/ufs_readwrite.c	21 Feb 2022 17:07:45 -0000	1.128
+++ sys/ufs/ufs/ufs_readwrite.c	26 Jul 2025 16:06:23 -0000
@@ -333,6 +333,10 @@
 
 		eob = ufs_blkroundup(fs, osize);
 		uvm_vnp_setwritesize(vp, eob);
+		if (UFS_WAPBL_CHECK(vp->v_mount) == EAGAIN) {
+			UFS_WAPBL_END(vp->v_mount);
+			UFS_WAPBL_BEGIN(vp->v_mount);
+		}
 		error = ufs_balloc_range(vp, osize, eob - osize, cred, aflag);
 		if (error)
 			goto out;
@@ -388,6 +394,10 @@
 		}
 
 		if (!overwrite) {
+			if (UFS_WAPBL_CHECK(vp->v_mount) == EAGAIN) {
+				UFS_WAPBL_END(vp->v_mount);
+				UFS_WAPBL_BEGIN(vp->v_mount);
+			}
 			error = ufs_balloc_range(vp, uio->uio_offset, bytelen,
 			    cred, aflag);
 			if (error)
@@ -399,6 +411,10 @@
 			genfs_node_unlock(vp);
 			if (error)
 				break;
+			if (UFS_WAPBL_CHECK(ump->um_mountp) == EAGAIN) {
+				UFS_WAPBL_END(ump->um_mountp);
+				UFS_WAPBL_BEGIN(ump->um_mountp);
+			}
 			ubc_flags |= UBC_FAULTBUSY;
 		}
 
Index: sys/ufs/ufs/ufs_wapbl.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_wapbl.h,v
retrieving revision 1.19
diff -u -r1.19 ufs_wapbl.h
--- sys/ufs/ufs/ufs_wapbl.h	11 Apr 2020 17:43:54 -0000	1.19
+++ sys/ufs/ufs/ufs_wapbl.h	26 Jul 2025 16:06:23 -0000
@@ -36,6 +36,7 @@
 #if defined(_KERNEL_OPT)
 #include "opt_wapbl.h"
 #endif
+#include <sys/wapbl.h>
 
 /*
  * Information for the journal location stored in the superblock.
@@ -94,6 +95,21 @@
 	return 0;
 }
 
+#define UFS_WAPBL_THRESH(mp) ((2 * UFS_NIADDR + 3) * mp->mnt_stat.f_bsize)
+
+static __inline int
+ufs_wapbl_check(struct mount *mp, const char *func, int line)
+{
+	if (mp->mnt_wapbl) {
+		size_t capacity, usage, cgusage;
+		(void)wapbl_usage(mp->mnt_wapbl, &capacity, &usage, &cgusage);
+		size_t threshold = capacity - UFS_WAPBL_THRESH(mp);
+		if (usage + cgusage >= threshold)
+			return EAGAIN;
+	}
+	return 0;
+}
+
 static __inline void
 ufs_wapbl_end(struct mount *mp)
 {
@@ -104,6 +120,8 @@
 
 #define	UFS_WAPBL_BEGIN(mp)						\
 	ufs_wapbl_begin(mp, __func__, __LINE__)
+#define	UFS_WAPBL_CHECK(mp)						\
+	ufs_wapbl_check(mp, __func__, __LINE__)
 #define	UFS_WAPBL_END(mp) ufs_wapbl_end(mp)
 
 #define	UFS_WAPBL_UPDATE(vp, access, modify, flags)			\
@@ -145,6 +163,7 @@
 
 #else /* ! WAPBL */
 #define	UFS_WAPBL_BEGIN(mp) (__USE(mp), 0)
+#define	UFS_WAPBL_CHECK(mp) (__USE(mp), 0)
 #define	UFS_WAPBL_END(mp)	do { } while (0)
 #define	UFS_WAPBL_UPDATE(vp, access, modify, flags)	do { } while (0)
 #define	UFS_WAPBL_JLOCK_ASSERT(mp)
