Allow VOP_ALLOCATE to be iterative, and have kern_posix_fallocate(9)

drive looping and potentially yielding.

Requested by:	kib
This commit is contained in:
Matthew D Fleming 2011-04-19 16:36:24 +00:00
parent 51268821a9
commit 1ce4508f6d
3 changed files with 61 additions and 48 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
};