Merge tag 'kvm-x86-pmu-6.6-fixes' of https://github.com/kvm-x86/linux into HEAD
[platform/kernel/linux-starfive.git] / block / fops.c
index 838ffad..acff3d5 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/falloc.h>
 #include <linux/suspend.h>
 #include <linux/fs.h>
+#include <linux/iomap.h>
 #include <linux/module.h>
 #include "blk.h"
 
@@ -23,15 +24,6 @@ static inline struct inode *bdev_file_inode(struct file *file)
        return file->f_mapping->host;
 }
 
-static int blkdev_get_block(struct inode *inode, sector_t iblock,
-               struct buffer_head *bh, int create)
-{
-       bh->b_bdev = I_BDEV(inode);
-       bh->b_blocknr = iblock;
-       set_buffer_mapped(bh);
-       return 0;
-}
-
 static blk_opf_t dio_bio_write_op(struct kiocb *iocb)
 {
        blk_opf_t opf = REQ_OP_WRITE | REQ_SYNC | REQ_IDLE;
@@ -387,6 +379,37 @@ static ssize_t blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
        return __blkdev_direct_IO(iocb, iter, bio_max_segs(nr_pages));
 }
 
+static int blkdev_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+               unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
+{
+       struct block_device *bdev = I_BDEV(inode);
+       loff_t isize = i_size_read(inode);
+
+       iomap->bdev = bdev;
+       iomap->offset = ALIGN_DOWN(offset, bdev_logical_block_size(bdev));
+       if (iomap->offset >= isize)
+               return -EIO;
+       iomap->type = IOMAP_MAPPED;
+       iomap->addr = iomap->offset;
+       iomap->length = isize - iomap->offset;
+       iomap->flags |= IOMAP_F_BUFFER_HEAD; /* noop for !CONFIG_BUFFER_HEAD */
+       return 0;
+}
+
+static const struct iomap_ops blkdev_iomap_ops = {
+       .iomap_begin            = blkdev_iomap_begin,
+};
+
+#ifdef CONFIG_BUFFER_HEAD
+static int blkdev_get_block(struct inode *inode, sector_t iblock,
+               struct buffer_head *bh, int create)
+{
+       bh->b_bdev = I_BDEV(inode);
+       bh->b_blocknr = iblock;
+       set_buffer_mapped(bh);
+       return 0;
+}
+
 static int blkdev_writepage(struct page *page, struct writeback_control *wbc)
 {
        return block_write_full_page(page, blkdev_get_block, wbc);
@@ -429,10 +452,58 @@ const struct address_space_operations def_blk_aops = {
        .writepage      = blkdev_writepage,
        .write_begin    = blkdev_write_begin,
        .write_end      = blkdev_write_end,
-       .direct_IO      = blkdev_direct_IO,
        .migrate_folio  = buffer_migrate_folio_norefs,
        .is_dirty_writeback = buffer_check_dirty_writeback,
 };
+#else /* CONFIG_BUFFER_HEAD */
+static int blkdev_read_folio(struct file *file, struct folio *folio)
+{
+       return iomap_read_folio(folio, &blkdev_iomap_ops);
+}
+
+static void blkdev_readahead(struct readahead_control *rac)
+{
+       iomap_readahead(rac, &blkdev_iomap_ops);
+}
+
+static int blkdev_map_blocks(struct iomap_writepage_ctx *wpc,
+               struct inode *inode, loff_t offset)
+{
+       loff_t isize = i_size_read(inode);
+
+       if (WARN_ON_ONCE(offset >= isize))
+               return -EIO;
+       if (offset >= wpc->iomap.offset &&
+           offset < wpc->iomap.offset + wpc->iomap.length)
+               return 0;
+       return blkdev_iomap_begin(inode, offset, isize - offset,
+                                 IOMAP_WRITE, &wpc->iomap, NULL);
+}
+
+static const struct iomap_writeback_ops blkdev_writeback_ops = {
+       .map_blocks             = blkdev_map_blocks,
+};
+
+static int blkdev_writepages(struct address_space *mapping,
+               struct writeback_control *wbc)
+{
+       struct iomap_writepage_ctx wpc = { };
+
+       return iomap_writepages(mapping, wbc, &wpc, &blkdev_writeback_ops);
+}
+
+const struct address_space_operations def_blk_aops = {
+       .dirty_folio    = filemap_dirty_folio,
+       .release_folio          = iomap_release_folio,
+       .invalidate_folio       = iomap_invalidate_folio,
+       .read_folio             = blkdev_read_folio,
+       .readahead              = blkdev_readahead,
+       .writepages             = blkdev_writepages,
+       .is_partially_uptodate  = iomap_is_partially_uptodate,
+       .error_remove_page      = generic_error_remove_page,
+       .migrate_folio          = filemap_migrate_folio,
+};
+#endif /* CONFIG_BUFFER_HEAD */
 
 /*
  * for a block special file file_inode(file)->i_size is zero
@@ -506,7 +577,7 @@ static int blkdev_open(struct inode *inode, struct file *filp)
         * during an unstable branch.
         */
        filp->f_flags |= O_LARGEFILE;
-       filp->f_mode |= FMODE_BUF_RASYNC;
+       filp->f_mode |= FMODE_BUF_RASYNC | FMODE_CAN_ODIRECT;
 
        /*
         * Use the file private data to store the holder for exclusive openes.
@@ -534,6 +605,35 @@ static int blkdev_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
+static ssize_t
+blkdev_direct_write(struct kiocb *iocb, struct iov_iter *from)
+{
+       size_t count = iov_iter_count(from);
+       ssize_t written;
+
+       written = kiocb_invalidate_pages(iocb, count);
+       if (written) {
+               if (written == -EBUSY)
+                       return 0;
+               return written;
+       }
+
+       written = blkdev_direct_IO(iocb, from);
+       if (written > 0) {
+               kiocb_invalidate_post_direct_write(iocb, count);
+               iocb->ki_pos += written;
+               count -= written;
+       }
+       if (written != -EIOCBQUEUED)
+               iov_iter_revert(from, count - iov_iter_count(from));
+       return written;
+}
+
+static ssize_t blkdev_buffered_write(struct kiocb *iocb, struct iov_iter *from)
+{
+       return iomap_file_buffered_write(iocb, from, &blkdev_iomap_ops);
+}
+
 /*
  * Write data to the block device.  Only intended for the block device itself
  * and the raw driver which basically is a fake block device.
@@ -543,7 +643,8 @@ static int blkdev_release(struct inode *inode, struct file *filp)
  */
 static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
-       struct block_device *bdev = I_BDEV(iocb->ki_filp->f_mapping->host);
+       struct file *file = iocb->ki_filp;
+       struct block_device *bdev = I_BDEV(file->f_mapping->host);
        struct inode *bd_inode = bdev->bd_inode;
        loff_t size = bdev_nr_bytes(bdev);
        size_t shorted = 0;
@@ -570,7 +671,19 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
                iov_iter_truncate(from, size);
        }
 
-       ret = __generic_file_write_iter(iocb, from);
+       ret = file_update_time(file);
+       if (ret)
+               return ret;
+
+       if (iocb->ki_flags & IOCB_DIRECT) {
+               ret = blkdev_direct_write(iocb, from);
+               if (ret >= 0 && iov_iter_count(from))
+                       ret = direct_write_fallback(iocb, from, ret,
+                                       blkdev_buffered_write(iocb, from));
+       } else {
+               ret = blkdev_buffered_write(iocb, from);
+       }
+
        if (ret > 0)
                ret = generic_write_sync(iocb, ret);
        iov_iter_reexpand(from, iov_iter_count(from) + shorted);