When getnewbuf_reuse_bp() is called to reclaim some (clean) buffer,

the vnode owning the buffer is not locked.  More, it cannot be locked
safely, since getnewbuf_reuse_bp() is called from newbuf(), and some
other vnode is already locked, for which reused buffer will be
reassigned.

As the consequence, reclamation of the owning vnode could go in
parallel, in particular, the call to vnode_destroy_vobject(), which
deallocates the vm object and zeroes the v_bufobj->bo_object.  Note
that the pages wired by the buffer are left wired and can be safely
freed by the vfs_vmio_release() without the need for the vm object
lock.  Also, seeing stale pointer to the v_object is safe due to vm
object type stability.

Check for bo_bufobj != NULL and cache the value in local variable to
avoid trying to lock NULL vm object.

Reported and tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2014-12-08 16:42:34 +00:00
parent f87c8878e6
commit 904ed548bb

View File

@ -1883,15 +1883,18 @@ out:
static void
vfs_vmio_release(struct buf *bp)
{
int i;
vm_object_t obj;
vm_page_t m;
int i;
if ((bp->b_flags & B_UNMAPPED) == 0) {
BUF_CHECK_MAPPED(bp);
pmap_qremove(trunc_page((vm_offset_t)bp->b_data), bp->b_npages);
} else
BUF_CHECK_UNMAPPED(bp);
VM_OBJECT_WLOCK(bp->b_bufobj->bo_object);
obj = bp->b_bufobj->bo_object;
if (obj != NULL)
VM_OBJECT_WLOCK(obj);
for (i = 0; i < bp->b_npages; i++) {
m = bp->b_pages[i];
bp->b_pages[i] = NULL;
@ -1916,7 +1919,8 @@ vfs_vmio_release(struct buf *bp)
vm_page_try_to_cache(m);
vm_page_unlock(m);
}
VM_OBJECT_WUNLOCK(bp->b_bufobj->bo_object);
if (obj != NULL)
VM_OBJECT_WUNLOCK(obj);
if (bp->b_bufsize) {
bufspacewakeup();