Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[platform/kernel/linux-starfive.git] / fs / ext4 / file.c
index 6bdf61a..0b8b449 100644 (file)
@@ -202,8 +202,9 @@ ext4_extending_io(struct inode *inode, loff_t offset, size_t len)
        return false;
 }
 
-/* Is IO overwriting allocated and initialized blocks? */
-static bool ext4_overwrite_io(struct inode *inode, loff_t pos, loff_t len)
+/* Is IO overwriting allocated or initialized blocks? */
+static bool ext4_overwrite_io(struct inode *inode,
+                             loff_t pos, loff_t len, bool *unwritten)
 {
        struct ext4_map_blocks map;
        unsigned int blkbits = inode->i_blkbits;
@@ -217,12 +218,15 @@ static bool ext4_overwrite_io(struct inode *inode, loff_t pos, loff_t len)
        blklen = map.m_len;
 
        err = ext4_map_blocks(NULL, inode, &map, 0);
+       if (err != blklen)
+               return false;
        /*
         * 'err==len' means that all of the blocks have been preallocated,
-        * regardless of whether they have been initialized or not. To exclude
-        * unwritten extents, we need to check m_flags.
+        * regardless of whether they have been initialized or not. We need to
+        * check m_flags to distinguish the unwritten extents.
         */
-       return err == blklen && (map.m_flags & EXT4_MAP_MAPPED);
+       *unwritten = !(map.m_flags & EXT4_MAP_MAPPED);
+       return true;
 }
 
 static ssize_t ext4_generic_write_checks(struct kiocb *iocb,
@@ -431,11 +435,16 @@ static const struct iomap_dio_ops ext4_dio_write_ops = {
  * - For extending writes case we don't take the shared lock, since it requires
  *   updating inode i_disksize and/or orphan handling with exclusive lock.
  *
- * - shared locking will only be true mostly with overwrites. Otherwise we will
- *   switch to exclusive i_rwsem lock.
+ * - shared locking will only be true mostly with overwrites, including
+ *   initialized blocks and unwritten blocks. For overwrite unwritten blocks
+ *   we protect splitting extents by i_data_sem in ext4_inode_info, so we can
+ *   also release exclusive i_rwsem lock.
+ *
+ * - Otherwise we will switch to exclusive i_rwsem lock.
  */
 static ssize_t ext4_dio_write_checks(struct kiocb *iocb, struct iov_iter *from,
-                                    bool *ilock_shared, bool *extend)
+                                    bool *ilock_shared, bool *extend,
+                                    bool *unwritten)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(file);
@@ -459,7 +468,7 @@ restart:
         * in file_modified().
         */
        if (*ilock_shared && (!IS_NOSEC(inode) || *extend ||
-            !ext4_overwrite_io(inode, offset, count))) {
+            !ext4_overwrite_io(inode, offset, count, unwritten))) {
                if (iocb->ki_flags & IOCB_NOWAIT) {
                        ret = -EAGAIN;
                        goto out;
@@ -491,7 +500,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
        loff_t offset = iocb->ki_pos;
        size_t count = iov_iter_count(from);
        const struct iomap_ops *iomap_ops = &ext4_iomap_ops;
-       bool extend = false, unaligned_io = false;
+       bool extend = false, unaligned_io = false, unwritten = false;
        bool ilock_shared = true;
 
        /*
@@ -534,7 +543,8 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
                return ext4_buffered_write_iter(iocb, from);
        }
 
-       ret = ext4_dio_write_checks(iocb, from, &ilock_shared, &extend);
+       ret = ext4_dio_write_checks(iocb, from,
+                                   &ilock_shared, &extend, &unwritten);
        if (ret <= 0)
                return ret;
 
@@ -582,7 +592,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
                ext4_journal_stop(handle);
        }
 
-       if (ilock_shared)
+       if (ilock_shared && !unwritten)
                iomap_ops = &ext4_iomap_overwrite_ops;
        ret = iomap_dio_rw(iocb, from, iomap_ops, &ext4_dio_write_ops,
                           (unaligned_io || extend) ? IOMAP_DIO_FORCE_WAIT : 0,