Exemplo n.º 1
0
/*
 * 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);
}
Exemplo n.º 2
0
/*
 * 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);
}
Exemplo n.º 3
0
/*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);
}
Exemplo n.º 4
0
/*
 * 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);
}