Skip to content

Commit 5ff8eaa

Browse files
htejunaxboe
authored andcommitted
writeback: keep superblock pinned during cgroup writeback association switches
If cgroup writeback is in use, an inode is associated with a cgroup for writeback. If the inode's main dirtier changes to another cgroup, the association gets updated asynchronously. Nothing was pinning the superblock while such switches are in progress and superblock could go away while async switching is pending or in progress leading to crashes like the following. kernel BUG at fs/jbd2/transaction.c:319! invalid opcode: 0000 [rib#1] SMP DEBUG_PAGEALLOC CPU: 1 PID: 29158 Comm: kworker/1:10 Not tainted 4.5.0-rc3 torvalds#51 Hardware name: Google Google, BIOS Google 01/01/2011 Workqueue: events inode_switch_wbs_work_fn task: ffff880213dbbd40 ti: ffff880209264000 task.ti: ffff880209264000 RIP: 0010:[<ffffffff803e6922>] [<ffffffff803e6922>] start_this_handle+0x382/0x3e0 RSP: 0018:ffff880209267c30 EFLAGS: 00010202 ... Call Trace: [<ffffffff803e6be4>] jbd2__journal_start+0xf4/0x190 [<ffffffff803cfc7e>] __ext4_journal_start_sb+0x4e/0x70 [<ffffffff803b31ec>] ext4_evict_inode+0x12c/0x3d0 [<ffffffff8035338b>] evict+0xbb/0x190 [<ffffffff80354190>] iput+0x130/0x190 [<ffffffff80360223>] inode_switch_wbs_work_fn+0x343/0x4c0 [<ffffffff80279819>] process_one_work+0x129/0x300 [<ffffffff80279b16>] worker_thread+0x126/0x480 [<ffffffff8027ed14>] kthread+0xc4/0xe0 [<ffffffff809771df>] ret_from_fork+0x3f/0x70 Fix it by bumping s_active while cgroup association switching is in flight. Signed-off-by: Tejun Heo <[email protected]> Reported-and-tested-by: Tahsin Erdogan <[email protected]> Link: http://lkml.kernel.org/g/CAAeU0aNCq7LGODvVGRU-oU_o-6enii5ey0p1c26D1ZzYwkDc5A@mail.gmail.com Fixes: d10c809 ("writeback: implement foreign cgroup inode bdi_writeback switching") Cc: [email protected] #v4.5+ Signed-off-by: Jens Axboe <[email protected]>
1 parent 2d99b55 commit 5ff8eaa

File tree

1 file changed

+11
-4
lines changed

1 file changed

+11
-4
lines changed

fs/fs-writeback.c

+11-4
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ static void inode_switch_wbs_work_fn(struct work_struct *work)
317317
struct inode_switch_wbs_context *isw =
318318
container_of(work, struct inode_switch_wbs_context, work);
319319
struct inode *inode = isw->inode;
320+
struct super_block *sb = inode->i_sb;
320321
struct address_space *mapping = inode->i_mapping;
321322
struct bdi_writeback *old_wb = inode->i_wb;
322323
struct bdi_writeback *new_wb = isw->new_wb;
@@ -423,6 +424,7 @@ static void inode_switch_wbs_work_fn(struct work_struct *work)
423424
wb_put(new_wb);
424425

425426
iput(inode);
427+
deactivate_super(sb);
426428
kfree(isw);
427429
}
428430

@@ -469,11 +471,14 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
469471

470472
/* while holding I_WB_SWITCH, no one else can update the association */
471473
spin_lock(&inode->i_lock);
474+
472475
if (inode->i_state & (I_WB_SWITCH | I_FREEING) ||
473-
inode_to_wb(inode) == isw->new_wb) {
474-
spin_unlock(&inode->i_lock);
475-
goto out_free;
476-
}
476+
inode_to_wb(inode) == isw->new_wb)
477+
goto out_unlock;
478+
479+
if (!atomic_inc_not_zero(&inode->i_sb->s_active))
480+
goto out_unlock;
481+
477482
inode->i_state |= I_WB_SWITCH;
478483
spin_unlock(&inode->i_lock);
479484

@@ -489,6 +494,8 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
489494
call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn);
490495
return;
491496

497+
out_unlock:
498+
spin_unlock(&inode->i_lock);
492499
out_free:
493500
if (isw->new_wb)
494501
wb_put(isw->new_wb);

0 commit comments

Comments
 (0)