aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/vfs_bio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/vfs_bio.c')
-rw-r--r--sys/kern/vfs_bio.c76
1 files changed, 47 insertions, 29 deletions
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index b5466fb2cd53..7bcc61c27109 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -4002,16 +4002,37 @@ getblkx(struct vnode *vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
/*
* 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;
}
error = BUF_TIMELOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL, "getblku", 0,
0);
- if (error != 0)
+ if (error != 0) {
+ KASSERT(error == EBUSY,
+ ("getblk: unexpected error %d from buf try-lock", error));
+ /*
+ * We failed a buf try-lock.
+ *
+ * With GB_LOCK_NOWAIT, just return, rather than taking the
+ * bufobj interlock and trying again, since we would probably
+ * fail again anyway. This is okay even if the buf's identity
+ * changed and we contended on the wrong lock, as changing
+ * identity itself requires the buf lock, and we could have
+ * contended on the right lock.
+ */
+ if ((flags & GB_LOCK_NOWAIT) != 0)
+ return (error);
goto loop;
+ }
/* Verify buf identify has not changed since lookup. */
if (bp->b_bufobj == bo && bp->b_lblkno == blkno)
@@ -4020,6 +4041,10 @@ getblkx(struct vnode *vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
/* It changed, fallback to locked lookup. */
BUF_UNLOCK_RAW(bp);
+ /* As above, with GB_LOCK_NOWAIT, just return. */
+ if ((flags & GB_LOCK_NOWAIT) != 0)
+ return (EBUSY);
+
loop:
BO_RLOCK(bo);
bp = gbincore(bo, blkno);
@@ -4212,35 +4237,29 @@ newbuf_unlocked:
}
/*
- * This code is used to make sure that a buffer is not
- * created while the getnewbuf routine is blocked.
- * This can be a problem whether the vnode is locked or not.
- * If the buffer is created out from under us, we have to
- * throw away the one we just created.
*
- * Note: this must occur before we associate the buffer
- * with the vp especially considering limitations in
- * the splay tree implementation when dealing with duplicate
- * lblkno's.
- */
- BO_LOCK(bo);
- if (gbincore(bo, blkno)) {
- BO_UNLOCK(bo);
- bp->b_flags |= B_INVAL;
- bufspace_release(bufdomain(bp), maxsize);
- brelse(bp);
- goto loop;
- }
-
- /*
* Insert the buffer into the hash, so that it can
* be found by incore.
+ *
+ * We don't hold the bufobj interlock while allocating the new
+ * buffer. Consequently, we can race on buffer creation. This
+ * can be a problem whether the vnode is locked or not. If the
+ * buffer is created out from under us, we have to throw away
+ * the one we just created.
*/
bp->b_lblkno = blkno;
bp->b_blkno = d_blkno;
bp->b_offset = offset;
- bgetvp(vp, bp);
- BO_UNLOCK(bo);
+ error = bgetvp(vp, bp);
+ if (error != 0) {
+ KASSERT(error == EEXIST,
+ ("getblk: unexpected error %d from bgetvp",
+ error));
+ bp->b_flags |= B_INVAL;
+ bufspace_release(bufdomain(bp), maxsize);
+ brelse(bp);
+ goto loop;
+ }
/*
* set B_VMIO bit. allocbuf() the buffer bigger. Since the
@@ -5466,16 +5485,16 @@ DB_SHOW_COMMAND(buffer, db_show_buffer)
}
db_printf("buf at %p\n", bp);
- db_printf("b_flags = 0x%b, b_xflags=0x%b\n",
+ db_printf("b_flags = 0x%b, b_xflags = 0x%b\n",
(u_int)bp->b_flags, PRINT_BUF_FLAGS,
(u_int)bp->b_xflags, PRINT_BUF_XFLAGS);
- db_printf("b_vflags=0x%b b_ioflags0x%b\n",
+ db_printf("b_vflags = 0x%b, b_ioflags = 0x%b\n",
(u_int)bp->b_vflags, PRINT_BUF_VFLAGS,
(u_int)bp->b_ioflags, PRINT_BIO_FLAGS);
db_printf(
"b_error = %d, b_bufsize = %ld, b_bcount = %ld, b_resid = %ld\n"
- "b_bufobj = (%p), b_data = %p\n, b_blkno = %jd, b_lblkno = %jd, "
- "b_vp = %p, b_dep = %p\n",
+ "b_bufobj = %p, b_data = %p\n"
+ "b_blkno = %jd, b_lblkno = %jd, b_vp = %p, b_dep = %p\n",
bp->b_error, bp->b_bufsize, bp->b_bcount, bp->b_resid,
bp->b_bufobj, bp->b_data, (intmax_t)bp->b_blkno,
(intmax_t)bp->b_lblkno, bp->b_vp, bp->b_dep.lh_first);
@@ -5512,7 +5531,6 @@ DB_SHOW_COMMAND(buffer, db_show_buffer)
#elif defined(BUF_TRACKING)
db_printf("b_io_tracking: %s\n", bp->b_io_tracking);
#endif
- db_printf(" ");
}
DB_SHOW_COMMAND_FLAGS(bufqueues, bufqueues, DB_CMD_MEMSAFE)