ext4: convert DAX reads to iomap infrastructure
authorJan Kara <jack@suse.cz>
Sun, 20 Nov 2016 22:36:06 +0000 (17:36 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 20 Nov 2016 22:36:06 +0000 (17:36 -0500)
Implement basic iomap_begin function that handles reading and use it for
DAX reads.

Reviewed-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/ext4.h
fs/ext4/file.c
fs/ext4/inode.c

index bdf1e5ee8642aa929294e8f397d38811ae1f7566..da82de65035074674a5dce90746e5bf426dff3a2 100644 (file)
@@ -3266,6 +3266,8 @@ static inline bool ext4_aligned_io(struct inode *inode, loff_t off, loff_t len)
        return IS_ALIGNED(off, blksize) && IS_ALIGNED(len, blksize);
 }
 
+extern struct iomap_ops ext4_iomap_ops;
+
 #endif /* __KERNEL__ */
 
 #define EFSBADCRC      EBADMSG         /* Bad CRC detected */
index 9facb4dc5c702777c0af9435229c60030ea197b0..1f25c644cb120d3d1fe1565bbd6cec3472b11d93 100644 (file)
 #include "xattr.h"
 #include "acl.h"
 
+#ifdef CONFIG_FS_DAX
+static ssize_t ext4_dax_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+       struct inode *inode = file_inode(iocb->ki_filp);
+       ssize_t ret;
+
+       inode_lock_shared(inode);
+       /*
+        * Recheck under inode lock - at this point we are sure it cannot
+        * change anymore
+        */
+       if (!IS_DAX(inode)) {
+               inode_unlock_shared(inode);
+               /* Fallback to buffered IO in case we cannot support DAX */
+               return generic_file_read_iter(iocb, to);
+       }
+       ret = dax_iomap_rw(iocb, to, &ext4_iomap_ops);
+       inode_unlock_shared(inode);
+
+       file_accessed(iocb->ki_filp);
+       return ret;
+}
+#endif
+
+static ssize_t ext4_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+       if (!iov_iter_count(to))
+               return 0; /* skip atime */
+
+#ifdef CONFIG_FS_DAX
+       if (IS_DAX(file_inode(iocb->ki_filp)))
+               return ext4_dax_read_iter(iocb, to);
+#endif
+       return generic_file_read_iter(iocb, to);
+}
+
 /*
  * Called when an inode is released. Note that this is different
  * from ext4_file_open: open gets called at every open, but release
@@ -690,7 +726,7 @@ loff_t ext4_llseek(struct file *file, loff_t offset, int whence)
 
 const struct file_operations ext4_file_operations = {
        .llseek         = ext4_llseek,
-       .read_iter      = generic_file_read_iter,
+       .read_iter      = ext4_file_read_iter,
        .write_iter     = ext4_file_write_iter,
        .unlocked_ioctl = ext4_ioctl,
 #ifdef CONFIG_COMPAT
index 733fda1c3646da25397a826a81fa6090ce9f312f..5e0526fed165fd0edc9e0bb5eaa55c1b36df4767 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/printk.h>
 #include <linux/slab.h>
 #include <linux/bitops.h>
+#include <linux/iomap.h>
 
 #include "ext4_jbd2.h"
 #include "xattr.h"
@@ -3318,6 +3319,59 @@ int ext4_dax_get_block(struct inode *inode, sector_t iblock,
        clear_buffer_new(bh_result);
        return 0;
 }
+
+static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+                           unsigned flags, struct iomap *iomap)
+{
+       unsigned int blkbits = inode->i_blkbits;
+       unsigned long first_block = offset >> blkbits;
+       unsigned long last_block = (offset + length - 1) >> blkbits;
+       struct ext4_map_blocks map;
+       int ret;
+
+       if (flags & IOMAP_WRITE)
+               return -EIO;
+
+       if (WARN_ON_ONCE(ext4_has_inline_data(inode)))
+               return -ERANGE;
+
+       map.m_lblk = first_block;
+       map.m_len = last_block - first_block + 1;
+
+       ret = ext4_map_blocks(NULL, inode, &map, 0);
+       if (ret < 0)
+               return ret;
+
+       iomap->flags = 0;
+       iomap->bdev = inode->i_sb->s_bdev;
+       iomap->offset = first_block << blkbits;
+
+       if (ret == 0) {
+               iomap->type = IOMAP_HOLE;
+               iomap->blkno = IOMAP_NULL_BLOCK;
+               iomap->length = (u64)map.m_len << blkbits;
+       } else {
+               if (map.m_flags & EXT4_MAP_MAPPED) {
+                       iomap->type = IOMAP_MAPPED;
+               } else if (map.m_flags & EXT4_MAP_UNWRITTEN) {
+                       iomap->type = IOMAP_UNWRITTEN;
+               } else {
+                       WARN_ON_ONCE(1);
+                       return -EIO;
+               }
+               iomap->blkno = (sector_t)map.m_pblk << (blkbits - 9);
+               iomap->length = (u64)map.m_len << blkbits;
+       }
+
+       if (map.m_flags & EXT4_MAP_NEW)
+               iomap->flags |= IOMAP_F_NEW;
+       return 0;
+}
+
+struct iomap_ops ext4_iomap_ops = {
+       .iomap_begin            = ext4_iomap_begin,
+};
+
 #else
 /* Just define empty function, it will never get called. */
 int ext4_dax_get_block(struct inode *inode, sector_t iblock,