]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ext4: do not convert the unwritten extents if data writeback fails
authorBaokun Li <libaokun1@huawei.com>
Wed, 22 Jan 2025 11:05:26 +0000 (19:05 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 29 May 2025 09:12:44 +0000 (11:12 +0200)
[ Upstream commit e856f93e0fb249955f7d5efb18fe20500a9ccc6d ]

When dioread_nolock is turned on (the default), it will convert unwritten
extents to written at ext4_end_io_end(), even if the data writeback fails.

It leads to the possibility that stale data may be exposed when the
physical block corresponding to the file data is read-only (i.e., writes
return -EIO, but reads are normal).

Therefore a new ext4_io_end->flags EXT4_IO_END_FAILED is added, which
indicates that some bio write-back failed in the current ext4_io_end.
When this flag is set, the unwritten to written conversion is no longer
performed. Users can read the data normally until the caches are dropped,
after that, the failed extents can only be read to all 0.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Zhang Yi <yi.zhang@huawei.com>
Link: https://patch.msgid.link/20250122110533.4116662-3-libaokun@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/ext4/ext4.h
fs/ext4/page-io.c

index df30d9f235123b78dc333101dbf8474b1f5478c3..d4d285d9993115f6605dff9cc563151f5f352225 100644 (file)
@@ -278,7 +278,8 @@ struct ext4_system_blocks {
 /*
  * Flags for ext4_io_end->flags
  */
-#define        EXT4_IO_END_UNWRITTEN   0x0001
+#define EXT4_IO_END_UNWRITTEN  0x0001
+#define EXT4_IO_END_FAILED     0x0002
 
 struct ext4_io_end_vec {
        struct list_head list;          /* list of io_end_vec */
index 69b8a7221a2b19cf14a0a701cb06488f5bdbd478..f041a5d93716fb09e2e148453bbf36d2e2c0d502 100644 (file)
@@ -181,14 +181,25 @@ static int ext4_end_io_end(ext4_io_end_t *io_end)
                   "list->prev 0x%p\n",
                   io_end, inode->i_ino, io_end->list.next, io_end->list.prev);
 
-       io_end->handle = NULL;  /* Following call will use up the handle */
-       ret = ext4_convert_unwritten_io_end_vec(handle, io_end);
+       /*
+        * Do not convert the unwritten extents if data writeback fails,
+        * or stale data may be exposed.
+        */
+       io_end->handle = NULL;  /* Following call will use up the handle */
+       if (unlikely(io_end->flag & EXT4_IO_END_FAILED)) {
+               ret = -EIO;
+               if (handle)
+                       jbd2_journal_free_reserved(handle);
+       } else {
+               ret = ext4_convert_unwritten_io_end_vec(handle, io_end);
+       }
        if (ret < 0 && !ext4_forced_shutdown(inode->i_sb)) {
                ext4_msg(inode->i_sb, KERN_EMERG,
                         "failed to convert unwritten extents to written "
                         "extents -- potential data loss!  "
                         "(inode %lu, error %d)", inode->i_ino, ret);
        }
+
        ext4_clear_io_unwritten_flag(io_end);
        ext4_release_io_end(io_end);
        return ret;
@@ -344,6 +355,7 @@ static void ext4_end_bio(struct bio *bio)
                             bio->bi_status, inode->i_ino,
                             (unsigned long long)
                             bi_sector >> (inode->i_blkbits - 9));
+               io_end->flag |= EXT4_IO_END_FAILED;
                mapping_set_error(inode->i_mapping,
                                blk_status_to_errno(bio->bi_status));
        }