diff options
Diffstat (limited to 'sys/kern/vfs_bio.c')
-rw-r--r-- | sys/kern/vfs_bio.c | 76 |
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) |