From 557da38095dca348e8c02b36b483b12e44fc521f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 17 May 2019 16:37:50 -0700 Subject: [PATCH 1/3] loop: avoid EAGAIN, if offset or block_size are changed Previously, there was a bug where user could see stale buffer cache (e.g, 512B) attached in the 4KB-sized pager cache, when the block size was changed from 512B to 4KB. That was fixed by: commit 5db470e229e2 ("loop: drop caches if offset or block_size are changed") But, there were some regression reports saying the fix returns EAGAIN easily. So, this patch removes previously added EAGAIN condition, nrpages != 0. Instead, it changes the flow like this: - sync_blockdev() - blk_mq_freeze_queue() : change the loop configuration - blk_mq_unfreeze_queue() - sync_blockdev() - invalidate_bdev() After invalidating the buffer cache, we must see the full valid 4KB page. Additional concern came from Bart in which we can lose some data when changing the lo_offset. In that case, this patch adds: - sync_blockdev() - blk_set_queue_dying - blk_mq_freeze_queue() : change the loop configuration - blk_mq_unfreeze_queue() - blk_queue_flag_clear(QUEUE_FLAG_DYING); - sync_blockdev() - invalidate_bdev() Report: https://bugs.chromium.org/p/chromium/issues/detail?id=938958#c38 Cc: Cc: Jens Axboe Cc: linux-block@vger.kernel.org Cc: Bart Van Assche Fixes: 5db470e229e2 ("loop: drop caches if offset or block_size are changed") Reported-by: Gwendal Grignou Reported-by: grygorii tertychnyi Reviewed-by: Bart Van Assche Signed-off-by: Jaegeuk Kim --- drivers/block/loop.c | 56 +++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index a42c49e04954b..2926cb9ff9ad6 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1247,6 +1247,8 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) kuid_t uid = current_uid(); struct block_device *bdev; bool partscan = false; + bool drop_request = false; + bool drop_cache = false; err = mutex_lock_killable(&loop_ctl_mutex); if (err) @@ -1266,14 +1268,21 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) goto out_unlock; } + if (lo->lo_offset != info->lo_offset) + drop_request = true; if (lo->lo_offset != info->lo_offset || - lo->lo_sizelimit != info->lo_sizelimit) { - sync_blockdev(lo->lo_device); - kill_bdev(lo->lo_device); - } + lo->lo_sizelimit != info->lo_sizelimit) + drop_cache = true; - /* I/O need to be drained during transfer transition */ - blk_mq_freeze_queue(lo->lo_queue); + sync_blockdev(lo->lo_device); + + if (drop_request) { + blk_set_queue_dying(lo->lo_queue); + blk_mq_freeze_queue_wait(lo->lo_queue); + } else { + /* I/O need to be drained during transfer transition */ + blk_mq_freeze_queue(lo->lo_queue); + } err = loop_release_xfer(lo); if (err) @@ -1300,14 +1309,6 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if (lo->lo_offset != info->lo_offset || lo->lo_sizelimit != info->lo_sizelimit) { - /* kill_bdev should have truncated all the pages */ - if (lo->lo_device->bd_inode->i_mapping->nrpages) { - err = -EAGAIN; - pr_warn("%s: loop%d (%s) has still dirty pages (nrpages=%lu)\n", - __func__, lo->lo_number, lo->lo_file_name, - lo->lo_device->bd_inode->i_mapping->nrpages); - goto out_unfreeze; - } if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) { err = -EFBIG; goto out_unfreeze; @@ -1344,6 +1345,8 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) out_unfreeze: blk_mq_unfreeze_queue(lo->lo_queue); + if (drop_request) + blk_queue_flag_clear(QUEUE_FLAG_DYING, lo->lo_queue); if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) && !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { @@ -1352,6 +1355,12 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) bdev = lo->lo_device; partscan = true; } + + /* truncate stale pages cached by previous operations */ + if (!err && drop_cache) { + sync_blockdev(lo->lo_device); + invalidate_bdev(lo->lo_device); + } out_unlock: mutex_unlock(&loop_ctl_mutex); if (partscan) @@ -1533,8 +1542,6 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg) static int loop_set_block_size(struct loop_device *lo, unsigned long arg) { - int err = 0; - if (lo->lo_state != Lo_bound) return -ENXIO; @@ -1545,27 +1552,18 @@ static int loop_set_block_size(struct loop_device *lo, unsigned long arg) return 0; sync_blockdev(lo->lo_device); - kill_bdev(lo->lo_device); - blk_mq_freeze_queue(lo->lo_queue); - /* kill_bdev should have truncated all the pages */ - if (lo->lo_device->bd_inode->i_mapping->nrpages) { - err = -EAGAIN; - pr_warn("%s: loop%d (%s) has still dirty pages (nrpages=%lu)\n", - __func__, lo->lo_number, lo->lo_file_name, - lo->lo_device->bd_inode->i_mapping->nrpages); - goto out_unfreeze; - } - blk_queue_logical_block_size(lo->lo_queue, arg); blk_queue_physical_block_size(lo->lo_queue, arg); blk_queue_io_min(lo->lo_queue, arg); loop_update_dio(lo); -out_unfreeze: blk_mq_unfreeze_queue(lo->lo_queue); - return err; + /* truncate stale pages cached by previous operations */ + sync_blockdev(lo->lo_device); + invalidate_bdev(lo->lo_device); + return 0; } static int lo_simple_ioctl(struct loop_device *lo, unsigned int cmd, From ecd9c492bb20c430262d103c26f55ae39891c3d4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 25 May 2020 08:20:26 -0700 Subject: [PATCH 2/3] f2fs: stop gc/discard operations in cp_error case Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 6 ++++++ fs/f2fs/segment.c | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 5b95d5a146eb6..0e8ed3db84429 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -50,6 +50,12 @@ static int gc_thread_func(void *data) if (kthread_should_stop()) break; + if (unlikely(is_sbi_flag_set(sbi, SBI_NEED_FSCK) || + f2fs_cp_error(sbi))) { + increase_sleep_time(gc_th, &wait_ms); + continue; + } + if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { increase_sleep_time(gc_th, &wait_ms); stat_other_skip_bggc_count(sbi); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1c48ec866b8cd..29ee941beb481 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1062,6 +1062,10 @@ static void __check_sit_bitmap(struct f2fs_sb_info *sbi, size = max_blocks; map = (unsigned long *)(sentry->cur_valid_map); offset = __find_rev_next_bit(map, size, offset); + + if (unlikely(f2fs_cp_error(sbi))) + break; + f2fs_bug_on(sbi, offset != size); blk = START_BLOCK(sbi, segno + 1); } @@ -1131,7 +1135,8 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi, if (dc->state != D_PREP) return 0; - if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) + if (unlikely(is_sbi_flag_set(sbi, SBI_NEED_FSCK) || + f2fs_cp_error(sbi))) return 0; trace_f2fs_issue_discard(bdev, dc->start, dc->len); @@ -1733,7 +1738,8 @@ static int issue_discard_thread(void *data) continue; if (kthread_should_stop()) return 0; - if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) { + if (unlikely(is_sbi_flag_set(sbi, SBI_NEED_FSCK) || + f2fs_cp_error(sbi))) { wait_ms = dpolicy.max_interval; continue; } From ed8f3b257d98b3c0716dfed80e36633f1c0da15a Mon Sep 17 00:00:00 2001 From: "Eric.Y" Date: Wed, 27 May 2020 11:03:46 +0800 Subject: [PATCH 3/3] f2fs:compress: Update isize after compress_write_end, otherwise apendwriting some files would fail when cluster.idx did not add --- fs/f2fs/data.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 48a622b95b76e..9229e4715f376 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3437,6 +3437,9 @@ static int f2fs_write_end(struct file *file, /* overwrite compressed file */ if (f2fs_compressed_file(inode) && fsdata) { f2fs_compress_write_end(inode, fsdata, page->index, copied); + if (pos + copied > i_size_read(inode) && + !f2fs_verity_in_progress(inode)) + f2fs_i_size_write(inode, pos + copied); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return copied; }