ext2: Move direct-io to use iomap
[sfrench/cifs-2.6.git] / fs / ext2 / file.c
index 7491637871391b4a99672625bdd25efcac5b2113..98add36c1a5907da7a7fec0fec2bb6f16d5f9da5 100644 (file)
@@ -162,12 +162,124 @@ int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
        return ret;
 }
 
+static ssize_t ext2_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file->f_mapping->host;
+       ssize_t ret;
+
+       inode_lock_shared(inode);
+       ret = iomap_dio_rw(iocb, to, &ext2_iomap_ops, NULL, 0, NULL, 0);
+       inode_unlock_shared(inode);
+
+       return ret;
+}
+
+static int ext2_dio_write_end_io(struct kiocb *iocb, ssize_t size,
+                                int error, unsigned int flags)
+{
+       loff_t pos = iocb->ki_pos;
+       struct inode *inode = file_inode(iocb->ki_filp);
+
+       if (error)
+               goto out;
+
+       /*
+        * If we are extending the file, we have to update i_size here before
+        * page cache gets invalidated in iomap_dio_rw(). This prevents racing
+        * buffered reads from zeroing out too much from page cache pages.
+        * Note that all extending writes always happens synchronously with
+        * inode lock held by ext2_dio_write_iter(). So it is safe to update
+        * inode size here for extending file writes.
+        */
+       pos += size;
+       if (pos > i_size_read(inode)) {
+               i_size_write(inode, pos);
+               mark_inode_dirty(inode);
+       }
+out:
+       return error;
+}
+
+static const struct iomap_dio_ops ext2_dio_write_ops = {
+       .end_io = ext2_dio_write_end_io,
+};
+
+static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file->f_mapping->host;
+       ssize_t ret;
+       unsigned int flags = 0;
+       unsigned long blocksize = inode->i_sb->s_blocksize;
+       loff_t offset = iocb->ki_pos;
+       loff_t count = iov_iter_count(from);
+
+       inode_lock(inode);
+       ret = generic_write_checks(iocb, from);
+       if (ret <= 0)
+               goto out_unlock;
+
+       ret = kiocb_modified(iocb);
+       if (ret)
+               goto out_unlock;
+
+       /* use IOMAP_DIO_FORCE_WAIT for unaligned or extending writes */
+       if (iocb->ki_pos + iov_iter_count(from) > i_size_read(inode) ||
+          (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(from), blocksize)))
+               flags |= IOMAP_DIO_FORCE_WAIT;
+
+       ret = iomap_dio_rw(iocb, from, &ext2_iomap_ops, &ext2_dio_write_ops,
+                          flags, NULL, 0);
+
+       /* ENOTBLK is magic return value for fallback to buffered-io */
+       if (ret == -ENOTBLK)
+               ret = 0;
+
+       if (ret < 0 && ret != -EIOCBQUEUED)
+               ext2_write_failed(inode->i_mapping, offset + count);
+
+       /* handle case for partial write and for fallback to buffered write */
+       if (ret >= 0 && iov_iter_count(from)) {
+               loff_t pos, endbyte;
+               ssize_t status;
+               int ret2;
+
+               iocb->ki_flags &= ~IOCB_DIRECT;
+               pos = iocb->ki_pos;
+               status = generic_perform_write(iocb, from);
+               if (unlikely(status < 0)) {
+                       ret = status;
+                       goto out_unlock;
+               }
+
+               iocb->ki_pos += status;
+               ret += status;
+               endbyte = pos + status - 1;
+               ret2 = filemap_write_and_wait_range(inode->i_mapping, pos,
+                                                   endbyte);
+               if (!ret2)
+                       invalidate_mapping_pages(inode->i_mapping,
+                                                pos >> PAGE_SHIFT,
+                                                endbyte >> PAGE_SHIFT);
+               if (ret > 0)
+                       generic_write_sync(iocb, ret);
+       }
+
+out_unlock:
+       inode_unlock(inode);
+       return ret;
+}
+
 static ssize_t ext2_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
 #ifdef CONFIG_FS_DAX
        if (IS_DAX(iocb->ki_filp->f_mapping->host))
                return ext2_dax_read_iter(iocb, to);
 #endif
+       if (iocb->ki_flags & IOCB_DIRECT)
+               return ext2_dio_read_iter(iocb, to);
+
        return generic_file_read_iter(iocb, to);
 }
 
@@ -177,6 +289,9 @@ static ssize_t ext2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
        if (IS_DAX(iocb->ki_filp->f_mapping->host))
                return ext2_dax_write_iter(iocb, from);
 #endif
+       if (iocb->ki_flags & IOCB_DIRECT)
+               return ext2_dio_write_iter(iocb, from);
+
        return generic_file_write_iter(iocb, from);
 }