Skip to content

Commit 417104b

Browse files
nedbassbehlendorf
authored andcommitted
Use cached feature info in spa_add_feature_stats()
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 #3082
1 parent 0e86d30 commit 417104b

File tree

4 files changed

+51
-11
lines changed

4 files changed

+51
-11
lines changed

include/sys/spa_impl.h

+1
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ struct spa {
236236
uint64_t spa_feat_for_read_obj; /* required to read from pool */
237237
uint64_t spa_feat_desc_obj; /* Feature descriptions */
238238
uint64_t spa_feat_enabled_txg_obj; /* Feature enabled txg */
239+
nvlist_t *spa_feat_stats; /* Cache of enabled features */
239240
/* cache feature refcounts */
240241
uint64_t spa_feat_refcount_cache[SPA_FEATURES];
241242
taskqid_t spa_deadman_tqid; /* Task id */

module/zfs/spa.c

+48-10
Original file line numberDiff line numberDiff line change
@@ -3202,23 +3202,19 @@ spa_add_l2cache(spa_t *spa, nvlist_t *config)
32023202
}
32033203

32043204
static void
3205-
spa_add_feature_stats(spa_t *spa, nvlist_t *config)
3205+
spa_feature_stats_from_disk(spa_t *spa, nvlist_t *features)
32063206
{
3207-
nvlist_t *features;
32083207
zap_cursor_t zc;
32093208
zap_attribute_t za;
32103209

3211-
ASSERT(spa_config_held(spa, SCL_CONFIG, RW_READER));
3212-
VERIFY(nvlist_alloc(&features, NV_UNIQUE_NAME, KM_SLEEP) == 0);
3213-
32143210
if (spa->spa_feat_for_read_obj != 0) {
32153211
for (zap_cursor_init(&zc, spa->spa_meta_objset,
32163212
spa->spa_feat_for_read_obj);
32173213
zap_cursor_retrieve(&zc, &za) == 0;
32183214
zap_cursor_advance(&zc)) {
32193215
ASSERT(za.za_integer_length == sizeof (uint64_t) &&
32203216
za.za_num_integers == 1);
3221-
VERIFY3U(0, ==, nvlist_add_uint64(features, za.za_name,
3217+
VERIFY0(nvlist_add_uint64(features, za.za_name,
32223218
za.za_first_integer));
32233219
}
32243220
zap_cursor_fini(&zc);
@@ -3231,15 +3227,57 @@ spa_add_feature_stats(spa_t *spa, nvlist_t *config)
32313227
zap_cursor_advance(&zc)) {
32323228
ASSERT(za.za_integer_length == sizeof (uint64_t) &&
32333229
za.za_num_integers == 1);
3234-
VERIFY3U(0, ==, nvlist_add_uint64(features, za.za_name,
3230+
VERIFY0(nvlist_add_uint64(features, za.za_name,
32353231
za.za_first_integer));
32363232
}
32373233
zap_cursor_fini(&zc);
32383234
}
3235+
}
3236+
3237+
static void
3238+
spa_feature_stats_from_cache(spa_t *spa, nvlist_t *features)
3239+
{
3240+
int i;
3241+
3242+
for (i = 0; i < SPA_FEATURES; i++) {
3243+
zfeature_info_t feature = spa_feature_table[i];
3244+
uint64_t refcount;
3245+
3246+
if (feature_get_refcount(spa, &feature, &refcount) != 0)
3247+
continue;
3248+
3249+
VERIFY0(nvlist_add_uint64(features, feature.fi_guid, refcount));
3250+
}
3251+
}
3252+
3253+
/*
3254+
* Store a list of pool features and their reference counts in the
3255+
* config.
3256+
*
3257+
* The first time this is called on a spa, allocate a new nvlist, fetch
3258+
* the pool features and reference counts from disk, then save the list
3259+
* in the spa. In subsequent calls on the same spa use the saved nvlist
3260+
* and refresh its values from the cached reference counts. This
3261+
* ensures we don't block here on I/O on a suspended pool so 'zpool
3262+
* clear' can resume the pool.
3263+
*/
3264+
static void
3265+
spa_add_feature_stats(spa_t *spa, nvlist_t *config)
3266+
{
3267+
nvlist_t *features = spa->spa_feat_stats;
3268+
3269+
ASSERT(spa_config_held(spa, SCL_CONFIG, RW_READER));
3270+
3271+
if (features != NULL) {
3272+
spa_feature_stats_from_cache(spa, features);
3273+
} else {
3274+
VERIFY0(nvlist_alloc(&features, NV_UNIQUE_NAME, KM_SLEEP));
3275+
spa->spa_feat_stats = features;
3276+
spa_feature_stats_from_disk(spa, features);
3277+
}
32393278

3240-
VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
3241-
features) == 0);
3242-
nvlist_free(features);
3279+
VERIFY0(nvlist_add_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
3280+
features));
32433281
}
32443282

32453283
int

module/zfs/spa_misc.c

+1
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ spa_remove(spa_t *spa)
643643

644644
nvlist_free(spa->spa_label_features);
645645
nvlist_free(spa->spa_load_info);
646+
nvlist_free(spa->spa_feat_stats);
646647
spa_config_set(spa, NULL);
647648

648649
refcount_destroy(&spa->spa_refcount);

module/zfs/zfeature.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ spa_features_check(spa_t *spa, boolean_t for_write,
228228
*
229229
* Note: well-designed features will not need to use this; they should
230230
* use spa_feature_is_enabled() and spa_feature_is_active() instead.
231-
* However, this is non-static for zdb and zhack.
231+
* However, this is non-static for zdb, zhack, and spa_add_feature_stats().
232232
*/
233233
int
234234
feature_get_refcount(spa_t *spa, zfeature_info_t *feature, uint64_t *res)

0 commit comments

Comments
 (0)