Forwarded: [PATCH] f2fs: fix hung task in block_operations during checkpoint

syzbot posted 1 patch 1 week, 6 days ago
There is a newer version of this series
fs/f2fs/checkpoint.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
Forwarded: [PATCH] f2fs: fix hung task in block_operations during checkpoint
Posted by syzbot 1 week, 6 days ago
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] f2fs: fix hung task in block_operations during checkpoint
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

    f2fs_sync_inode_meta() can return 0 (success) even when
    f2fs_update_inode_page() fails and triggers f2fs_stop_checkpoint().
    This happens because the error flag check only occurs at the start
    of each loop iteration, not after f2fs_update_inode_page() returns.

    When I/O errors occur:
    1. f2fs_update_inode_page() retries 8 times then calls
       f2fs_stop_checkpoint(), which sets CP_ERROR_FLAG
    2. f2fs_sync_inode_meta() returns 0 without checking the error flag
    3. block_operations() sees success and loops back to retry_flush_quotas
    4. Dirty inodes remain on list (sync failed), loop repeats forever
    5. Checkpoint never completes, waiters block indefinitely

    This causes hung tasks when operations like unlink wait for checkpoint
    completion while holding locks that other tasks need.

    Fix by checking f2fs_cp_error() after processing each inode in
    f2fs_sync_inode_meta() to detect errors from f2fs_update_inode_page().

    Reported-by: syzbot+4235e4d7b6fd75704528@syzkaller.appspotmail.com
    Closes: https://syzkaller.appspot.com/bug?extid=4235e4d7b6fd75704528
    Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/f2fs/checkpoint.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index bbe07e3a6c75..05b1495b6fb6 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -1134,12 +1134,19 @@ static int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi)
 	struct inode *inode;
 	struct f2fs_inode_info *fi;
 	s64 total = get_pages(sbi, F2FS_DIRTY_IMETA);
+	printk(KERN_ERR "f2fs_sync_inode_meta: total=%lld\n", total);
 
 	while (total--) {
+		printk(KERN_ERR "f2fs_sync_inode_meta: loop iteration, total=%lld\n", total);
+
 		if (unlikely(f2fs_cp_error(sbi)))
 			return -EIO;
-
+		printk(KERN_ERR "f2fs_sync_inode_meta: before spin_lock\n");
+		
 		spin_lock(&sbi->inode_lock[DIRTY_META]);
+		        printk(KERN_ERR "f2fs_sync_inode_meta: after spin_lock\n");
+		printk(KERN_ERR "f2fs_sync_inode_meta: after spin_lock, cp_error=%d\n", f2fs_cp_error(sbi));
+
 		if (list_empty(head)) {
 			spin_unlock(&sbi->inode_lock[DIRTY_META]);
 			return 0;
@@ -1152,9 +1159,13 @@ static int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi)
 			sync_inode_metadata(inode, 0);
 
 			/* it's on eviction */
-			if (is_inode_flag_set(inode, FI_DIRTY_INODE))
+			if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
+				printk(KERN_ERR "I AM DONE \n");
 				f2fs_update_inode_page(inode);
+			}
 			iput(inode);
+			if (unlikely(f2fs_cp_error(sbi)))
+				return -EIO;
 		}
 	}
 	return 0;
@@ -1275,14 +1286,25 @@ static int block_operations(struct f2fs_sb_info *sbi)
 		atomic_inc(&sbi->wb_sync_req[NODE]);
 		err = f2fs_sync_node_pages(sbi, &wbc, false, FS_CP_NODE_IO);
 		atomic_dec(&sbi->wb_sync_req[NODE]);
+		printk(KERN_ERR "block_operations: f2fs_sync_node_pages returned %d, cp_error=%d\n", 
+		       err, f2fs_cp_error(sbi));
 		if (err) {
 			f2fs_up_write(&sbi->node_change);
 			f2fs_unlock_all(sbi);
 			return err;
 		}
+		/* Check if I/O error set CP_ERROR_FLAG */
+		if (unlikely(f2fs_cp_error(sbi))) {
+			printk(KERN_ERR "NOT ALLOWED \n");
+			f2fs_up_write(&sbi->node_change);
+			f2fs_unlock_all(sbi);
+			return -EIO;
+		}
 		cond_resched();
 		goto retry_flush_nodes;
 	}
+	printk(KERN_ERR "block_operations: no more dirty nodes\n");
+
 
 	/*
 	 * sbi->node_change is used only for AIO write_begin path which produces
-- 
2.43.0