]> granicus.if.org Git - zfs/commitdiff
Use cached feature info in spa_add_feature_stats()
authorNed Bass <bass6@llnl.gov>
Thu, 26 Feb 2015 20:24:11 +0000 (12:24 -0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 5 Mar 2015 22:11:10 +0000 (14:11 -0800)
Avoid issuing I/O to the pool when retrieving feature flags information.
Trying to read the ZAPs from disk means that zpool clear would hang if
the pool is suspended and recovery would require a reboot. To keep the
feature stats resident in memory, we hang a cached nvlist off of the
spa.  It is built up from disk the first time spa_add_feature_stats() is
called, and refreshed thereafter using the cached feature reference
counts. spa_add_feature_stats() gets called at pool import time so we
can be sure the cached nvlist will be available if the pool is later
suspended.

Signed-off-by: Ned Bass <bass6@llnl.gov>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #3082

include/sys/spa_impl.h
module/zfs/spa.c
module/zfs/spa_misc.c
module/zfs/zfeature.c

index 1cb535b9f55c2bb57d669dad9515cbc835f49d3e..72e04e8cfe3572a42eba3078edc28c0b1ecdf089 100644 (file)
@@ -236,6 +236,7 @@ struct spa {
        uint64_t        spa_feat_for_read_obj;  /* required to read from pool */
        uint64_t        spa_feat_desc_obj;      /* Feature descriptions */
        uint64_t        spa_feat_enabled_txg_obj; /* Feature enabled txg */
+       nvlist_t        *spa_feat_stats;        /* Cache of enabled features */
        /* cache feature refcounts */
        uint64_t        spa_feat_refcount_cache[SPA_FEATURES];
        taskqid_t       spa_deadman_tqid;       /* Task id */
index 81cef19529c36a7bfcc21e95fec2de37f6116c50..8bee15094685ce5be1bd03cfca93d0f09605f537 100644 (file)
@@ -3202,15 +3202,11 @@ spa_add_l2cache(spa_t *spa, nvlist_t *config)
 }
 
 static void
-spa_add_feature_stats(spa_t *spa, nvlist_t *config)
+spa_feature_stats_from_disk(spa_t *spa, nvlist_t *features)
 {
-       nvlist_t *features;
        zap_cursor_t zc;
        zap_attribute_t za;
 
-       ASSERT(spa_config_held(spa, SCL_CONFIG, RW_READER));
-       VERIFY(nvlist_alloc(&features, NV_UNIQUE_NAME, KM_SLEEP) == 0);
-
        if (spa->spa_feat_for_read_obj != 0) {
                for (zap_cursor_init(&zc, spa->spa_meta_objset,
                    spa->spa_feat_for_read_obj);
@@ -3218,7 +3214,7 @@ spa_add_feature_stats(spa_t *spa, nvlist_t *config)
                    zap_cursor_advance(&zc)) {
                        ASSERT(za.za_integer_length == sizeof (uint64_t) &&
                            za.za_num_integers == 1);
-                       VERIFY3U(0, ==, nvlist_add_uint64(features, za.za_name,
+                       VERIFY0(nvlist_add_uint64(features, za.za_name,
                            za.za_first_integer));
                }
                zap_cursor_fini(&zc);
@@ -3231,15 +3227,57 @@ spa_add_feature_stats(spa_t *spa, nvlist_t *config)
                    zap_cursor_advance(&zc)) {
                        ASSERT(za.za_integer_length == sizeof (uint64_t) &&
                            za.za_num_integers == 1);
-                       VERIFY3U(0, ==, nvlist_add_uint64(features, za.za_name,
+                       VERIFY0(nvlist_add_uint64(features, za.za_name,
                            za.za_first_integer));
                }
                zap_cursor_fini(&zc);
        }
+}
+
+static void
+spa_feature_stats_from_cache(spa_t *spa, nvlist_t *features)
+{
+       int i;
+
+       for (i = 0; i < SPA_FEATURES; i++) {
+               zfeature_info_t feature = spa_feature_table[i];
+               uint64_t refcount;
+
+               if (feature_get_refcount(spa, &feature, &refcount) != 0)
+                       continue;
+
+               VERIFY0(nvlist_add_uint64(features, feature.fi_guid, refcount));
+       }
+}
+
+/*
+ * Store a list of pool features and their reference counts in the
+ * config.
+ *
+ * The first time this is called on a spa, allocate a new nvlist, fetch
+ * the pool features and reference counts from disk, then save the list
+ * in the spa. In subsequent calls on the same spa use the saved nvlist
+ * and refresh its values from the cached reference counts.  This
+ * ensures we don't block here on I/O on a suspended pool so 'zpool
+ * clear' can resume the pool.
+ */
+static void
+spa_add_feature_stats(spa_t *spa, nvlist_t *config)
+{
+       nvlist_t *features = spa->spa_feat_stats;
+
+       ASSERT(spa_config_held(spa, SCL_CONFIG, RW_READER));
+
+       if (features != NULL) {
+               spa_feature_stats_from_cache(spa, features);
+       } else {
+               VERIFY0(nvlist_alloc(&features, NV_UNIQUE_NAME, KM_SLEEP));
+               spa->spa_feat_stats = features;
+               spa_feature_stats_from_disk(spa, features);
+       }
 
-       VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
-           features) == 0);
-       nvlist_free(features);
+       VERIFY0(nvlist_add_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
+           features));
 }
 
 int
index ce6f02dc4b1a8736e10acfe667abf1fcea2899ab..ec0c0195947c0c2841ce49ef19e0a07d62685e1b 100644 (file)
@@ -643,6 +643,7 @@ spa_remove(spa_t *spa)
 
        nvlist_free(spa->spa_label_features);
        nvlist_free(spa->spa_load_info);
+       nvlist_free(spa->spa_feat_stats);
        spa_config_set(spa, NULL);
 
        refcount_destroy(&spa->spa_refcount);
index e6cab2c0348402d412adc1682d7daa6a21fabf54..352376f22b9ed8a097efd37bb52d67a86ddb9b0c 100644 (file)
@@ -228,7 +228,7 @@ spa_features_check(spa_t *spa, boolean_t for_write,
  *
  * Note: well-designed features will not need to use this; they should
  * use spa_feature_is_enabled() and spa_feature_is_active() instead.
- * However, this is non-static for zdb and zhack.
+ * However, this is non-static for zdb, zhack, and spa_add_feature_stats().
  */
 int
 feature_get_refcount(spa_t *spa, zfeature_info_t *feature, uint64_t *res)