summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authormdf <mdf@FreeBSD.org>2011-04-19 16:36:24 +0000
committermdf <mdf@FreeBSD.org>2011-04-19 16:36:24 +0000
commit597ae9f19b6fef74ff11610f93edbbe7203826bc (patch)
tree78d599ef539b474a5c063ebdaa0cbc1a6980fc47 /sys/kern
parentcc6bebd7b6fa6e0e167807baca1a190b89f16203 (diff)
downloadFreeBSD-src-597ae9f19b6fef74ff11610f93edbbe7203826bc.zip
FreeBSD-src-597ae9f19b6fef74ff11610f93edbbe7203826bc.tar.gz
Allow VOP_ALLOCATE to be iterative, and have kern_posix_fallocate(9)
drive looping and potentially yielding. Requested by: kib
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/vfs_default.c44
-rw-r--r--sys/kern/vfs_syscalls.c59
-rw-r--r--sys/kern/vnode_if.src6
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;
};
OpenPOWER on IntegriCloud