NetBSD Problem Report #60531

From www@netbsd.org  Sun Aug  2 14:39:25 2026
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
	 client-signature RSA-PSS (2048 bits) client-digest SHA256)
	(Client CN "mail.netbsd.org", Issuer "YR2" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 122C01A923B
	for <gnats-bugs@gnats.NetBSD.org>; Sun,  2 Aug 2026 14:39:25 +0000 (UTC)
Message-Id: <20260802143923.8DB6E1A923C@mollari.NetBSD.org>
Date: Sun,  2 Aug 2026 14:39:23 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: looping "vflushbuf: dirty" message
X-Send-Pr-Version: www-1.0
X-From4GNATS: "campbell+netbsd@mumble.net via gnats" <gnats-admin@NetBSD.org>

>Number:         60531
>Notify-List:    hannken
>Category:       kern
>Synopsis:       looping "vflushbuf: dirty" message
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Aug 02 14:40:00 +0000 2026
>Last-Modified:  Mon Aug 17 15:55:01 +0000 2026
>Originator:     Taylor R Campbell
>Release:        10.1
>Organization:
The NetBSD Foundation, Inc.
>Environment:
NetBSD franklin.NetBSD.org 10.1_STABLE NetBSD 10.1_STABLE (FRANKLIN) #0: Tue Jan 28 19:43:34 UTC 2025  spz@franklin.NetBSD.org:/home/netbsd/10/amd64/obj/sys/arch/amd64/compile/FRANKLIN amd64
>Description:

	Sometimes, under heavy load (possibly snapshot-related), the
	kernel starts to spew this to the console:

vflushbuf: dirty: vnode 0xffffea44c2bd54c0 flags 0x4010<MPSAFE,ONWORKLST>
    tag VT_UFS(1) type VBLK(3) mount 0xffffea4ab61a2000 typedata 0xffffea4ab5f92560
    usecount 123652 writecount 0 holdcount 6035
    size 0 writesize 0 numoutput 0
    data 0xffffea44c2602558 lock 0xffffea44c2bd5680
    state LOADED key(0xffffea4ab61a2000 8) dd 23 23 00 00 00 00 00
    lrulisthd 0xffffffff80e88390
    tag VT_UFS, ino 2302941, on dev 4, 0 flags 0x0, nlink 1
    mode 060640, owner 0, group 5, size 0

	or

vflushbuf: dirty: vnode 0xffffcca82a4784c0 flags 0x4010<MPSAFE,ONWORKLST>
        tag VT_UFS(1) type VBLK(3) mount 0xffffcca82c32b000 typedata 0xffffcca82c13cf00
        usecount 30 writecount 0 holdcount 32
        size 0 writesize 0 numoutput 0
        data 0xffffcca82ab9a668 lock 0xffffcca82a478680
        state LOADED key(0xffffcca82c32b000 8) dd 23 23 00 00 00 00 00
        lrulisthd 0xffffffff80e88390
        tag VT_UFS, ino 2302941, on dev 4, 0 flags 0x0, nlink 1
        mode 060640, owner 0, group 5, size 0

	An incomprehensibly terse message like this is scary and bad
	enough for warning fatigue even if it is otherwise harmless,
	but it also sometimes renders the machine unusable by spewing
	constantly to the console.

	The inode and mounts in question here are:

$ ls -li /dev/sd0a /dev/dk0
2302941 brw-r-----  1 root  operator  168, 0 Oct 31  2015 /dev/dk0
2301566 brw-r-----  1 root  operator    4, 0 Oct 31  2015 /dev/sd0a
$ mount | grep -e dk0 -e sd0a
/dev/sd0a on / type ffs (log, noatime, nodevmtime, local)
/dev/dk0 on /storage type ffs (local)

	The messages have manifested on different machines over time,
	but these are the only ones I have handy to review.  From the
	sample I have, and from discussion with hannken@, I suspect
	it's always VBLK devices.

	When this last happened, there was an active snapshot of /
	being dumped, and heavy I/O to /storage to write dumps from
	this host and from various other hosts.

	There's a comment atop vflushbuf saying it assumes that holding
	the vnode lock (which it doesn't assert!) guarantees that new
	dirty buffers won't be queued, but I suspect there may be a
	path to queueing new dirty buffers _for VBLK vnodes_:

    330 /*
    331  * Flush all dirty buffers from a vnode.
    332  * Called with the underlying vnode locked, which should prevent new dirty
    333  * buffers from being queued.
    334  */
    335 int
    336 vflushbuf(struct vnode *vp, int flags)
    337 {
...
    349 loop:
    350 	mutex_enter(&bufcache_lock);
    351 	for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
    352 		KASSERT(bp->b_vp == vp);
...
    372 	}
    373 	mutex_exit(&bufcache_lock);
    374 
    375 	if (!sync)
    376 		return 0;
    377 
    378 	mutex_enter(vp->v_interlock);
    379 	while (vp->v_numoutput != 0)
    380 		cv_wait(&vp->v_cv, vp->v_interlock);
    381 	dirty = !LIST_EMPTY(&vp->v_dirtyblkhd);
    382 	mutex_exit(vp->v_interlock);
    383 
    384 	if (dirty) {
    385 		vprint("vflushbuf: dirty", vp);
    386 		goto loop;
    387 	}

>How-To-Repeat:

	not sure, maybe just heavy I/O and running sync(8)

>Fix:

	Yes, please!

	Possible workaround: print the message only if the vnode type
	is not VBLK.

>Release-Note:

>Audit-Trail:

From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: hannken@NetBSD.org
Subject: Re: kern/60531: looping "vflushbuf: dirty" message
Date: Sun, 2 Aug 2026 15:25:08 +0000

 I added KASSERT(VOP_ISLOCKED(vp)) to vflushbuf and to the path in
 reassignbuf that puts a buffer on vp->v_dirtyblkhd, and immediately
 hit:

 # mount -u -o rw,noatime,nodevmtime,log /
 [  20.8447068] panic: kernel diagnostic assertion "VOP_ISLOCKED(vp)" failed=
 : file "/home/riastradh/netbsd/current/src/sys/kern/vfs_subr.c", line 526
 [  20.8611358] cpu0: Begin traceback...
 [  20.8611358] trace fp ffffc000c64ff680
 [  20.8786823] fp ffffc000c64ff6b0 vpanic() at ffffc0000061f864 netbsd:vpan=
 ic+0x1bc
 [  20.8877138] fp ffffc000c64ff710 kern_assert() at ffffc000008c68b8 netbsd=
 :kern_assert+0x58
 [  20.9087004] fp ffffc000c64ff7a0 reassignbuf() at ffffc0000068639c netbsd=
 :reassignbuf+0x2bc
 [  20.9297182] fp ffffc000c64ff7d0 bdwrite() at ffffc000006743e8 netbsd:bdw=
 rite+0x128
 [  20.9507110] fp ffffc000c64ff800 ffs_nodealloccg() at ffffc00000536178 ne=
 tbsd:ffs_nodealloccg+0x730
 [  20.9827495] fp ffffc000c64ff8a0 ffs_hashalloc() at ffffc00000535278 netb=
 sd:ffs_hashalloc+0x38
 [  21.0036885] fp ffffc000c64ff900 ffs_valloc() at ffffc00000538400 netbsd:=
 ffs_valloc+0xe0
 [  21.0248940] fp ffffc000c64ff970 ffs_newvnode() at ffffc00000546598 netbs=
 d:ffs_newvnode+0x88
 [  21.0457100] fp ffffc000c64ffa00 vcache_new() at ffffc00000696738 netbsd:=
 vcache_new+0x94
 [  21.0667119] fp ffffc000c64ffa80 wapbl_log_position() at ffffc0000054ac3c=
  netbsd:wapbl_log_position+0x1f8
 [  21.0987335] fp ffffc000c64ffca0 ffs_wapbl_start() at ffffc0000054b9b4 ne=
 tbsd:ffs_wapbl_start+0x1e8
 [  21.1197893] fp ffffc000c64ffd00 ffs_mount() at ffffc00000548564 netbsd:f=
 fs_mount+0x564
 [  21.1407173] fp ffffc000c64ffd60 do_sys_mount() at ffffc0000068a4c4 netbs=
 d:do_sys_mount+0x584
 [  21.1617312] fp ffffc000c64ffe10 sys___mount50() at ffffc0000068a88c netb=
 sd:sys___mount50+0x28
 [  21.1826953] fp ffffc000c64ffe20 syscall() at ffffc000000be2c4 netbsd:sys=
 call+0x104

 So I think vflushbuf's assumption that buffers are added to
 v_dirtyblkhd only while the vnode lock is held may not be valid!

 Now, this path involves creating a new vnode, so that may not be
 relevant here.

From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: hannken@NetBSD.org, dholland@NetBSD.org
Subject: Re: kern/60531: looping "vflushbuf: dirty" message
Date: Sun, 2 Aug 2026 19:43:58 +0000

 > Date: Sun, 2 Aug 2026 15:25:08 +0000
 > From: Taylor R Campbell <riastradh@NetBSD.org>
 >=20
 > I added KASSERT(VOP_ISLOCKED(vp)) to vflushbuf and to the path in
 > reassignbuf that puts a buffer on vp->v_dirtyblkhd, and immediately
 > hit:
 >=20
 > # mount -u -o rw,noatime,nodevmtime,log /
 > [  20.8447068] panic: kernel diagnostic assertion "VOP_ISLOCKED(vp)" fail=
 ed: file "/home/riastradh/netbsd/current/src/sys/kern/vfs_subr.c", line 526
 > [  20.8611358] cpu0: Begin traceback...
 > [  20.8611358] trace fp ffffc000c64ff680
 > ...
 > [  20.9297182] fp ffffc000c64ff7d0 bdwrite() at ffffc000006743e8 netbsd:b=
 dwrite+0x128
 > [  20.9507110] fp ffffc000c64ff800 ffs_nodealloccg() at ffffc00000536178 =
 netbsd:ffs_nodealloccg+0x730
 > ...
 > [  21.0457100] fp ffffc000c64ffa00 vcache_new() at ffffc00000696738 netbs=
 d:vcache_new+0x94
 > ...
 > So I think vflushbuf's assumption that buffers are added to
 > v_dirtyblkhd only while the vnode lock is held may not be valid!
 >=20
 > Now, this path involves creating a new vnode, so that may not be
 > relevant here.

 dholland and I took a closer look at this path, and it turns out the
 assertion failure is not about the vnode being created -- it's about
 the block device the file system is mounted from:

    1261 static daddr_t
    1262 ffs_nodealloccg(struct inode *ip, u_int cg, daddr_t ipref, int mode=
 , int realsize,
    1263     int flags)
    1264 {
 ...
    1291 	error =3D bread(ip->i_devvp, FFS_FSBTODB(fs, cgtod(fs, cg)),
    1292 		(int)fs->fs_cgsize, B_MODIFY, &bp);
    1293 	if (error)
    1294 		goto fail;
 ...
    1416 	if (ibp !=3D NULL) {
    1417 		bwrite(ibp);
    1418 		bwrite(bp);
    1419 	} else
 =3D> 1420 		bdwrite(bp);
    1421 	return ((ino_t)(cg * fs->fs_ipg + ipref));

 https://nxr.netbsd.org/xref/src/sys/ufs/ffs/ffs_alloc.c?r=3D1.175#1252

 Here ip->i_devvp is presumably the block device the file system is
 mounted from (why do we even have ip->i_devvp instead of just using
 ump->um_devvp?):

    2182 int
    2183 ffs_loadvnode(struct mount *mp, struct vnode *vp,
    2184     const void *key, size_t key_len, const void **new_key)
    2185 {
 ...
    2212 	ip->i_devvp =3D ump->um_devvp;

 https://nxr.netbsd.org/xref/src/sys/ufs/ffs/ffs_vfsops.c?r=3D1.385#2212

    2234 int
    2235 ffs_newvnode(struct mount *mp, struct vnode *dvp, struct vnode *vp,
    2236     struct vattr *vap, kauth_cred_t cred, void *extra,
    2237     size_t *key_len, const void **new_key)
    2238 {
 ...
    2335 	ip->i_devvp =3D ump->um_devvp;

 https://nxr.netbsd.org/xref/src/sys/ufs/ffs/ffs_vfsops.c?r=3D1.385#2335

 I suspect there a are a lot of paths like this: bread/bdwrite on the
 block device to edit file system metadata.

 What are the bad consequences of calling reassignbuf without the vnode
 lock held?  Well, vflushbuf can only guarantee completion in bounded
 time if either:

 (a) other threads are blocked from adding new dirty bufs,
 or
 (b) vflushbuf can distinguish the bufs that were already on the queue
     when it started from those that were added after it started, e.g.
     with another queue or a flush generation counter.

 Without one of those two options, v_numoutput might never reach zero.
 On the other hand, once it _has_ reached zero once, we don't really
 need to loop at all: the printf is only evidence that the system
 _could_ have waited indefinitely in vflushbuf because new bwrites kept
 appearing as we waited -- but it _didn't_, because v_numoutput finally
 went down to zero.

 So I think we should do some combination of:

 1. delete the `goto loop' part

 2. put the vprint under #ifdef DEBUG

 3. vprint only once per call to vflushbuf, or at most twice -- once
    when we notice a potential problem, once when we've finally broken
    out of the loop

 4. apply a global rate limit to the vprint

 5. put a flush generation number on struct buf and wait for that to
    drain (i.e., wait until there are no dirty bufs queued with the old
    generation number) instead of waiting for v_numoutput to drain


 Curiously, this printf was put under #ifdef DIAGNOSTIC in the CSRG BSD
 tree back in 1992 when vflushbuf was deleted and open-coded in
 ffs_fsync:

  int
  ffs_fsync(ap)
 -	struct vop_fsync_args *ap;
 +	struct vop_fsync_args /* {
 +		struct vnode *a_vp;
 +		struct ucred *a_cred;
 +		int a_waitfor;
 +		struct proc *a_p;
 +	} */ *ap;
  {
 ...
 -	vflushbuf(ap->a_vp, ap->a_waitfor =3D=3D MNT_WAIT ? B_SYNC : 0);
 +	/*
 +	 * Flush all dirty buffers associated with a vnode.
 +	 */
 +loop:
 +	s =3D splbio();
 +	for (bp =3D vp->v_dirtyblkhd; bp; bp =3D nbp) {
 +		nbp =3D bp->b_blockf;
 +		if ((bp->b_flags & B_BUSY))
 +			continue;
 +		if ((bp->b_flags & B_DELWRI) =3D=3D 0)
 +			panic("ffs_fsync: not dirty");
 +		bremfree(bp);
 +		bp->b_flags |=3D B_BUSY;
 +		splx(s);
 +		/*
 +		 * Wait for I/O associated with indirect blocks to complete,
 +		 * since there is no way to quickly wait for them below.
 +		 */
 +		if (bp->b_vp =3D=3D vp || ap->a_waitfor =3D=3D MNT_NOWAIT)
 +			(void) bawrite(bp);
 +		else
 +			(void) bwrite(bp);
 +		goto loop;
 +	}
 +	if (ap->a_waitfor =3D=3D MNT_WAIT) {
 +		while (vp->v_numoutput) {
 +			vp->v_flag |=3D VBWAIT;
 +			sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1);
 +		}
 +#ifdef DIAGNOSTIC
 +		if (vp->v_dirtyblkhd) {
 +			vprint("ffs_fsync: dirty", vp);
 +			goto loop;
 +		}
 +#endif
 +	}

 https://github.com/robohack/ucb-csrg-bsd/commit/fce3da350147b2a731b529826ce=
 af7f89fb33311

From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: hannken@NetBSD.org, dholland@NetBSD.org
Subject: Re: kern/60531: looping "vflushbuf: dirty" message
Date: Sun, 2 Aug 2026 20:20:48 +0000

 Found another path to reassignbuf called without the vnode lock, this
 time for a VREG vnode instead of VBLK -- this one is definitely
 snapshot-related (presumably the vnode is the snapshot backing store,
 since it's marked VV_SYSTEM), and could cause the vflushbuf warning
 message and/or almost unbounded running time for the vflushbuf loop
 (really, bounded by the file system size; once the whole fs is backed
 up, there won't be any more writes to it):

 # mount
 /dev/dk1 on / type ffs (log, local)
 ptyfs on /dev/pts type ptyfs (local)
 tmpfs on /tmp type tmpfs (local)
 tmpfs on /var/run type tmpfs (local)
 tmpfs on /var/shm type tmpfs (local)
 /dev/vio9p0 on /mnt type puffs|9p
 /dev/ld4a on /mnt type ffs (local)
 # dump -X -0ant -f - / | gzip -nc >/mnt/var/tmp/dump.0
   [20:10:11 UTC] DUMP: Found /dev/rdk1 on / in /etc/fstab
   [20:10:11 UTC] DUMP: Date of this level 0 dump: Sun Aug  2 20:10:11 2026
   [20:10:11 UTC] DUMP: Date of last level 0 dump: the epoch
   [20:10:11 UTC] DUMP: Dumping a snapshot of /dev/rdk1 (/) to standard outp=
 ut
   [20:10:11 UTC] DUMP: Label: none
   [20:10:13 UTC] DUMP: mapping (Pass I) [regular files]
   [20:10:19 UTC] DUMP: mapping (Pass II) [directories]
   [20:10:19 UTC] DUMP: estimated 3140383 tape blocks.
   [20:10:19 UTC] DUMP: Volume 1 started at: Sun Aug  2 20:10:19 2026
   [20:10:19 UTC] DUMP: dumping (Pass III) [directories]
 [ 479.4390328] panic: kernel diagnostic assertion "vp->v_type =3D=3D VBLK |=
 | VOP_ISLOCKED(vp)" failed: file "/home/riastradh/netbsd/current/src/sys/ke=
 rn/vfs_subr.c", line 562 vp=3D0xffff00007cf02800 state=3D3
 [ 479.4577654] cpu0: Begin traceback...
 [ 479.4577654] trace fp ffffc000c63c78f0
 [ 479.4790797] fp ffffc000c63c7920 vpanic() at ffffc0000061f864 netbsd:vpan=
 ic+0x1bc
 [ 479.5110599] fp ffffc000c63c7980 kern_assert() at ffffc000008c6958 netbsd=
 :kern_assert+0x58
 [ 479.5425878] fp ffffc000c63c7a10 reassignbuf() at ffffc00000686434 netbsd=
 :reassignbuf+0x2d4
 [ 479.5740262] fp ffffc000c63c7a40 bdwrite() at ffffc000006743e8 netbsd:bdw=
 rite+0x128
 [ 479.5961369] fp ffffc000c63c7a70 ffs_balloc() at ffffc0000053c240 netbsd:=
 ffs_balloc+0x15e0
 [ 479.6380279] fp ffffc000c63c7bd0 wrsnapblk() at ffffc000005401c4 netbsd:w=
 rsnapblk+0x44
 [ 479.6800518] fp ffffc000c63c7c10 ffs_copyonwrite() at ffffc00000540b50 ne=
 tbsd:ffs_copyonwrite+0x1a4
 [ 479.7140520] fp ffffc000c63c7ca0 fscow_run() at ffffc0000069236c netbsd:f=
 scow_run+0xbc
 [ 479.7349917] fp ffffc000c63c7ce0 bread() at ffffc00000676278 netbsd:bread=
 +0x58
 [ 479.7680479] fp ffffc000c63c7d10 ffs_update() at ffffc0000053ed94 netbsd:=
 ffs_update+0x128
 [ 479.8000553] fp ffffc000c63c7d70 ffs_sync() at ffffc00000548ae8 netbsd:ff=
 s_sync+0x1e8
 [ 479.8209946] fp ffffc000c63c7df0 VFS_SYNC() at ffffc000006874e4 netbsd:VF=
 S_SYNC+0x24
 [ 479.8540963] fp ffffc000c63c7e20 sched_sync() at ffffc00000687670 netbsd:=
 sched_sync+0x110
 [ 479.8860508] tf ffffc000c63c7ed0 el0_trap() at ffffc000000c0ff0 netbsd:el=
 1_trap_exit+0x68
 [ 479.8960548] cpu0: End traceback...
 Stopped in pid 0.124 (system) at        netbsd:cpu_Debugger+0xc:        ldp
 x29, x30, [sp],#16
 db{0}> show vnode ffff00007cf02800
 OBJECT 0xffff00007cf02800: locked=3D0, pgops=3D0xffffc00000aebb48, npages=
 =3D0, refs=3D1

 vnode 0xffff00007cf02800 flags 0x12<SYSTEM,MPSAFE>
 tag VT_UFS(1) type VREG(1) mount 0xffff00007daf2000 typedata 0x0
 usecount 1 writecount 0 holdcount 410
 size f9c00050 writesize f9c00050 numoutput 0
 data 0xffff00007d0d94c0 lock 0xffff00007cf02a80
 state LOADED key(0xffff00007daf2000 8) 67 1f 00 00 00 00 00 00
 lrulisthd 0xffffc00001265e10
 db{0}> call vprint(0,ffff00007cf02800)
 vnode 0xffff00007cf02800 flags 0x12<SYSTEM,MPSAFE>
 [ 479.8960548]  tag VT_UFS(1) type VREG(1) mount 0xffff00007daf2000 typedat=
 a 0x0
 [ 479.8960548]  usecount 1 writecount 0 holdcount 410
 [ 479.8960548]  size f9c00050 writesize f9c00050 numoutput 0
 [ 479.8960548]  data 0xffff00007d0d94c0 lock 0xffff00007cf02a80
 [ 479.8960548]  state LOADED key(0xffff00007daf2000 8) 67 1f 00 00 00 00 00=
  00
 [ 479.8960548]  lrulisthd 0xffffc00001265e10
 [ 479.8960548]  tag VT_UFS, ino 8039, on dev 168, 1 flags 0x6, nlink 0
 [ 479.8960548]  mode 0100600, owner 0, group 0, size 4190109776
 0

From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: hannken@NetBSD.org, dholland@NetBSD.org
Subject: Re: kern/60531: looping "vflushbuf: dirty" message
Date: Mon, 3 Aug 2026 00:38:22 +0000

 This is a multi-part message in MIME format.
 --=_QJyEfqlAfncoM8fB56hLDQdX5vxg3caH

 I prepared a pair of patches:

 1. WORKAROUND: pr60531-vflushbufdirty-ratelimit.patch works around the
    problem of overwhelming the console by putting the message under
    #ifdef DEBUG, and limiting it to once per call, and globally
    rate-limiting the output under bufcache_lock.

    It doesn't fix the underlying problem that the time vflushbuf can
    take is unbounded even if each write completes in bounded time --
    it's just a low-risk change for quick pullup, because this is
    interfering with backups on TNF infrastructure, and probably others
    too.

 2. FIX: pr60531-vflushbufdirty-flushgen.patch fixes the problem that
    the time vflushbuf can take is unbounded, not by making the invalid
    assumption that reassignbuf(bp, vp) hold vp's vnode lock, but
    rather by using a global flush generation counter:

    - For dirty buffers, reassignbuf(bp, vp) sets bp->b_flushgen to the
      global bufcache_flushgen.

    - vflushbuf(vp) grabs the current bufcache_flushgen as f,
      increments bufcache_flushgen, and then waits until all buffers bp
      on vp->v_dirtyblkhd with bp->b_flushgen <= f have completed.

    This should properly fix the underlying problem.

    (Haven't yet thought about whether vinvalbuf or vtruncbuf need to
    consider flushgen too.)

 --=_QJyEfqlAfncoM8fB56hLDQdX5vxg3caH
 Content-Type: text/plain; charset="ISO-8859-1"; name="pr60531-vflushbufdirty-ratelimit"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="pr60531-vflushbufdirty-ratelimit.patch"

 # HG changeset patch
 # User Taylor R Campbell <riastradh@NetBSD.org>
 # Date 1785716340 0
 #      Mon Aug 03 00:19:00 2026 +0000
 # Branch trunk
 # Node ID 2c597c41062d49e186d14e507ecf9c07b940937f
 # Parent  1f2d855671ed4d4eee9ff4420a357756f04e271a
 # EXP-Topic riastradh-pr60531-vflushbufdirty
 vflushbuf: Print `vflushbuf: dirty' warning only once per call.

 And rate-limit it globally.

 This can happen when vflushbuf is competing with an onslaught of
 concurrent I/O, which might be happening indefinitely, so we are
 likely to have to print a _lot_ of warnings, potentially overwhelming
 the console, until the underlying problem is fixed by teaching
 vflushbuf to wait only for the writes that began before have
 completed, not also all writes that have begun since.

 This stop-gap measure intended to be low-risk for pullup to release
 branches.

 PR kern/60531: looping "vflushbuf: dirty" message

 diff -r 1f2d855671ed -r 2c597c41062d sys/kern/vfs_subr.c
 --- a/sys/kern/vfs_subr.c	Sat Aug 01 05:12:21 2026 +0000
 +++ b/sys/kern/vfs_subr.c	Mon Aug 03 00:19:00 2026 +0000
 @@ -338,6 +338,9 @@ vflushbuf(struct vnode *vp, int flags)
  	struct buf *bp, *nbp;
  	int error, pflags;
  	bool dirty, sync;
 +#ifdef DEBUG
 +	bool warned =3D false;
 +#endif
 =20
  	sync =3D (flags & FSYNC_WAIT) !=3D 0;
  	pflags =3D PGO_CLEANIT | PGO_ALLPAGES |
 @@ -382,7 +385,18 @@ loop:
  	mutex_exit(vp->v_interlock);
 =20
  	if (dirty) {
 -		vprint("vflushbuf: dirty", vp);
 +#ifdef DEBUG
 +		if (!warned) {
 +			static struct timeval vflushbuf_warntime;
 +			const struct timeval interval =3D {1,0};
 +
 +			mutex_enter(&bufcache_lock);
 +			if (ratecheck(&vflushbuf_warntime, &interval))
 +				vprint("vflushbuf: dirty", vp);
 +			mutex_exit(&bufcache_lock);
 +			warned =3D true;
 +		}
 +#endif
  		goto loop;
  	}
 =20

 --=_QJyEfqlAfncoM8fB56hLDQdX5vxg3caH
 Content-Type: text/plain; charset="ISO-8859-1"; name="pr60531-vflushbufdirty-flushgen"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="pr60531-vflushbufdirty-flushgen.patch"

 # HG changeset patch
 # User Taylor R Campbell <riastradh@NetBSD.org>
 # Date 1785715442 0
 #      Mon Aug 03 00:04:02 2026 +0000
 # Branch trunk
 # Node ID 1946a089abf1ad98fd95ac56d7030395dc4213a3
 # Parent  1f2d855671ed4d4eee9ff4420a357756f04e271a
 # EXP-Topic riastradh-pr60531-vflushbufdirty
 vfs: Fix `vflushbuf: dirty'.

 Currently, vflushbuf(vp) requires the caller to hold vp's vnode lock,
 and assumes that reassignbuf(..., vp) is only done while vp's vnode
 lock is held.

 Under this assumption, vp->v_dirtyblkhd can never have dirty blocks
 added to it once vflushbuf starts, so the set of block writes that
 vflushbuf must wait for is limited to those dirty blocks that were
 already queued before it began.  Thus, it can safely wait until the
 vnode's number of active block writes (v_numoutput) has gone to zero,
 which it will in bounded time as long as each block write
 independently completes in bounded time.

 Unfortunately, this assumption is invalid, in at least two
 circumstances:

 - vp is a block device vnode on which another file system is mounted,
   and the file system uses getblk/bread/bwrite on vp to edit file
   system metadata.  These paths do not take vp's vnode lock.  Perhaps
   these could take vp's vnode lock because they will generally only
   hold _another file system's_ vnode lock, but these paths are
   extensively scattered through file system logic and confidently
   finding them and adjusting them to take the vnode lock -- and
   proving that it's safe to do so -- seems likely no small task.

 - vp is a snapshot backing store file on ffs (SF_SNAPSHOT).  Data
   copied on write for the snapshot is written with getblk/bwrite.
   This path does not take vp's vnode lock, and taking it might be
   troublesome because the cow logic runs while holding other vnode
   locks, so it is a high risk of deadlocks.

 To avoid this assumption, we instead cache a global flush generation
 number in each dirty block as it is queued in reassignbuf.  Each call
 to vflushbuf advances the flush generation.  This way, vflushbuf can
 detect which dirty blocks were already there when it started, and
 which ones are new, and wait only until the ones that were already
 there are done.

 Since the buffercache(9) path never has B_PHYS set, I repurposed two
 existing members of struct buf (b_proc, b_saveaddr) that are used
 only for B_PHYS buffers, to make room for for a uint64_t flush
 generation number used only for !B_PHYS buffers.  Using a 64-bit
 generation number guarantees that it will never wrap around in our
 lifetimes, even if we issue sync(8) sequentially once per nanosecond,
 and it completes in that time too.

 PR kern/60531: looping "vflushbuf: dirty" message

 diff -r 1f2d855671ed -r 1946a089abf1 sys/kern/vfs_subr.c
 --- a/sys/kern/vfs_subr.c	Sat Aug 01 05:12:21 2026 +0000
 +++ b/sys/kern/vfs_subr.c	Mon Aug 03 00:04:02 2026 +0000
 @@ -171,6 +171,8 @@ const int	vttoif_tab[9] =3D {
  	(bp)->b_vnbufs.le_next =3D NOLIST;				\
  }
 =20
 +static uint64_t bufcache_flushgen =3D 1;
 +
  int doforce =3D 1;		/* 1 =3D> permit forcible unmounting */
 =20
  /*
 @@ -329,8 +331,7 @@ restart:
 =20
  /*
   * Flush all dirty buffers from a vnode.
 - * Called with the underlying vnode locked, which should prevent new dirty
 - * buffers from being queued.
 + * Called with the underlying vnode locked.
   */
  int
  vflushbuf(struct vnode *vp, int flags)
 @@ -338,7 +339,11 @@ vflushbuf(struct vnode *vp, int flags)
  	struct buf *bp, *nbp;
  	int error, pflags;
  	bool dirty, sync;
 +	uint64_t flushgen;
 =20
 +	KASSERT(VOP_ISLOCKED(vp));
 +
 +	flushgen =3D 0;
  	sync =3D (flags & FSYNC_WAIT) !=3D 0;
  	pflags =3D PGO_CLEANIT | PGO_ALLPAGES |
  	    (sync ? PGO_SYNCIO : 0) |
 @@ -346,12 +351,20 @@ vflushbuf(struct vnode *vp, int flags)
  	rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
  	(void) VOP_PUTPAGES(vp, 0, 0, pflags);
 =20
 +	/*
 +	 * Ensure every dirty block associated with this vnode has
 +	 * begun to be written out (BC_BUSY, I/O in progress) -- any
 +	 * delayed writes must delay no longer.
 +	 */
  loop:
  	mutex_enter(&bufcache_lock);
 +	if (flushgen =3D=3D 0)
 +		flushgen =3D bufcache_flushgen++;
  	for (bp =3D LIST_FIRST(&vp->v_dirtyblkhd); bp; bp =3D nbp) {
  		KASSERT(bp->b_vp =3D=3D vp);
 +		KASSERT((bp->b_flags & B_PHYS) =3D=3D 0);
  		nbp =3D LIST_NEXT(bp, b_vnbufs);
 -		if ((bp->b_cflags & BC_BUSY))
 +		if ((bp->b_cflags & BC_BUSY) !=3D 0 || bp->b_flushgen > flushgen)
  			continue;
  		if ((bp->b_oflags & BO_DELWRI) =3D=3D 0)
  			panic("vflushbuf: not dirty, bp %p", bp);
 @@ -375,17 +388,23 @@ loop:
  	if (!sync)
  		return 0;
 =20
 +	/*
 +	 * Wait until all pending writes issued before we incremented
 +	 * bufcache_flushgen have completed.
 +	 */
  	mutex_enter(vp->v_interlock);
 -	while (vp->v_numoutput !=3D 0)
 -		cv_wait(&vp->v_cv, vp->v_interlock);
 -	dirty =3D !LIST_EMPTY(&vp->v_dirtyblkhd);
 +	do {
 +		dirty =3D false;
 +		for (bp =3D LIST_FIRST(&vp->v_dirtyblkhd); bp; bp =3D nbp) {
 +			if (bp->b_flushgen > flushgen)
 +				continue;
 +			dirty =3D true;
 +			cv_wait(&vp->v_cv, vp->v_interlock);
 +			break;
 +		}
 +	} while (dirty);
  	mutex_exit(vp->v_interlock);
 =20
 -	if (dirty) {
 -		vprint("vflushbuf: dirty", vp);
 -		goto loop;
 -	}
 -
  	return 0;
  }
 =20
 @@ -497,6 +516,7 @@ reassignbuf(struct buf *bp, struct vnode
  	KASSERT(bp->b_objlock =3D=3D vp->v_interlock);
  	KASSERT(mutex_owned(vp->v_interlock));
  	KASSERT((bp->b_cflags & BC_BUSY) !=3D 0);
 +	KASSERT((bp->b_flags & B_PHYS) =3D=3D 0);
 =20
  	/*
  	 * Delete from old vnode list, if on one.
 @@ -535,6 +555,7 @@ reassignbuf(struct buf *bp, struct vnode
  			    (vp->v_mount->mnt_flag & MNT_ASYNC) =3D=3D 0)
  				vn_syncer_add_to_worklist(vp, delayx);
  		}
 +		bp->b_flushgen =3D bufcache_flushgen;
  	}
  	bufinsvn(bp, listheadp);
  }
 diff -r 1f2d855671ed -r 1946a089abf1 sys/sys/buf.h
 --- a/sys/sys/buf.h	Sat Aug 01 05:12:21 2026 +0000
 +++ b/sys/sys/buf.h	Mon Aug 03 00:04:02 2026 +0000
 @@ -135,8 +135,15 @@ struct buf {
  						      (partition relative) */
  	daddr_t			b_rawblkno;	/* b: raw physical block number
  						      (volume relative) */
 -	struct proc		*b_proc;	/* b: proc if BB_PHYS */
 -	void			*b_saveaddr;	/* b: saved b_data for physio */
 +	union {
 +		/* B_PHYS */
 +		struct {
 +			struct proc *b_proc;	/* b: proc if B_PHYS */
 +			void	*b_saveaddr;	/* b: saved b_data for physio */
 +		};
 +		/* !B_PHYS */
 +		uint64_t	b_flushgen;	/* b: flush generation */
 +	};
  	struct cpu_info		*b_ci;		/* b: originating CPU */
 =20
  	/*

 --=_QJyEfqlAfncoM8fB56hLDQdX5vxg3caH--

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60531 CVS commit: src/sys/kern
Date: Thu, 6 Aug 2026 22:28:24 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Thu Aug  6 22:28:23 UTC 2026

 Modified Files:
 	src/sys/kern: vfs_subr.c

 Log Message:
 vflushbuf: Print `vflushbuf: dirty' warning only once per call.

 And do it only under DEBUG, and rate-limit it globally.

 This can happen when vflushbuf is competing with an onslaught of
 concurrent I/O on a snapshot or block device (which is done without
 holding the vnode lock, despite what the comment says).  That
 concurrent I/O might be happening indefinitely, so we are likely to
 have to print a _lot_ of warnings, potentially overwhelming the
 console, until the underlying problem is fixed by teaching vflushbuf
 to wait only for the writes that began before have completed, not
 also all writes that have begun since.

 This stop-gap measure intended to be low-risk for pullup to release
 branches.

 PR kern/60531: looping "vflushbuf: dirty" message


 To generate a diff of this commit:
 cvs rdiff -u -r1.503 -r1.504 src/sys/kern/vfs_subr.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "J. Hannken-Illjes" <hannken@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: kern/60531: looping "vflushbuf: dirty" message
Date: Fri, 7 Aug 2026 16:39:33 +0000

 --ItY42kjenHJt4ZkN
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline

 The FIX will not work, it is not ok to replace 'vp->v_numoutput == 0' with
 'LIST_EMPTY(&vp->v_dirtyblkhd)'.

 Buffers are removed from v_dirtyblkhd through reassignbuf() when bwrite()
 initiates the write while v_numoutput gets decremented through
 biodone()->vwakekup() when the I/O has completed.

 With your patch VOP_FSYNC(vp, FSYNC_WAIT) would return before all buffers
 are written to disk.

 The first part should be sufficient to prevent looping here, an updfated
 patch is attached.

 -- 
 J. Hannken-Illjes - hannken@netbsd.org

 --ItY42kjenHJt4ZkN
 Content-Type: text/x-diff; charset=us-ascii
 Content-Disposition: attachment; filename=pr60531.patch

 diff -r 983d69604667 sys/kern/vfs_subr.c
 --- sys/kern/vfs_subr.c	Fri Aug 07 13:18:45 2026 +0200
 +++ sys/kern/vfs_subr.c	Fri Aug 07 18:28:00 2026 +0200
 @@ -171,6 +171,8 @@ const int	vttoif_tab[9] = {
  	(bp)->b_vnbufs.le_next = NOLIST;				\
  }

 +static uint64_t bufcache_flushgen = 1;
 +
  int doforce = 1;		/* 1 => permit forcible unmounting */

  /*
 @@ -329,19 +331,19 @@ restart:

  /*
   * Flush all dirty buffers from a vnode.
 - * Called with the underlying vnode locked, which should prevent new dirty
 - * buffers from being queued.
 + * Called with the underlying vnode locked.
   */
  int
  vflushbuf(struct vnode *vp, int flags)
  {
  	struct buf *bp, *nbp;
  	int error, pflags;
 -	bool dirty, sync;
 -#ifdef DEBUG
 -	bool warned = false;
 -#endif
 +	bool sync;
 +	uint64_t flushgen;

 +	KASSERT(VOP_ISLOCKED(vp));
 +
 +	flushgen = 0;
  	sync = (flags & FSYNC_WAIT) != 0;
  	pflags = PGO_CLEANIT | PGO_ALLPAGES |
  	    (sync ? PGO_SYNCIO : 0) |
 @@ -349,12 +351,20 @@ vflushbuf(struct vnode *vp, int flags)
  	rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
  	(void) VOP_PUTPAGES(vp, 0, 0, pflags);

 +	/*
 +	 * Ensure every dirty block associated with this vnode has
 +	 * begun to be written out (BC_BUSY, I/O in progress) -- any
 +	 * delayed writes must delay no longer.
 +	 */
  loop:
  	mutex_enter(&bufcache_lock);
 +	if (flushgen == 0)
 +		flushgen = bufcache_flushgen++;
  	for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
  		KASSERT(bp->b_vp == vp);
 +		KASSERT((bp->b_flags & B_PHYS) == 0);
  		nbp = LIST_NEXT(bp, b_vnbufs);
 -		if ((bp->b_cflags & BC_BUSY))
 +		if ((bp->b_cflags & BC_BUSY) != 0 || bp->b_flushgen > flushgen)
  			continue;
  		if ((bp->b_oflags & BO_DELWRI) == 0)
  			panic("vflushbuf: not dirty, bp %p", bp);
 @@ -378,28 +388,17 @@ loop:
  	if (!sync)
  		return 0;

 +	/*
 +	 * Wait until all pending writes issued before we incremented
 +	 * bufcache_flushgen have completed.
 +	 */
  	mutex_enter(vp->v_interlock);
  	while (vp->v_numoutput != 0)
  		cv_wait(&vp->v_cv, vp->v_interlock);
 -	dirty = !LIST_EMPTY(&vp->v_dirtyblkhd);
 +	KASSERT((vp->v_type == VBLK && spec_node_getmountedfs(vp)) ||
 +	    LIST_EMPTY(&vp->v_dirtyblkhd));
  	mutex_exit(vp->v_interlock);

 -	if (dirty) {
 -#ifdef DEBUG
 -		if (!warned) {
 -			static struct timeval vflushbuf_warntime;
 -			const struct timeval interval = {60,0};
 -
 -			mutex_enter(&bufcache_lock);
 -			if (ratecheck(&vflushbuf_warntime, &interval))
 -				vprint("vflushbuf: dirty", vp);
 -			mutex_exit(&bufcache_lock);
 -			warned = true;
 -		}
 -#endif
 -		goto loop;
 -	}
 -
  	return 0;
  }

 @@ -511,6 +510,7 @@ reassignbuf(struct buf *bp, struct vnode
  	KASSERT(bp->b_objlock == vp->v_interlock);
  	KASSERT(mutex_owned(vp->v_interlock));
  	KASSERT((bp->b_cflags & BC_BUSY) != 0);
 +	KASSERT((bp->b_flags & B_PHYS) == 0);

  	/*
  	 * Delete from old vnode list, if on one.
 @@ -549,6 +549,7 @@ reassignbuf(struct buf *bp, struct vnode
  			    (vp->v_mount->mnt_flag & MNT_ASYNC) == 0)
  				vn_syncer_add_to_worklist(vp, delayx);
  		}
 +		bp->b_flushgen = bufcache_flushgen;
  	}
  	bufinsvn(bp, listheadp);
  }
 diff -r 983d69604667 sys/sys/buf.h
 --- sys/sys/buf.h	Fri Aug 07 13:18:45 2026 +0200
 +++ sys/sys/buf.h	Fri Aug 07 18:28:00 2026 +0200
 @@ -135,8 +135,15 @@ struct buf {
  						      (partition relative) */
  	daddr_t			b_rawblkno;	/* b: raw physical block number
  						      (volume relative) */
 -	struct proc		*b_proc;	/* b: proc if BB_PHYS */
 -	void			*b_saveaddr;	/* b: saved b_data for physio */
 +	union {
 +		/* B_PHYS */
 +		struct {
 +			struct proc *b_proc;	/* b: proc if B_PHYS */
 +			void	*b_saveaddr;	/* b: saved b_data for physio */
 +		};
 +		/* !B_PHYS */
 +		uint64_t	b_flushgen;	/* b: flush generation */
 +	};
  	struct cpu_info		*b_ci;		/* b: originating CPU */

  	/*

 --ItY42kjenHJt4ZkN--

From: Taylor R Campbell <riastradh@NetBSD.org>
To: "J. Hannken-Illjes" <hannken@NetBSD.org>
Cc: gnats-bugs@NetBSD.org, kern-bug-people@NetBSD.org, gnats-admin@NetBSD.org,
	netbsd-bugs@NetBSD.org, dholland@NetBSD.org
Subject: Re: kern/60531: looping "vflushbuf: dirty" message
Date: Fri, 7 Aug 2026 17:29:13 +0000

 > Date: Fri, 7 Aug 2026 16:39:33 +0000
 > From: "J. Hannken-Illjes" <hannken@netbsd.org>
 >  
 > The FIX will not work, it is not ok to replace 'vp->v_numoutput == 0' with
 > 'LIST_EMPTY(&vp->v_dirtyblkhd)'.
 > 
 > Buffers are removed from v_dirtyblkhd through reassignbuf() when bwrite()
 > initiates the write while v_numoutput gets decremented through
 > biodone()->vwakekup() when the I/O has completed.
 >  
 > With your patch VOP_FSYNC(vp, FSYNC_WAIT) would return before all buffers
 > are written to disk.

 Thanks, that would be bad!

 > The first part should be sufficient to prevent looping here, an updfated
 > patch is attached.
 > [...]
 >  +	/*
 >  +	 * Wait until all pending writes issued before we incremented
 >  +	 * bufcache_flushgen have completed.
 >  +	 */
 >   	mutex_enter(vp->v_interlock);
 >   	while (vp->v_numoutput != 0)
 >   		cv_wait(&vp->v_cv, vp->v_interlock);

 The trouble with waiting for vp->v_numoutput is that if there's
 concurrent I/O on a block device or a snapshot, it can keep adding
 more writes to v_numoutput so that vflushbuf(9) never completes.

 So I don't think this change accomplishes much: we add some more
 bookkeeping around flushgen, but we may continue waiting even if we've
 long since passed that flushgen.

 In fact this change may even create a deadlock, because:

 (a) vflushbuf holds the vnode lock while it waits, and
 (b) it does not issue bwrite for any buffers past flushgen,

 so nothing else can flush those buffers concurrently either with
 vflushbuf.

 So perhaps we need to wait until:

 (a) v_dirtyblkhd has no entries before flushgen,
 AND
 (b) v_cleanblkhd has no entries before flushgen that are still being
     written out.

 Not sure exactly how to detect (b).  Is a buffercache(9) buf bp still
 being written out iff

 (bp->b_flags & B_READ) == 0 &&
 (bp->b_oflags & BO_DONE) == 0?

 I'm not really happy about the prospect of iterating over v_cleanblkhd
 but if the buffers in question -- those currently being written out --
 aren't on v_dirtyblkhd, I don't see another way to tell when they've
 all passed the flushgen short of either iterating over v_cleanblkhd,
 or adding a new queue like v_writingblkhd and dealing with all the
 consequences of that.

 >  +	KASSERT((vp->v_type == VBLK && spec_node_getmountedfs(vp)) ||
 >  +	    LIST_EMPTY(&vp->v_dirtyblkhd));

 Won't this assertion also fire if vp is a VREG snapshot, since the
 snapshot write path also uses reassignbuf without holding the vnode
 lock, as I noted earlier?

 https://mail-index.NetBSD.org/netbsd-bugs/2026/08/02/msg094240.html

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60531 CVS commit: [netbsd-11] src/sys/kern
Date: Mon, 17 Aug 2026 15:47:55 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Mon Aug 17 15:47:55 UTC 2026

 Modified Files:
 	src/sys/kern [netbsd-11]: vfs_subr.c

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #415):

 	sys/kern/vfs_subr.c: revision 1.504

 vflushbuf: Print `vflushbuf: dirty' warning only once per call.

 And do it only under DEBUG, and rate-limit it globally.

 This can happen when vflushbuf is competing with an onslaught of
 concurrent I/O on a snapshot or block device (which is done without
 holding the vnode lock, despite what the comment says).  That
 concurrent I/O might be happening indefinitely, so we are likely to
 have to print a _lot_ of warnings, potentially overwhelming the
 console, until the underlying problem is fixed by teaching vflushbuf
 to wait only for the writes that began before have completed, not
 also all writes that have begun since.

 This stop-gap measure intended to be low-risk for pullup to release
 branches.

 PR kern/60531: looping "vflushbuf: dirty" message


 To generate a diff of this commit:
 cvs rdiff -u -r1.502.2.1 -r1.502.2.2 src/sys/kern/vfs_subr.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60531 CVS commit: [netbsd-10] src/sys/kern
Date: Mon, 17 Aug 2026 15:49:23 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Mon Aug 17 15:49:23 UTC 2026

 Modified Files:
 	src/sys/kern [netbsd-10]: vfs_subr.c

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #1325):

 	sys/kern/vfs_subr.c: revision 1.504

 vflushbuf: Print `vflushbuf: dirty' warning only once per call.

 And do it only under DEBUG, and rate-limit it globally.

 This can happen when vflushbuf is competing with an onslaught of
 concurrent I/O on a snapshot or block device (which is done without
 holding the vnode lock, despite what the comment says).  That
 concurrent I/O might be happening indefinitely, so we are likely to
 have to print a _lot_ of warnings, potentially overwhelming the
 console, until the underlying problem is fixed by teaching vflushbuf
 to wait only for the writes that began before have completed, not
 also all writes that have begun since.

 This stop-gap measure intended to be low-risk for pullup to release
 branches.

 PR kern/60531: looping "vflushbuf: dirty" message


 To generate a diff of this commit:
 cvs rdiff -u -r1.496.2.1 -r1.496.2.2 src/sys/kern/vfs_subr.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60531 CVS commit: [netbsd-9] src/sys/kern
Date: Mon, 17 Aug 2026 15:51:04 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Mon Aug 17 15:51:04 UTC 2026

 Modified Files:
 	src/sys/kern [netbsd-9]: vfs_subr.c

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #2045):

 	sys/kern/vfs_subr.c: revision 1.504

 vflushbuf: Print `vflushbuf: dirty' warning only once per call.

 And do it only under DEBUG, and rate-limit it globally.

 This can happen when vflushbuf is competing with an onslaught of
 concurrent I/O on a snapshot or block device (which is done without
 holding the vnode lock, despite what the comment says).  That
 concurrent I/O might be happening indefinitely, so we are likely to
 have to print a _lot_ of warnings, potentially overwhelming the
 console, until the underlying problem is fixed by teaching vflushbuf
 to wait only for the writes that began before have completed, not
 also all writes that have begun since.

 This stop-gap measure intended to be low-risk for pullup to release
 branches.

 PR kern/60531: looping "vflushbuf: dirty" message


 To generate a diff of this commit:
 cvs rdiff -u -r1.471 -r1.471.4.1 src/sys/kern/vfs_subr.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.