Print this page
4171 clean up spa_feature_*() interfaces
4172 implement extensible_dataset feature for use by other zpool features
Reviewed by: Max Grossman <max.grossman@delphix.com>
Reviewed by: Christopher Siden <christopher.siden@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/zhack/zhack.c
+++ new/usr/src/cmd/zhack/zhack.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2013 by Delphix. All rights reserved.
24 24 * Copyright (c) 2013 Steven Hartland. All rights reserved.
25 25 */
26 26
27 27 /*
28 28 * zhack is a debugging tool that can write changes to ZFS pool using libzpool
29 29 * for testing purposes. Altering pools with zhack is unsupported and may
30 30 * result in corrupted pools.
31 31 */
32 32
33 33 #include <stdio.h>
34 34 #include <stdlib.h>
35 35 #include <ctype.h>
36 36 #include <sys/zfs_context.h>
37 37 #include <sys/spa.h>
38 38 #include <sys/spa_impl.h>
39 39 #include <sys/dmu.h>
40 40 #include <sys/zap.h>
41 41 #include <sys/zfs_znode.h>
42 42 #include <sys/dsl_synctask.h>
43 43 #include <sys/vdev.h>
44 44 #include <sys/fs/zfs.h>
45 45 #include <sys/dmu_objset.h>
46 46 #include <sys/dsl_pool.h>
47 47 #include <sys/zio_checksum.h>
48 48 #include <sys/zio_compress.h>
49 49 #include <sys/zfeature.h>
50 50 #include <sys/dmu_tx.h>
51 51 #undef ZFS_MAXNAMELEN
52 52 #undef verify
53 53 #include <libzfs.h>
54 54
55 55 extern boolean_t zfeature_checks_disable;
56 56
57 57 const char cmdname[] = "zhack";
58 58 libzfs_handle_t *g_zfs;
59 59 static importargs_t g_importargs;
60 60 static char *g_pool;
61 61 static boolean_t g_readonly;
62 62
63 63 static void
64 64 usage(void)
65 65 {
66 66 (void) fprintf(stderr,
67 67 "Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
68 68 "where <subcommand> <args> is one of the following:\n"
69 69 "\n", cmdname);
70 70
71 71 (void) fprintf(stderr,
72 72 " feature stat <pool>\n"
73 73 " print information about enabled features\n"
74 74 " feature enable [-d desc] <pool> <feature>\n"
75 75 " add a new enabled feature to the pool\n"
76 76 " -d <desc> sets the feature's description\n"
77 77 " feature ref [-md] <pool> <feature>\n"
78 78 " change the refcount on the given feature\n"
79 79 " -d decrease instead of increase the refcount\n"
80 80 " -m add the feature to the label if increasing refcount\n"
81 81 "\n"
82 82 " <feature> : should be a feature guid\n");
83 83 exit(1);
84 84 }
85 85
86 86
87 87 static void
88 88 fatal(spa_t *spa, void *tag, const char *fmt, ...)
89 89 {
90 90 va_list ap;
91 91
92 92 if (spa != NULL) {
93 93 spa_close(spa, tag);
94 94 (void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
95 95 }
96 96
97 97 va_start(ap, fmt);
98 98 (void) fprintf(stderr, "%s: ", cmdname);
99 99 (void) vfprintf(stderr, fmt, ap);
100 100 va_end(ap);
101 101 (void) fprintf(stderr, "\n");
102 102
103 103 exit(1);
104 104 }
105 105
106 106 /* ARGSUSED */
107 107 static int
108 108 space_delta_cb(dmu_object_type_t bonustype, void *data,
109 109 uint64_t *userp, uint64_t *groupp)
110 110 {
111 111 /*
112 112 * Is it a valid type of object to track?
113 113 */
114 114 if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
115 115 return (ENOENT);
116 116 (void) fprintf(stderr, "modifying object that needs user accounting");
117 117 abort();
118 118 /* NOTREACHED */
119 119 }
120 120
121 121 /*
122 122 * Target is the dataset whose pool we want to open.
123 123 */
124 124 static void
125 125 import_pool(const char *target, boolean_t readonly)
126 126 {
127 127 nvlist_t *config;
128 128 nvlist_t *pools;
129 129 int error;
130 130 char *sepp;
131 131 spa_t *spa;
132 132 nvpair_t *elem;
133 133 nvlist_t *props;
134 134 const char *name;
135 135
136 136 kernel_init(readonly ? FREAD : (FREAD | FWRITE));
137 137 g_zfs = libzfs_init();
138 138 ASSERT(g_zfs != NULL);
139 139
140 140 dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
141 141
142 142 g_readonly = readonly;
143 143
144 144 /*
145 145 * If we only want readonly access, it's OK if we find
146 146 * a potentially-active (ie, imported into the kernel) pool from the
147 147 * default cachefile.
148 148 */
149 149 if (readonly && spa_open(target, &spa, FTAG) == 0) {
150 150 spa_close(spa, FTAG);
151 151 return;
152 152 }
153 153
154 154 g_importargs.unique = B_TRUE;
155 155 g_importargs.can_be_active = readonly;
156 156 g_pool = strdup(target);
157 157 if ((sepp = strpbrk(g_pool, "/@")) != NULL)
158 158 *sepp = '\0';
159 159 g_importargs.poolname = g_pool;
160 160 pools = zpool_search_import(g_zfs, &g_importargs);
161 161
162 162 if (nvlist_empty(pools)) {
163 163 if (!g_importargs.can_be_active) {
164 164 g_importargs.can_be_active = B_TRUE;
165 165 if (zpool_search_import(g_zfs, &g_importargs) != NULL ||
166 166 spa_open(target, &spa, FTAG) == 0) {
167 167 fatal(spa, FTAG, "cannot import '%s': pool is "
168 168 "active; run " "\"zpool export %s\" "
169 169 "first\n", g_pool, g_pool);
170 170 }
171 171 }
172 172
173 173 fatal(NULL, FTAG, "cannot import '%s': no such pool "
174 174 "available\n", g_pool);
175 175 }
176 176
177 177 elem = nvlist_next_nvpair(pools, NULL);
178 178 name = nvpair_name(elem);
179 179 verify(nvpair_value_nvlist(elem, &config) == 0);
180 180
181 181 props = NULL;
182 182 if (readonly) {
183 183 verify(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
184 184 verify(nvlist_add_uint64(props,
185 185 zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
186 186 }
187 187
188 188 zfeature_checks_disable = B_TRUE;
189 189 error = spa_import(name, config, props, ZFS_IMPORT_NORMAL);
190 190 zfeature_checks_disable = B_FALSE;
191 191 if (error == EEXIST)
192 192 error = 0;
193 193
194 194 if (error)
195 195 fatal(NULL, FTAG, "can't import '%s': %s", name,
196 196 strerror(error));
197 197 }
198 198
199 199 static void
200 200 zhack_spa_open(const char *target, boolean_t readonly, void *tag, spa_t **spa)
201 201 {
202 202 int err;
203 203
204 204 import_pool(target, readonly);
205 205
206 206 zfeature_checks_disable = B_TRUE;
207 207 err = spa_open(target, spa, tag);
208 208 zfeature_checks_disable = B_FALSE;
209 209
210 210 if (err != 0)
211 211 fatal(*spa, FTAG, "cannot open '%s': %s", target,
212 212 strerror(err));
213 213 if (spa_version(*spa) < SPA_VERSION_FEATURES) {
214 214 fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
215 215 target, (int)spa_version(*spa));
216 216 }
217 217 }
218 218
219 219 static void
220 220 dump_obj(objset_t *os, uint64_t obj, const char *name)
221 221 {
222 222 zap_cursor_t zc;
223 223 zap_attribute_t za;
224 224
225 225 (void) printf("%s_obj:\n", name);
226 226
227 227 for (zap_cursor_init(&zc, os, obj);
228 228 zap_cursor_retrieve(&zc, &za) == 0;
229 229 zap_cursor_advance(&zc)) {
230 230 if (za.za_integer_length == 8) {
231 231 ASSERT(za.za_num_integers == 1);
232 232 (void) printf("\t%s = %llu\n",
233 233 za.za_name, (u_longlong_t)za.za_first_integer);
234 234 } else {
235 235 ASSERT(za.za_integer_length == 1);
236 236 char val[1024];
237 237 VERIFY(zap_lookup(os, obj, za.za_name,
238 238 1, sizeof (val), val) == 0);
239 239 (void) printf("\t%s = %s\n", za.za_name, val);
240 240 }
241 241 }
242 242 zap_cursor_fini(&zc);
243 243 }
244 244
245 245 static void
246 246 dump_mos(spa_t *spa)
247 247 {
248 248 nvlist_t *nv = spa->spa_label_features;
249 249
250 250 (void) printf("label config:\n");
251 251 for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
252 252 pair != NULL;
253 253 pair = nvlist_next_nvpair(nv, pair)) {
254 254 (void) printf("\t%s\n", nvpair_name(pair));
255 255 }
256 256 }
257 257
258 258 static void
259 259 zhack_do_feature_stat(int argc, char **argv)
260 260 {
261 261 spa_t *spa;
262 262 objset_t *os;
263 263 char *target;
264 264
265 265 argc--;
266 266 argv++;
267 267
268 268 if (argc < 1) {
269 269 (void) fprintf(stderr, "error: missing pool name\n");
270 270 usage();
271 271 }
272 272 target = argv[0];
273 273
274 274 zhack_spa_open(target, B_TRUE, FTAG, &spa);
275 275 os = spa->spa_meta_objset;
↓ open down ↓ |
275 lines elided |
↑ open up ↑ |
276 276
277 277 dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
278 278 dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
279 279 dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
280 280 dump_mos(spa);
281 281
282 282 spa_close(spa, FTAG);
283 283 }
284 284
285 285 static void
286 -feature_enable_sync(void *arg, dmu_tx_t *tx)
286 +zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
287 287 {
288 288 spa_t *spa = dmu_tx_pool(tx)->dp_spa;
289 289 zfeature_info_t *feature = arg;
290 290
291 - spa_feature_enable(spa, feature, tx);
291 + feature_enable_sync(spa, feature, tx);
292 +
292 293 spa_history_log_internal(spa, "zhack enable feature", tx,
293 294 "guid=%s can_readonly=%u",
294 295 feature->fi_guid, feature->fi_can_readonly);
295 296 }
296 297
297 298 static void
298 299 zhack_do_feature_enable(int argc, char **argv)
299 300 {
300 301 char c;
301 302 char *desc, *target;
302 303 spa_t *spa;
303 304 objset_t *mos;
304 305 zfeature_info_t feature;
305 - zfeature_info_t *nodeps[] = { NULL };
306 + spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
306 307
307 308 /*
308 309 * Features are not added to the pool's label until their refcounts
309 310 * are incremented, so fi_mos can just be left as false for now.
310 311 */
311 312 desc = NULL;
312 313 feature.fi_uname = "zhack";
313 314 feature.fi_mos = B_FALSE;
314 315 feature.fi_can_readonly = B_FALSE;
315 316 feature.fi_depends = nodeps;
316 317
317 318 optind = 1;
318 319 while ((c = getopt(argc, argv, "rmd:")) != -1) {
319 320 switch (c) {
320 321 case 'r':
321 322 feature.fi_can_readonly = B_TRUE;
322 323 break;
323 324 case 'd':
324 325 desc = strdup(optarg);
325 326 break;
326 327 default:
327 328 usage();
328 329 break;
329 330 }
330 331 }
331 332
332 333 if (desc == NULL)
333 334 desc = strdup("zhack injected");
334 335 feature.fi_desc = desc;
335 336
336 337 argc -= optind;
337 338 argv += optind;
338 339
339 340 if (argc < 2) {
340 341 (void) fprintf(stderr, "error: missing feature or pool name\n");
341 342 usage();
↓ open down ↓ |
26 lines elided |
↑ open up ↑ |
342 343 }
343 344 target = argv[0];
344 345 feature.fi_guid = argv[1];
345 346
346 347 if (!zfeature_is_valid_guid(feature.fi_guid))
347 348 fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
348 349
349 350 zhack_spa_open(target, B_FALSE, FTAG, &spa);
350 351 mos = spa->spa_meta_objset;
351 352
352 - if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
353 + if (zfeature_is_supported(feature.fi_guid))
353 354 fatal(spa, FTAG, "'%s' is a real feature, will not enable");
354 355 if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
355 356 fatal(spa, FTAG, "feature already enabled: %s",
356 357 feature.fi_guid);
357 358
358 359 VERIFY0(dsl_sync_task(spa_name(spa), NULL,
359 - feature_enable_sync, &feature, 5));
360 + zhack_feature_enable_sync, &feature, 5));
360 361
361 362 spa_close(spa, FTAG);
362 363
363 364 free(desc);
364 365 }
365 366
366 367 static void
367 368 feature_incr_sync(void *arg, dmu_tx_t *tx)
368 369 {
369 370 spa_t *spa = dmu_tx_pool(tx)->dp_spa;
370 371 zfeature_info_t *feature = arg;
372 + uint64_t refcount;
371 373
372 - spa_feature_incr(spa, feature, tx);
374 + VERIFY0(feature_get_refcount(spa, feature, &refcount));
375 + feature_sync(spa, feature, refcount + 1, tx);
373 376 spa_history_log_internal(spa, "zhack feature incr", tx,
374 377 "guid=%s", feature->fi_guid);
375 378 }
376 379
377 380 static void
378 381 feature_decr_sync(void *arg, dmu_tx_t *tx)
379 382 {
380 383 spa_t *spa = dmu_tx_pool(tx)->dp_spa;
381 384 zfeature_info_t *feature = arg;
385 + uint64_t refcount;
382 386
383 - spa_feature_decr(spa, feature, tx);
387 + VERIFY0(feature_get_refcount(spa, feature, &refcount));
388 + feature_sync(spa, feature, refcount - 1, tx);
384 389 spa_history_log_internal(spa, "zhack feature decr", tx,
385 390 "guid=%s", feature->fi_guid);
386 391 }
387 392
388 393 static void
389 394 zhack_do_feature_ref(int argc, char **argv)
390 395 {
391 396 char c;
392 397 char *target;
393 398 boolean_t decr = B_FALSE;
394 399 spa_t *spa;
395 400 objset_t *mos;
396 401 zfeature_info_t feature;
397 - zfeature_info_t *nodeps[] = { NULL };
402 + spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
398 403
399 404 /*
400 405 * fi_desc does not matter here because it was written to disk
401 406 * when the feature was enabled, but we need to properly set the
402 407 * feature for read or write based on the information we read off
403 408 * disk later.
404 409 */
405 410 feature.fi_uname = "zhack";
406 411 feature.fi_mos = B_FALSE;
407 412 feature.fi_desc = NULL;
408 413 feature.fi_depends = nodeps;
409 414
410 415 optind = 1;
411 416 while ((c = getopt(argc, argv, "md")) != -1) {
412 417 switch (c) {
413 418 case 'm':
414 419 feature.fi_mos = B_TRUE;
415 420 break;
416 421 case 'd':
417 422 decr = B_TRUE;
418 423 break;
419 424 default:
420 425 usage();
421 426 break;
422 427 }
423 428 }
424 429 argc -= optind;
425 430 argv += optind;
426 431
427 432 if (argc < 2) {
428 433 (void) fprintf(stderr, "error: missing feature or pool name\n");
429 434 usage();
↓ open down ↓ |
22 lines elided |
↑ open up ↑ |
430 435 }
431 436 target = argv[0];
432 437 feature.fi_guid = argv[1];
433 438
434 439 if (!zfeature_is_valid_guid(feature.fi_guid))
435 440 fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
436 441
437 442 zhack_spa_open(target, B_FALSE, FTAG, &spa);
438 443 mos = spa->spa_meta_objset;
439 444
440 - if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
441 - fatal(spa, FTAG, "'%s' is a real feature, will not change "
442 - "refcount");
445 + if (zfeature_is_supported(feature.fi_guid)) {
446 + fatal(spa, FTAG,
447 + "'%s' is a real feature, will not change refcount");
448 + }
443 449
444 450 if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
445 451 feature.fi_guid)) {
446 452 feature.fi_can_readonly = B_FALSE;
447 453 } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
448 454 feature.fi_guid)) {
449 455 feature.fi_can_readonly = B_TRUE;
450 456 } else {
451 457 fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
452 458 }
453 459
454 - if (decr && !spa_feature_is_active(spa, &feature))
455 - fatal(spa, FTAG, "feature refcount already 0: %s",
456 - feature.fi_guid);
460 + if (decr) {
461 + uint64_t count;
462 + if (feature_get_refcount(spa, &feature, &count) == 0 &&
463 + count != 0) {
464 + fatal(spa, FTAG, "feature refcount already 0: %s",
465 + feature.fi_guid);
466 + }
467 + }
457 468
458 469 VERIFY0(dsl_sync_task(spa_name(spa), NULL,
459 470 decr ? feature_decr_sync : feature_incr_sync, &feature, 5));
460 471
461 472 spa_close(spa, FTAG);
462 473 }
463 474
464 475 static int
465 476 zhack_do_feature(int argc, char **argv)
466 477 {
467 478 char *subcommand;
468 479
469 480 argc--;
470 481 argv++;
471 482 if (argc == 0) {
472 483 (void) fprintf(stderr,
473 484 "error: no feature operation specified\n");
474 485 usage();
475 486 }
476 487
477 488 subcommand = argv[0];
478 489 if (strcmp(subcommand, "stat") == 0) {
479 490 zhack_do_feature_stat(argc, argv);
480 491 } else if (strcmp(subcommand, "enable") == 0) {
481 492 zhack_do_feature_enable(argc, argv);
482 493 } else if (strcmp(subcommand, "ref") == 0) {
483 494 zhack_do_feature_ref(argc, argv);
484 495 } else {
485 496 (void) fprintf(stderr, "error: unknown subcommand: %s\n",
486 497 subcommand);
487 498 usage();
488 499 }
489 500
490 501 return (0);
491 502 }
492 503
493 504 #define MAX_NUM_PATHS 1024
494 505
495 506 int
496 507 main(int argc, char **argv)
497 508 {
498 509 extern void zfs_prop_init(void);
499 510
500 511 char *path[MAX_NUM_PATHS];
501 512 const char *subcommand;
502 513 int rv = 0;
503 514 char c;
504 515
505 516 g_importargs.path = path;
506 517
507 518 dprintf_setup(&argc, argv);
508 519 zfs_prop_init();
509 520
510 521 while ((c = getopt(argc, argv, "c:d:")) != -1) {
511 522 switch (c) {
512 523 case 'c':
513 524 g_importargs.cachefile = optarg;
514 525 break;
515 526 case 'd':
516 527 assert(g_importargs.paths < MAX_NUM_PATHS);
517 528 g_importargs.path[g_importargs.paths++] = optarg;
518 529 break;
519 530 default:
520 531 usage();
521 532 break;
522 533 }
523 534 }
524 535
525 536 argc -= optind;
526 537 argv += optind;
527 538 optind = 1;
528 539
529 540 if (argc == 0) {
530 541 (void) fprintf(stderr, "error: no command specified\n");
531 542 usage();
532 543 }
533 544
534 545 subcommand = argv[0];
535 546
536 547 if (strcmp(subcommand, "feature") == 0) {
537 548 rv = zhack_do_feature(argc, argv);
538 549 } else {
539 550 (void) fprintf(stderr, "error: unknown subcommand: %s\n",
540 551 subcommand);
541 552 usage();
542 553 }
543 554
544 555 if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
545 556 fatal(NULL, FTAG, "pool export failed; "
546 557 "changes may not be committed to disk\n");
547 558 }
548 559
549 560 libzfs_fini(g_zfs);
550 561 kernel_fini();
551 562
552 563 return (rv);
553 564 }
↓ open down ↓ |
87 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX