aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOmar Sandoval2019-09-16 13:30:54 -0500
committerGreg Kroah-Hartman2019-12-31 05:37:44 -0600
commit9e5ae20bb9b5e37d9ec07fe7933e14b4bc19f75f (patch)
tree506bd8fa7d3bb2d773577e7a94ba658aafc53c5e
parent9402dae57784ffc81e0f142647a205b13e700a10 (diff)
downloadkernel-9e5ae20bb9b5e37d9ec07fe7933e14b4bc19f75f.tar.gz
kernel-9e5ae20bb9b5e37d9ec07fe7933e14b4bc19f75f.tar.xz
kernel-9e5ae20bb9b5e37d9ec07fe7933e14b4bc19f75f.zip
btrfs: don't prematurely free work in end_workqueue_fn()
[ Upstream commit 9be490f1e15c34193b1aae17da58e14dd9f55a95 ] Currently, end_workqueue_fn() frees the end_io_wq entry (which embeds the work item) and then calls bio_endio(). This is another potential instance of the bug in "btrfs: don't prematurely free work in run_ordered_work()". In particular, the endio call may depend on other work items. For example, btrfs_end_dio_bio() can call btrfs_subio_endio_read() -> __btrfs_correct_data_nocsum() -> dio_read_error() -> submit_dio_repair_bio(), which submits a bio that is also completed through a end_workqueue_fn() work item. However, __btrfs_correct_data_nocsum() waits for the newly submitted bio to complete, thus it depends on another work item. This example currently usually works because we use different workqueue helper functions for BTRFS_WQ_ENDIO_DATA and BTRFS_WQ_ENDIO_DIO_REPAIR. However, it may deadlock with stacked filesystems and is fragile overall. The proper fix is to free the work item at the very end of the work function, so let's do that. Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: Omar Sandoval <osandov@fb.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--fs/btrfs/disk-io.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 813834552aa1..a8ea56218d6b 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1679,8 +1679,8 @@ static void end_workqueue_fn(struct btrfs_work *work)
1679 bio->bi_status = end_io_wq->status; 1679 bio->bi_status = end_io_wq->status;
1680 bio->bi_private = end_io_wq->private; 1680 bio->bi_private = end_io_wq->private;
1681 bio->bi_end_io = end_io_wq->end_io; 1681 bio->bi_end_io = end_io_wq->end_io;
1682 kmem_cache_free(btrfs_end_io_wq_cache, end_io_wq);
1683 bio_endio(bio); 1682 bio_endio(bio);
1683 kmem_cache_free(btrfs_end_io_wq_cache, end_io_wq);
1684} 1684}
1685 1685
1686static int cleaner_kthread(void *arg) 1686static int cleaner_kthread(void *arg)