/* * 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, zpool_errata_t *erratap) { 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; uint64_t errata = 0; unsigned long system_hostid = gethostid() & 0xffffffff; 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 != system_hostid && 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)) return (ZPOOL_STATUS_FAULTED_DEV_NR); if (vs->vs_state == VDEV_STATE_CANT_OPEN && find_vdev_problem(nvroot, vdev_missing)) return (ZPOOL_STATUS_MISSING_DEV_NR); if (vs->vs_state == VDEV_STATE_CANT_OPEN && find_vdev_problem(nvroot, vdev_broken)) 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)) return (ZPOOL_STATUS_FAULTED_DEV_R); if (find_vdev_problem(nvroot, vdev_missing)) return (ZPOOL_STATUS_MISSING_DEV_R); if (find_vdev_problem(nvroot, vdev_broken)) return (ZPOOL_STATUS_CORRUPT_LABEL_R); /* * Devices with errors */ if (!isimport && find_vdev_problem(nvroot, vdev_errors)) return (ZPOOL_STATUS_FAILING_DEV); /* * Offlined devices */ if (find_vdev_problem(nvroot, vdev_offlined)) return (ZPOOL_STATUS_OFFLINE_DEV); /* * Removed device */ if (find_vdev_problem(nvroot, vdev_removed)) return (ZPOOL_STATUS_REMOVED_DEV); /* * 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); } } /* * Informational errata available. */ (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRATA, &errata); if (errata) { *erratap = errata; return (ZPOOL_STATUS_ERRATA); } return (ZPOOL_STATUS_OK); }
/* * Function: it_config_commit() * * Informs the iscsit service that the configuration has changed and * commits the new configuration to persistent store by calling * stmfSetProviderData. This function can be called multiple times * during a configuration sequence if necessary. * * Parameters: * cfg A C representation of the current iSCSI configuration * * Return Values: * 0 Success * ENOMEM Could not allocate resources * EINVAL Invalid it_config_t structure * TBD ioctl() failed * TBD could not save config to STMF */ int it_config_commit(it_config_t *cfg) { int ret; nvlist_t *cfgnv = NULL; char *packednv = NULL; int iscsit_fd = -1; size_t pnv_size; iscsit_ioc_set_config_t iop; it_tgt_t *tgtp; if (!cfg) { return (EINVAL); } ret = it_config_to_nv(cfg, &cfgnv); if (ret == 0) { ret = nvlist_size(cfgnv, &pnv_size, NV_ENCODE_NATIVE); } /* * If the iscsit service is enabled, send the changes to the * kernel first. Kernel will be the final sanity check before * the config is saved persistently. * * This somewhat leaves open the simultaneous-change hole * that STMF was trying to solve, but is a better sanity * check and allows for graceful handling of target renames. */ if ((ret == 0) && is_iscsit_enabled()) { packednv = malloc(pnv_size); if (!packednv) { ret = ENOMEM; } else { ret = nvlist_pack(cfgnv, &packednv, &pnv_size, NV_ENCODE_NATIVE, 0); } if (ret == 0) { iscsit_fd = open(ISCSIT_NODE, O_RDWR|O_EXCL); if (iscsit_fd != -1) { iop.set_cfg_vers = ISCSIT_API_VERS0; iop.set_cfg_pnvlist = packednv; iop.set_cfg_pnvlist_len = pnv_size; if ((ioctl(iscsit_fd, ISCSIT_IOC_SET_CONFIG, &iop)) != 0) { ret = errno; } (void) close(iscsit_fd); } else { ret = errno; } } if (packednv != NULL) { free(packednv); } } /* * Before saving the config persistently, remove any * PROP_OLD_TARGET_NAME entries. This is only interesting to * the active service. */ if (ret == 0) { boolean_t changed = B_FALSE; tgtp = cfg->config_tgt_list; for (; tgtp != NULL; tgtp = tgtp->tgt_next) { if (!tgtp->tgt_properties) { continue; } if (nvlist_exists(tgtp->tgt_properties, PROP_OLD_TARGET_NAME)) { (void) nvlist_remove_all(tgtp->tgt_properties, PROP_OLD_TARGET_NAME); changed = B_TRUE; } } if (changed) { /* rebuild the config nvlist */ nvlist_free(cfgnv); cfgnv = NULL; ret = it_config_to_nv(cfg, &cfgnv); } } /* * stmfGetProviderDataProt() checks to ensure * that the config data hasn't changed since we fetched it. * * The kernel now has a version we need to save persistently. * CLI will 'do the right thing' and warn the user if it * gets STMF_ERROR_PROV_DATA_STALE. We'll try once to revert * the kernel to the persistently saved data, but ultimately, * it's up to the administrator to validate things are as they * want them to be. */ if (ret == 0) { ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv, STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token)); if (ret == STMF_STATUS_SUCCESS) { ret = 0; } else if (ret == STMF_ERROR_NOMEM) { ret = ENOMEM; } else if (ret == STMF_ERROR_PROV_DATA_STALE) { int st; it_config_t *rcfg = NULL; st = it_config_load(&rcfg); if (st == 0) { (void) it_config_commit(rcfg); it_config_free(rcfg); } } } if (cfgnv) { nvlist_free(cfgnv); } return (ret); }
/*ARGSUSED*/ static void spa_history_log_sync(void *arg, dmu_tx_t *tx) { nvlist_t *nvl = arg; spa_t *spa = dmu_tx_pool(tx)->dp_spa; objset_t *mos = spa->spa_meta_objset; dmu_buf_t *dbp; spa_history_phys_t *shpp; size_t reclen; uint64_t le_len; char *record_packed = NULL; int ret; /* * If we have an older pool that doesn't have a command * history object, create it now. */ mutex_enter(&spa->spa_history_lock); if (!spa->spa_history) spa_history_create_obj(spa, tx); mutex_exit(&spa->spa_history_lock); /* * Get the offset of where we need to write via the bonus buffer. * Update the offset when the write completes. */ VERIFY0(dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp)); shpp = dbp->db_data; dmu_buf_will_dirty(dbp, tx); #ifdef ZFS_DEBUG { dmu_object_info_t doi; dmu_object_info_from_db(dbp, &doi); ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_SPA_HISTORY_OFFSETS); } #endif fnvlist_add_uint64(nvl, ZPOOL_HIST_TIME, gethrestime_sec()); #ifdef _KERNEL fnvlist_add_string(nvl, ZPOOL_HIST_HOST, utsname.nodename); #endif if (nvlist_exists(nvl, ZPOOL_HIST_CMD)) { zfs_dbgmsg("command: %s", fnvlist_lookup_string(nvl, ZPOOL_HIST_CMD)); } else if (nvlist_exists(nvl, ZPOOL_HIST_INT_NAME)) { if (nvlist_exists(nvl, ZPOOL_HIST_DSNAME)) { zfs_dbgmsg("txg %lld %s %s (id %llu) %s", fnvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG), fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME), fnvlist_lookup_string(nvl, ZPOOL_HIST_DSNAME), fnvlist_lookup_uint64(nvl, ZPOOL_HIST_DSID), fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR)); } else { zfs_dbgmsg("txg %lld %s %s", fnvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG), fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME), fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR)); } } else if (nvlist_exists(nvl, ZPOOL_HIST_IOCTL)) { zfs_dbgmsg("ioctl %s", fnvlist_lookup_string(nvl, ZPOOL_HIST_IOCTL)); } VERIFY3U(nvlist_pack(nvl, &record_packed, &reclen, NV_ENCODE_NATIVE, KM_PUSHPAGE), ==, 0); mutex_enter(&spa->spa_history_lock); /* write out the packed length as little endian */ le_len = LE_64((uint64_t)reclen); ret = spa_history_write(spa, &le_len, sizeof (le_len), shpp, tx); if (!ret) ret = spa_history_write(spa, record_packed, reclen, shpp, tx); /* The first command is the create, which we keep forever */ if (ret == 0 && shpp->sh_pool_create_len == 0 && nvlist_exists(nvl, ZPOOL_HIST_CMD)) { shpp->sh_pool_create_len = shpp->sh_bof = shpp->sh_eof; } mutex_exit(&spa->spa_history_lock); fnvlist_pack_free(record_packed, reclen); dmu_buf_rele(dbp, FTAG); fnvlist_free(nvl); }
/* * Goes through the config property list and validates * each entry. If errs is non-NULL, will return explicit errors * for each property that fails validation. */ static int it_validate_configprops(nvlist_t *nvl, nvlist_t *errs) { int errcnt = 0; nvpair_t *nvp = NULL; data_type_t nvtype; char *name; char *val; struct sockaddr_storage sa; boolean_t update_rad_server = B_FALSE; char *rad_server; char *auth = NULL; if (!nvl) { return (0); } while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { name = nvpair_name(nvp); nvtype = nvpair_type(nvp); if (!name) { continue; } val = NULL; /* prefetch string value as we mostly need it */ if (nvtype == DATA_TYPE_STRING) { (void) nvpair_value_string(nvp, &val); } if (strcmp(name, PROP_ALIAS) == 0) { if (!val) { PROPERR(errs, name, gettext("must be a string value")); errcnt++; } } else if (strcmp(name, PROP_AUTH) == 0) { if (!val) { PROPERR(errs, name, gettext("must be a string value")); errcnt++; continue; } if ((strcmp(val, PA_AUTH_NONE) != 0) && (strcmp(val, PA_AUTH_CHAP) != 0) && (strcmp(val, PA_AUTH_RADIUS) != 0)) { PROPERR(errs, PROP_AUTH, gettext("must be none, chap or radius")); errcnt++; } auth = val; } else if (strcmp(name, PROP_ISNS_ENABLED) == 0) { if (nvtype != DATA_TYPE_BOOLEAN_VALUE) { PROPERR(errs, name, gettext("must be a boolean value")); errcnt++; } } else if (strcmp(name, PROP_ISNS_SERVER) == 0) { char **arr = NULL; uint32_t acount = 0; (void) nvlist_lookup_string_array(nvl, name, &arr, &acount); while (acount > 0) { if (strcasecmp(arr[acount - 1], "none") == 0) { break; } if ((it_common_convert_sa(arr[acount - 1], &sa, 0)) == NULL) { PROPERR(errs, arr[acount - 1], gettext("invalid address")); errcnt++; } acount--; } } else if (strcmp(name, PROP_RADIUS_SECRET) == 0) { if (!val) { PROPERR(errs, name, gettext("must be a string value")); errcnt++; continue; } } else if (strcmp(name, PROP_RADIUS_SERVER) == 0) { struct sockaddr_storage sa; if (!val) { PROPERR(errs, name, gettext("must be a string value")); errcnt++; continue; } if ((it_common_convert_sa(val, &sa, DEFAULT_RADIUS_PORT)) == NULL) { PROPERR(errs, name, gettext("invalid address")); errcnt++; } else { /* * rewrite this property to ensure port * number is added. */ if (sockaddr_to_str(&sa, &rad_server) == 0) { update_rad_server = B_TRUE; } } } else { /* unrecognized property */ PROPERR(errs, name, gettext("unrecognized property")); errcnt++; } } /* * If we successfully reformatted the radius server to add the port * number then update the nvlist */ if (update_rad_server) { (void) nvlist_add_string(nvl, PROP_RADIUS_SERVER, rad_server); free(rad_server); } /* * if auth = radius, ensure radius server & secret are set. */ if (auth) { if (strcmp(auth, PA_AUTH_RADIUS) == 0) { /* need server & secret for radius */ if (!nvlist_exists(nvl, PROP_RADIUS_SERVER)) { PROPERR(errs, PROP_RADIUS_SERVER, gettext("missing required property")); errcnt++; } if (!nvlist_exists(nvl, PROP_RADIUS_SECRET)) { PROPERR(errs, PROP_RADIUS_SECRET, gettext("missing required property")); errcnt++; } } } if (errcnt) { return (EINVAL); } return (0); }