Both cluster_rbuild() and cluster_wbuild() sometimes set the pages

shared busy without first draining the hard busy state.  Previously it
went unnoticed since VPO_BUSY and m->busy fields were distinct, and
vm_page_io_start() did not verified that the passed page has VPO_BUSY
flag cleared, but such page state is wrong.  New implementation is
more strict and catched this case.

Drain the busy state as needed, before calling vm_page_sbusy().

Tested by:	pho, jkim
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Konstantin Belousov 2013-08-22 18:26:45 +00:00
parent e68c64f0ba
commit 4f8cf6e59b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=254668
3 changed files with 28 additions and 11 deletions

View File

@ -108,7 +108,6 @@ static void vm_hold_load_pages(struct buf *bp, vm_offset_t from,
static void vfs_page_set_valid(struct buf *bp, vm_ooffset_t off, vm_page_t m);
static void vfs_page_set_validclean(struct buf *bp, vm_ooffset_t off,
vm_page_t m);
static void vfs_drain_busy_pages(struct buf *bp);
static void vfs_clean_pages_dirty_buf(struct buf *bp);
static void vfs_setdirty_locked_object(struct buf *bp);
static void vfs_vmio_release(struct buf *bp);
@ -3983,7 +3982,7 @@ vfs_page_set_validclean(struct buf *bp, vm_ooffset_t off, vm_page_t m)
* Ensure that all buffer pages are not exclusive busied. If any page is
* exclusive busy, drain it.
*/
static void
void
vfs_drain_busy_pages(struct buf *bp)
{
vm_page_t m;

View File

@ -315,7 +315,7 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn,
daddr_t bn;
off_t off;
long tinc, tsize;
int i, inc, j, toff;
int i, inc, j, k, toff;
KASSERT(size == vp->v_mount->mnt_stat.f_iosize,
("cluster_rbuild: size %ld != f_iosize %jd\n",
@ -378,7 +378,15 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn,
inc = btodb(size);
bo = &vp->v_bufobj;
for (bn = blkno, i = 0; i < run; ++i, bn += inc) {
if (i != 0) {
if (i == 0) {
VM_OBJECT_WLOCK(tbp->b_bufobj->bo_object);
vfs_drain_busy_pages(tbp);
vm_object_pip_add(tbp->b_bufobj->bo_object,
tbp->b_npages);
for (k = 0; k < tbp->b_npages; k++)
vm_page_sbusy(tbp->b_pages[k]);
VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object);
} else {
if ((bp->b_npages * PAGE_SIZE) +
round_page(size) > vp->v_mount->mnt_iosize_max) {
break;
@ -424,14 +432,23 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn,
if ((tbp->b_pages[j]->valid &
vm_page_bits(toff, tinc)) != 0)
break;
if (vm_page_xbusied(tbp->b_pages[j]))
break;
vm_object_pip_add(tbp->b_bufobj->bo_object, 1);
vm_page_sbusy(tbp->b_pages[j]);
off += tinc;
tsize -= tinc;
}
VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object);
if (tsize > 0) {
clean_sbusy:
vm_object_pip_add(tbp->b_bufobj->bo_object, -j);
for (k = 0; k < j; k++)
vm_page_sunbusy(tbp->b_pages[k]);
VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object);
bqrelse(tbp);
break;
}
VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object);
/*
* Set a read-ahead mark as appropriate
@ -451,8 +468,8 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn,
if (tbp->b_blkno == tbp->b_lblkno) {
tbp->b_blkno = bn;
} else if (tbp->b_blkno != bn) {
brelse(tbp);
break;
VM_OBJECT_WLOCK(tbp->b_bufobj->bo_object);
goto clean_sbusy;
}
}
/*
@ -466,10 +483,8 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn,
for (j = 0; j < tbp->b_npages; j += 1) {
vm_page_t m;
m = tbp->b_pages[j];
vm_page_sbusy(m);
vm_object_pip_add(m->object, 1);
if ((bp->b_npages == 0) ||
(bp->b_pages[bp->b_npages-1] != m)) {
(bp->b_pages[bp->b_npages-1] != m)) {
bp->b_pages[bp->b_npages] = m;
bp->b_npages++;
}
@ -944,7 +959,9 @@ cluster_wbuild(struct vnode *vp, long size, daddr_t start_lbn, int len,
vm_page_t m;
VM_OBJECT_WLOCK(tbp->b_bufobj->bo_object);
if (i != 0) { /* if not first buffer */
if (i == 0) {
vfs_drain_busy_pages(tbp);
} else { /* if not first buffer */
for (j = 0; j < tbp->b_npages; j += 1) {
m = tbp->b_pages[j];
if (vm_page_xbusied(m)) {

View File

@ -501,6 +501,7 @@ void bufstrategy(struct bufobj *, struct buf *);
void brelse(struct buf *);
void bqrelse(struct buf *);
int vfs_bio_awrite(struct buf *);
void vfs_drain_busy_pages(struct buf *bp);
struct buf * getpbuf(int *);
struct buf *incore(struct bufobj *, daddr_t);
struct buf *gbincore(struct bufobj *, daddr_t);