Changeset View
Changeset View
Standalone View
Standalone View
sys/contrib/openzfs/cmd/zdb/zdb.c
Context not available. | |||||
* | * | ||||
* [1] Portions of this software were developed by Allan Jude | * [1] Portions of this software were developed by Allan Jude | ||||
* under sponsorship from the FreeBSD Foundation. | * under sponsorship from the FreeBSD Foundation. | ||||
* Copyright (c) 2021 Allan Jude | |||||
*/ | */ | ||||
#include <stdio.h> | #include <stdio.h> | ||||
Context not available. | |||||
"\t%s -m [-AFLPX] [-e [-V] [-p <path> ...]] [-t <txg>] " | "\t%s -m [-AFLPX] [-e [-V] [-p <path> ...]] [-t <txg>] " | ||||
"[-U <cache>]\n\t\t<poolname> [<vdev> [<metaslab> ...]]\n" | "[-U <cache>]\n\t\t<poolname> [<vdev> [<metaslab> ...]]\n" | ||||
"\t%s -O <dataset> <path>\n" | "\t%s -O <dataset> <path>\n" | ||||
"\t%s -r <dataset> <path> <destination>\n" | |||||
"\t%s -R [-A] [-e [-V] [-p <path> ...]] [-U <cache>]\n" | "\t%s -R [-A] [-e [-V] [-p <path> ...]] [-U <cache>]\n" | ||||
"\t\t<poolname> <vdev>:<offset>:<size>[:<flags>]\n" | "\t\t<poolname> <vdev>:<offset>:<size>[:<flags>]\n" | ||||
"\t%s -E [-A] word0:word1:...:word15\n" | "\t%s -E [-A] word0:word1:...:word15\n" | ||||
"\t%s -S [-AP] [-e [-V] [-p <path> ...]] [-U <cache>] " | "\t%s -S [-AP] [-e [-V] [-p <path> ...]] [-U <cache>] " | ||||
"<poolname>\n\n", | "<poolname>\n\n", | ||||
cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, | cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, | ||||
cmdname, cmdname, cmdname); | cmdname, cmdname, cmdname, cmdname); | ||||
(void) fprintf(stderr, " Dataset name must include at least one " | (void) fprintf(stderr, " Dataset name must include at least one " | ||||
"separator character '/' or '@'\n"); | "separator character '/' or '@'\n"); | ||||
Context not available. | |||||
(void) fprintf(stderr, " -m metaslabs\n"); | (void) fprintf(stderr, " -m metaslabs\n"); | ||||
(void) fprintf(stderr, " -M metaslab groups\n"); | (void) fprintf(stderr, " -M metaslab groups\n"); | ||||
(void) fprintf(stderr, " -O perform object lookups by path\n"); | (void) fprintf(stderr, " -O perform object lookups by path\n"); | ||||
(void) fprintf(stderr, " -r copy an object by path to file\n"); | |||||
(void) fprintf(stderr, " -R read and display block from a " | (void) fprintf(stderr, " -R read and display block from a " | ||||
"device\n"); | "device\n"); | ||||
(void) fprintf(stderr, " -s report stats on zdb's I/O\n"); | (void) fprintf(stderr, " -s report stats on zdb's I/O\n"); | ||||
Context not available. | |||||
* for the last one. | * for the last one. | ||||
*/ | */ | ||||
static int | static int | ||||
dump_path_impl(objset_t *os, uint64_t obj, char *name) | dump_path_impl(objset_t *os, uint64_t obj, char *name, uint64_t *retobj) | ||||
{ | { | ||||
int err; | int err; | ||||
boolean_t header = B_TRUE; | boolean_t header = B_TRUE; | ||||
Context not available. | |||||
switch (doi.doi_type) { | switch (doi.doi_type) { | ||||
case DMU_OT_DIRECTORY_CONTENTS: | case DMU_OT_DIRECTORY_CONTENTS: | ||||
if (s != NULL && *(s + 1) != '\0') | if (s != NULL && *(s + 1) != '\0') | ||||
return (dump_path_impl(os, child_obj, s + 1)); | return (dump_path_impl(os, child_obj, s + 1, retobj)); | ||||
/*FALLTHROUGH*/ | /*FALLTHROUGH*/ | ||||
case DMU_OT_PLAIN_FILE_CONTENTS: | case DMU_OT_PLAIN_FILE_CONTENTS: | ||||
dump_object(os, child_obj, dump_opt['v'], &header, NULL, 0); | if (retobj != NULL) { | ||||
*retobj = child_obj; | |||||
} else { | |||||
dump_object(os, child_obj, dump_opt['v'], &header, | |||||
NULL, 0); | |||||
} | |||||
return (0); | return (0); | ||||
default: | default: | ||||
(void) fprintf(stderr, "object %llu has non-file/directory " | (void) fprintf(stderr, "object %llu has non-file/directory " | ||||
Context not available. | |||||
* Dump the blocks for the object specified by path inside the dataset. | * Dump the blocks for the object specified by path inside the dataset. | ||||
*/ | */ | ||||
static int | static int | ||||
dump_path(char *ds, char *path) | dump_path(char *ds, char *path, uint64_t *retobj) | ||||
{ | { | ||||
int err; | int err; | ||||
objset_t *os; | objset_t *os; | ||||
Context not available. | |||||
(void) snprintf(curpath, sizeof (curpath), "dataset=%s path=/", ds); | (void) snprintf(curpath, sizeof (curpath), "dataset=%s path=/", ds); | ||||
err = dump_path_impl(os, root_obj, path); | err = dump_path_impl(os, root_obj, path, retobj); | ||||
close_objset(os, FTAG); | close_objset(os, FTAG); | ||||
return (err); | return (err); | ||||
} | } | ||||
static int | |||||
zdb_copy_object(objset_t *os, uint64_t srcobj, char *destfile) | |||||
{ | |||||
int err = 0; | |||||
uint64_t size, readsize, oursize, offset; | |||||
ssize_t writesize; | |||||
sa_handle_t *hdl; | |||||
(void) printf("Copying object %" PRIu64 " to file %s\n", srcobj, | |||||
destfile); | |||||
VERIFY3P(os, ==, sa_os); | |||||
if ((err = sa_handle_get(os, srcobj, NULL, SA_HDL_PRIVATE, &hdl))) { | |||||
(void) printf("Failed to get handle for SA znode\n"); | |||||
return (err); | |||||
} | |||||
if ((err = sa_lookup(hdl, sa_attr_table[ZPL_SIZE], &size, 8))) { | |||||
(void) sa_handle_destroy(hdl); | |||||
return (err); | |||||
} | |||||
(void) sa_handle_destroy(hdl); | |||||
(void) printf("Object %" PRIu64 " is %" PRIu64 " bytes\n", srcobj, | |||||
size); | |||||
if (size == 0) { | |||||
return (EINVAL); | |||||
} | |||||
int fd = open(destfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); | |||||
/* | |||||
* We cap the size at 1 mebibyte here to prevent | |||||
* allocation failures and nigh-infinite printing if the | |||||
* object is extremely large. | |||||
*/ | |||||
oursize = MIN(size, 1 << 20); | |||||
offset = 0; | |||||
char *buf = kmem_alloc(oursize, KM_NOSLEEP); | |||||
if (buf == NULL) { | |||||
return (ENOMEM); | |||||
} | |||||
while (offset < size) { | |||||
readsize = MIN(size - offset, 1 << 20); | |||||
err = dmu_read(os, srcobj, offset, readsize, buf, 0); | |||||
if (err != 0) { | |||||
(void) printf("got error %u from dmu_read\n", err); | |||||
kmem_free(buf, oursize); | |||||
return (err); | |||||
} | |||||
if (dump_opt['v'] > 3) { | |||||
(void) printf("Read offset=%" PRIu64 " size=%" PRIu64 | |||||
" error=%d\n", offset, readsize, err); | |||||
} | |||||
writesize = write(fd, buf, readsize); | |||||
if (writesize < 0) { | |||||
err = errno; | |||||
break; | |||||
} else if (writesize != readsize) { | |||||
/* Incomplete write */ | |||||
(void) fprintf(stderr, "Short write, only wrote %llu of" | |||||
" %" PRIu64 " bytes, exiting...\n", | |||||
(u_longlong_t)writesize, readsize); | |||||
break; | |||||
} | |||||
offset += readsize; | |||||
} | |||||
(void) close(fd); | |||||
if (buf != NULL) | |||||
kmem_free(buf, oursize); | |||||
return (err); | |||||
} | |||||
static int | static int | ||||
dump_label(const char *dev) | dump_label(const char *dev) | ||||
{ | { | ||||
Context not available. | |||||
* metaslabs. We want to set them up for | * metaslabs. We want to set them up for | ||||
* zio_claim(). | * zio_claim(). | ||||
*/ | */ | ||||
vdev_metaslab_group_create(vd); | |||||
VERIFY0(vdev_metaslab_init(vd, 0)); | VERIFY0(vdev_metaslab_init(vd, 0)); | ||||
vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; | vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; | ||||
Context not available. | |||||
*/ | */ | ||||
spa->spa_normal_class->mc_ops = &zdb_metaslab_ops; | spa->spa_normal_class->mc_ops = &zdb_metaslab_ops; | ||||
spa->spa_log_class->mc_ops = &zdb_metaslab_ops; | spa->spa_log_class->mc_ops = &zdb_metaslab_ops; | ||||
spa->spa_embedded_log_class->mc_ops = &zdb_metaslab_ops; | |||||
zcb->zcb_vd_obsolete_counts = | zcb->zcb_vd_obsolete_counts = | ||||
umem_zalloc(rvd->vdev_children * sizeof (uint32_t *), | umem_zalloc(rvd->vdev_children * sizeof (uint32_t *), | ||||
Context not available. | |||||
vdev_t *rvd = spa->spa_root_vdev; | vdev_t *rvd = spa->spa_root_vdev; | ||||
for (unsigned c = 0; c < rvd->vdev_children; c++) { | for (unsigned c = 0; c < rvd->vdev_children; c++) { | ||||
vdev_t *vd = rvd->vdev_child[c]; | vdev_t *vd = rvd->vdev_child[c]; | ||||
metaslab_group_t *mg __maybe_unused = vd->vdev_mg; | |||||
if (zcb->zcb_vd_obsolete_counts[c] != NULL) { | if (zcb->zcb_vd_obsolete_counts[c] != NULL) { | ||||
leaks |= zdb_check_for_obsolete_leaks(vd, zcb); | leaks |= zdb_check_for_obsolete_leaks(vd, zcb); | ||||
Context not available. | |||||
for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { | for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { | ||||
metaslab_t *msp = vd->vdev_ms[m]; | metaslab_t *msp = vd->vdev_ms[m]; | ||||
ASSERT3P(mg, ==, msp->ms_group); | ASSERT3P(msp->ms_group, ==, (msp->ms_group->mg_class == | ||||
spa_embedded_log_class(spa)) ? | |||||
vd->vdev_log_mg : vd->vdev_mg); | |||||
/* | /* | ||||
* ms_allocatable has been overloaded | * ms_allocatable has been overloaded | ||||
Context not available. | |||||
zcb.zcb_totalasize = metaslab_class_get_alloc(spa_normal_class(spa)); | zcb.zcb_totalasize = metaslab_class_get_alloc(spa_normal_class(spa)); | ||||
zcb.zcb_totalasize += metaslab_class_get_alloc(spa_special_class(spa)); | zcb.zcb_totalasize += metaslab_class_get_alloc(spa_special_class(spa)); | ||||
zcb.zcb_totalasize += metaslab_class_get_alloc(spa_dedup_class(spa)); | zcb.zcb_totalasize += metaslab_class_get_alloc(spa_dedup_class(spa)); | ||||
zcb.zcb_totalasize += | |||||
metaslab_class_get_alloc(spa_embedded_log_class(spa)); | |||||
zcb.zcb_start = zcb.zcb_lastprint = gethrtime(); | zcb.zcb_start = zcb.zcb_lastprint = gethrtime(); | ||||
err = traverse_pool(spa, 0, flags, zdb_blkptr_cb, &zcb); | err = traverse_pool(spa, 0, flags, zdb_blkptr_cb, &zcb); | ||||
Context not available. | |||||
total_alloc = norm_alloc + | total_alloc = norm_alloc + | ||||
metaslab_class_get_alloc(spa_log_class(spa)) + | metaslab_class_get_alloc(spa_log_class(spa)) + | ||||
metaslab_class_get_alloc(spa_embedded_log_class(spa)) + | |||||
metaslab_class_get_alloc(spa_special_class(spa)) + | metaslab_class_get_alloc(spa_special_class(spa)) + | ||||
metaslab_class_get_alloc(spa_dedup_class(spa)) + | metaslab_class_get_alloc(spa_dedup_class(spa)) + | ||||
get_unflushed_alloc_space(spa); | get_unflushed_alloc_space(spa); | ||||
Context not available. | |||||
100.0 * alloc / space); | 100.0 * alloc / space); | ||||
} | } | ||||
if (spa_embedded_log_class(spa)->mc_allocator[0].mca_rotor != NULL) { | |||||
uint64_t alloc = metaslab_class_get_alloc( | |||||
spa_embedded_log_class(spa)); | |||||
uint64_t space = metaslab_class_get_space( | |||||
spa_embedded_log_class(spa)); | |||||
(void) printf("\t%-16s %14llu used: %5.2f%%\n", | |||||
"Embedded log class", (u_longlong_t)alloc, | |||||
100.0 * alloc / space); | |||||
} | |||||
for (i = 0; i < NUM_BP_EMBEDDED_TYPES; i++) { | for (i = 0; i < NUM_BP_EMBEDDED_TYPES; i++) { | ||||
if (zcb.zcb_embedded_blocks[i] == 0) | if (zcb.zcb_embedded_blocks[i] == 0) | ||||
continue; | continue; | ||||
Context not available. | |||||
nvlist_t *policy = NULL; | nvlist_t *policy = NULL; | ||||
uint64_t max_txg = UINT64_MAX; | uint64_t max_txg = UINT64_MAX; | ||||
int64_t objset_id = -1; | int64_t objset_id = -1; | ||||
uint64_t object; | |||||
int flags = ZFS_IMPORT_MISSING_LOG; | int flags = ZFS_IMPORT_MISSING_LOG; | ||||
int rewind = ZPOOL_NEVER_REWIND; | int rewind = ZPOOL_NEVER_REWIND; | ||||
char *spa_config_path_env, *objset_str; | char *spa_config_path_env, *objset_str; | ||||
Context not available. | |||||
zfs_btree_verify_intensity = 3; | zfs_btree_verify_intensity = 3; | ||||
while ((c = getopt(argc, argv, | while ((c = getopt(argc, argv, | ||||
"AbcCdDeEFGhiI:klLmMo:Op:PqRsSt:uU:vVx:XYyZ")) != -1) { | "AbcCdDeEFGhiI:klLmMo:Op:PqrRsSt:uU:vVx:XYyZ")) != -1) { | ||||
switch (c) { | switch (c) { | ||||
case 'b': | case 'b': | ||||
case 'c': | case 'c': | ||||
Context not available. | |||||
case 'm': | case 'm': | ||||
case 'M': | case 'M': | ||||
case 'O': | case 'O': | ||||
case 'r': | |||||
case 'R': | case 'R': | ||||
case 's': | case 's': | ||||
case 'S': | case 'S': | ||||
Context not available. | |||||
(void) fprintf(stderr, "-p option requires use of -e\n"); | (void) fprintf(stderr, "-p option requires use of -e\n"); | ||||
usage(); | usage(); | ||||
} | } | ||||
if (dump_opt['d']) { | if (dump_opt['d'] || dump_opt['r']) { | ||||
/* <pool>[/<dataset | objset id> is accepted */ | /* <pool>[/<dataset | objset id> is accepted */ | ||||
if (argv[2] && (objset_str = strchr(argv[2], '/')) != NULL && | if (argv[2] && (objset_str = strchr(argv[2], '/')) != NULL && | ||||
objset_str++ != NULL) { | objset_str++ != NULL) { | ||||
Context not available. | |||||
verbose = MAX(verbose, 1); | verbose = MAX(verbose, 1); | ||||
for (c = 0; c < 256; c++) { | for (c = 0; c < 256; c++) { | ||||
if (dump_all && strchr("AeEFklLOPRSXy", c) == NULL) | if (dump_all && strchr("AeEFklLOPrRSXy", c) == NULL) | ||||
dump_opt[c] = 1; | dump_opt[c] = 1; | ||||
if (dump_opt[c]) | if (dump_opt[c]) | ||||
dump_opt[c] += verbose; | dump_opt[c] += verbose; | ||||
Context not available. | |||||
if (argc != 2) | if (argc != 2) | ||||
usage(); | usage(); | ||||
dump_opt['v'] = verbose + 3; | dump_opt['v'] = verbose + 3; | ||||
return (dump_path(argv[0], argv[1])); | return (dump_path(argv[0], argv[1], NULL)); | ||||
} | |||||
if (dump_opt['r']) { | |||||
if (argc != 3) | |||||
usage(); | |||||
dump_opt['v'] = verbose; | |||||
error = dump_path(argv[0], argv[1], &object); | |||||
} | } | ||||
if (dump_opt['X'] || dump_opt['F']) | if (dump_opt['X'] || dump_opt['F']) | ||||
Context not available. | |||||
argv++; | argv++; | ||||
argc--; | argc--; | ||||
if (!dump_opt['R']) { | if (dump_opt['r']) { | ||||
error = zdb_copy_object(os, object, argv[1]); | |||||
} else if (!dump_opt['R']) { | |||||
flagbits['d'] = ZOR_FLAG_DIRECTORY; | flagbits['d'] = ZOR_FLAG_DIRECTORY; | ||||
flagbits['f'] = ZOR_FLAG_PLAIN_FILE; | flagbits['f'] = ZOR_FLAG_PLAIN_FILE; | ||||
flagbits['m'] = ZOR_FLAG_SPACE_MAP; | flagbits['m'] = ZOR_FLAG_SPACE_MAP; | ||||
Context not available. |