Author: kib Date: Wed Jun 1 21:00:28 2011 New Revision: 222586 URL: http://svn.freebsd.org/changeset/base/222586
Log: In the VOP_PUTPAGES() implementations, change the default error from VM_PAGER_AGAIN to VM_PAGER_ERROR for the uwritten pages. Return VM_PAGER_AGAIN for the partially written page. Always forward at least one page in the loop of vm_object_page_clean(). VM_PAGER_ERROR causes the page reactivation and does not clear the page dirty state, so the write is not lost. The change fixes an infinite loop in vm_object_page_clean() when the filesystem returns permanent errors for some page writes. Reported and tested by: gavin Reviewed by: alc, rmacklem MFC after: 1 week Modified: head/sys/fs/nfsclient/nfs_clbio.c head/sys/fs/nwfs/nwfs_io.c head/sys/fs/smbfs/smbfs_io.c head/sys/nfsclient/nfs_bio.c head/sys/vm/vm_object.c head/sys/vm/vnode_pager.c head/sys/vm/vnode_pager.h Modified: head/sys/fs/nfsclient/nfs_clbio.c ============================================================================== --- head/sys/fs/nfsclient/nfs_clbio.c Wed Jun 1 20:09:49 2011 (r222585) +++ head/sys/fs/nfsclient/nfs_clbio.c Wed Jun 1 21:00:28 2011 (r222586) @@ -302,7 +302,7 @@ ncl_putpages(struct vop_putpages_args *a } for (i = 0; i < npages; i++) - rtvals[i] = VM_PAGER_AGAIN; + rtvals[i] = VM_PAGER_ERROR; /* * When putting pages, do not extend file past EOF. @@ -345,16 +345,9 @@ ncl_putpages(struct vop_putpages_args *a pmap_qremove(kva, npages); relpbuf(bp, &ncl_pbuf_freecnt); - if (!error) { - int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; - for (i = 0; i < nwritten; i++) { - rtvals[i] = VM_PAGER_OK; - vm_page_undirty(pages[i]); - } - if (must_commit) { - ncl_clearcommit(vp->v_mount); - } - } + vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid); + if (must_commit) + ncl_clearcommit(vp->v_mount); return rtvals[0]; } Modified: head/sys/fs/nwfs/nwfs_io.c ============================================================================== --- head/sys/fs/nwfs/nwfs_io.c Wed Jun 1 20:09:49 2011 (r222585) +++ head/sys/fs/nwfs/nwfs_io.c Wed Jun 1 21:00:28 2011 (r222586) @@ -544,7 +544,7 @@ nwfs_putpages(ap) npages = btoc(count); for (i = 0; i < npages; i++) { - rtvals[i] = VM_PAGER_AGAIN; + rtvals[i] = VM_PAGER_ERROR; } bp = getpbuf(&nwfs_pbuf_freecnt); @@ -569,13 +569,8 @@ nwfs_putpages(ap) pmap_qremove(kva, npages); relpbuf(bp, &nwfs_pbuf_freecnt); - if (!error) { - int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; - for (i = 0; i < nwritten; i++) { - rtvals[i] = VM_PAGER_OK; - vm_page_undirty(pages[i]); - } - } + if (!error) + vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid); return rtvals[0]; #endif /* NWFS_RWCACHE */ } Modified: head/sys/fs/smbfs/smbfs_io.c ============================================================================== --- head/sys/fs/smbfs/smbfs_io.c Wed Jun 1 20:09:49 2011 (r222585) +++ head/sys/fs/smbfs/smbfs_io.c Wed Jun 1 21:00:28 2011 (r222586) @@ -609,7 +609,7 @@ smbfs_putpages(ap) npages = btoc(count); for (i = 0; i < npages; i++) { - rtvals[i] = VM_PAGER_AGAIN; + rtvals[i] = VM_PAGER_ERROR; } bp = getpbuf(&smbfs_pbuf_freecnt); @@ -639,13 +639,8 @@ smbfs_putpages(ap) relpbuf(bp, &smbfs_pbuf_freecnt); - if (!error) { - int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; - for (i = 0; i < nwritten; i++) { - rtvals[i] = VM_PAGER_OK; - vm_page_undirty(pages[i]); - } - } + if (!error) + vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid); return rtvals[0]; #endif /* SMBFS_RWGENERIC */ } Modified: head/sys/nfsclient/nfs_bio.c ============================================================================== --- head/sys/nfsclient/nfs_bio.c Wed Jun 1 20:09:49 2011 (r222585) +++ head/sys/nfsclient/nfs_bio.c Wed Jun 1 21:00:28 2011 (r222586) @@ -300,7 +300,7 @@ nfs_putpages(struct vop_putpages_args *a } for (i = 0; i < npages; i++) - rtvals[i] = VM_PAGER_AGAIN; + rtvals[i] = VM_PAGER_ERROR; /* * When putting pages, do not extend file past EOF. @@ -344,11 +344,7 @@ nfs_putpages(struct vop_putpages_args *a relpbuf(bp, &nfs_pbuf_freecnt); if (!error) { - int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; - for (i = 0; i < nwritten; i++) { - rtvals[i] = VM_PAGER_OK; - vm_page_undirty(pages[i]); - } + vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid); if (must_commit) { nfs_clearcommit(vp->v_mount); } Modified: head/sys/vm/vm_object.c ============================================================================== --- head/sys/vm/vm_object.c Wed Jun 1 20:09:49 2011 (r222585) +++ head/sys/vm/vm_object.c Wed Jun 1 21:00:28 2011 (r222586) @@ -852,6 +852,21 @@ rescan: flags, &clearobjflags); if (object->generation != curgeneration) goto rescan; + + /* + * If the VOP_PUTPAGES() did a truncated write, so + * that even the first page of the run is not fully + * written, vm_pageout_flush() returns 0 as the run + * length. Since the condition that caused truncated + * write may be permanent, e.g. exhausted free space, + * accepting n == 0 would cause an infinite loop. + * + * Forwarding the iterator leaves the unwritten page + * behind, but there is not much we can do there if + * filesystem refuses to write it. + */ + if (n == 0) + n = 1; np = vm_page_find_least(object, pi + n); } #if 0 Modified: head/sys/vm/vnode_pager.c ============================================================================== --- head/sys/vm/vnode_pager.c Wed Jun 1 20:09:49 2011 (r222585) +++ head/sys/vm/vnode_pager.c Wed Jun 1 21:00:28 2011 (r222586) @@ -1089,7 +1089,7 @@ vnode_pager_generic_putpages(struct vnod count = bytecount / PAGE_SIZE; for (i = 0; i < count; i++) - rtvals[i] = VM_PAGER_AGAIN; + rtvals[i] = VM_PAGER_ERROR; if ((int64_t)ma[0]->pindex < 0) { printf("vnode_pager_putpages: attempt to write meta-data!!! -- 0x%lx(%lx)\n", @@ -1191,3 +1191,20 @@ vnode_pager_generic_putpages(struct vnod } return rtvals[0]; } + +void +vnode_pager_undirty_pages(vm_page_t *ma, int *rtvals, int written) +{ + int i, pos; + + for (i = 0, pos = 0; pos < written; i++, pos += PAGE_SIZE) { + if (pos < trunc_page(written)) { + rtvals[i] = VM_PAGER_OK; + vm_page_undirty(ma[i]); + } else { + /* Partially written page. */ + rtvals[i] = VM_PAGER_AGAIN; + vm_page_clear_dirty(ma[i], 0, written & PAGE_MASK); + } + } +} Modified: head/sys/vm/vnode_pager.h ============================================================================== --- head/sys/vm/vnode_pager.h Wed Jun 1 20:09:49 2011 (r222585) +++ head/sys/vm/vnode_pager.h Wed Jun 1 21:00:28 2011 (r222586) @@ -49,5 +49,8 @@ int vnode_pager_generic_getpages(struct int vnode_pager_generic_putpages(struct vnode *vp, vm_page_t *m, int count, boolean_t sync, int *rtvals); + +void vnode_pager_undirty_pages(vm_page_t *ma, int *rtvals, int written); + #endif /* _KERNEL */ #endif /* _VNODE_PAGER_ */ _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"