void avl_walk(avl_tree_t *root, void(*hook)(void *)){ if(root){ avl_walk(root->left, hook); hook(root->data); avl_walk(root->right, hook); } }
/* * Access the node immediately before or after an insertion point. * * "avl_index_t" is a (avl_node_t *) with the bottom bit indicating a child * * Return value: * NULL: no node in the given direction * "void *" of the found tree node */ void * avl_nearest(avl_tree_t *tree, avl_index_t where, int direction) { int child = AVL_INDEX2CHILD(where); avl_node_t *node = AVL_INDEX2NODE(where); void *data; size_t off = tree->avl_offset; if (node == NULL) { return (NULL); } data = AVL_NODE2DATA(node, off); if (child != direction) return (data); return (avl_walk(tree, data, direction)); }
/* * Access the node immediately before or after an insertion point. * * "avl_index_t" is a (avl_node_t *) with the bottom bit indicating a child * * Return value: * NULL: no node in the given direction * "void *" of the found tree node */ void * avl_nearest(avl_tree_t *tree, avl_index_t where, int direction) { int child = AVL_INDEX2CHILD(where); avl_node_t *node = AVL_INDEX2NODE(where); void *data; size_t off = tree->avl_offset; if (node == NULL) { if (tree->avl_root != NULL) filebench_log(LOG_ERROR, "Null Node Pointer Supplied"); return (NULL); } data = AVL_NODE2DATA(node, off); if (child != direction) return (data); return (avl_walk(tree, data, direction)); }
/* * Given a list of directories to search, find all pools stored on disk. This * includes partial pools which are not available to import. If no args are * given (argc is 0), then the default directory (/dev/dsk) is searched. * poolname or guid (but not both) are provided by the caller when trying * to import a specific pool. */ static nvlist_t * zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg) { int i, dirs = iarg->paths; struct dirent *dp; char path[MAXPATHLEN]; char *end, **dir = iarg->path; size_t pathleft; nvlist_t *ret = NULL; pool_list_t pools = { 0 }; pool_entry_t *pe, *penext; vdev_entry_t *ve, *venext; config_entry_t *ce, *cenext; name_entry_t *ne, *nenext; avl_tree_t slice_cache; rdsk_node_t *slice; void *cookie; verify(iarg->poolname == NULL || iarg->guid == 0); if (dirs == 0) { #ifdef HAVE_LIBBLKID /* Use libblkid to scan all device for their type */ if (zpool_find_import_blkid(hdl, &pools) == 0) goto skip_scanning; (void) zfs_error_fmt(hdl, EZFS_BADCACHE, dgettext(TEXT_DOMAIN, "blkid failure falling back " "to manual probing")); #endif /* HAVE_LIBBLKID */ dir = zpool_default_import_path; dirs = DEFAULT_IMPORT_PATH_SIZE; } /* * Go through and read the label configuration information from every * possible device, organizing the information according to pool GUID * and toplevel GUID. */ for (i = 0; i < dirs; i++) { taskq_t *t; char rdsk[MAXPATHLEN]; int dfd; boolean_t config_failed = B_FALSE; DIR *dirp; /* use realpath to normalize the path */ if (realpath(dir[i], path) == 0) { /* it is safe to skip missing search paths */ if (errno == ENOENT) continue; zfs_error_aux(hdl, strerror(errno)); (void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]); goto error; } end = &path[strlen(path)]; *end++ = '/'; *end = 0; pathleft = &path[sizeof (path)] - end; /* * Using raw devices instead of block devices when we're * reading the labels skips a bunch of slow operations during * close(2) processing, so we replace /dev/dsk with /dev/rdsk. */ if (strcmp(path, ZFS_DISK_ROOTD) == 0) (void) strlcpy(rdsk, ZFS_RDISK_ROOTD, sizeof (rdsk)); else (void) strlcpy(rdsk, path, sizeof (rdsk)); if ((dfd = open(rdsk, O_RDONLY)) < 0 || (dirp = fdopendir(dfd)) == NULL) { if (dfd >= 0) (void) close(dfd); zfs_error_aux(hdl, strerror(errno)); (void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(TEXT_DOMAIN, "cannot open '%s'"), rdsk); goto error; } avl_create(&slice_cache, slice_cache_compare, sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node)); /* * This is not MT-safe, but we have no MT consumers of libzfs */ while ((dp = readdir(dirp)) != NULL) { const char *name = dp->d_name; if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) continue; slice = zfs_alloc(hdl, sizeof (rdsk_node_t)); slice->rn_name = zfs_strdup(hdl, name); slice->rn_avl = &slice_cache; slice->rn_dfd = dfd; slice->rn_hdl = hdl; slice->rn_nozpool = B_FALSE; avl_add(&slice_cache, slice); } /* * create a thread pool to do all of this in parallel; * rn_nozpool is not protected, so this is racy in that * multiple tasks could decide that the same slice can * not hold a zpool, which is benign. Also choose * double the number of processors; we hold a lot of * locks in the kernel, so going beyond this doesn't * buy us much. */ t = taskq_create("z_import", 2 * max_ncpus, defclsyspri, 2 * max_ncpus, INT_MAX, TASKQ_PREPOPULATE); for (slice = avl_first(&slice_cache); slice; (slice = avl_walk(&slice_cache, slice, AVL_AFTER))) (void) taskq_dispatch(t, zpool_open_func, slice, TQ_SLEEP); taskq_wait(t); taskq_destroy(t); cookie = NULL; while ((slice = avl_destroy_nodes(&slice_cache, &cookie)) != NULL) { if (slice->rn_config != NULL && !config_failed) { nvlist_t *config = slice->rn_config; boolean_t matched = B_TRUE; if (iarg->poolname != NULL) { char *pname; matched = nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &pname) == 0 && strcmp(iarg->poolname, pname) == 0; } else if (iarg->guid != 0) { uint64_t this_guid; matched = nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &this_guid) == 0 && iarg->guid == this_guid; } if (!matched) { nvlist_free(config); } else { /* * use the non-raw path for the config */ (void) strlcpy(end, slice->rn_name, pathleft); if (add_config(hdl, &pools, path, i+1, slice->rn_num_labels, config) != 0) config_failed = B_TRUE; } } free(slice->rn_name); free(slice); } avl_destroy(&slice_cache); (void) closedir(dirp); if (config_failed) goto error; } #ifdef HAVE_LIBBLKID skip_scanning: #endif ret = get_configs(hdl, &pools, iarg->can_be_active, iarg->policy); error: for (pe = pools.pools; pe != NULL; pe = penext) { penext = pe->pe_next; for (ve = pe->pe_vdevs; ve != NULL; ve = venext) { venext = ve->ve_next; for (ce = ve->ve_configs; ce != NULL; ce = cenext) { cenext = ce->ce_next; if (ce->ce_config) nvlist_free(ce->ce_config); free(ce); } free(ve); } free(pe); } for (ne = pools.names; ne != NULL; ne = nenext) { nenext = ne->ne_next; free(ne->ne_name); free(ne); } return (ret); }
/* * Given a list of directories to search, find all pools stored on disk. This * includes partial pools which are not available to import. If no args are * given (argc is 0), then the default directory (/dev/dsk) is searched. * poolname or guid (but not both) are provided by the caller when trying * to import a specific pool. */ static nvlist_t * zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg) { int i, dirs = iarg->paths; struct dirent64 *dp; char path[MAXPATHLEN]; char *end, **dir = iarg->path; size_t pathleft; nvlist_t *ret = NULL; static char *default_dir = "/dev/dsk"; pool_list_t pools = { 0 }; pool_entry_t *pe, *penext; vdev_entry_t *ve, *venext; config_entry_t *ce, *cenext; name_entry_t *ne, *nenext; avl_tree_t slice_cache; rdsk_node_t *slice; void *cookie; if (dirs == 0) { dirs = 1; dir = &default_dir; } /* * Go through and read the label configuration information from every * possible device, organizing the information according to pool GUID * and toplevel GUID. */ for (i = 0; i < dirs; i++) { tpool_t *t; char *rdsk; int dfd; boolean_t config_failed = B_FALSE; DIR *dirp; /* use realpath to normalize the path */ if (realpath(dir[i], path) == 0) { (void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]); goto error; } end = &path[strlen(path)]; *end++ = '/'; *end = 0; pathleft = &path[sizeof (path)] - end; /* * Using raw devices instead of block devices when we're * reading the labels skips a bunch of slow operations during * close(2) processing, so we replace /dev/dsk with /dev/rdsk. */ if (strcmp(path, "/dev/dsk/") == 0) rdsk = "/dev/rdsk/"; else rdsk = path; if ((dfd = open64(rdsk, O_RDONLY)) < 0 || (dirp = fdopendir(dfd)) == NULL) { if (dfd >= 0) (void) close(dfd); zfs_error_aux(hdl, strerror(errno)); (void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(TEXT_DOMAIN, "cannot open '%s'"), rdsk); goto error; } avl_create(&slice_cache, slice_cache_compare, sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node)); /* * This is not MT-safe, but we have no MT consumers of libzfs */ while ((dp = readdir64(dirp)) != NULL) { const char *name = dp->d_name; if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) continue; slice = zfs_alloc(hdl, sizeof (rdsk_node_t)); slice->rn_name = zfs_strdup(hdl, name); slice->rn_avl = &slice_cache; slice->rn_dfd = dfd; slice->rn_hdl = hdl; slice->rn_nozpool = B_FALSE; avl_add(&slice_cache, slice); } /* * create a thread pool to do all of this in parallel; * rn_nozpool is not protected, so this is racy in that * multiple tasks could decide that the same slice can * not hold a zpool, which is benign. Also choose * double the number of processors; we hold a lot of * locks in the kernel, so going beyond this doesn't * buy us much. */ t = tpool_create(1, 2 * sysconf(_SC_NPROCESSORS_ONLN), 0, NULL); for (slice = avl_first(&slice_cache); slice; (slice = avl_walk(&slice_cache, slice, AVL_AFTER))) (void) tpool_dispatch(t, zpool_open_func, slice); tpool_wait(t); tpool_destroy(t); cookie = NULL; while ((slice = avl_destroy_nodes(&slice_cache, &cookie)) != NULL) { if (slice->rn_config != NULL && !config_failed) { nvlist_t *config = slice->rn_config; boolean_t matched = B_TRUE; if (iarg->poolname != NULL) { char *pname; matched = nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &pname) == 0 && strcmp(iarg->poolname, pname) == 0; } else if (iarg->guid != 0) { uint64_t this_guid; matched = nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &this_guid) == 0 && iarg->guid == this_guid; } if (!matched) { nvlist_free(config); } else { /* * use the non-raw path for the config */ (void) strlcpy(end, slice->rn_name, pathleft); if (add_config(hdl, &pools, path, config) != 0) config_failed = B_TRUE; } } free(slice->rn_name); free(slice); } avl_destroy(&slice_cache); (void) closedir(dirp); if (config_failed) goto error; } ret = get_configs(hdl, &pools, iarg->can_be_active); error: for (pe = pools.pools; pe != NULL; pe = penext) { penext = pe->pe_next; for (ve = pe->pe_vdevs; ve != NULL; ve = venext) { venext = ve->ve_next; for (ce = ve->ve_configs; ce != NULL; ce = cenext) { cenext = ce->ce_next; if (ce->ce_config) nvlist_free(ce->ce_config); free(ce); } free(ve); } free(pe); } for (ne = pools.names; ne != NULL; ne = nenext) { nenext = ne->ne_next; free(ne->ne_name); free(ne); } return (ret); }