Skip to content

Commit

Permalink
Use cached feature info in spa_add_feature_stats()
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Closes openzfs#3082
  • Loading branch information
nedbass authored and DeHackEd committed Apr 5, 2015
1 parent c0f9da2 commit 8717cc0
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 11 deletions.
1 change: 1 addition & 0 deletions include/sys/spa_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
58 changes: 48 additions & 10 deletions module/zfs/spa.c
Original file line number Diff line number Diff line change
Expand Up @@ -3202,23 +3202,19 @@ 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);
zap_cursor_retrieve(&zc, &za) == 0;
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);
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions module/zfs/spa_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion module/zfs/zfeature.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 8717cc0

Please sign in to comment.