Commit 89319d31 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Al Viro

fs: remove aio_run_iocb

Pass the ABI iocb structure to aio_setup_rw and let it handle the
non-vectored I/O case as well.  With that and a new helper for the AIO
return value handling we can now define new aio_read and aio_write
helpers that implement reads and writes in a self-contained way without
duplicating too much code.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 723c0384
...@@ -1392,110 +1392,100 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) ...@@ -1392,110 +1392,100 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
return -EINVAL; return -EINVAL;
} }
typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); static int aio_setup_rw(int rw, struct iocb *iocb, struct iovec **iovec,
bool vectored, bool compat, struct iov_iter *iter)
static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len,
struct iovec **iovec,
bool compat,
struct iov_iter *iter)
{ {
void __user *buf = (void __user *)(uintptr_t)iocb->aio_buf;
size_t len = iocb->aio_nbytes;
if (!vectored) {
ssize_t ret = import_single_range(rw, buf, len, *iovec, iter);
*iovec = NULL;
return ret;
}
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
if (compat) if (compat)
return compat_import_iovec(rw, return compat_import_iovec(rw, buf, len, UIO_FASTIOV, iovec,
(struct compat_iovec __user *)buf, iter);
len, UIO_FASTIOV, iovec, iter);
#endif #endif
return import_iovec(rw, (struct iovec __user *)buf, return import_iovec(rw, buf, len, UIO_FASTIOV, iovec, iter);
len, UIO_FASTIOV, iovec, iter);
} }
/* static inline ssize_t aio_ret(struct kiocb *req, ssize_t ret)
* aio_run_iocb: {
* Performs the initial checks and io submission. switch (ret) {
case -EIOCBQUEUED:
return ret;
case -ERESTARTSYS:
case -ERESTARTNOINTR:
case -ERESTARTNOHAND:
case -ERESTART_RESTARTBLOCK:
/*
* There's no easy way to restart the syscall since other AIO's
* may be already running. Just fail this IO with EINTR.
*/ */
static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, ret = -EINTR;
char __user *buf, size_t len, bool compat) /*FALLTHRU*/
default:
aio_complete(req, ret, 0);
return 0;
}
}
static ssize_t aio_read(struct kiocb *req, struct iocb *iocb, bool vectored,
bool compat)
{ {
struct file *file = req->ki_filp; struct file *file = req->ki_filp;
ssize_t ret;
int rw;
fmode_t mode;
rw_iter_op *iter_op;
struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
struct iov_iter iter; struct iov_iter iter;
ssize_t ret;
switch (opcode) { if (unlikely(!(file->f_mode & FMODE_READ)))
case IOCB_CMD_PREAD:
case IOCB_CMD_PREADV:
mode = FMODE_READ;
rw = READ;
iter_op = file->f_op->read_iter;
goto rw_common;
case IOCB_CMD_PWRITE:
case IOCB_CMD_PWRITEV:
mode = FMODE_WRITE;
rw = WRITE;
iter_op = file->f_op->write_iter;
goto rw_common;
rw_common:
if (unlikely(!(file->f_mode & mode)))
return -EBADF; return -EBADF;
if (unlikely(!file->f_op->read_iter))
if (!iter_op)
return -EINVAL; return -EINVAL;
if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) ret = aio_setup_rw(READ, iocb, &iovec, vectored, compat, &iter);
ret = aio_setup_vectored_rw(rw, buf, len, if (ret)
&iovec, compat, &iter); return ret;
else { ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter));
ret = import_single_range(rw, buf, len, iovec, &iter);
iovec = NULL;
}
if (!ret) if (!ret)
ret = rw_verify_area(rw, file, &req->ki_pos, ret = aio_ret(req, file->f_op->read_iter(req, &iter));
iov_iter_count(&iter));
if (ret < 0) {
kfree(iovec); kfree(iovec);
return ret; return ret;
} }
get_file(file);
if (rw == WRITE)
file_start_write(file);
ret = iter_op(req, &iter);
if (rw == WRITE) static ssize_t aio_write(struct kiocb *req, struct iocb *iocb, bool vectored,
file_end_write(file); bool compat)
fput(file); {
kfree(iovec); struct file *file = req->ki_filp;
break; struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
struct iov_iter iter;
ssize_t ret;
default: if (unlikely(!(file->f_mode & FMODE_WRITE)))
pr_debug("EINVAL: no operation provided\n"); return -EBADF;
if (unlikely(!file->f_op->write_iter))
return -EINVAL; return -EINVAL;
}
if (ret != -EIOCBQUEUED) { ret = aio_setup_rw(WRITE, iocb, &iovec, vectored, compat, &iter);
/* if (ret)
* There's no easy way to restart the syscall since other AIO's return ret;
* may be already running. Just fail this IO with EINTR. ret = rw_verify_area(WRITE, file, &req->ki_pos, iov_iter_count(&iter));
*/ if (!ret) {
if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR || file_start_write(file);
ret == -ERESTARTNOHAND || ret = aio_ret(req, file->f_op->write_iter(req, &iter));
ret == -ERESTART_RESTARTBLOCK)) file_end_write(file);
ret = -EINTR;
aio_complete(req, ret, 0);
} }
kfree(iovec);
return 0; return ret;
} }
static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
struct iocb *iocb, bool compat) struct iocb *iocb, bool compat)
{ {
struct aio_kiocb *req; struct aio_kiocb *req;
struct file *file;
ssize_t ret; ssize_t ret;
/* enforce forwards compatibility on users */ /* enforce forwards compatibility on users */
...@@ -1518,7 +1508,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, ...@@ -1518,7 +1508,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
if (unlikely(!req)) if (unlikely(!req))
return -EAGAIN; return -EAGAIN;
req->common.ki_filp = fget(iocb->aio_fildes); req->common.ki_filp = file = fget(iocb->aio_fildes);
if (unlikely(!req->common.ki_filp)) { if (unlikely(!req->common.ki_filp)) {
ret = -EBADF; ret = -EBADF;
goto out_put_req; goto out_put_req;
...@@ -1553,13 +1543,29 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, ...@@ -1553,13 +1543,29 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
req->ki_user_iocb = user_iocb; req->ki_user_iocb = user_iocb;
req->ki_user_data = iocb->aio_data; req->ki_user_data = iocb->aio_data;
ret = aio_run_iocb(&req->common, iocb->aio_lio_opcode, get_file(file);
(char __user *)(unsigned long)iocb->aio_buf, switch (iocb->aio_lio_opcode) {
iocb->aio_nbytes, case IOCB_CMD_PREAD:
compat); ret = aio_read(&req->common, iocb, false, compat);
if (ret) break;
goto out_put_req; case IOCB_CMD_PWRITE:
ret = aio_write(&req->common, iocb, false, compat);
break;
case IOCB_CMD_PREADV:
ret = aio_read(&req->common, iocb, true, compat);
break;
case IOCB_CMD_PWRITEV:
ret = aio_write(&req->common, iocb, true, compat);
break;
default:
pr_debug("invalid aio operation %d\n", iocb->aio_lio_opcode);
ret = -EINVAL;
break;
}
fput(file);
if (ret && ret != -EIOCBQUEUED)
goto out_put_req;
return 0; return 0;
out_put_req: out_put_req:
put_reqs_available(ctx, 1); put_reqs_available(ctx, 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment