Commit fd244524 authored by Chao Yu's avatar Chao Yu Committed by Jaegeuk Kim
Browse files

f2fs: compress: fix to cover normal cluster write with cp_rwsem



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: 4c8ff709 ("f2fs: support data compression")
Reviewed-by: default avatarDaeho Jeong <daehojeong@google.com>
Signed-off-by: default avatarChao Yu <chao@kernel.org>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 8a430dd4
Loading
Loading
Loading
Loading
+18 −9
Original line number Diff line number Diff line
@@ -1443,12 +1443,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);

@@ -1463,6 +1465,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;
@@ -1487,7 +1493,7 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc,
		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) {
@@ -1495,26 +1501,29 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc,
				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,
+2 −1
Original line number Diff line number Diff line
@@ -2839,7 +2839,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,
@@ -2920,6 +2920,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
	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);
		}