Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libzfs/libzfs_config.c
48378 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
/*
24
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25
* Use is subject to license terms.
26
*/
27
28
/*
29
* Copyright (c) 2012 by Delphix. All rights reserved.
30
* Copyright (c) 2015 by Syneto S.R.L. All rights reserved.
31
* Copyright 2016 Nexenta Systems, Inc.
32
*/
33
34
/*
35
* The pool configuration repository is stored in /etc/zfs/zpool.cache as a
36
* single packed nvlist. While it would be nice to just read in this
37
* file from userland, this wouldn't work from a local zone. So we have to have
38
* a zpool ioctl to return the complete configuration for all pools. In the
39
* global zone, this will be identical to reading the file and unpacking it in
40
* userland.
41
*/
42
43
#include <errno.h>
44
#include <sys/stat.h>
45
#include <fcntl.h>
46
#include <stddef.h>
47
#include <string.h>
48
#include <unistd.h>
49
#include <libintl.h>
50
#include <libuutil.h>
51
52
#include "libzfs_impl.h"
53
54
typedef struct config_node {
55
char *cn_name;
56
nvlist_t *cn_config;
57
uu_avl_node_t cn_avl;
58
} config_node_t;
59
60
static int
61
config_node_compare(const void *a, const void *b, void *unused)
62
{
63
(void) unused;
64
const config_node_t *ca = (config_node_t *)a;
65
const config_node_t *cb = (config_node_t *)b;
66
67
int ret = strcmp(ca->cn_name, cb->cn_name);
68
69
if (ret < 0)
70
return (-1);
71
else if (ret > 0)
72
return (1);
73
else
74
return (0);
75
}
76
77
void
78
namespace_clear(libzfs_handle_t *hdl)
79
{
80
if (hdl->libzfs_ns_avl) {
81
config_node_t *cn;
82
void *cookie = NULL;
83
84
while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl,
85
&cookie)) != NULL) {
86
nvlist_free(cn->cn_config);
87
free(cn->cn_name);
88
free(cn);
89
}
90
91
uu_avl_destroy(hdl->libzfs_ns_avl);
92
hdl->libzfs_ns_avl = NULL;
93
}
94
95
if (hdl->libzfs_ns_avlpool) {
96
uu_avl_pool_destroy(hdl->libzfs_ns_avlpool);
97
hdl->libzfs_ns_avlpool = NULL;
98
}
99
}
100
101
/*
102
* Loads the pool namespace, or re-loads it if the cache has changed.
103
*/
104
static int
105
namespace_reload(libzfs_handle_t *hdl)
106
{
107
nvlist_t *config;
108
config_node_t *cn;
109
nvpair_t *elem;
110
zfs_cmd_t zc = {"\0"};
111
void *cookie;
112
113
if (hdl->libzfs_ns_gen == 0) {
114
/*
115
* This is the first time we've accessed the configuration
116
* cache. Initialize the AVL tree and then fall through to the
117
* common code.
118
*/
119
if ((hdl->libzfs_ns_avlpool = uu_avl_pool_create("config_pool",
120
sizeof (config_node_t),
121
offsetof(config_node_t, cn_avl),
122
config_node_compare, UU_DEFAULT)) == NULL)
123
return (no_memory(hdl));
124
125
if ((hdl->libzfs_ns_avl = uu_avl_create(hdl->libzfs_ns_avlpool,
126
NULL, UU_DEFAULT)) == NULL)
127
return (no_memory(hdl));
128
}
129
130
zcmd_alloc_dst_nvlist(hdl, &zc, 0);
131
132
for (;;) {
133
zc.zc_cookie = hdl->libzfs_ns_gen;
134
if (zfs_ioctl(hdl, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {
135
switch (errno) {
136
case EEXIST:
137
/*
138
* The namespace hasn't changed.
139
*/
140
zcmd_free_nvlists(&zc);
141
return (0);
142
143
case ENOMEM:
144
zcmd_expand_dst_nvlist(hdl, &zc);
145
break;
146
147
default:
148
zcmd_free_nvlists(&zc);
149
return (zfs_standard_error(hdl, errno,
150
dgettext(TEXT_DOMAIN, "failed to read "
151
"pool configuration")));
152
}
153
} else {
154
hdl->libzfs_ns_gen = zc.zc_cookie;
155
break;
156
}
157
}
158
159
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
160
zcmd_free_nvlists(&zc);
161
return (-1);
162
}
163
164
zcmd_free_nvlists(&zc);
165
166
/*
167
* Clear out any existing configuration information.
168
*/
169
cookie = NULL;
170
while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl, &cookie)) != NULL) {
171
nvlist_free(cn->cn_config);
172
free(cn->cn_name);
173
free(cn);
174
}
175
176
elem = NULL;
177
while ((elem = nvlist_next_nvpair(config, elem)) != NULL) {
178
nvlist_t *child;
179
uu_avl_index_t where;
180
181
cn = zfs_alloc(hdl, sizeof (config_node_t));
182
cn->cn_name = zfs_strdup(hdl, nvpair_name(elem));
183
child = fnvpair_value_nvlist(elem);
184
if (nvlist_dup(child, &cn->cn_config, 0) != 0) {
185
free(cn->cn_name);
186
free(cn);
187
nvlist_free(config);
188
return (no_memory(hdl));
189
}
190
verify(uu_avl_find(hdl->libzfs_ns_avl, cn, NULL, &where)
191
== NULL);
192
193
uu_avl_insert(hdl->libzfs_ns_avl, cn, where);
194
}
195
196
nvlist_free(config);
197
return (0);
198
}
199
200
/*
201
* Retrieve the configuration for the given pool. The configuration is an nvlist
202
* describing the vdevs, as well as the statistics associated with each one.
203
*/
204
nvlist_t *
205
zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
206
{
207
if (oldconfig)
208
*oldconfig = zhp->zpool_old_config;
209
return (zhp->zpool_config);
210
}
211
212
/*
213
* Retrieves a list of enabled features and their refcounts and caches it in
214
* the pool handle.
215
*/
216
nvlist_t *
217
zpool_get_features(zpool_handle_t *zhp)
218
{
219
nvlist_t *config, *features;
220
221
config = zpool_get_config(zhp, NULL);
222
223
if (config == NULL || !nvlist_exists(config,
224
ZPOOL_CONFIG_FEATURE_STATS)) {
225
int error;
226
boolean_t missing = B_FALSE;
227
228
error = zpool_refresh_stats(zhp, &missing);
229
230
if (error != 0 || missing)
231
return (NULL);
232
233
config = zpool_get_config(zhp, NULL);
234
}
235
236
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
237
&features) != 0)
238
return (NULL);
239
240
return (features);
241
}
242
243
/*
244
* Refresh the vdev statistics associated with the given pool. This is used in
245
* iostat to show configuration changes and determine the delta from the last
246
* time the function was called. This function can fail, in case the pool has
247
* been destroyed.
248
*/
249
int
250
zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
251
{
252
zfs_cmd_t zc = {"\0"};
253
int error;
254
nvlist_t *config;
255
libzfs_handle_t *hdl = zhp->zpool_hdl;
256
257
*missing = B_FALSE;
258
(void) strcpy(zc.zc_name, zhp->zpool_name);
259
260
if (zhp->zpool_config_size == 0)
261
zhp->zpool_config_size = 1 << 16;
262
263
zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size);
264
265
for (;;) {
266
if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_STATS,
267
&zc) == 0) {
268
/*
269
* The real error is returned in the zc_cookie field.
270
*/
271
error = zc.zc_cookie;
272
break;
273
}
274
275
if (errno == ENOMEM)
276
zcmd_expand_dst_nvlist(hdl, &zc);
277
else {
278
zcmd_free_nvlists(&zc);
279
if (errno == ENOENT || errno == EINVAL)
280
*missing = B_TRUE;
281
zhp->zpool_state = POOL_STATE_UNAVAIL;
282
return (0);
283
}
284
}
285
286
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
287
zcmd_free_nvlists(&zc);
288
return (-1);
289
}
290
291
zcmd_free_nvlists(&zc);
292
293
zhp->zpool_config_size = zc.zc_nvlist_dst_size;
294
295
if (zhp->zpool_config != NULL) {
296
nvlist_free(zhp->zpool_old_config);
297
298
zhp->zpool_old_config = zhp->zpool_config;
299
}
300
301
zhp->zpool_config = config;
302
if (error)
303
zhp->zpool_state = POOL_STATE_UNAVAIL;
304
else
305
zhp->zpool_state = POOL_STATE_ACTIVE;
306
307
return (0);
308
}
309
310
/*
311
* Copies the pool config and state from szhp to dzhp. szhp and dzhp must
312
* represent the same pool. Used by pool_list_refresh() to avoid another
313
* round-trip into the kernel to get stats already collected earlier in the
314
* function.
315
*/
316
void
317
zpool_refresh_stats_from_handle(zpool_handle_t *dzhp, zpool_handle_t *szhp)
318
{
319
VERIFY0(strcmp(dzhp->zpool_name, szhp->zpool_name));
320
nvlist_free(dzhp->zpool_old_config);
321
dzhp->zpool_old_config = dzhp->zpool_config;
322
dzhp->zpool_config = fnvlist_dup(szhp->zpool_config);
323
dzhp->zpool_config_size = szhp->zpool_config_size;
324
dzhp->zpool_state = szhp->zpool_state;
325
}
326
327
/*
328
* The following environment variables are undocumented
329
* and should be used for testing purposes only:
330
*
331
* __ZFS_POOL_EXCLUDE - don't iterate over the pools it lists
332
* __ZFS_POOL_RESTRICT - iterate only over the pools it lists
333
*
334
* This function returns B_TRUE if the pool should be skipped
335
* during iteration.
336
*/
337
boolean_t
338
zpool_skip_pool(const char *poolname)
339
{
340
static boolean_t initialized = B_FALSE;
341
static const char *exclude = NULL;
342
static const char *restricted = NULL;
343
344
const char *cur, *end;
345
int len;
346
int namelen = strlen(poolname);
347
348
if (!initialized) {
349
initialized = B_TRUE;
350
exclude = getenv("__ZFS_POOL_EXCLUDE");
351
restricted = getenv("__ZFS_POOL_RESTRICT");
352
}
353
354
if (exclude != NULL) {
355
cur = exclude;
356
do {
357
end = strchr(cur, ' ');
358
len = (NULL == end) ? strlen(cur) : (end - cur);
359
if (len == namelen && 0 == strncmp(cur, poolname, len))
360
return (B_TRUE);
361
cur += (len + 1);
362
} while (NULL != end);
363
}
364
365
if (NULL == restricted)
366
return (B_FALSE);
367
368
cur = restricted;
369
do {
370
end = strchr(cur, ' ');
371
len = (NULL == end) ? strlen(cur) : (end - cur);
372
373
if (len == namelen && 0 == strncmp(cur, poolname, len)) {
374
return (B_FALSE);
375
}
376
377
cur += (len + 1);
378
} while (NULL != end);
379
380
return (B_TRUE);
381
}
382
383
/*
384
* Iterate over all pools in the system.
385
*/
386
int
387
zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)
388
{
389
config_node_t *cn;
390
zpool_handle_t *zhp;
391
int ret;
392
393
/*
394
* If someone makes a recursive call to zpool_iter(), we want to avoid
395
* refreshing the namespace because that will invalidate the parent
396
* context. We allow recursive calls, but simply re-use the same
397
* namespace AVL tree.
398
*/
399
if (!hdl->libzfs_pool_iter && namespace_reload(hdl) != 0)
400
return (-1);
401
402
hdl->libzfs_pool_iter++;
403
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
404
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
405
406
if (zpool_skip_pool(cn->cn_name))
407
continue;
408
409
if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0) {
410
hdl->libzfs_pool_iter--;
411
return (-1);
412
}
413
414
if (zhp == NULL)
415
continue;
416
417
if ((ret = func(zhp, data)) != 0) {
418
hdl->libzfs_pool_iter--;
419
return (ret);
420
}
421
}
422
hdl->libzfs_pool_iter--;
423
424
return (0);
425
}
426
427
/*
428
* Iterate over root datasets, calling the given function for each. The zfs
429
* handle passed each time must be explicitly closed by the callback.
430
*/
431
int
432
zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)
433
{
434
config_node_t *cn;
435
zfs_handle_t *zhp;
436
int ret;
437
438
if (namespace_reload(hdl) != 0)
439
return (-1);
440
441
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
442
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
443
444
if (zpool_skip_pool(cn->cn_name))
445
continue;
446
447
if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)
448
continue;
449
450
if ((ret = func(zhp, data)) != 0)
451
return (ret);
452
}
453
454
return (0);
455
}
456
457