zfs roopool: add support for multi-vdev configurations

Tested by:	madpilot
MFC after:	10 days
This commit is contained in:
Andriy Gapon 2012-11-24 13:23:15 +00:00
parent e1fccde2c9
commit 328998eac1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=243502
2 changed files with 151 additions and 41 deletions

View File

@ -3755,54 +3755,120 @@ spa_import_rootpool(char *devpath, char *devid)
#else
extern int
vdev_geom_read_pool_label(const char *name, nvlist_t **config);
extern int vdev_geom_read_pool_label(const char *name, nvlist_t ***configs,
uint64_t *count);
static nvlist_t *
spa_generate_rootconf(const char *name)
{
nvlist_t **configs, **tops;
nvlist_t *config;
nvlist_t *nvtop, *nvroot;
nvlist_t *best_cfg, *nvtop, *nvroot;
uint64_t *holes;
uint64_t best_txg;
uint64_t nchildren;
uint64_t pgid;
uint64_t count;
uint64_t i;
uint_t nholes;
if (vdev_geom_read_pool_label(name, &config) != 0)
if (vdev_geom_read_pool_label(name, &configs, &count) != 0)
return (NULL);
ASSERT3U(count, !=, 0);
best_txg = 0;
for (i = 0; i < count; i++) {
uint64_t txg;
VERIFY(nvlist_lookup_uint64(configs[i], ZPOOL_CONFIG_POOL_TXG,
&txg) == 0);
if (txg > best_txg) {
best_txg = txg;
best_cfg = configs[i];
}
}
/*
* Multi-vdev root pool configuration discovery is not supported yet.
*/
nchildren = 0;
nvlist_lookup_uint64(config, ZPOOL_CONFIG_VDEV_CHILDREN, &nchildren);
if (nchildren != 1) {
nvlist_free(config);
return (NULL);
VERIFY(nvlist_lookup_uint64(best_cfg, ZPOOL_CONFIG_VDEV_CHILDREN,
&nchildren) == 0);
holes = NULL;
nvlist_lookup_uint64_array(best_cfg, ZPOOL_CONFIG_HOLE_ARRAY,
&holes, &nholes);
tops = kmem_alloc(nchildren * sizeof(void *), KM_SLEEP | KM_ZERO);
for (i = 0; i < nchildren; i++) {
if (i >= count)
break;
if (configs[i] == NULL)
continue;
VERIFY(nvlist_lookup_nvlist(configs[i], ZPOOL_CONFIG_VDEV_TREE,
&nvtop) == 0);
nvlist_dup(nvtop, &tops[i], KM_SLEEP);
}
for (i = 0; holes != NULL && i < nholes; i++) {
if (i >= nchildren)
continue;
if (tops[holes[i]] != NULL)
continue;
nvlist_alloc(&tops[holes[i]], NV_UNIQUE_NAME, KM_SLEEP);
VERIFY(nvlist_add_string(tops[holes[i]], ZPOOL_CONFIG_TYPE,
VDEV_TYPE_HOLE) == 0);
VERIFY(nvlist_add_uint64(tops[holes[i]], ZPOOL_CONFIG_ID,
holes[i]) == 0);
VERIFY(nvlist_add_uint64(tops[holes[i]], ZPOOL_CONFIG_GUID,
0) == 0);
}
for (i = 0; i < nchildren; i++) {
if (tops[i] != NULL)
continue;
nvlist_alloc(&tops[i], NV_UNIQUE_NAME, KM_SLEEP);
VERIFY(nvlist_add_string(tops[i], ZPOOL_CONFIG_TYPE,
VDEV_TYPE_MISSING) == 0);
VERIFY(nvlist_add_uint64(tops[i], ZPOOL_CONFIG_ID,
i) == 0);
VERIFY(nvlist_add_uint64(tops[i], ZPOOL_CONFIG_GUID,
0) == 0);
}
/*
* Add this top-level vdev to the child array.
* Create pool config based on the best vdev config.
*/
VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvtop) == 0);
VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
&pgid) == 0);
nvlist_dup(best_cfg, &config, KM_SLEEP);
/*
* Put this pool's top-level vdevs into a root vdev.
*/
VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
&pgid) == 0);
VERIFY(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, KM_SLEEP) == 0);
VERIFY(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
VDEV_TYPE_ROOT) == 0);
VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) == 0);
VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, pgid) == 0);
VERIFY(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
&nvtop, 1) == 0);
tops, nchildren) == 0);
/*
* Replace the existing vdev_tree with the new root vdev in
* this pool's configuration (remove the old, add the new).
*/
VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot) == 0);
/*
* Drop vdev config elements that should not be present at pool level.
*/
nvlist_remove(config, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64);
nvlist_remove(config, ZPOOL_CONFIG_TOP_GUID, DATA_TYPE_UINT64);
for (i = 0; i < count; i++)
nvlist_free(configs[i]);
kmem_free(configs, count * sizeof(void *));
for (i = 0; i < nchildren; i++)
nvlist_free(tops[i]);
kmem_free(tops, nchildren * sizeof(void *));
nvlist_free(nvroot);
return (config);
}

View File

@ -292,27 +292,74 @@ vdev_geom_read_config(struct g_consumer *cp, nvlist_t **config)
return (*config == NULL ? ENOENT : 0);
}
static int
vdev_geom_check_config(nvlist_t *config, const char *name, uint64_t *best_txg)
static void
resize_configs(nvlist_t ***configs, uint64_t *count, uint64_t id)
{
uint64_t vdev_guid;
uint64_t txg;
nvlist_t **new_configs;
uint64_t i;
if (id < *count)
return;
new_configs = kmem_alloc((id + 1) * sizeof(nvlist_t *),
KM_SLEEP | KM_ZERO);
for (i = 0; i < *count; i++)
new_configs[i] = (*configs)[i];
if (*configs != NULL)
kmem_free(*configs, *count * sizeof(void *));
*configs = new_configs;
*count = id + 1;
}
static void
process_vdev_config(nvlist_t ***configs, uint64_t *count, nvlist_t *cfg,
const char *name, uint64_t* known_pool_guid)
{
nvlist_t *vdev_tree;
uint64_t pool_guid;
uint64_t vdev_guid, known_guid;
uint64_t id, txg, known_txg;
char *pname;
int i;
if (nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &pname) != 0 ||
if (nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &pname) != 0 ||
strcmp(pname, name) != 0)
return (ENOENT);
goto ignore;
ZFS_LOG(1, "found pool: %s", pname);
if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_GUID, &pool_guid) != 0)
goto ignore;
txg = 0;
nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg);
if (txg <= *best_txg)
return (ENOENT);
*best_txg = txg;
ZFS_LOG(1, "txg: %ju", (uintmax_t)*best_txg);
if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_TOP_GUID, &vdev_guid) != 0)
goto ignore;
return (0);
if (nvlist_lookup_nvlist(cfg, ZPOOL_CONFIG_VDEV_TREE, &vdev_tree) != 0)
goto ignore;
if (nvlist_lookup_uint64(vdev_tree, ZPOOL_CONFIG_ID, &id) != 0)
goto ignore;
VERIFY(nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_TXG, &txg) == 0);
if (*known_pool_guid != 0) {
if (pool_guid != *known_pool_guid)
goto ignore;
} else
*known_pool_guid = pool_guid;
resize_configs(configs, count, id);
if ((*configs)[id] != NULL) {
VERIFY(nvlist_lookup_uint64((*configs)[id],
ZPOOL_CONFIG_POOL_TXG, &known_txg) == 0);
if (txg <= known_txg)
goto ignore;
nvlist_free((*configs)[id]);
}
(*configs)[id] = cfg;
return;
ignore:
nvlist_free(cfg);
}
static int
@ -339,14 +386,15 @@ vdev_geom_detach_taster(struct g_consumer *cp)
}
int
vdev_geom_read_pool_label(const char *name, nvlist_t **config)
vdev_geom_read_pool_label(const char *name,
nvlist_t ***configs, uint64_t *count)
{
struct g_class *mp;
struct g_geom *gp, *zgp;
struct g_provider *pp;
struct g_consumer *zcp;
nvlist_t *vdev_cfg;
uint64_t best_txg;
uint64_t pool_guid;
int error;
DROP_GIANT();
@ -357,8 +405,9 @@ vdev_geom_read_pool_label(const char *name, nvlist_t **config)
zgp->orphan = vdev_geom_taste_orphan;
zcp = g_new_consumer(zgp);
best_txg = 0;
*config = NULL;
*configs = NULL;
*count = 0;
pool_guid = 0;
LIST_FOREACH(mp, &g_classes, class) {
if (mp == &zfs_vdev_class)
continue;
@ -378,14 +427,8 @@ vdev_geom_read_pool_label(const char *name, nvlist_t **config)
continue;
ZFS_LOG(1, "successfully read vdev config");
error = vdev_geom_check_config(vdev_cfg, name,
&best_txg);
if (error != 0) {
nvlist_free(vdev_cfg);
continue;
}
nvlist_free(*config);
*config = vdev_cfg;
process_vdev_config(configs, count,
vdev_cfg, name, &pool_guid);
}
}
}
@ -394,7 +437,8 @@ vdev_geom_read_pool_label(const char *name, nvlist_t **config)
g_destroy_geom(zgp);
g_topology_unlock();
PICKUP_GIANT();
return (*config == NULL ? ENOENT : 0);
return (*count > 0 ? 0 : ENOENT);
}
static uint64_t