diff options
author | Qu Wenruo <quwenruo@cn.fujitsu.com> | 2014-10-30 04:52:31 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-11-25 08:55:38 -0500 |
commit | 084b6e7c7607bbeb28544da659c3f5981a4689b0 (patch) | |
tree | 1a80b5597373cf5d64e6cecac1de16615af588f2 /fs/btrfs | |
parent | ad27c0dab76a7abc8809ec41ae59cf67de5ea906 (diff) |
btrfs: Fix a lockdep warning when running xfstest.
The following lockdep warning is triggered during xfstests:
[ 1702.980872] =========================================================
[ 1702.981181] [ INFO: possible irq lock inversion dependency detected ]
[ 1702.981482] 3.18.0-rc1 #27 Not tainted
[ 1702.981781] ---------------------------------------------------------
[ 1702.982095] kswapd0/77 just changed the state of lock:
[ 1702.982415] (&delayed_node->mutex){+.+.-.}, at: [<ffffffffa03b0b51>] __btrfs_release_delayed_node+0x41/0x1f0 [btrfs]
[ 1702.982794] but this lock took another, RECLAIM_FS-unsafe lock in the past:
[ 1702.983160] (&fs_info->dev_replace.lock){+.+.+.}
and interrupts could create inverse lock ordering between them.
[ 1702.984675]
other info that might help us debug this:
[ 1702.985524] Chain exists of:
&delayed_node->mutex --> &found->groups_sem --> &fs_info->dev_replace.lock
[ 1702.986799] Possible interrupt unsafe locking scenario:
[ 1702.987681] CPU0 CPU1
[ 1702.988137] ---- ----
[ 1702.988598] lock(&fs_info->dev_replace.lock);
[ 1702.989069] local_irq_disable();
[ 1702.989534] lock(&delayed_node->mutex);
[ 1702.990038] lock(&found->groups_sem);
[ 1702.990494] <Interrupt>
[ 1702.990938] lock(&delayed_node->mutex);
[ 1702.991407]
*** DEADLOCK ***
It is because the btrfs_kobj_{add/rm}_device() will call memory
allocation with GFP_KERNEL,
which may flush fs page cache to free space, waiting for it self to do
the commit, causing the deadlock.
To solve the problem, move btrfs_kobj_{add/rm}_device() out of the
dev_replace lock range, also involing split the
btrfs_rm_dev_replace_srcdev() function into remove and free parts.
Now only btrfs_rm_dev_replace_remove_srcdev() is called in dev_replace
lock range, and kobj_{add/rm} and btrfs_rm_dev_replace_free_srcdev() are
called out of the lock range.
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/dev-replace.c | 11 | ||||
-rw-r--r-- | fs/btrfs/volumes.c | 10 | ||||
-rw-r--r-- | fs/btrfs/volumes.h | 6 |
3 files changed, 18 insertions, 9 deletions
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 971c061eb1ce..3fbd0628620b 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c | |||
@@ -577,15 +577,11 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, | |||
577 | list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); | 577 | list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); |
578 | fs_info->fs_devices->rw_devices++; | 578 | fs_info->fs_devices->rw_devices++; |
579 | 579 | ||
580 | /* replace the sysfs entry */ | ||
581 | btrfs_kobj_rm_device(fs_info, src_device); | ||
582 | btrfs_kobj_add_device(fs_info, tgt_device); | ||
583 | |||
584 | btrfs_dev_replace_unlock(dev_replace); | 580 | btrfs_dev_replace_unlock(dev_replace); |
585 | 581 | ||
586 | btrfs_rm_dev_replace_blocked(fs_info); | 582 | btrfs_rm_dev_replace_blocked(fs_info); |
587 | 583 | ||
588 | btrfs_rm_dev_replace_srcdev(fs_info, src_device); | 584 | btrfs_rm_dev_replace_remove_srcdev(fs_info, src_device); |
589 | 585 | ||
590 | btrfs_rm_dev_replace_unblocked(fs_info); | 586 | btrfs_rm_dev_replace_unblocked(fs_info); |
591 | 587 | ||
@@ -600,6 +596,11 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, | |||
600 | mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); | 596 | mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); |
601 | mutex_unlock(&uuid_mutex); | 597 | mutex_unlock(&uuid_mutex); |
602 | 598 | ||
599 | /* replace the sysfs entry */ | ||
600 | btrfs_kobj_rm_device(fs_info, src_device); | ||
601 | btrfs_kobj_add_device(fs_info, tgt_device); | ||
602 | btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); | ||
603 | |||
603 | /* write back the superblocks */ | 604 | /* write back the superblocks */ |
604 | trans = btrfs_start_transaction(root, 0); | 605 | trans = btrfs_start_transaction(root, 0); |
605 | if (!IS_ERR(trans)) | 606 | if (!IS_ERR(trans)) |
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d47289c715c8..01920515f90d 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -1800,8 +1800,8 @@ error_undo: | |||
1800 | goto error_brelse; | 1800 | goto error_brelse; |
1801 | } | 1801 | } |
1802 | 1802 | ||
1803 | void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info, | 1803 | void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info, |
1804 | struct btrfs_device *srcdev) | 1804 | struct btrfs_device *srcdev) |
1805 | { | 1805 | { |
1806 | struct btrfs_fs_devices *fs_devices; | 1806 | struct btrfs_fs_devices *fs_devices; |
1807 | 1807 | ||
@@ -1829,6 +1829,12 @@ void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info, | |||
1829 | 1829 | ||
1830 | if (srcdev->bdev) | 1830 | if (srcdev->bdev) |
1831 | fs_devices->open_devices--; | 1831 | fs_devices->open_devices--; |
1832 | } | ||
1833 | |||
1834 | void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info, | ||
1835 | struct btrfs_device *srcdev) | ||
1836 | { | ||
1837 | struct btrfs_fs_devices *fs_devices = srcdev->fs_devices; | ||
1832 | 1838 | ||
1833 | call_rcu(&srcdev->rcu, free_device); | 1839 | call_rcu(&srcdev->rcu, free_device); |
1834 | 1840 | ||
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 08980fa23039..4cc00e64427e 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h | |||
@@ -448,8 +448,10 @@ void btrfs_init_devices_late(struct btrfs_fs_info *fs_info); | |||
448 | int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info); | 448 | int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info); |
449 | int btrfs_run_dev_stats(struct btrfs_trans_handle *trans, | 449 | int btrfs_run_dev_stats(struct btrfs_trans_handle *trans, |
450 | struct btrfs_fs_info *fs_info); | 450 | struct btrfs_fs_info *fs_info); |
451 | void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info, | 451 | void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info, |
452 | struct btrfs_device *srcdev); | 452 | struct btrfs_device *srcdev); |
453 | void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info, | ||
454 | struct btrfs_device *srcdev); | ||
453 | void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, | 455 | void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, |
454 | struct btrfs_device *tgtdev); | 456 | struct btrfs_device *tgtdev); |
455 | void btrfs_init_dev_replace_tgtdev_for_resume(struct btrfs_fs_info *fs_info, | 457 | void btrfs_init_dev_replace_tgtdev_for_resume(struct btrfs_fs_info *fs_info, |