diff options
author | Boris Protopopov <[email protected]> | 2017-05-11 16:40:33 -0400 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2017-05-16 19:42:47 -0400 |
commit | 07783588bcb513a3a1f4d995b5d4685a9cfc89e5 (patch) | |
tree | 2afd0ae2cda927e8869fa9492bb45d0b4aece1dc /module/zfs | |
parent | 3d6da72d183dc655a7dc8fd59f57748fc5c1806c (diff) |
Revert commit 1ee159f4
Fix lock order inversion with zvol_open() as it did not account
for use of zvols as vdevs. The latter use cases resulted in the
lock order inversion deadlocks that involved spa_namespace_lock
and bdev->bd_mutex.
Signed-off-by: Boris Protopopov <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Issue #6065
Issue #6134
Diffstat (limited to 'module/zfs')
-rw-r--r-- | module/zfs/zvol.c | 31 |
1 files changed, 29 insertions, 2 deletions
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 6cd366602..3bf28e1d4 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -1150,12 +1150,36 @@ static int zvol_first_open(zvol_state_t *zv) { objset_t *os; - int error; + int error, locked = 0; + + /* + * In all other cases the spa_namespace_lock is taken before the + * bdev->bd_mutex lock. But in this case the Linux __blkdev_get() + * function calls fops->open() with the bdev->bd_mutex lock held. + * This deadlock can be easily observed with zvols used as vdevs. + * + * To avoid a potential lock inversion deadlock we preemptively + * try to take the spa_namespace_lock(). Normally it will not + * be contended and this is safe because spa_open_common() handles + * the case where the caller already holds the spa_namespace_lock. + * + * When it is contended we risk a lock inversion if we were to + * block waiting for the lock. Luckily, the __blkdev_get() + * function allows us to return -ERESTARTSYS which will result in + * bdev->bd_mutex being dropped, reacquired, and fops->open() being + * called again. This process can be repeated safely until both + * locks are acquired. + */ + if (!mutex_owned(&spa_namespace_lock)) { + locked = mutex_tryenter(&spa_namespace_lock); + if (!locked) + return (-SET_ERROR(ERESTARTSYS)); + } /* lie and say we're read-only */ error = dmu_objset_own(zv->zv_name, DMU_OST_ZVOL, 1, zv, &os); if (error) - return (SET_ERROR(-error)); + goto out_mutex; zv->zv_objset = os; @@ -1166,6 +1190,9 @@ zvol_first_open(zvol_state_t *zv) zv->zv_objset = NULL; } +out_mutex: + if (locked) + mutex_exit(&spa_namespace_lock); return (SET_ERROR(-error)); } |