Fix zpool on zvol deadlock
authorMassimo Maggi <massimo@mmmm.it>
Fri, 18 Jan 2013 17:44:09 +0000 (09:44 -0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 18 Jan 2013 17:44:55 +0000 (09:44 -0800)
Commit 65d56083b4617a4cade0cff68cbbaf68114169d6 fixes the lock
inversion between spa_namespace_lock and bdev->bd_mutex but only
for the first user of spa_namespace_lock: dmu_objset_own().
Later spa_namespace_lock gets acquired by dsl_prop_get_integer()
though dsl_prop_get()->dsl_dataset_hold()->dsl_dir_open_spa()->
spa_open()->spa_open_common() without this "protection".  By
moving the mutex release after this second use, even this
acquisition of the lock is "protected" by the ERESTARTSYS trick.

Signed-off-by: Massimo Maggi <me@massimo-maggi.eu>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1220

module/zfs/zvol.c

index d4d533f..c6cfac2 100644 (file)
@@ -920,24 +920,20 @@ zvol_first_open(zvol_state_t *zv)
 
        /* lie and say we're read-only */
        error = dmu_objset_own(zv->zv_name, DMU_OST_ZVOL, 1, zvol_tag, &os);
-
-       if (locked)
-               mutex_exit(&spa_namespace_lock);
-
        if (error)
-               return (-error);
+               goto out_mutex;
 
        error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize);
        if (error) {
-               dmu_objset_disown(os, zvol_tag);
-               return (-error);
+               dmu_objset_disown(os, zvol_tag);
+               goto out_mutex;
        }
 
        zv->zv_objset = os;
        error = dmu_bonus_hold(os, ZVOL_OBJ, zvol_tag, &zv->zv_dbuf);
        if (error) {
-               dmu_objset_disown(os, zvol_tag);
-               return (-error);
+               dmu_objset_disown(os, zvol_tag);
+               goto out_mutex;
        }
 
        set_capacity(zv->zv_disk, volsize >> 9);
@@ -946,13 +942,17 @@ zvol_first_open(zvol_state_t *zv)
 
        VERIFY(dsl_prop_get_integer(zv->zv_name, "readonly", &ro, NULL) == 0);
        if (ro || dmu_objset_is_snapshot(os)) {
-                set_disk_ro(zv->zv_disk, 1);
-               zv->zv_flags |= ZVOL_RDONLY;
+               set_disk_ro(zv->zv_disk, 1);
+               zv->zv_flags |= ZVOL_RDONLY;
        } else {
-                set_disk_ro(zv->zv_disk, 0);
-               zv->zv_flags &= ~ZVOL_RDONLY;
+               set_disk_ro(zv->zv_disk, 0);
+               zv->zv_flags &= ~ZVOL_RDONLY;
        }
 
+out_mutex:
+       if (locked)
+               mutex_exit(&spa_namespace_lock);
+
        return (-error);
 }