Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 986d3453 authored by Jiufei Xue's avatar Jiufei Xue Committed by Greg Kroah-Hartman
Browse files

fs/writeback.c: use rcu_barrier() to wait for inflight wb switches going into workqueue when umount

commit ec084de929e419e51bcdafaafe567d9e7d0273b7 upstream.

synchronize_rcu() didn't wait for call_rcu() callbacks, so inode wb
switch may not go to the workqueue after synchronize_rcu().  Thus
previous scheduled switches was not finished even flushing the
workqueue, which will cause a NULL pointer dereferenced followed below.

  VFS: Busy inodes after unmount of vdd. Self-destruct in 5 seconds.  Have a nice day...
  BUG: unable to handle kernel NULL pointer dereference at 0000000000000278
    evict+0xb3/0x180
    iput+0x1b0/0x230
    inode_switch_wbs_work_fn+0x3c0/0x6a0
    worker_thread+0x4e/0x490
    ? process_one_work+0x410/0x410
    kthread+0xe6/0x100
    ret_from_fork+0x39/0x50

Replace the synchronize_rcu() call with a rcu_barrier() to wait for all
pending callbacks to finish.  And inc isw_nr_in_flight after call_rcu()
in inode_switch_wbs() to make more sense.

Link: http://lkml.kernel.org/r/20190429024108.54150-1-jiufei.xue@linux.alibaba.com


Signed-off-by: default avatarJiufei Xue <jiufei.xue@linux.alibaba.com>
Acked-by: default avatarTejun Heo <tj@kernel.org>
Suggested-by: default avatarTejun Heo <tj@kernel.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a80da82d
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -530,8 +530,6 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)

	isw->inode = inode;

	atomic_inc(&isw_nr_in_flight);

	/*
	 * In addition to synchronizing among switchers, I_WB_SWITCH tells
	 * the RCU protected stat update paths to grab the i_page
@@ -539,6 +537,9 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
	 * Let's continue after I_WB_SWITCH is guaranteed to be visible.
	 */
	call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn);

	atomic_inc(&isw_nr_in_flight);

	goto out_unlock;

out_free:
@@ -908,7 +909,11 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,
void cgroup_writeback_umount(void)
{
	if (atomic_read(&isw_nr_in_flight)) {
		synchronize_rcu();
		/*
		 * Use rcu_barrier() to wait for all pending callbacks to
		 * ensure that all in-flight wb switches are in the workqueue.
		 */
		rcu_barrier();
		flush_workqueue(isw_wq);
	}
}