From 5536c0dee278796f04cfa3b549e2a1d8edb717b7 Mon Sep 17 00:00:00 2001 From: Ameer Hamza Date: Fri, 9 Aug 2024 03:16:46 +0500 Subject: [PATCH] Sync AUX label during pool import Spare and l2cache vdev labels are not updated during import. Therefore, if disk paths are updated between pool export and import, the AUX label still shows the old paths. This patch syncs the AUX label during import to show the correct path information. Reviewed-by: Tony Hutter Reviewed-by: Umer Saleem Reviewed-by: Brian Behlendorf Reviewed-by: Alexander Motin Signed-off-by: Ameer Hamza Closes #15817 --- include/sys/spa_impl.h | 1 + module/zfs/spa.c | 2 + module/zfs/vdev_label.c | 123 ++++++++++++++++++++++++++-------------- 3 files changed, 82 insertions(+), 44 deletions(-) diff --git a/include/sys/spa_impl.h b/include/sys/spa_impl.h index 47f3493274..4fc6f22fcb 100644 --- a/include/sys/spa_impl.h +++ b/include/sys/spa_impl.h @@ -146,6 +146,7 @@ struct spa_aux_vdev { vdev_t **sav_vdevs; /* devices */ int sav_count; /* number devices */ boolean_t sav_sync; /* sync the device list */ + boolean_t sav_label_sync; /* sync aux labels */ nvlist_t **sav_pending; /* pending device additions */ uint_t sav_npending; /* # pending devices */ }; diff --git a/module/zfs/spa.c b/module/zfs/spa.c index 66f766cf9b..cafc7196c3 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -6817,6 +6817,7 @@ spa_import(char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags) spa_load_spares(spa); spa_config_exit(spa, SCL_ALL, FTAG); spa->spa_spares.sav_sync = B_TRUE; + spa->spa_spares.sav_label_sync = B_TRUE; } if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0) { @@ -6832,6 +6833,7 @@ spa_import(char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags) spa_load_l2cache(spa); spa_config_exit(spa, SCL_ALL, FTAG); spa->spa_l2cache.sav_sync = B_TRUE; + spa->spa_l2cache.sav_label_sync = B_TRUE; } /* diff --git a/module/zfs/vdev_label.c b/module/zfs/vdev_label.c index ed592514fd..47346dd5ac 100644 --- a/module/zfs/vdev_label.c +++ b/module/zfs/vdev_label.c @@ -1007,6 +1007,47 @@ vdev_inuse(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason, return (state == POOL_STATE_ACTIVE); } +static nvlist_t * +vdev_aux_label_generate(vdev_t *vd, boolean_t reason_spare) +{ + /* + * For inactive hot spares and level 2 ARC devices, we generate + * a special label that identifies as a mutually shared hot + * spare or l2cache device. We write the label in case of + * addition or removal of hot spare or l2cache vdev (in which + * case we want to revert the labels). + */ + nvlist_t *label = fnvlist_alloc(); + fnvlist_add_uint64(label, ZPOOL_CONFIG_VERSION, + spa_version(vd->vdev_spa)); + fnvlist_add_uint64(label, ZPOOL_CONFIG_POOL_STATE, reason_spare ? + POOL_STATE_SPARE : POOL_STATE_L2CACHE); + fnvlist_add_uint64(label, ZPOOL_CONFIG_GUID, vd->vdev_guid); + + /* + * This is merely to facilitate reporting the ashift of the + * cache device through zdb. The actual retrieval of the + * ashift (in vdev_alloc()) uses the nvlist + * spa->spa_l2cache->sav_config (populated in + * spa_ld_open_aux_vdevs()). + */ + if (!reason_spare) + fnvlist_add_uint64(label, ZPOOL_CONFIG_ASHIFT, vd->vdev_ashift); + + /* + * Add path information to help find it during pool import + */ + if (vd->vdev_path != NULL) + fnvlist_add_string(label, ZPOOL_CONFIG_PATH, vd->vdev_path); + if (vd->vdev_devid != NULL) + fnvlist_add_string(label, ZPOOL_CONFIG_DEVID, vd->vdev_devid); + if (vd->vdev_physpath != NULL) { + fnvlist_add_string(label, ZPOOL_CONFIG_PHYS_PATH, + vd->vdev_physpath); + } + return (label); +} + /* * Initialize a vdev label. We check to make sure each leaf device is not in * use, and writable. We put down an initial label which we will later @@ -1121,49 +1162,7 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason) * be written again with a meaningful txg by spa_sync(). */ if (reason_spare || reason_l2cache) { - /* - * For inactive hot spares and level 2 ARC devices, we generate - * a special label that identifies as a mutually shared hot - * spare or l2cache device. We write the label in case of - * addition or removal of hot spare or l2cache vdev (in which - * case we want to revert the labels). - */ - VERIFY(nvlist_alloc(&label, NV_UNIQUE_NAME, KM_SLEEP) == 0); - - VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_VERSION, - spa_version(spa)) == 0); - VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_POOL_STATE, - reason_spare ? POOL_STATE_SPARE : POOL_STATE_L2CACHE) == 0); - VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_GUID, - vd->vdev_guid) == 0); - - /* - * This is merely to facilitate reporting the ashift of the - * cache device through zdb. The actual retrieval of the - * ashift (in vdev_alloc()) uses the nvlist - * spa->spa_l2cache->sav_config (populated in - * spa_ld_open_aux_vdevs()). - */ - if (reason_l2cache) { - VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_ASHIFT, - vd->vdev_ashift) == 0); - } - - /* - * Add path information to help find it during pool import - */ - if (vd->vdev_path != NULL) { - VERIFY(nvlist_add_string(label, ZPOOL_CONFIG_PATH, - vd->vdev_path) == 0); - } - if (vd->vdev_devid != NULL) { - VERIFY(nvlist_add_string(label, ZPOOL_CONFIG_DEVID, - vd->vdev_devid) == 0); - } - if (vd->vdev_physpath != NULL) { - VERIFY(nvlist_add_string(label, ZPOOL_CONFIG_PHYS_PATH, - vd->vdev_physpath) == 0); - } + label = vdev_aux_label_generate(vd, reason_spare); /* * When spare or l2cache (aux) vdev is added during pool @@ -1900,6 +1899,8 @@ vdev_label_sync(zio_t *zio, uint64_t *good_writes, abd_t *vp_abd; char *buf; size_t buflen; + vdev_t *pvd = vd->vdev_parent; + boolean_t spare_in_use = B_FALSE; for (int c = 0; c < vd->vdev_children; c++) { vdev_label_sync(zio, good_writes, @@ -1920,10 +1921,17 @@ vdev_label_sync(zio_t *zio, uint64_t *good_writes, if (vd->vdev_ops == &vdev_draid_spare_ops) return; + if (pvd && pvd->vdev_ops == &vdev_spare_ops) + spare_in_use = B_TRUE; + /* * Generate a label describing the top-level config to which we belong. */ - label = spa_config_generate(vd->vdev_spa, vd, txg, B_FALSE); + if ((vd->vdev_isspare && !spare_in_use) || vd->vdev_isl2cache) { + label = vdev_aux_label_generate(vd, vd->vdev_isspare); + } else { + label = spa_config_generate(vd->vdev_spa, vd, txg, B_FALSE); + } vp_abd = abd_alloc_linear(sizeof (vdev_phys_t), B_TRUE); abd_zero(vp_abd, sizeof (vdev_phys_t)); @@ -1973,6 +1981,24 @@ vdev_label_sync_list(spa_t *spa, int l, uint64_t txg, int flags) zio_nowait(vio); } + /* + * AUX path may have changed during import + */ + spa_aux_vdev_t *sav[2] = {&spa->spa_spares, &spa->spa_l2cache}; + for (int i = 0; i < 2; i++) { + for (int v = 0; v < sav[i]->sav_count; v++) { + uint64_t *good_writes; + if (!sav[i]->sav_label_sync) + continue; + good_writes = kmem_zalloc(sizeof (uint64_t), KM_SLEEP); + zio_t *vio = zio_null(zio, spa, NULL, + vdev_label_sync_ignore_done, good_writes, flags); + vdev_label_sync(vio, good_writes, sav[i]->sav_vdevs[v], + l, txg, flags); + zio_nowait(vio); + } + } + error = zio_wait(zio); /* @@ -1983,6 +2009,15 @@ vdev_label_sync_list(spa_t *spa, int l, uint64_t txg, int flags) for (vd = list_head(dl); vd != NULL; vd = list_next(dl, vd)) zio_flush(zio, vd); + for (int i = 0; i < 2; i++) { + if (!sav[i]->sav_label_sync) + continue; + for (int v = 0; v < sav[i]->sav_count; v++) + zio_flush(zio, sav[i]->sav_vdevs[v]); + if (l == 1) + sav[i]->sav_label_sync = B_FALSE; + } + (void) zio_wait(zio); return (error);