diff options
-rw-r--r-- | sys/kern/vfs_default.c | 44 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 59 | ||||
-rw-r--r-- | sys/kern/vnode_if.src | 6 |
3 files changed, 61 insertions, 48 deletions
diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c index 6fd4b97..e5f1254 100644 --- a/sys/kern/vfs_default.c +++ b/sys/kern/vfs_default.c @@ -865,25 +865,25 @@ vop_stdallocate(struct vop_allocate_args *ap) 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 *ap) } 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 *ap) 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 *ap) 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); } diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index ae408ca..33f7c5b 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -4678,12 +4678,11 @@ kern_posix_fallocate(struct thread *td, int fd, off_t offset, off_t len) 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, int fd, off_t offset, off_t len) 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); diff --git a/sys/kern/vnode_if.src b/sys/kern/vnode_if.src index fe838ec..f4ac4a2 100644 --- a/sys/kern/vnode_if.src +++ b/sys/kern/vnode_if.src @@ -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; }; |