]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Long hold the dataset during upgrade
authorArkadiusz Bubała <arkadiusz.bubala@open-e.com>
Fri, 10 Nov 2017 21:37:10 +0000 (22:37 +0100)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 10 Nov 2017 21:37:10 +0000 (13:37 -0800)
If the receive or rollback is performed while filesystem is upgrading
the objset may be evicted in `dsl_dataset_clone_swap_sync_impl`. This
will lead to NULL pointer dereference when upgrade tries to access
evicted objset.

This commit adds long hold of dataset during whole upgrade process.
The receive and rollback will return an EBUSY error until the
upgrade is not finished.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Arkadiusz Bubała <arkadiusz.bubala@open-e.com>
Closes #5295
Closes #6837

module/zfs/dmu_objset.c
module/zfs/zfs_ioctl.c
module/zfs/zfs_vfsops.c

index fca936ac2aa97165fc666d4b79c9fba9c93451a2..e596b70e9ca0063d819902e02c8fedcc7fba9e05 100644 (file)
@@ -28,6 +28,7 @@
  * Copyright (c) 2015, STRATO AG, Inc. All rights reserved.
  * Copyright (c) 2016 Actifio, Inc. All rights reserved.
  * Copyright 2017 Nexenta Systems, Inc.
+ * Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
  */
 
 /* Portions Copyright 2010 Robert Milkowski */
@@ -79,6 +80,8 @@ int dmu_find_threads = 0;
  */
 int dmu_rescan_dnode_threshold = 1 << DN_MAX_INDBLKSHIFT;
 
+static char *upgrade_tag = "upgrade_tag";
+
 static void dmu_objset_find_dp_cb(void *arg);
 
 static void dmu_objset_upgrade(objset_t *os, dmu_objset_upgrade_cb_t cb);
@@ -704,13 +707,12 @@ dmu_objset_own(const char *name, dmu_objset_type_t type,
                return (err);
        }
 
-       dsl_pool_rele(dp, FTAG);
-
        /* user accounting requires the dataset to be decrypted */
        if (dmu_objset_userobjspace_upgradable(*osp) &&
            (ds->ds_dir->dd_crypto_obj == 0 || decrypt))
                dmu_objset_userobjspace_upgrade(*osp);
 
+       dsl_pool_rele(dp, FTAG);
        return (0);
 }
 
@@ -1311,6 +1313,7 @@ dmu_objset_upgrade_task_cb(void *data)
        os->os_upgrade_exit = B_TRUE;
        os->os_upgrade_id = 0;
        mutex_exit(&os->os_upgrade_lock);
+       dsl_dataset_long_rele(dmu_objset_ds(os), upgrade_tag);
 }
 
 static void
@@ -1319,6 +1322,9 @@ dmu_objset_upgrade(objset_t *os, dmu_objset_upgrade_cb_t cb)
        if (os->os_upgrade_id != 0)
                return;
 
+       ASSERT(dsl_pool_config_held(dmu_objset_pool(os)));
+       dsl_dataset_long_hold(dmu_objset_ds(os), upgrade_tag);
+
        mutex_enter(&os->os_upgrade_lock);
        if (os->os_upgrade_id == 0 && os->os_upgrade_status == 0) {
                os->os_upgrade_exit = B_FALSE;
@@ -1326,8 +1332,10 @@ dmu_objset_upgrade(objset_t *os, dmu_objset_upgrade_cb_t cb)
                os->os_upgrade_id = taskq_dispatch(
                    os->os_spa->spa_upgrade_taskq,
                    dmu_objset_upgrade_task_cb, os, TQ_SLEEP);
-               if (os->os_upgrade_id == TASKQID_INVALID)
+               if (os->os_upgrade_id == TASKQID_INVALID) {
+                       dsl_dataset_long_rele(dmu_objset_ds(os), upgrade_tag);
                        os->os_upgrade_status = ENOMEM;
+               }
        }
        mutex_exit(&os->os_upgrade_lock);
 }
@@ -1343,7 +1351,9 @@ dmu_objset_upgrade_stop(objset_t *os)
                os->os_upgrade_id = 0;
                mutex_exit(&os->os_upgrade_lock);
 
-               taskq_cancel_id(os->os_spa->spa_upgrade_taskq, id);
+               if ((taskq_cancel_id(os->os_spa->spa_upgrade_taskq, id)) == 0) {
+                       dsl_dataset_long_rele(dmu_objset_ds(os), upgrade_tag);
+               }
                txg_wait_synced(os->os_spa->spa_dsl_pool, 0);
        } else {
                mutex_exit(&os->os_upgrade_lock);
index 9bd9b58bad8cc18ebd996c1d27632cdac3d11a88..b132a68859ff41554b86eefec93f8318be3de1f4 100644 (file)
@@ -5275,9 +5275,6 @@ zfs_ioc_userobjspace_upgrade(zfs_cmd_t *zc)
        if (error != 0)
                return (error);
 
-       dsl_dataset_long_hold(dmu_objset_ds(os), FTAG);
-       dsl_pool_rele(dmu_objset_pool(os), FTAG);
-
        if (dmu_objset_userobjspace_upgradable(os)) {
                mutex_enter(&os->os_upgrade_lock);
                if (os->os_upgrade_id == 0) {
@@ -5290,11 +5287,14 @@ zfs_ioc_userobjspace_upgrade(zfs_cmd_t *zc)
                        mutex_exit(&os->os_upgrade_lock);
                }
 
+               dsl_pool_rele(dmu_objset_pool(os), FTAG);
+
                taskq_wait_id(os->os_spa->spa_upgrade_taskq, os->os_upgrade_id);
                error = os->os_upgrade_status;
+       } else {
+               dsl_pool_rele(dmu_objset_pool(os), FTAG);
        }
 
-       dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
        dsl_dataset_rele_flags(dmu_objset_ds(os), DS_HOLD_FLAG_DECRYPT, FTAG);
 
        return (error);
index 33557157b61b21d6174a76428074e52ad7843ee4..af5593c2e0c58cafe904d64780384a9d760b0f18 100644 (file)
@@ -834,8 +834,13 @@ zfs_fuid_overobjquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid)
        int err;
 
        if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) {
-               if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os))
+               if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os)) {
+                       dsl_pool_config_enter(
+                           dmu_objset_pool(zfsvfs->z_os), FTAG);
                        dmu_objset_userobjspace_upgrade(zfsvfs->z_os);
+                       dsl_pool_config_exit(
+                           dmu_objset_pool(zfsvfs->z_os), FTAG);
+               }
                return (B_FALSE);
        }