]> xenbits.xensource.com Git - people/aperard/linux.git/commitdiff
f2fs: compress: fix to cover normal cluster write with cp_rwsem
authorChao Yu <chao@kernel.org>
Fri, 12 Jan 2024 19:41:28 +0000 (03:41 +0800)
committerSasha Levin <sashal@kernel.org>
Tue, 26 Mar 2024 22:20:54 +0000 (18:20 -0400)
[ Upstream commit fd244524c2cf07b5f4c3fe8abd6a99225c76544b ]

When we overwrite compressed cluster w/ normal cluster, we should
not unlock cp_rwsem during f2fs_write_raw_pages(), otherwise data
will be corrupted if partial blocks were persisted before CP & SPOR,
due to cluster metadata wasn't updated atomically.

Fixes: 4c8ff7095bef ("f2fs: support data compression")
Reviewed-by: Daeho Jeong <daehojeong@google.com>
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/f2fs/compress.c
fs/f2fs/data.c

index 459bf10f297a2be4477f7c2a3510c5d4673a0434..553950962842ad9613de355752e4badfbfe1a9a6 100644 (file)
@@ -1462,12 +1462,14 @@ void f2fs_compress_write_end_io(struct bio *bio, struct page *page)
 }
 
 static int f2fs_write_raw_pages(struct compress_ctx *cc,
-                                       int *submitted,
+                                       int *submitted_p,
                                        struct writeback_control *wbc,
                                        enum iostat_type io_type)
 {
        struct address_space *mapping = cc->inode->i_mapping;
-       int _submitted, compr_blocks, ret, i;
+       struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
+       int submitted, compr_blocks, i;
+       int ret = 0;
 
        compr_blocks = f2fs_compressed_blocks(cc);
 
@@ -1482,6 +1484,10 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc,
        if (compr_blocks < 0)
                return compr_blocks;
 
+       /* overwrite compressed cluster w/ normal cluster */
+       if (compr_blocks > 0)
+               f2fs_lock_op(sbi);
+
        for (i = 0; i < cc->cluster_size; i++) {
                if (!cc->rpages[i])
                        continue;
@@ -1506,7 +1512,7 @@ continue_unlock:
                if (!clear_page_dirty_for_io(cc->rpages[i]))
                        goto continue_unlock;
 
-               ret = f2fs_write_single_data_page(cc->rpages[i], &_submitted,
+               ret = f2fs_write_single_data_page(cc->rpages[i], &submitted,
                                                NULL, NULL, wbc, io_type,
                                                compr_blocks, false);
                if (ret) {
@@ -1514,26 +1520,29 @@ continue_unlock:
                                unlock_page(cc->rpages[i]);
                                ret = 0;
                        } else if (ret == -EAGAIN) {
+                               ret = 0;
                                /*
                                 * for quota file, just redirty left pages to
                                 * avoid deadlock caused by cluster update race
                                 * from foreground operation.
                                 */
                                if (IS_NOQUOTA(cc->inode))
-                                       return 0;
-                               ret = 0;
+                                       goto out;
                                f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
                                goto retry_write;
                        }
-                       return ret;
+                       goto out;
                }
 
-               *submitted += _submitted;
+               *submitted_p += submitted;
        }
 
-       f2fs_balance_fs(F2FS_M_SB(mapping), true);
+out:
+       if (compr_blocks > 0)
+               f2fs_unlock_op(sbi);
 
-       return 0;
+       f2fs_balance_fs(sbi, true);
+       return ret;
 }
 
 int f2fs_write_multi_pages(struct compress_ctx *cc,
index 6f649a60859fcd31d4d85c0cb431b99769d77c31..3f0ba71451c27d318a0a67a15c3665a6d83ddb68 100644 (file)
@@ -2779,7 +2779,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
                .encrypted_page = NULL,
                .submitted = 0,
                .compr_blocks = compr_blocks,
-               .need_lock = LOCK_RETRY,
+               .need_lock = compr_blocks ? LOCK_DONE : LOCK_RETRY,
                .post_read = f2fs_post_read_required(inode) ? 1 : 0,
                .io_type = io_type,
                .io_wbc = wbc,
@@ -2859,6 +2859,7 @@ write:
        if (err == -EAGAIN) {
                err = f2fs_do_write_data_page(&fio);
                if (err == -EAGAIN) {
+                       f2fs_bug_on(sbi, compr_blocks);
                        fio.need_lock = LOCK_REQ;
                        err = f2fs_do_write_data_page(&fio);
                }