Fix zvol_init() lock inversion
During module load we could deadlock because the zvol_init() callpath took the spa_namespace_lock before the zvol_state_lock. The rest of the zvol code takes the locks in the opposite order. In particular, I observed the following deadlock cause by the lock inversion. I've fixed the ording by creating an unlocked version of zvol_create_minor and zvol_remove_minor. This allows me to take the zvol_state_lock before the spa_namespace_lock in zvol_cr_minors_common and simply call the unlocked version.
This commit is contained in:
parent
f162433deb
commit
88b37fbe57
|
@ -1071,13 +1071,8 @@ zvol_free(zvol_state_t *zv)
|
||||||
kmem_free(zv, sizeof (zvol_state_t));
|
kmem_free(zv, sizeof (zvol_state_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int
|
||||||
* Create a block device minor node and setup the linkage between it
|
__zvol_create_minor(const char *name)
|
||||||
* and the specified volume. Once this function returns the block
|
|
||||||
* device is live and ready for use.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zvol_create_minor(const char *name)
|
|
||||||
{
|
{
|
||||||
zvol_state_t *zv;
|
zvol_state_t *zv;
|
||||||
objset_t *os;
|
objset_t *os;
|
||||||
|
@ -1085,7 +1080,7 @@ zvol_create_minor(const char *name)
|
||||||
unsigned minor = 0;
|
unsigned minor = 0;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
mutex_enter(&zvol_state_lock);
|
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
||||||
|
|
||||||
zv = zvol_find_by_name(name);
|
zv = zvol_find_by_name(name);
|
||||||
if (zv) {
|
if (zv) {
|
||||||
|
@ -1128,9 +1123,44 @@ zvol_create_minor(const char *name)
|
||||||
out_dmu_objset_disown:
|
out_dmu_objset_disown:
|
||||||
dmu_objset_disown(os, zvol_tag);
|
dmu_objset_disown(os, zvol_tag);
|
||||||
out:
|
out:
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a block device minor node and setup the linkage between it
|
||||||
|
* and the specified volume. Once this function returns the block
|
||||||
|
* device is live and ready for use.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zvol_create_minor(const char *name)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
mutex_enter(&zvol_state_lock);
|
||||||
|
error = __zvol_create_minor(name);
|
||||||
mutex_exit(&zvol_state_lock);
|
mutex_exit(&zvol_state_lock);
|
||||||
|
|
||||||
return (-error);
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
__zvol_remove_minor(const char *name)
|
||||||
|
{
|
||||||
|
zvol_state_t *zv;
|
||||||
|
|
||||||
|
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
||||||
|
|
||||||
|
zv = zvol_find_by_name(name);
|
||||||
|
if (zv == NULL)
|
||||||
|
return (ENXIO);
|
||||||
|
|
||||||
|
if (zv->zv_open_count > 0)
|
||||||
|
return (EBUSY);
|
||||||
|
|
||||||
|
zvol_remove(zv);
|
||||||
|
zvol_free(zv);
|
||||||
|
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1139,25 +1169,10 @@ out:
|
||||||
int
|
int
|
||||||
zvol_remove_minor(const char *name)
|
zvol_remove_minor(const char *name)
|
||||||
{
|
{
|
||||||
zvol_state_t *zv;
|
int error;
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
mutex_enter(&zvol_state_lock);
|
mutex_enter(&zvol_state_lock);
|
||||||
|
error = __zvol_remove_minor(name);
|
||||||
zv = zvol_find_by_name(name);
|
|
||||||
if (zv == NULL) {
|
|
||||||
error = ENXIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zv->zv_open_count > 0) {
|
|
||||||
error = EBUSY;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
zvol_remove(zv);
|
|
||||||
zvol_free(zv);
|
|
||||||
out:
|
|
||||||
mutex_exit(&zvol_state_lock);
|
mutex_exit(&zvol_state_lock);
|
||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
|
@ -1170,7 +1185,7 @@ zvol_create_minors_cb(spa_t *spa, uint64_t dsobj,
|
||||||
if (strchr(dsname, '/') == NULL)
|
if (strchr(dsname, '/') == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return zvol_create_minor(dsname);
|
return __zvol_create_minor(dsname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1183,6 +1198,7 @@ zvol_create_minors(const char *pool)
|
||||||
spa_t *spa = NULL;
|
spa_t *spa = NULL;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
|
mutex_enter(&zvol_state_lock);
|
||||||
if (pool) {
|
if (pool) {
|
||||||
error = dmu_objset_find_spa(NULL, pool, zvol_create_minors_cb,
|
error = dmu_objset_find_spa(NULL, pool, zvol_create_minors_cb,
|
||||||
NULL, DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS);
|
NULL, DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS);
|
||||||
|
@ -1197,6 +1213,7 @@ zvol_create_minors(const char *pool)
|
||||||
}
|
}
|
||||||
mutex_exit(&spa_namespace_lock);
|
mutex_exit(&spa_namespace_lock);
|
||||||
}
|
}
|
||||||
|
mutex_exit(&zvol_state_lock);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue