Implement a wrapper for aio_read()/write() to allow async IO on files
not implementing the iter version of read/write, such as sysfs. This
mimics how readv/writev uses non-iter ops in do_loop_readv_writev().

Signed-off-by: Bjorn Andersson <[email protected]>
---
 fs/aio.c | 52 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 7 deletions(-)

diff --git a/fs/aio.c b/fs/aio.c
index f9f441b59966..0137a1a9bef1 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -1514,12 +1514,44 @@ static inline void aio_rw_done(struct kiocb *req, 
ssize_t ret)
        }
 }
 
+static ssize_t aio_iter_readv_writev(struct file *file, struct kiocb *req,
+                                    struct iov_iter *iter, int type)
+{
+       ssize_t ret = 0;
+       ssize_t nr;
+
+       while (iov_iter_count(iter)) {
+               struct iovec iovec = iov_iter_iovec(iter);
+
+               if (type == READ) {
+                       nr = file->f_op->read(file, iovec.iov_base,
+                                             iovec.iov_len, &req->ki_pos);
+               } else {
+                       nr = file->f_op->write(file, iovec.iov_base,
+                                              iovec.iov_len, &req->ki_pos);
+               }
+
+               if (nr < 0) {
+                       ret = nr;
+                       break;
+               }
+
+               ret += nr;
+               if (nr != iovec.iov_len)
+                       break;
+               iov_iter_advance(iter, nr);
+       }
+
+       return ret;
+}
+
 static int aio_read(struct kiocb *req, const struct iocb *iocb,
                        bool vectored, bool compat)
 {
        struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
        struct iov_iter iter;
        struct file *file;
+       ssize_t count;
        int ret;
 
        ret = aio_prep_rw(req, iocb);
@@ -1529,15 +1561,18 @@ static int aio_read(struct kiocb *req, const struct 
iocb *iocb,
        if (unlikely(!(file->f_mode & FMODE_READ)))
                return -EBADF;
        ret = -EINVAL;
-       if (unlikely(!file->f_op->read_iter))
-               return -EINVAL;
 
        ret = aio_setup_rw(READ, iocb, &iovec, vectored, compat, &iter);
        if (ret < 0)
                return ret;
        ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter));
-       if (!ret)
-               aio_rw_done(req, call_read_iter(file, req, &iter));
+       if (!ret) {
+               if (likely(file->f_op->read_iter))
+                       count = call_read_iter(file, req, &iter);
+               else
+                       count = aio_iter_readv_writev(file, req, &iter, READ);
+               aio_rw_done(req, count);
+       }
        kfree(iovec);
        return ret;
 }
@@ -1548,6 +1583,7 @@ static int aio_write(struct kiocb *req, const struct iocb 
*iocb,
        struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
        struct iov_iter iter;
        struct file *file;
+       ssize_t count;
        int ret;
 
        ret = aio_prep_rw(req, iocb);
@@ -1557,8 +1593,6 @@ static int aio_write(struct kiocb *req, const struct iocb 
*iocb,
 
        if (unlikely(!(file->f_mode & FMODE_WRITE)))
                return -EBADF;
-       if (unlikely(!file->f_op->write_iter))
-               return -EINVAL;
 
        ret = aio_setup_rw(WRITE, iocb, &iovec, vectored, compat, &iter);
        if (ret < 0)
@@ -1577,7 +1611,11 @@ static int aio_write(struct kiocb *req, const struct 
iocb *iocb,
                        __sb_writers_release(file_inode(file)->i_sb, 
SB_FREEZE_WRITE);
                }
                req->ki_flags |= IOCB_WRITE;
-               aio_rw_done(req, call_write_iter(file, req, &iter));
+               if (likely(file->f_op->write_iter))
+                       count = call_write_iter(file, req, &iter);
+               else
+                       count = aio_iter_readv_writev(file, req, &iter, WRITE);
+               aio_rw_done(req, count);
        }
        kfree(iovec);
        return ret;
-- 
2.18.0

Reply via email to