Skip to content

Commit 55f21e1

Browse files
fdmananagregkh
authored andcommitted
Btrfs: fix warning when replaying log after fsync of a tmpfile
commit f2d72f4 upstream. When replaying a log which contains a tmpfile (which necessarily has a link count of 0) we end up calling inc_nlink(), at fs/btrfs/tree-log.c:replay_one_buffer(), which produces a warning like the following: [195191.943673] WARNING: CPU: 0 PID: 6924 at fs/inode.c:342 inc_nlink+0x33/0x40 [195191.943723] CPU: 0 PID: 6924 Comm: mount Not tainted 4.19.0-rc6-btrfs-next-38 #1 [195191.943724] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.11.2-0-gf9626ccb91-prebuilt.qemu-project.org 04/01/2014 [195191.943726] RIP: 0010:inc_nlink+0x33/0x40 [195191.943728] RSP: 0018:ffffb96e425e3870 EFLAGS: 00010246 [195191.943730] RAX: 0000000000000000 RBX: ffff8c0d1e6af4f0 RCX: 0000000000000006 [195191.943731] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff8c0d1e6af4f0 [195191.943731] RBP: 0000000000000097 R08: 0000000000000001 R09: 0000000000000000 [195191.943732] R10: 0000000000000000 R11: 0000000000000000 R12: ffffb96e425e3a60 [195191.943733] R13: ffff8c0d10cff0c8 R14: ffff8c0d0d515348 R15: ffff8c0d78a1b3f8 [195191.943735] FS: 00007f570ee24480(0000) GS:ffff8c0dfb200000(0000) knlGS:0000000000000000 [195191.943736] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [195191.943737] CR2: 00005593286277c8 CR3: 00000000bb8f2006 CR4: 00000000003606f0 [195191.943739] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [195191.943740] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [195191.943741] Call Trace: [195191.943778] replay_one_buffer+0x797/0x7d0 [btrfs] [195191.943802] walk_up_log_tree+0x1c1/0x250 [btrfs] [195191.943809] ? rcu_read_lock_sched_held+0x3f/0x70 [195191.943825] walk_log_tree+0xae/0x1d0 [btrfs] [195191.943840] btrfs_recover_log_trees+0x1d7/0x4d0 [btrfs] [195191.943856] ? replay_dir_deletes+0x280/0x280 [btrfs] [195191.943870] open_ctree+0x1c3b/0x22a0 [btrfs] [195191.943887] btrfs_mount_root+0x6b4/0x800 [btrfs] [195191.943894] ? rcu_read_lock_sched_held+0x3f/0x70 [195191.943899] ? pcpu_alloc+0x55b/0x7c0 [195191.943906] ? mount_fs+0x3b/0x140 [195191.943908] mount_fs+0x3b/0x140 [195191.943912] ? __init_waitqueue_head+0x36/0x50 [195191.943916] vfs_kern_mount+0x62/0x160 [195191.943927] btrfs_mount+0x134/0x890 [btrfs] [195191.943936] ? rcu_read_lock_sched_held+0x3f/0x70 [195191.943938] ? pcpu_alloc+0x55b/0x7c0 [195191.943943] ? mount_fs+0x3b/0x140 [195191.943952] ? btrfs_remount+0x570/0x570 [btrfs] [195191.943954] mount_fs+0x3b/0x140 [195191.943956] ? __init_waitqueue_head+0x36/0x50 [195191.943960] vfs_kern_mount+0x62/0x160 [195191.943963] do_mount+0x1f9/0xd40 [195191.943967] ? memdup_user+0x4b/0x70 [195191.943971] ksys_mount+0x7e/0xd0 [195191.943974] __x64_sys_mount+0x21/0x30 [195191.943977] do_syscall_64+0x60/0x1b0 [195191.943980] entry_SYSCALL_64_after_hwframe+0x49/0xbe [195191.943983] RIP: 0033:0x7f570e4e524a [195191.943986] RSP: 002b:00007ffd83589478 EFLAGS: 00000206 ORIG_RAX: 00000000000000a5 [195191.943989] RAX: ffffffffffffffda RBX: 0000563f335b2060 RCX: 00007f570e4e524a [195191.943990] RDX: 0000563f335b2240 RSI: 0000563f335b2280 RDI: 0000563f335b2260 [195191.943992] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000020 [195191.943993] R10: 00000000c0ed0000 R11: 0000000000000206 R12: 0000563f335b2260 [195191.943994] R13: 0000563f335b2240 R14: 0000000000000000 R15: 00000000ffffffff [195191.944002] irq event stamp: 8688 [195191.944010] hardirqs last enabled at (8687): [<ffffffff9cb004c3>] console_unlock+0x503/0x640 [195191.944012] hardirqs last disabled at (8688): [<ffffffff9ca037dd>] trace_hardirqs_off_thunk+0x1a/0x1c [195191.944018] softirqs last enabled at (8638): [<ffffffff9cc0a5d1>] __set_page_dirty_nobuffers+0x101/0x150 [195191.944020] softirqs last disabled at (8634): [<ffffffff9cc26bbe>] wb_wakeup_delayed+0x2e/0x60 [195191.944022] ---[ end trace 5d6e873a9a0b811a ]--- This happens because the inode does not have the flag I_LINKABLE set, which is a runtime only flag, not meant to be persisted, set when the inode is created through open(2) if the flag O_EXCL is not passed to it. Except for the warning, there are no other consequences (like corruptions or metadata inconsistencies). Since it's pointless to replay a tmpfile as it would be deleted in a later phase of the log replay procedure (it has a link count of 0), fix this by not logging tmpfiles and if a tmpfile is found in a log (created by a kernel without this change), skip the replay of the inode. A test case for fstests follows soon. Fixes: 471d557 ("Btrfs: fix loss of prealloc extents past i_size after fsync log replay") CC: [email protected] # 4.18+ Reported-by: Martin Steigerwald <[email protected]> Link: https://lore.kernel.org/linux-btrfs/3666619.NTnn27ZJZE@merkaba/ Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 1d6d4a0 commit 55f21e1

File tree

1 file changed

+32
-10
lines changed

1 file changed

+32
-10
lines changed

fs/btrfs/tree-log.c

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,13 @@ struct walk_control {
258258
/* what stage of the replay code we're currently in */
259259
int stage;
260260

261+
/*
262+
* Ignore any items from the inode currently being processed. Needs
263+
* to be set every time we find a BTRFS_INODE_ITEM_KEY and we are in
264+
* the LOG_WALK_REPLAY_INODES stage.
265+
*/
266+
bool ignore_cur_inode;
267+
261268
/* the root we are currently replaying */
262269
struct btrfs_root *replay_dest;
263270

@@ -2487,6 +2494,20 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
24872494

24882495
inode_item = btrfs_item_ptr(eb, i,
24892496
struct btrfs_inode_item);
2497+
/*
2498+
* If we have a tmpfile (O_TMPFILE) that got fsync'ed
2499+
* and never got linked before the fsync, skip it, as
2500+
* replaying it is pointless since it would be deleted
2501+
* later. We skip logging tmpfiles, but it's always
2502+
* possible we are replaying a log created with a kernel
2503+
* that used to log tmpfiles.
2504+
*/
2505+
if (btrfs_inode_nlink(eb, inode_item) == 0) {
2506+
wc->ignore_cur_inode = true;
2507+
continue;
2508+
} else {
2509+
wc->ignore_cur_inode = false;
2510+
}
24902511
ret = replay_xattr_deletes(wc->trans, root, log,
24912512
path, key.objectid);
24922513
if (ret)
@@ -2524,16 +2545,8 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
25242545
root->fs_info->sectorsize);
25252546
ret = btrfs_drop_extents(wc->trans, root, inode,
25262547
from, (u64)-1, 1);
2527-
/*
2528-
* If the nlink count is zero here, the iput
2529-
* will free the inode. We bump it to make
2530-
* sure it doesn't get freed until the link
2531-
* count fixup is done.
2532-
*/
25332548
if (!ret) {
2534-
if (inode->i_nlink == 0)
2535-
inc_nlink(inode);
2536-
/* Update link count and nbytes. */
2549+
/* Update the inode's nbytes. */
25372550
ret = btrfs_update_inode(wc->trans,
25382551
root, inode);
25392552
}
@@ -2548,6 +2561,9 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
25482561
break;
25492562
}
25502563

2564+
if (wc->ignore_cur_inode)
2565+
continue;
2566+
25512567
if (key.type == BTRFS_DIR_INDEX_KEY &&
25522568
wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
25532569
ret = replay_one_dir_item(wc->trans, root, path,
@@ -5643,7 +5659,13 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
56435659
if (ret)
56445660
goto end_no_trans;
56455661

5646-
if (btrfs_inode_in_log(inode, trans->transid)) {
5662+
/*
5663+
* Skip already logged inodes or inodes corresponding to tmpfiles
5664+
* (since logging them is pointless, a link count of 0 means they
5665+
* will never be accessible).
5666+
*/
5667+
if (btrfs_inode_in_log(inode, trans->transid) ||
5668+
inode->vfs_inode.i_nlink == 0) {
56475669
ret = BTRFS_NO_LOG_SYNC;
56485670
goto end_no_trans;
56495671
}

0 commit comments

Comments
 (0)