aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fs-writeback.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2016-02-16 13:34:07 -0500
committerJens Axboe <axboe@fb.com>2016-02-16 13:34:07 -0500
commit5ff8eaac1636bf6deae86491f4818c4c69d1a9ac (patch)
treed4f5814a06bfebb36aea4c07b43a70381b48ed42 /fs/fs-writeback.c
parent2d99b55d378c996b9692a0c93dd25f4ed5d58934 (diff)
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 [#1] SMP DEBUG_PAGEALLOC CPU: 1 PID: 29158 Comm: kworker/1:10 Not tainted 4.5.0-rc3 #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 <tj@kernel.org> Reported-and-tested-by: Tahsin Erdogan <tahsin@google.com> Link: http://lkml.kernel.org/g/CAAeU0aNCq7LGODvVGRU-oU_o-6enii5ey0p1c26D1ZzYwkDc5A@mail.gmail.com Fixes: d10c80955265 ("writeback: implement foreign cgroup inode bdi_writeback switching") Cc: stable@vger.kernel.org #v4.5+ Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'fs/fs-writeback.c')
-rw-r--r--fs/fs-writeback.c15
1 files changed, 11 insertions, 4 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 6915c950e6e8..1f76d8950a57 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -317,6 +317,7 @@ static void inode_switch_wbs_work_fn(struct work_struct *work)
317 struct inode_switch_wbs_context *isw = 317 struct inode_switch_wbs_context *isw =
318 container_of(work, struct inode_switch_wbs_context, work); 318 container_of(work, struct inode_switch_wbs_context, work);
319 struct inode *inode = isw->inode; 319 struct inode *inode = isw->inode;
320 struct super_block *sb = inode->i_sb;
320 struct address_space *mapping = inode->i_mapping; 321 struct address_space *mapping = inode->i_mapping;
321 struct bdi_writeback *old_wb = inode->i_wb; 322 struct bdi_writeback *old_wb = inode->i_wb;
322 struct bdi_writeback *new_wb = isw->new_wb; 323 struct bdi_writeback *new_wb = isw->new_wb;
@@ -423,6 +424,7 @@ skip_switch:
423 wb_put(new_wb); 424 wb_put(new_wb);
424 425
425 iput(inode); 426 iput(inode);
427 deactivate_super(sb);
426 kfree(isw); 428 kfree(isw);
427} 429}
428 430
@@ -469,11 +471,14 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
469 471
470 /* while holding I_WB_SWITCH, no one else can update the association */ 472 /* while holding I_WB_SWITCH, no one else can update the association */
471 spin_lock(&inode->i_lock); 473 spin_lock(&inode->i_lock);
474
472 if (inode->i_state & (I_WB_SWITCH | I_FREEING) || 475 if (inode->i_state & (I_WB_SWITCH | I_FREEING) ||
473 inode_to_wb(inode) == isw->new_wb) { 476 inode_to_wb(inode) == isw->new_wb)
474 spin_unlock(&inode->i_lock); 477 goto out_unlock;
475 goto out_free; 478
476 } 479 if (!atomic_inc_not_zero(&inode->i_sb->s_active))
480 goto out_unlock;
481
477 inode->i_state |= I_WB_SWITCH; 482 inode->i_state |= I_WB_SWITCH;
478 spin_unlock(&inode->i_lock); 483 spin_unlock(&inode->i_lock);
479 484
@@ -489,6 +494,8 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
489 call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn); 494 call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn);
490 return; 495 return;
491 496
497out_unlock:
498 spin_unlock(&inode->i_lock);
492out_free: 499out_free:
493 if (isw->new_wb) 500 if (isw->new_wb)
494 wb_put(isw->new_wb); 501 wb_put(isw->new_wb);