forked from Minki/linux
73beece9ca
Xfstests btrfs/011 complains about a deadlock warning,
[ 1226.649039] =========================================================
[ 1226.649039] [ INFO: possible irq lock inversion dependency detected ]
[ 1226.649039] 4.1.0+ #270 Not tainted
[ 1226.649039] ---------------------------------------------------------
[ 1226.652955] kswapd0/46 just changed the state of lock:
[ 1226.652955] (&delayed_node->mutex){+.+.-.}, at: [<ffffffff81458735>] __btrfs_release_delayed_node+0x45/0x1d0
[ 1226.652955] but this lock took another, RECLAIM_FS-unsafe lock in the past:
[ 1226.652955] (&fs_info->dev_replace.lock){+.+.+.}
and interrupts could create inverse lock ordering between them.
[ 1226.652955]
other info that might help us debug this:
[ 1226.652955] Chain exists of:
&delayed_node->mutex --> &found->groups_sem --> &fs_info->dev_replace.lock
[ 1226.652955] Possible interrupt unsafe locking scenario:
[ 1226.652955] CPU0 CPU1
[ 1226.652955] ---- ----
[ 1226.652955] lock(&fs_info->dev_replace.lock);
[ 1226.652955] local_irq_disable();
[ 1226.652955] lock(&delayed_node->mutex);
[ 1226.652955] lock(&found->groups_sem);
[ 1226.652955] <Interrupt>
[ 1226.652955] lock(&delayed_node->mutex);
[ 1226.652955]
*** DEADLOCK ***
Commit 084b6e7c76
("btrfs: Fix a lockdep warning when running xfstest.") tried
to fix a similar one that has the exactly same warning, but with that, we still
run to this.
The above lock chain comes from
btrfs_commit_transaction
->btrfs_run_delayed_items
...
->__btrfs_update_delayed_inode
...
->__btrfs_cow_block
...
->find_free_extent
->cache_block_group
->load_free_space_cache
->btrfs_readpages
->submit_one_bio
...
->__btrfs_map_block
->btrfs_dev_replace_lock
However, with high memory pressure, tasks which hold dev_replace.lock can
be interrupted by kswapd and then kswapd is intended to release memory occupied
by superblock, inodes and dentries, where we may call evict_inode, and it comes
to
[ 1226.652955] [<ffffffff81458735>] __btrfs_release_delayed_node+0x45/0x1d0
[ 1226.652955] [<ffffffff81459e74>] btrfs_remove_delayed_node+0x24/0x30
[ 1226.652955] [<ffffffff8140c5fe>] btrfs_evict_inode+0x34e/0x700
delayed_node->mutex may be acquired in __btrfs_release_delayed_node(), and it leads
to a ABBA deadlock.
To fix this, we can use "blocking rwlock" used in the case of extent_buffer, but
things are simpler here since we only needs read's spinlock to blocking lock.
With this, btrfs/011 no more produces warnings in dmesg.
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: David Sterba <dsterba@suse.com>
48 lines
2.0 KiB
C
48 lines
2.0 KiB
C
/*
|
|
* Copyright (C) STRATO AG 2012. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License v2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 021110-1307, USA.
|
|
*/
|
|
|
|
#if !defined(__BTRFS_DEV_REPLACE__)
|
|
#define __BTRFS_DEV_REPLACE__
|
|
|
|
struct btrfs_ioctl_dev_replace_args;
|
|
|
|
int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info);
|
|
int btrfs_run_dev_replace(struct btrfs_trans_handle *trans,
|
|
struct btrfs_fs_info *fs_info);
|
|
void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info);
|
|
int btrfs_dev_replace_start(struct btrfs_root *root,
|
|
struct btrfs_ioctl_dev_replace_args *args);
|
|
void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
|
|
struct btrfs_ioctl_dev_replace_args *args);
|
|
int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info,
|
|
struct btrfs_ioctl_dev_replace_args *args);
|
|
void btrfs_dev_replace_suspend_for_unmount(struct btrfs_fs_info *fs_info);
|
|
int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info);
|
|
int btrfs_dev_replace_is_ongoing(struct btrfs_dev_replace *dev_replace);
|
|
void btrfs_dev_replace_lock(struct btrfs_dev_replace *dev_replace, int rw);
|
|
void btrfs_dev_replace_unlock(struct btrfs_dev_replace *dev_replace, int rw);
|
|
void btrfs_dev_replace_set_lock_blocking(struct btrfs_dev_replace *dev_replace);
|
|
void btrfs_dev_replace_clear_lock_blocking(
|
|
struct btrfs_dev_replace *dev_replace);
|
|
|
|
static inline void btrfs_dev_replace_stats_inc(atomic64_t *stat_value)
|
|
{
|
|
atomic64_inc(stat_value);
|
|
}
|
|
#endif
|