Author: kib
Date: Thu Aug 22 18:26:45 2013
New Revision: 254668
URL: http://svnweb.freebsd.org/changeset/base/254668

Log:
  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

Modified:
  head/sys/kern/vfs_bio.c
  head/sys/kern/vfs_cluster.c
  head/sys/sys/buf.h

Modified: head/sys/kern/vfs_bio.c
==============================================================================
--- head/sys/kern/vfs_bio.c     Thu Aug 22 18:12:24 2013        (r254667)
+++ head/sys/kern/vfs_bio.c     Thu Aug 22 18:26:45 2013        (r254668)
@@ -108,7 +108,6 @@ static void vm_hold_load_pages(struct bu
 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, 
  * 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;

Modified: head/sys/kern/vfs_cluster.c
==============================================================================
--- head/sys/kern/vfs_cluster.c Thu Aug 22 18:12:24 2013        (r254667)
+++ head/sys/kern/vfs_cluster.c Thu Aug 22 18:26:45 2013        (r254668)
@@ -315,7 +315,7 @@ cluster_rbuild(struct vnode *vp, u_quad_
        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_
        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_
                                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_
                        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_
                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 si
                                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)) {

Modified: head/sys/sys/buf.h
==============================================================================
--- head/sys/sys/buf.h  Thu Aug 22 18:12:24 2013        (r254667)
+++ head/sys/sys/buf.h  Thu Aug 22 18:26:45 2013        (r254668)
@@ -501,6 +501,7 @@ void        bufstrategy(struct bufobj *, struct
 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);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to