[PATCH] Add vector AIO support
This work is initially done by Zach Brown to add support for vectored aio. These are the core changes for AIO to support IOCB_CMD_PREADV/IOCB_CMD_PWRITEV. [akpm@osdl.org: huge build fix] Signed-off-by: Zach Brown <zach.brown@oracle.com> Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Badari Pulavarty <pbadari@us.ibm.com> Acked-by: Benjamin LaHaise <bcrl@kvack.org> Acked-by: James Morris <jmorris@namei.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
committed by
Linus Torvalds
parent
543ade1fc9
commit
eed4e51fb6
131
fs/read_write.c
131
fs/read_write.c
@@ -511,6 +511,74 @@ ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov,
|
||||
/* A write operation does a read from user space and vice versa */
|
||||
#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
|
||||
|
||||
ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
|
||||
unsigned long nr_segs, unsigned long fast_segs,
|
||||
struct iovec *fast_pointer,
|
||||
struct iovec **ret_pointer)
|
||||
{
|
||||
unsigned long seg;
|
||||
ssize_t ret;
|
||||
struct iovec *iov = fast_pointer;
|
||||
|
||||
/*
|
||||
* SuS says "The readv() function *may* fail if the iovcnt argument
|
||||
* was less than or equal to 0, or greater than {IOV_MAX}. Linux has
|
||||
* traditionally returned zero for zero segments, so...
|
||||
*/
|
||||
if (nr_segs == 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* First get the "struct iovec" from user memory and
|
||||
* verify all the pointers
|
||||
*/
|
||||
if (nr_segs > UIO_MAXIOV) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (nr_segs > fast_segs) {
|
||||
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
|
||||
if (iov == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the Single Unix Specification we should return EINVAL
|
||||
* if an element length is < 0 when cast to ssize_t or if the
|
||||
* total length would overflow the ssize_t return value of the
|
||||
* system call.
|
||||
*/
|
||||
ret = 0;
|
||||
for (seg = 0; seg < nr_segs; seg++) {
|
||||
void __user *buf = iov[seg].iov_base;
|
||||
ssize_t len = (ssize_t)iov[seg].iov_len;
|
||||
|
||||
/* see if we we're about to use an invalid len or if
|
||||
* it's about to overflow ssize_t */
|
||||
if (len < 0 || (ret + len < ret)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (unlikely(!access_ok(vrfy_dir(type), buf, len))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret += len;
|
||||
}
|
||||
out:
|
||||
*ret_pointer = iov;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t do_readv_writev(int type, struct file *file,
|
||||
const struct iovec __user * uvector,
|
||||
unsigned long nr_segs, loff_t *pos)
|
||||
@@ -519,64 +587,20 @@ static ssize_t do_readv_writev(int type, struct file *file,
|
||||
struct iovec iovstack[UIO_FASTIOV];
|
||||
struct iovec *iov = iovstack;
|
||||
ssize_t ret;
|
||||
int seg;
|
||||
io_fn_t fn;
|
||||
iov_fn_t fnv;
|
||||
|
||||
/*
|
||||
* SuS says "The readv() function *may* fail if the iovcnt argument
|
||||
* was less than or equal to 0, or greater than {IOV_MAX}. Linux has
|
||||
* traditionally returned zero for zero segments, so...
|
||||
*/
|
||||
ret = 0;
|
||||
if (nr_segs == 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* First get the "struct iovec" from user memory and
|
||||
* verify all the pointers
|
||||
*/
|
||||
ret = -EINVAL;
|
||||
if (nr_segs > UIO_MAXIOV)
|
||||
goto out;
|
||||
if (!file->f_op)
|
||||
goto out;
|
||||
if (nr_segs > UIO_FASTIOV) {
|
||||
ret = -ENOMEM;
|
||||
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
|
||||
if (!iov)
|
||||
goto out;
|
||||
}
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Single unix specification:
|
||||
* We should -EINVAL if an element length is not >= 0 and fitting an
|
||||
* ssize_t. The total length is fitting an ssize_t
|
||||
*
|
||||
* Be careful here because iov_len is a size_t not an ssize_t
|
||||
*/
|
||||
tot_len = 0;
|
||||
ret = -EINVAL;
|
||||
for (seg = 0; seg < nr_segs; seg++) {
|
||||
void __user *buf = iov[seg].iov_base;
|
||||
ssize_t len = (ssize_t)iov[seg].iov_len;
|
||||
|
||||
if (len < 0) /* size_t not fitting an ssize_t .. */
|
||||
goto out;
|
||||
if (unlikely(!access_ok(vrfy_dir(type), buf, len)))
|
||||
goto Efault;
|
||||
tot_len += len;
|
||||
if ((ssize_t)tot_len < 0) /* maths overflow on the ssize_t */
|
||||
goto out;
|
||||
}
|
||||
if (tot_len == 0) {
|
||||
ret = 0;
|
||||
if (!file->f_op) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = rw_copy_check_uvector(type, uvector, nr_segs,
|
||||
ARRAY_SIZE(iovstack), iovstack, &iov);
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
|
||||
tot_len = ret;
|
||||
ret = rw_verify_area(type, file, pos, tot_len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -609,9 +633,6 @@ out:
|
||||
fsnotify_modify(file->f_dentry);
|
||||
}
|
||||
return ret;
|
||||
Efault:
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,
|
||||
|
Reference in New Issue
Block a user