Signed-off-by: Jens Axboe <[email protected]>
---
 fs/block_dev.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 66 insertions(+), 10 deletions(-)

diff --git a/fs/block_dev.c b/fs/block_dev.c
index 2010997fd326..62ca4ce21222 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -176,9 +176,68 @@ static struct inode *bdev_file_inode(struct file *file)
        return file->f_mapping->host;
 }
 
+static void blkdev_bio_end_io_async(struct bio *bio)
+{
+       struct kiocb *iocb = bio->bi_private;
+
+       iocb->ki_complete(iocb, bio->bi_error, 0);
+
+       if (bio_op(bio) == REQ_OP_READ)
+               bio_check_pages_dirty(bio);
+       else
+               bio_put(bio);
+}
+
+static ssize_t
+__blkdev_direct_IO_async(struct kiocb *iocb, struct iov_iter *iter,
+                        int nr_pages)
+{
+       struct file *file = iocb->ki_filp;
+       struct block_device *bdev = I_BDEV(bdev_file_inode(file));
+       unsigned blkbits = blksize_bits(bdev_logical_block_size(bdev));
+       loff_t pos = iocb->ki_pos;
+       struct bio *bio;
+       ssize_t ret;
+
+       if ((pos | iov_iter_alignment(iter)) & ((1 << blkbits) - 1))
+               return -EINVAL;
+
+       bio = bio_alloc(GFP_KERNEL, nr_pages);
+       if (!bio)
+               return -ENOMEM;
+
+       bio->bi_bdev = bdev;
+       bio->bi_iter.bi_sector = pos >> blkbits;
+       bio->bi_private = iocb;
+       bio->bi_end_io = blkdev_bio_end_io_async;
+
+       ret = bio_iov_iter_get_pages(bio, iter);
+       if (unlikely(ret))
+               return ret;
+
+       /*
+        * Overload bio size in error. If it gets set, we lose the
+        * size, but we don't need the size for that case. IO is limited
+        * to BIO_MAX_PAGES, so we can't overflow.
+        */
+       ret = bio->bi_error = bio->bi_iter.bi_size;
+
+       if (iov_iter_rw(iter) == READ) {
+               bio_set_op_attrs(bio, REQ_OP_READ, 0);
+               bio_set_pages_dirty(bio);
+       } else {
+               bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_SYNC | REQ_IDLE);
+               task_io_account_write(ret);
+       }
+
+       submit_bio(bio);
+       iocb->ki_pos += ret;
+       return -EIOCBQUEUED;
+}
+
 #define DIO_INLINE_BIO_VECS 4
 
-static void blkdev_bio_end_io_simple(struct bio *bio)
+static void blkdev_bio_end_io_sync(struct bio *bio)
 {
        struct task_struct *waiter = bio->bi_private;
 
@@ -187,8 +246,7 @@ static void blkdev_bio_end_io_simple(struct bio *bio)
 }
 
 static ssize_t
-__blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter,
-               int nr_pages)
+__blkdev_direct_IO_sync(struct kiocb *iocb, struct iov_iter *iter, int 
nr_pages)
 {
        struct file *file = iocb->ki_filp;
        struct block_device *bdev = I_BDEV(bdev_file_inode(file));
@@ -218,7 +276,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct 
iov_iter *iter,
        bio.bi_bdev = bdev;
        bio.bi_iter.bi_sector = pos >> blkbits;
        bio.bi_private = current;
-       bio.bi_end_io = blkdev_bio_end_io_simple;
+       bio.bi_end_io = blkdev_bio_end_io_sync;
 
        ret = bio_iov_iter_get_pages(&bio, iter);
        if (unlikely(ret))
@@ -263,18 +321,16 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct 
iov_iter *iter,
 static ssize_t
 blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 {
-       struct file *file = iocb->ki_filp;
-       struct inode *inode = bdev_file_inode(file);
        int nr_pages;
 
        nr_pages = iov_iter_npages(iter, BIO_MAX_PAGES);
        if (!nr_pages)
                return 0;
+
        if (is_sync_kiocb(iocb))
-               return __blkdev_direct_IO_simple(iocb, iter, nr_pages);
-       return __blockdev_direct_IO(iocb, inode, I_BDEV(inode), iter,
-                                   blkdev_get_block, NULL, NULL,
-                                   DIO_SKIP_DIO_COUNT);
+               return __blkdev_direct_IO_sync(iocb, iter, nr_pages);
+
+       return __blkdev_direct_IO_async(iocb, iter, nr_pages);
 }
 
 int __sync_blockdev(struct block_device *bdev, int wait)
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to