diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -4002,9 +4002,15 @@ /* * With GB_NOCREAT we must be sure about not finding the buffer * as it may have been reassigned during unlocked lookup. + * If BO_NONSTERILE is still unset, no reassign has occurred. */ - if ((flags & GB_NOCREAT) != 0) + if ((flags & GB_NOCREAT) != 0) { + /* Ensure bo_flag is loaded after gbincore_unlocked. */ + atomic_thread_fence_acq(); + if ((bo->bo_flag & BO_NONSTERILE) == 0) + return (EEXIST); goto loop; + } goto newbuf_unlocked; } diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -3207,6 +3207,16 @@ bp, bp->b_vp, bp->b_flags); BO_LOCK(bo); + if ((bo->bo_flag & BO_NONSTERILE) == 0) { + /* + * Coordinate with getblk's unlocked lookup. Make + * BO_NONSTERILE visible before the first reassignbuf produces + * any side effect. This could be outside the bo lock if we + * used a separate atomic flag field. + */ + bo->bo_flag |= BO_NONSTERILE; + atomic_thread_fence_rel(); + } buf_vlist_remove(bp); /* diff --git a/sys/sys/bufobj.h b/sys/sys/bufobj.h --- a/sys/sys/bufobj.h +++ b/sys/sys/bufobj.h @@ -116,6 +116,7 @@ #define BO_WWAIT (1 << 1) /* Wait for output to complete */ #define BO_DEAD (1 << 2) /* Dead; only with INVARIANTS */ #define BO_NOBUFS (1 << 3) /* No bufs allowed */ +#define BO_NONSTERILE (1 << 4) /* Ever called reassignbuf() */ #define BO_LOCKPTR(bo) (&(bo)->bo_lock) #define BO_LOCK(bo) rw_wlock(BO_LOCKPTR((bo)))