static boolean_t nvlist_equal(nvlist_t *nvla, nvlist_t *nvlb) { if (fnvlist_num_pairs(nvla) != fnvlist_num_pairs(nvlb)) return (B_FALSE); /* * The nvlists have the same number of pairs and keys are unique, so * if every key in A is also in B and assigned to the same value, the * lists are identical. */ for (nvpair_t *pair = nvlist_next_nvpair(nvla, NULL); pair != NULL; pair = nvlist_next_nvpair(nvla, pair)) { char *key = nvpair_name(pair); if (!nvlist_exists(nvlb, key)) return (B_FALSE); if (nvpair_type(pair) != nvpair_type(fnvlist_lookup_nvpair(nvlb, key))) return (B_FALSE); switch (nvpair_type(pair)) { case DATA_TYPE_BOOLEAN_VALUE: if (fnvpair_value_boolean_value(pair) != fnvlist_lookup_boolean_value(nvlb, key)) { return (B_FALSE); } break; case DATA_TYPE_STRING: if (strcmp(fnvpair_value_string(pair), fnvlist_lookup_string(nvlb, key))) { return (B_FALSE); } break; case DATA_TYPE_INT64: if (fnvpair_value_int64(pair) != fnvlist_lookup_int64(nvlb, key)) { return (B_FALSE); } break; case DATA_TYPE_NVLIST: if (!nvlist_equal(fnvpair_value_nvlist(pair), fnvlist_lookup_nvlist(nvlb, key))) { return (B_FALSE); } break; default: (void) printf("Unexpected type for nvlist_equal\n"); return (B_FALSE); } } return (B_TRUE); }
static void test(const char *testname, boolean_t expect_success, boolean_t expect_match) { char *progstr = "input = ...; return {output=input}"; nvlist_t *outnvl; (void) printf("\nrunning test '%s'; input:\n", testname); dump_nvlist(nvl, 4); int err = lzc_channel_program(pool, progstr, 10 * 1000 * 1000, 10 * 1024 * 1024, nvl, &outnvl); (void) printf("lzc_channel_program returned %u\n", err); dump_nvlist(outnvl, 5); if (err == 0 && expect_match) { /* * Verify that outnvl is the same as input nvl, if we expect * them to be. The input and output will never match if the * input contains an array (since arrays are converted to lua * tables), so this is only asserted for some test cases. */ nvlist_t *real_outnvl = fnvlist_lookup_nvlist(outnvl, "return"); real_outnvl = fnvlist_lookup_nvlist(real_outnvl, "output"); if (!nvlist_equal(nvl, real_outnvl)) { unexpected_failures = B_TRUE; (void) printf("unexpected input/output mismatch for " "case: %s\n", testname); } } if (err != 0 && expect_success) { unexpected_failures = B_TRUE; (void) printf("unexpected FAIL of case: %s\n", testname); } fnvlist_free(nvl); nvl = fnvlist_alloc(); }
/* * Active pool health status. * * To determine the status for a pool, we make several passes over the config, * picking the most egregious error we find. In order of importance, we do the * following: * * - Check for a complete and valid configuration * - Look for any faulted or missing devices in a non-replicated config * - Check for any data errors * - Check for any faulted or missing devices in a replicated config * - Look for any devices showing errors * - Check for any resilvering devices * * There can obviously be multiple errors within a single pool, so this routine * only picks the most damaging of all the current errors to report. */ static zpool_status_t check_status(nvlist_t *config, boolean_t isimport) { nvlist_t *nvroot; vdev_stat_t *vs; pool_scan_stat_t *ps = NULL; uint_t vsc, psc; uint64_t nerr; uint64_t version; uint64_t stateval; uint64_t suspended; uint64_t hostid = 0; verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, &stateval) == 0); /* * Currently resilvering a vdev */ (void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc); if (ps && ps->pss_func == POOL_SCAN_RESILVER && ps->pss_state == DSS_SCANNING) return (ZPOOL_STATUS_RESILVERING); /* * Pool last accessed by another system. */ (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid); if (hostid != 0 && (unsigned long)hostid != gethostid() && stateval == POOL_STATE_ACTIVE) return (ZPOOL_STATUS_HOSTID_MISMATCH); /* * Newer on-disk version. */ if (vs->vs_state == VDEV_STATE_CANT_OPEN && vs->vs_aux == VDEV_AUX_VERSION_NEWER) return (ZPOOL_STATUS_VERSION_NEWER); /* * Unsupported feature(s). */ if (vs->vs_state == VDEV_STATE_CANT_OPEN && vs->vs_aux == VDEV_AUX_UNSUP_FEAT) { nvlist_t *nvinfo; verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0); if (nvlist_exists(nvinfo, ZPOOL_CONFIG_CAN_RDONLY)) return (ZPOOL_STATUS_UNSUP_FEAT_WRITE); return (ZPOOL_STATUS_UNSUP_FEAT_READ); } /* * Check that the config is complete. */ if (vs->vs_state == VDEV_STATE_CANT_OPEN && vs->vs_aux == VDEV_AUX_BAD_GUID_SUM) return (ZPOOL_STATUS_BAD_GUID_SUM); /* * Check whether the pool has suspended due to failed I/O. */ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED, &suspended) == 0) { if (suspended == ZIO_FAILURE_MODE_CONTINUE) return (ZPOOL_STATUS_IO_FAILURE_CONTINUE); return (ZPOOL_STATUS_IO_FAILURE_WAIT); } /* * Could not read a log. */ if (vs->vs_state == VDEV_STATE_CANT_OPEN && vs->vs_aux == VDEV_AUX_BAD_LOG) { return (ZPOOL_STATUS_BAD_LOG); } /* * Bad devices in non-replicated config. */ if (vs->vs_state == VDEV_STATE_CANT_OPEN && find_vdev_problem(nvroot, vdev_faulted, B_TRUE)) return (ZPOOL_STATUS_FAULTED_DEV_NR); if (vs->vs_state == VDEV_STATE_CANT_OPEN && find_vdev_problem(nvroot, vdev_missing, B_TRUE)) return (ZPOOL_STATUS_MISSING_DEV_NR); if (vs->vs_state == VDEV_STATE_CANT_OPEN && find_vdev_problem(nvroot, vdev_broken, B_TRUE)) return (ZPOOL_STATUS_CORRUPT_LABEL_NR); /* * Corrupted pool metadata */ if (vs->vs_state == VDEV_STATE_CANT_OPEN && vs->vs_aux == VDEV_AUX_CORRUPT_DATA) return (ZPOOL_STATUS_CORRUPT_POOL); /* * Persistent data errors. */ if (!isimport) { if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT, &nerr) == 0 && nerr != 0) return (ZPOOL_STATUS_CORRUPT_DATA); } /* * Missing devices in a replicated config. */ if (find_vdev_problem(nvroot, vdev_faulted, B_TRUE)) return (ZPOOL_STATUS_FAULTED_DEV_R); if (find_vdev_problem(nvroot, vdev_missing, B_TRUE)) return (ZPOOL_STATUS_MISSING_DEV_R); if (find_vdev_problem(nvroot, vdev_broken, B_TRUE)) return (ZPOOL_STATUS_CORRUPT_LABEL_R); /* * Devices with errors */ if (!isimport && find_vdev_problem(nvroot, vdev_errors, B_TRUE)) return (ZPOOL_STATUS_FAILING_DEV); /* * Offlined devices */ if (find_vdev_problem(nvroot, vdev_offlined, B_TRUE)) return (ZPOOL_STATUS_OFFLINE_DEV); /* * Removed device */ if (find_vdev_problem(nvroot, vdev_removed, B_TRUE)) return (ZPOOL_STATUS_REMOVED_DEV); /* * Suboptimal, but usable, ashift configuration. */ if (find_vdev_problem(nvroot, vdev_non_native_ashift, B_FALSE)) return (ZPOOL_STATUS_NON_NATIVE_ASHIFT); /* * Outdated, but usable, version */ if (SPA_VERSION_IS_SUPPORTED(version) && version != SPA_VERSION) return (ZPOOL_STATUS_VERSION_OLDER); /* * Usable pool with disabled features */ if (version >= SPA_VERSION_FEATURES) { int i; nvlist_t *feat; if (isimport) { feat = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO); feat = fnvlist_lookup_nvlist(feat, ZPOOL_CONFIG_ENABLED_FEAT); } else { feat = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS); } for (i = 0; i < SPA_FEATURES; i++) { zfeature_info_t *fi = &spa_feature_table[i]; if (!nvlist_exists(feat, fi->fi_guid)) return (ZPOOL_STATUS_FEAT_DISABLED); } } return (ZPOOL_STATUS_OK); }