Path: blob/main/sys/contrib/openzfs/lib/libzfs/libzfs_status.c
48378 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.24* Copyright (c) 2012 by Delphix. All rights reserved.25* Copyright (c) 2013 Steven Hartland. All rights reserved.26* Copyright (c) 2021, Colm Buckley <[email protected]>27*/2829/*30* This file contains the functions which analyze the status of a pool. This31* include both the status of an active pool, as well as the status exported32* pools. Returns one of the ZPOOL_STATUS_* defines describing the status of33* the pool. This status is independent (to a certain degree) from the state of34* the pool. A pool's state describes only whether or not it is capable of35* providing the necessary fault tolerance for data. The status describes the36* overall status of devices. A pool that is online can still have a device37* that is experiencing errors.38*39* Only a subset of the possible faults can be detected using 'zpool status',40* and not all possible errors correspond to a FMA message ID. The explanation41* is left up to the caller, depending on whether it is a live pool or an42* import.43*/4445#include <libzfs.h>46#include <libzutil.h>47#include <stdlib.h>48#include <string.h>49#include <unistd.h>50#include <sys/systeminfo.h>51#include "libzfs_impl.h"52#include "zfeature_common.h"5354/*55* Message ID table. This must be kept in sync with the ZPOOL_STATUS_* defines56* in include/libzfs.h. Note that there are some status results which go past57* the end of this table, and hence have no associated message ID.58*/59static const char *const zfs_msgid_table[] = {60"ZFS-8000-14", /* ZPOOL_STATUS_CORRUPT_CACHE */61"ZFS-8000-2Q", /* ZPOOL_STATUS_MISSING_DEV_R */62"ZFS-8000-3C", /* ZPOOL_STATUS_MISSING_DEV_NR */63"ZFS-8000-4J", /* ZPOOL_STATUS_CORRUPT_LABEL_R */64"ZFS-8000-5E", /* ZPOOL_STATUS_CORRUPT_LABEL_NR */65"ZFS-8000-6X", /* ZPOOL_STATUS_BAD_GUID_SUM */66"ZFS-8000-72", /* ZPOOL_STATUS_CORRUPT_POOL */67"ZFS-8000-8A", /* ZPOOL_STATUS_CORRUPT_DATA */68"ZFS-8000-9P", /* ZPOOL_STATUS_FAILING_DEV */69"ZFS-8000-A5", /* ZPOOL_STATUS_VERSION_NEWER */70"ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_MISMATCH */71"ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_ACTIVE */72"ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_REQUIRED */73"ZFS-8000-HC", /* ZPOOL_STATUS_IO_FAILURE_WAIT */74"ZFS-8000-JQ", /* ZPOOL_STATUS_IO_FAILURE_CONTINUE */75"ZFS-8000-MM", /* ZPOOL_STATUS_IO_FAILURE_MMP */76"ZFS-8000-K4", /* ZPOOL_STATUS_BAD_LOG */77"ZFS-8000-ER", /* ZPOOL_STATUS_ERRATA */78/*79* The following results have no message ID.80* ZPOOL_STATUS_UNSUP_FEAT_READ81* ZPOOL_STATUS_UNSUP_FEAT_WRITE82* ZPOOL_STATUS_FAULTED_DEV_R83* ZPOOL_STATUS_FAULTED_DEV_NR84* ZPOOL_STATUS_VERSION_OLDER85* ZPOOL_STATUS_FEAT_DISABLED86* ZPOOL_STATUS_RESILVERING87* ZPOOL_STATUS_OFFLINE_DEV88* ZPOOL_STATUS_REMOVED_DEV89* ZPOOL_STATUS_REBUILDING90* ZPOOL_STATUS_REBUILD_SCRUB91* ZPOOL_STATUS_COMPATIBILITY_ERR92* ZPOOL_STATUS_INCOMPATIBLE_FEAT93* ZPOOL_STATUS_OK94*/95};9697#define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0]))9899static int100vdev_missing(vdev_stat_t *vs, uint_t vsc, void *arg)101{102(void) vsc, (void) arg;103return (vs->vs_state == VDEV_STATE_CANT_OPEN &&104vs->vs_aux == VDEV_AUX_OPEN_FAILED);105}106107static int108vdev_faulted(vdev_stat_t *vs, uint_t vsc, void *arg)109{110(void) vsc, (void) arg;111return (vs->vs_state == VDEV_STATE_FAULTED);112}113114static int115vdev_errors(vdev_stat_t *vs, uint_t vsc, void *arg)116{117(void) vsc, (void) arg;118return (vs->vs_state == VDEV_STATE_DEGRADED ||119vs->vs_read_errors != 0 || vs->vs_write_errors != 0 ||120vs->vs_checksum_errors != 0);121}122123static int124vdev_broken(vdev_stat_t *vs, uint_t vsc, void *arg)125{126(void) vsc, (void) arg;127return (vs->vs_state == VDEV_STATE_CANT_OPEN);128}129130static int131vdev_offlined(vdev_stat_t *vs, uint_t vsc, void *arg)132{133(void) vsc, (void) arg;134return (vs->vs_state == VDEV_STATE_OFFLINE);135}136137static int138vdev_removed(vdev_stat_t *vs, uint_t vsc, void *arg)139{140(void) vsc, (void) arg;141return (vs->vs_state == VDEV_STATE_REMOVED);142}143144static int145vdev_non_native_ashift(vdev_stat_t *vs, uint_t vsc, void *arg)146{147uint64_t ashift = *(uint64_t *)arg;148149return (VDEV_STAT_VALID(vs_physical_ashift, vsc) &&150(ashift == 0 || vs->vs_configured_ashift < ashift) &&151vs->vs_configured_ashift < vs->vs_physical_ashift);152}153154/*155* Detect if any leaf devices that have seen errors or could not be opened.156*/157static boolean_t158find_vdev_problem(nvlist_t *vdev, int (*func)(vdev_stat_t *, uint_t, void *),159void *arg, boolean_t ignore_replacing)160{161nvlist_t **child;162uint_t c, children;163164/*165* Ignore problems within a 'replacing' vdev, since we're presumably in166* the process of repairing any such errors, and don't want to call them167* out again. We'll pick up the fact that a resilver is happening168* later.169*/170if (ignore_replacing == B_TRUE) {171const char *type = fnvlist_lookup_string(vdev,172ZPOOL_CONFIG_TYPE);173if (strcmp(type, VDEV_TYPE_REPLACING) == 0)174return (B_FALSE);175}176177if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child,178&children) == 0) {179for (c = 0; c < children; c++) {180if (find_vdev_problem(child[c], func, arg,181ignore_replacing))182return (B_TRUE);183}184} else {185uint_t vsc;186vdev_stat_t *vs = (vdev_stat_t *)fnvlist_lookup_uint64_array(187vdev, ZPOOL_CONFIG_VDEV_STATS, &vsc);188if (func(vs, vsc, arg) != 0)189return (B_TRUE);190}191192/*193* Check any L2 cache devs194*/195if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_L2CACHE, &child,196&children) == 0) {197for (c = 0; c < children; c++) {198if (find_vdev_problem(child[c], func, arg,199ignore_replacing))200return (B_TRUE);201}202}203204return (B_FALSE);205}206207/*208* Active pool health status.209*210* To determine the status for a pool, we make several passes over the config,211* picking the most egregious error we find. In order of importance, we do the212* following:213*214* - Check for a complete and valid configuration215* - Look for any faulted or missing devices in a non-replicated config216* - Check for any data errors217* - Check for any faulted or missing devices in a replicated config218* - Look for any devices showing errors219* - Check for any resilvering or rebuilding devices220*221* There can obviously be multiple errors within a single pool, so this routine222* only picks the most damaging of all the current errors to report.223*/224static zpool_status_t225check_status(nvlist_t *config, boolean_t isimport,226zpool_errata_t *erratap, const char *compat, uint64_t ashift)227{228pool_scan_stat_t *ps = NULL;229uint_t vsc, psc;230uint64_t suspended;231uint64_t hostid = 0;232uint64_t errata = 0;233unsigned long system_hostid = get_system_hostid();234235uint64_t version = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION);236nvlist_t *nvroot = fnvlist_lookup_nvlist(config,237ZPOOL_CONFIG_VDEV_TREE);238vdev_stat_t *vs = (vdev_stat_t *)fnvlist_lookup_uint64_array(nvroot,239ZPOOL_CONFIG_VDEV_STATS, &vsc);240uint64_t stateval = fnvlist_lookup_uint64(config,241ZPOOL_CONFIG_POOL_STATE);242243/*244* Currently resilvering a vdev245*/246(void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_SCAN_STATS,247(uint64_t **)&ps, &psc);248if (ps != NULL && ps->pss_func == POOL_SCAN_RESILVER &&249ps->pss_state == DSS_SCANNING)250return (ZPOOL_STATUS_RESILVERING);251252/*253* Currently rebuilding a vdev, check top-level vdevs.254*/255vdev_rebuild_stat_t *vrs = NULL;256nvlist_t **child;257uint_t c, i, children;258uint64_t rebuild_end_time = 0;259if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,260&child, &children) == 0) {261for (c = 0; c < children; c++) {262if ((nvlist_lookup_uint64_array(child[c],263ZPOOL_CONFIG_REBUILD_STATS,264(uint64_t **)&vrs, &i) == 0) && (vrs != NULL)) {265uint64_t state = vrs->vrs_state;266267if (state == VDEV_REBUILD_ACTIVE) {268return (ZPOOL_STATUS_REBUILDING);269} else if (state == VDEV_REBUILD_COMPLETE &&270vrs->vrs_end_time > rebuild_end_time) {271rebuild_end_time = vrs->vrs_end_time;272}273}274}275276/*277* If we can determine when the last scrub was run, and it278* was before the last rebuild completed, then recommend279* that the pool be scrubbed to verify all checksums. When280* ps is NULL we can infer the pool has never been scrubbed.281*/282if (rebuild_end_time > 0) {283if (ps != NULL) {284if ((ps->pss_state == DSS_FINISHED &&285ps->pss_func == POOL_SCAN_SCRUB &&286rebuild_end_time > ps->pss_end_time) ||287ps->pss_state == DSS_NONE)288return (ZPOOL_STATUS_REBUILD_SCRUB);289} else {290return (ZPOOL_STATUS_REBUILD_SCRUB);291}292}293}294295/*296* The multihost property is set and the pool may be active.297*/298if (vs->vs_state == VDEV_STATE_CANT_OPEN &&299vs->vs_aux == VDEV_AUX_ACTIVE) {300mmp_state_t mmp_state;301nvlist_t *nvinfo;302303nvinfo = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO);304mmp_state = fnvlist_lookup_uint64(nvinfo,305ZPOOL_CONFIG_MMP_STATE);306307if (mmp_state == MMP_STATE_ACTIVE)308return (ZPOOL_STATUS_HOSTID_ACTIVE);309else if (mmp_state == MMP_STATE_NO_HOSTID)310return (ZPOOL_STATUS_HOSTID_REQUIRED);311else312return (ZPOOL_STATUS_HOSTID_MISMATCH);313}314315/*316* Pool last accessed by another system.317*/318(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid);319if (hostid != 0 && (unsigned long)hostid != system_hostid &&320stateval == POOL_STATE_ACTIVE)321return (ZPOOL_STATUS_HOSTID_MISMATCH);322323/*324* Newer on-disk version.325*/326if (vs->vs_state == VDEV_STATE_CANT_OPEN &&327vs->vs_aux == VDEV_AUX_VERSION_NEWER)328return (ZPOOL_STATUS_VERSION_NEWER);329330/*331* Unsupported feature(s).332*/333if (vs->vs_state == VDEV_STATE_CANT_OPEN &&334vs->vs_aux == VDEV_AUX_UNSUP_FEAT) {335nvlist_t *nvinfo = fnvlist_lookup_nvlist(config,336ZPOOL_CONFIG_LOAD_INFO);337if (nvlist_exists(nvinfo, ZPOOL_CONFIG_CAN_RDONLY))338return (ZPOOL_STATUS_UNSUP_FEAT_WRITE);339return (ZPOOL_STATUS_UNSUP_FEAT_READ);340}341342/*343* Check that the config is complete.344*/345if (vs->vs_state == VDEV_STATE_CANT_OPEN &&346vs->vs_aux == VDEV_AUX_BAD_GUID_SUM)347return (ZPOOL_STATUS_BAD_GUID_SUM);348349/*350* Check whether the pool has suspended.351*/352if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED,353&suspended) == 0) {354uint64_t reason;355356if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED_REASON,357&reason) == 0 && reason == ZIO_SUSPEND_MMP)358return (ZPOOL_STATUS_IO_FAILURE_MMP);359360if (suspended == ZIO_FAILURE_MODE_CONTINUE)361return (ZPOOL_STATUS_IO_FAILURE_CONTINUE);362return (ZPOOL_STATUS_IO_FAILURE_WAIT);363}364365/*366* Could not read a log.367*/368if (vs->vs_state == VDEV_STATE_CANT_OPEN &&369vs->vs_aux == VDEV_AUX_BAD_LOG) {370return (ZPOOL_STATUS_BAD_LOG);371}372373/*374* Bad devices in non-replicated config.375*/376if (vs->vs_state == VDEV_STATE_CANT_OPEN &&377find_vdev_problem(nvroot, vdev_faulted, NULL, B_TRUE))378return (ZPOOL_STATUS_FAULTED_DEV_NR);379380if (vs->vs_state == VDEV_STATE_CANT_OPEN &&381find_vdev_problem(nvroot, vdev_missing, NULL, B_TRUE))382return (ZPOOL_STATUS_MISSING_DEV_NR);383384if (vs->vs_state == VDEV_STATE_CANT_OPEN &&385find_vdev_problem(nvroot, vdev_broken, NULL, B_TRUE))386return (ZPOOL_STATUS_CORRUPT_LABEL_NR);387388/*389* Corrupted pool metadata390*/391if (vs->vs_state == VDEV_STATE_CANT_OPEN &&392vs->vs_aux == VDEV_AUX_CORRUPT_DATA)393return (ZPOOL_STATUS_CORRUPT_POOL);394395/*396* Persistent data errors.397*/398if (!isimport) {399uint64_t nerr;400if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,401&nerr) == 0 && nerr != 0)402return (ZPOOL_STATUS_CORRUPT_DATA);403}404405/*406* Missing devices in a replicated config.407*/408if (find_vdev_problem(nvroot, vdev_faulted, NULL, B_TRUE))409return (ZPOOL_STATUS_FAULTED_DEV_R);410if (find_vdev_problem(nvroot, vdev_missing, NULL, B_TRUE))411return (ZPOOL_STATUS_MISSING_DEV_R);412if (find_vdev_problem(nvroot, vdev_broken, NULL, B_TRUE))413return (ZPOOL_STATUS_CORRUPT_LABEL_R);414415/*416* Devices with errors417*/418if (!isimport && find_vdev_problem(nvroot, vdev_errors, NULL, B_TRUE))419return (ZPOOL_STATUS_FAILING_DEV);420421/*422* Offlined devices423*/424if (find_vdev_problem(nvroot, vdev_offlined, NULL, B_TRUE))425return (ZPOOL_STATUS_OFFLINE_DEV);426427/*428* Removed device429*/430if (find_vdev_problem(nvroot, vdev_removed, NULL, B_TRUE))431return (ZPOOL_STATUS_REMOVED_DEV);432433/*434* Suboptimal, but usable, ashift configuration.435*/436if (!isimport &&437getenv("ZPOOL_STATUS_NON_NATIVE_ASHIFT_IGNORE") == NULL &&438find_vdev_problem(nvroot, vdev_non_native_ashift, &ashift, B_FALSE))439return (ZPOOL_STATUS_NON_NATIVE_ASHIFT);440441/*442* Informational errata available.443*/444(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRATA, &errata);445if (errata) {446*erratap = errata;447return (ZPOOL_STATUS_ERRATA);448}449450/*451* Outdated, but usable, version452*/453if (SPA_VERSION_IS_SUPPORTED(version) && version != SPA_VERSION) {454/* "legacy" compatibility disables old version reporting */455if (compat != NULL && strcmp(compat, ZPOOL_COMPAT_LEGACY) == 0)456return (ZPOOL_STATUS_OK);457else458return (ZPOOL_STATUS_VERSION_OLDER);459}460461/*462* Usable pool with disabled or superfluous features463* (superfluous = beyond what's requested by 'compatibility')464*/465if (version >= SPA_VERSION_FEATURES) {466int i;467nvlist_t *feat;468469if (isimport) {470feat = fnvlist_lookup_nvlist(config,471ZPOOL_CONFIG_LOAD_INFO);472if (nvlist_exists(feat, ZPOOL_CONFIG_ENABLED_FEAT))473feat = fnvlist_lookup_nvlist(feat,474ZPOOL_CONFIG_ENABLED_FEAT);475} else {476feat = fnvlist_lookup_nvlist(config,477ZPOOL_CONFIG_FEATURE_STATS);478}479480/* check against all features, or limited set? */481boolean_t c_features[SPA_FEATURES];482483switch (zpool_load_compat(compat, c_features, NULL, 0)) {484case ZPOOL_COMPATIBILITY_OK:485case ZPOOL_COMPATIBILITY_WARNTOKEN:486break;487default:488return (ZPOOL_STATUS_COMPATIBILITY_ERR);489}490for (i = 0; i < SPA_FEATURES; i++) {491zfeature_info_t *fi = &spa_feature_table[i];492if (!fi->fi_zfs_mod_supported ||493(fi->fi_flags & ZFEATURE_FLAG_NO_UPGRADE))494continue;495if (c_features[i] && !nvlist_exists(feat, fi->fi_guid))496return (ZPOOL_STATUS_FEAT_DISABLED);497if (!c_features[i] && nvlist_exists(feat, fi->fi_guid))498return (ZPOOL_STATUS_INCOMPATIBLE_FEAT);499}500}501502return (ZPOOL_STATUS_OK);503}504505zpool_status_t506zpool_get_status(zpool_handle_t *zhp, const char **msgid,507zpool_errata_t *errata)508{509/*510* pass in the desired feature set, as511* it affects check for disabled features512*/513char compatibility[ZFS_MAXPROPLEN];514if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY, compatibility,515ZFS_MAXPROPLEN, NULL, B_FALSE) != 0)516compatibility[0] = '\0';517518uint64_t ashift = zpool_get_prop_int(zhp, ZPOOL_PROP_ASHIFT, NULL);519520zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE, errata,521compatibility, ashift);522523if (msgid != NULL) {524if (ret >= NMSGID)525*msgid = NULL;526else527*msgid = zfs_msgid_table[ret];528}529return (ret);530}531532zpool_status_t533zpool_import_status(nvlist_t *config, const char **msgid,534zpool_errata_t *errata)535{536zpool_status_t ret = check_status(config, B_TRUE, errata, NULL, 0);537538if (ret >= NMSGID)539*msgid = NULL;540else541*msgid = zfs_msgid_table[ret];542543return (ret);544}545546547