]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Fix self-healing IO prior to dsl_pool_init() completion
authorGeLiXin <47034221@qq.com>
Sat, 21 May 2016 03:34:06 +0000 (11:34 +0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 27 May 2016 21:11:25 +0000 (14:11 -0700)
Async writes triggered by a self-healing IO may be issued before the
pool finishes the process of initialization.  This results in a NULL
dereference of `spa->spa_dsl_pool` in vdev_queue_max_async_writes().

George Wilson recommended addressing this issue by initializing the
passed `dsl_pool_t **` prior to dmu_objset_open_impl().  Since the
caller is passing the `spa->spa_dsl_pool` this has the effect of
ensuring it's initialized.

However, since this depends on the caller knowing they must pass
the `spa->spa_dsl_pool` an additional NULL check was added to
vdev_queue_max_async_writes().  This guards against any future
restructuring of the code which might result in dsl_pool_init()
being called differently.

Signed-off-by: GeLiXin <47034221@qq.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #4652

module/zfs/dsl_pool.c [changed mode: 0644->0755]
module/zfs/vdev_queue.c

old mode 100644 (file)
new mode 100755 (executable)
index ada0eac..cf5259a
@@ -182,12 +182,20 @@ dsl_pool_init(spa_t *spa, uint64_t txg, dsl_pool_t **dpp)
        int err;
        dsl_pool_t *dp = dsl_pool_open_impl(spa, txg);
 
+       /*
+        * Initialize the caller's dsl_pool_t structure before we actually open
+        * the meta objset.  This is done because a self-healing write zio may
+        * be issued as part of dmu_objset_open_impl() and the spa needs its
+        * dsl_pool_t initialized in order to handle the write.
+        */
+       *dpp = dp;
+
        err = dmu_objset_open_impl(spa, NULL, &dp->dp_meta_rootbp,
            &dp->dp_meta_objset);
-       if (err != 0)
+       if (err != 0) {
                dsl_pool_close(dp);
-       else
-               *dpp = dp;
+               *dpp = NULL;
+       }
 
        return (err);
 }
index d67912d965c85184a23d46ab4c09c0cea22e2c66..8522c2627fe6c8f8d2e110dde3f4058acf02fcc9 100644 (file)
@@ -249,20 +249,29 @@ static int
 vdev_queue_max_async_writes(spa_t *spa)
 {
        int writes;
-       uint64_t dirty = spa->spa_dsl_pool->dp_dirty_total;
+       uint64_t dirty = 0;
+       dsl_pool_t *dp = spa_get_dsl(spa);
        uint64_t min_bytes = zfs_dirty_data_max *
            zfs_vdev_async_write_active_min_dirty_percent / 100;
        uint64_t max_bytes = zfs_dirty_data_max *
            zfs_vdev_async_write_active_max_dirty_percent / 100;
 
+       /*
+        * Async writes may occur before the assignment of the spa's
+        * dsl_pool_t if a self-healing zio is issued prior to the
+        * completion of dmu_objset_open_impl().
+        */
+       if (dp == NULL)
+               return (zfs_vdev_async_write_max_active);
+
        /*
         * Sync tasks correspond to interactive user actions. To reduce the
         * execution time of those actions we push data out as fast as possible.
         */
-       if (spa_has_pending_synctask(spa)) {
+       if (spa_has_pending_synctask(spa))
                return (zfs_vdev_async_write_max_active);
-       }
 
+       dirty = dp->dp_dirty_total;
        if (dirty < min_bytes)
                return (zfs_vdev_async_write_min_active);
        if (dirty > max_bytes)