Author: mdf
Date: Tue Apr 19 16:36:24 2011
New Revision: 220846
URL: http://svn.freebsd.org/changeset/base/220846

Log:
  Allow VOP_ALLOCATE to be iterative, and have kern_posix_fallocate(9)
  drive looping and potentially yielding.
  
  Requested by: kib

Modified:
  head/sys/kern/vfs_default.c
  head/sys/kern/vfs_syscalls.c
  head/sys/kern/vnode_if.src

Modified: head/sys/kern/vfs_default.c
==============================================================================
--- head/sys/kern/vfs_default.c Tue Apr 19 16:33:08 2011        (r220845)
+++ head/sys/kern/vfs_default.c Tue Apr 19 16:36:24 2011        (r220846)
@@ -865,25 +865,25 @@ vop_stdallocate(struct vop_allocate_args
        struct iovec aiov;
        struct vattr vattr, *vap;
        struct uio auio;
-       off_t len, cur, offset;
+       off_t fsize, len, cur, offset;
        uint8_t *buf;
        struct thread *td;
        struct vnode *vp;
        size_t iosize;
-       int error, locked;
+       int error;
 
        buf = NULL;
        error = 0;
-       locked = 1;
        td = curthread;
        vap = &vattr;
        vp = ap->a_vp;
-       len = ap->a_len;
-       offset = ap->a_offset;
+       len = *ap->a_len;
+       offset = *ap->a_offset;
 
        error = VOP_GETATTR(vp, vap, td->td_ucred);
        if (error != 0)
                goto out;
+       fsize = vap->va_size;
        iosize = vap->va_blocksize;
        if (iosize == 0)
                iosize = BLKDEV_IOSIZE;
@@ -908,27 +908,22 @@ vop_stdallocate(struct vop_allocate_args
        } else
 #endif
        if (offset + len > vap->va_size) {
+               /*
+                * Test offset + len against the filesystem's maxfilesize.
+                */
                VATTR_NULL(vap);
                vap->va_size = offset + len;
                error = VOP_SETATTR(vp, vap, td->td_ucred);
                if (error != 0)
                        goto out;
+               VATTR_NULL(vap);
+               vap->va_size = fsize;
+               error = VOP_SETATTR(vp, vap, td->td_ucred);
+               if (error != 0)
+                       goto out;
        }
 
-       while (len > 0) {
-               if (should_yield()) {
-                       VOP_UNLOCK(vp, 0);
-                       locked = 0;
-                       kern_yield(-1);
-                       error = vn_lock(vp, LK_EXCLUSIVE);
-                       if (error != 0)
-                               break;
-                       locked = 1;
-                       error = VOP_GETATTR(vp, vap, td->td_ucred);
-                       if (error != 0)
-                               break;
-               }
-
+       for (;;) {
                /*
                 * Read and write back anything below the nominal file
                 * size.  There's currently no way outside the filesystem
@@ -939,7 +934,7 @@ vop_stdallocate(struct vop_allocate_args
                        cur -= (offset % iosize);
                if (cur > len)
                        cur = len;
-               if (offset < vap->va_size) {
+               if (offset < fsize) {
                        aiov.iov_base = buf;
                        aiov.iov_len = cur;
                        auio.uio_iov = &aiov;
@@ -976,12 +971,15 @@ vop_stdallocate(struct vop_allocate_args
 
                len -= cur;
                offset += cur;
+               if (len == 0)
+                       break;
+               if (should_yield())
+                       break;
        }
 
  out:
-       KASSERT(locked || error != 0, ("How'd I get unlocked with no error?"));
-       if (locked && error != 0)
-               VOP_UNLOCK(vp, 0);
+       *ap->a_len = len;
+       *ap->a_offset = offset;
        free(buf, M_TEMP);
        return (error);
 }

Modified: head/sys/kern/vfs_syscalls.c
==============================================================================
--- head/sys/kern/vfs_syscalls.c        Tue Apr 19 16:33:08 2011        
(r220845)
+++ head/sys/kern/vfs_syscalls.c        Tue Apr 19 16:36:24 2011        
(r220846)
@@ -4678,12 +4678,11 @@ kern_posix_fallocate(struct thread *td, 
        struct file *fp;
        struct mount *mp;
        struct vnode *vp;
-       int error, vfslocked, vnlocked;
+       off_t olen, ooffset;
+       int error, vfslocked;
 
        fp = NULL;
-       mp = NULL;
        vfslocked = 0;
-       vnlocked = 0;
        error = fget(td, fd, &fp);
        if (error != 0)
                goto out;
@@ -4718,28 +4717,44 @@ kern_posix_fallocate(struct thread *td, 
                goto out;
        }
 
-       bwillwrite();
-       vfslocked = VFS_LOCK_GIANT(vp->v_mount);
-       error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
-       if (error != 0)
-               goto out;
-       error = vn_lock(vp, LK_EXCLUSIVE);
-       if (error != 0)
-               goto out;
-       vnlocked = 1;
+       /* Allocating blocks may take a long time, so iterate. */
+       for (;;) {
+               olen = len;
+               ooffset = offset;
+
+               bwillwrite();
+               vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+               mp = NULL;
+               error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
+               if (error != 0) {
+                       VFS_UNLOCK_GIANT(vfslocked);
+                       break;
+               }
+               error = vn_lock(vp, LK_EXCLUSIVE);
+               if (error != 0) {
+                       vn_finished_write(mp);
+                       VFS_UNLOCK_GIANT(vfslocked);
+                       break;
+               }
 #ifdef MAC
-       error = mac_vnode_check_write(td->td_ucred, fp->f_cred, vp);
-       if (error != 0)
-               goto out;
+               error = mac_vnode_check_write(td->td_ucred, fp->f_cred, vp);
+               if (error == 0)
 #endif
-       error = VOP_ALLOCATE(vp, offset, len);
-       if (error != 0)
-               vnlocked = 0;
- out:
-       if (vnlocked)
+                       error = VOP_ALLOCATE(vp, &offset, &len);
                VOP_UNLOCK(vp, 0);
-       vn_finished_write(mp);
-       VFS_UNLOCK_GIANT(vfslocked);
+               vn_finished_write(mp);
+               VFS_UNLOCK_GIANT(vfslocked);
+
+               if (olen + ooffset != offset + len) {
+                       panic("offset + len changed from %jx/%jx to %jx/%jx",
+                           ooffset, olen, offset, len);
+               }
+               if (error != 0 || len == 0)
+                       break;
+               KASSERT(olen > len, ("Iteration did not make progress?"));
+               maybe_yield();
+       }
+ out:
        if (fp != NULL)
                fdrop(fp, td);
        return (error);

Modified: head/sys/kern/vnode_if.src
==============================================================================
--- head/sys/kern/vnode_if.src  Tue Apr 19 16:33:08 2011        (r220845)
+++ head/sys/kern/vnode_if.src  Tue Apr 19 16:36:24 2011        (r220846)
@@ -621,10 +621,10 @@ vop_vptocnp {
 };
 
 
-%% allocate    vp      E E U
+%% allocate    vp      E E E
 
 vop_allocate {
        IN struct vnode *vp;
-       IN off_t offset;
-       IN off_t len;
+       IN off_t *offset;
+       IN off_t *len;
 };
_______________________________________________
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