xref: /freebsd/cddl/usr.sbin/zfsd/vdev.cc (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
17a0c41d5SAlan Somers /*-
27a0c41d5SAlan Somers  * Copyright (c) 2011, 2012, 2013, 2014 Spectra Logic Corporation
37a0c41d5SAlan Somers  * All rights reserved.
47a0c41d5SAlan Somers  *
57a0c41d5SAlan Somers  * Redistribution and use in source and binary forms, with or without
67a0c41d5SAlan Somers  * modification, are permitted provided that the following conditions
77a0c41d5SAlan Somers  * are met:
87a0c41d5SAlan Somers  * 1. Redistributions of source code must retain the above copyright
97a0c41d5SAlan Somers  *    notice, this list of conditions, and the following disclaimer,
107a0c41d5SAlan Somers  *    without modification.
117a0c41d5SAlan Somers  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
127a0c41d5SAlan Somers  *    substantially similar to the "NO WARRANTY" disclaimer below
137a0c41d5SAlan Somers  *    ("Disclaimer") and any redistribution must be conditioned upon
147a0c41d5SAlan Somers  *    including a substantially similar Disclaimer requirement for further
157a0c41d5SAlan Somers  *    binary redistribution.
167a0c41d5SAlan Somers  *
177a0c41d5SAlan Somers  * NO WARRANTY
187a0c41d5SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
197a0c41d5SAlan Somers  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
207a0c41d5SAlan Somers  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
217a0c41d5SAlan Somers  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
227a0c41d5SAlan Somers  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
237a0c41d5SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
247a0c41d5SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
257a0c41d5SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
267a0c41d5SAlan Somers  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
277a0c41d5SAlan Somers  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
287a0c41d5SAlan Somers  * POSSIBILITY OF SUCH DAMAGES.
297a0c41d5SAlan Somers  *
307a0c41d5SAlan Somers  * Authors: Justin T. Gibbs     (Spectra Logic Corporation)
317a0c41d5SAlan Somers  */
327a0c41d5SAlan Somers 
337a0c41d5SAlan Somers /**
347a0c41d5SAlan Somers  * \file vdev.cc
357a0c41d5SAlan Somers  *
367a0c41d5SAlan Somers  * Implementation of the Vdev class.
377a0c41d5SAlan Somers  */
387a0c41d5SAlan Somers #include <syslog.h>
397a0c41d5SAlan Somers #include <sys/cdefs.h>
409e5787d2SMatt Macy #include <sys/byteorder.h>
417a0c41d5SAlan Somers #include <sys/fs/zfs.h>
427a0c41d5SAlan Somers 
437a0c41d5SAlan Somers #include <libzfs.h>
447a0c41d5SAlan Somers /*
457a0c41d5SAlan Somers  * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
467a0c41d5SAlan Somers  * C++ flush methods
477a0c41d5SAlan Somers  */
487a0c41d5SAlan Somers #undef   flush
497a0c41d5SAlan Somers 
507a0c41d5SAlan Somers #include <list>
517a0c41d5SAlan Somers #include <map>
527a0c41d5SAlan Somers #include <string>
537a0c41d5SAlan Somers #include <sstream>
547a0c41d5SAlan Somers 
557a0c41d5SAlan Somers #include <devdctl/guid.h>
567a0c41d5SAlan Somers #include <devdctl/event.h>
577a0c41d5SAlan Somers #include <devdctl/event_factory.h>
587a0c41d5SAlan Somers #include <devdctl/exception.h>
597a0c41d5SAlan Somers #include <devdctl/consumer.h>
607a0c41d5SAlan Somers 
617a0c41d5SAlan Somers #include "vdev.h"
627a0c41d5SAlan Somers #include "vdev_iterator.h"
637a0c41d5SAlan Somers #include "zfsd.h"
647a0c41d5SAlan Somers #include "zfsd_exception.h"
657a0c41d5SAlan Somers #include "zpool_list.h"
667a0c41d5SAlan Somers /*============================ Namespace Control =============================*/
677a0c41d5SAlan Somers using std::string;
687a0c41d5SAlan Somers using std::stringstream;
697a0c41d5SAlan Somers 
707a0c41d5SAlan Somers //- Special objects -----------------------------------------------------------
717a0c41d5SAlan Somers Vdev NonexistentVdev;
727a0c41d5SAlan Somers 
737a0c41d5SAlan Somers //- Vdev Inline Public Methods ------------------------------------------------
747a0c41d5SAlan Somers /*=========================== Class Implementations ==========================*/
757a0c41d5SAlan Somers /*----------------------------------- Vdev -----------------------------------*/
767a0c41d5SAlan Somers 
777a0c41d5SAlan Somers /* Special constructor for NonexistentVdev. */
Vdev()787a0c41d5SAlan Somers Vdev::Vdev()
797a0c41d5SAlan Somers  : m_poolConfig(NULL),
807a0c41d5SAlan Somers    m_config(NULL)
817a0c41d5SAlan Somers {}
827a0c41d5SAlan Somers 
837a0c41d5SAlan Somers bool
VdevLookupPoolGuid()847a0c41d5SAlan Somers Vdev::VdevLookupPoolGuid()
857a0c41d5SAlan Somers {
867a0c41d5SAlan Somers 	uint64_t guid;
877a0c41d5SAlan Somers 	if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid))
887a0c41d5SAlan Somers 		return (false);
897a0c41d5SAlan Somers 	m_poolGUID = guid;
907a0c41d5SAlan Somers 	return (true);
917a0c41d5SAlan Somers }
927a0c41d5SAlan Somers 
937a0c41d5SAlan Somers void
VdevLookupGuid()947a0c41d5SAlan Somers Vdev::VdevLookupGuid()
957a0c41d5SAlan Somers {
967a0c41d5SAlan Somers 	uint64_t guid;
977a0c41d5SAlan Somers 	if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0)
987a0c41d5SAlan Somers 		throw ZfsdException("Unable to extract vdev GUID "
997a0c41d5SAlan Somers 				    "from vdev config data.");
1007a0c41d5SAlan Somers 	m_vdevGUID = guid;
1017a0c41d5SAlan Somers }
1027a0c41d5SAlan Somers 
Vdev(zpool_handle_t * pool,nvlist_t * config)1037a0c41d5SAlan Somers Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config)
1047a0c41d5SAlan Somers  : m_poolConfig(zpool_get_config(pool, NULL)),
1057a0c41d5SAlan Somers    m_config(config)
1067a0c41d5SAlan Somers {
1077a0c41d5SAlan Somers 	if (!VdevLookupPoolGuid())
1087a0c41d5SAlan Somers 		throw ZfsdException("Can't extract pool GUID from handle.");
1097a0c41d5SAlan Somers 	VdevLookupGuid();
1107a0c41d5SAlan Somers }
1117a0c41d5SAlan Somers 
Vdev(nvlist_t * poolConfig,nvlist_t * config)1127a0c41d5SAlan Somers Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config)
1137a0c41d5SAlan Somers  : m_poolConfig(poolConfig),
1147a0c41d5SAlan Somers    m_config(config)
1157a0c41d5SAlan Somers {
1167a0c41d5SAlan Somers 	if (!VdevLookupPoolGuid())
1177a0c41d5SAlan Somers 		throw ZfsdException("Can't extract pool GUID from config.");
1187a0c41d5SAlan Somers 	VdevLookupGuid();
1197a0c41d5SAlan Somers }
1207a0c41d5SAlan Somers 
Vdev(nvlist_t * labelConfig)1217a0c41d5SAlan Somers Vdev::Vdev(nvlist_t *labelConfig)
1227a0c41d5SAlan Somers  : m_poolConfig(labelConfig),
1237a0c41d5SAlan Somers    m_config(labelConfig)
1247a0c41d5SAlan Somers {
1257a0c41d5SAlan Somers 	/*
1267a0c41d5SAlan Somers 	 * Spares do not have a Pool GUID.  Tolerate its absence.
1277a0c41d5SAlan Somers 	 * Code accessing this Vdev in a context where the Pool GUID is
1287a0c41d5SAlan Somers 	 * required will find it invalid (as it is upon Vdev construction)
1297a0c41d5SAlan Somers 	 * and act accordingly.
1307a0c41d5SAlan Somers 	 */
1317a0c41d5SAlan Somers 	(void) VdevLookupPoolGuid();
1327a0c41d5SAlan Somers 	VdevLookupGuid();
1337a0c41d5SAlan Somers 
1347a0c41d5SAlan Somers 	try {
1357a0c41d5SAlan Somers 		m_config = VdevIterator(labelConfig).Find(m_vdevGUID);
1367a0c41d5SAlan Somers 	} catch (const ZfsdException &exp) {
1377a0c41d5SAlan Somers 		/*
1387a0c41d5SAlan Somers 		 * When reading a spare's label, it is normal not to find
1397a0c41d5SAlan Somers 		 * a list of vdevs
1407a0c41d5SAlan Somers 		 */
1417a0c41d5SAlan Somers 		m_config = NULL;
1427a0c41d5SAlan Somers 	}
1437a0c41d5SAlan Somers }
1447a0c41d5SAlan Somers 
1457a0c41d5SAlan Somers bool
IsSpare() const1467a0c41d5SAlan Somers Vdev::IsSpare() const
1477a0c41d5SAlan Somers {
1487a0c41d5SAlan Somers 	uint64_t is_spare(0);
1497a0c41d5SAlan Somers 
1507a0c41d5SAlan Somers 	if (m_config == NULL)
1517a0c41d5SAlan Somers 		return (false);
1527a0c41d5SAlan Somers 
1537a0c41d5SAlan Somers 	(void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &is_spare);
1547a0c41d5SAlan Somers 	return (bool(is_spare));
1557a0c41d5SAlan Somers }
1567a0c41d5SAlan Somers 
1577a0c41d5SAlan Somers vdev_state
State() const1587a0c41d5SAlan Somers Vdev::State() const
1597a0c41d5SAlan Somers {
1607a0c41d5SAlan Somers 	uint64_t    *nvlist_array;
1617a0c41d5SAlan Somers 	vdev_stat_t *vs;
1627a0c41d5SAlan Somers 	uint_t       vsc;
1637a0c41d5SAlan Somers 
1647a0c41d5SAlan Somers 	if (m_config == NULL) {
1657a0c41d5SAlan Somers 		/*
1667a0c41d5SAlan Somers 		 * If we couldn't find the list of vdevs, that normally means
1677a0c41d5SAlan Somers 		 * that this is an available hotspare.  In that case, we will
1687a0c41d5SAlan Somers 		 * presume it to be healthy.  Even if this spare had formerly
1697a0c41d5SAlan Somers 		 * been in use, been degraded, and been replaced, the act of
1707a0c41d5SAlan Somers 		 * replacement wipes the degraded bit from the label.  So we
1717a0c41d5SAlan Somers 		 * have no choice but to presume that it is healthy.
1727a0c41d5SAlan Somers 		 */
1737a0c41d5SAlan Somers 		return (VDEV_STATE_HEALTHY);
1747a0c41d5SAlan Somers 	}
1757a0c41d5SAlan Somers 
1767a0c41d5SAlan Somers 	if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS,
1777a0c41d5SAlan Somers 				       &nvlist_array, &vsc) == 0) {
1787a0c41d5SAlan Somers 		vs = reinterpret_cast<vdev_stat_t *>(nvlist_array);
1797a0c41d5SAlan Somers 		return (static_cast<vdev_state>(vs->vs_state));
1807a0c41d5SAlan Somers 	}
1817a0c41d5SAlan Somers 
1827a0c41d5SAlan Somers 	/*
1837a0c41d5SAlan Somers 	 * Stats are not available.  This vdev was created from a label.
1847a0c41d5SAlan Somers 	 * Synthesize a state based on available data.
1857a0c41d5SAlan Somers 	 */
1867a0c41d5SAlan Somers 	uint64_t faulted(0);
1877a0c41d5SAlan Somers 	uint64_t degraded(0);
1887a0c41d5SAlan Somers 	(void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_FAULTED, &faulted);
1897a0c41d5SAlan Somers 	(void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_DEGRADED, &degraded);
1907a0c41d5SAlan Somers 	if (faulted)
1917a0c41d5SAlan Somers 		return (VDEV_STATE_FAULTED);
1927a0c41d5SAlan Somers 	if (degraded)
1937a0c41d5SAlan Somers 		return (VDEV_STATE_DEGRADED);
1947a0c41d5SAlan Somers 	return (VDEV_STATE_HEALTHY);
1957a0c41d5SAlan Somers }
1967a0c41d5SAlan Somers 
1977a0c41d5SAlan Somers std::list<Vdev>
Children()1987a0c41d5SAlan Somers Vdev::Children()
1997a0c41d5SAlan Somers {
2007a0c41d5SAlan Somers 	nvlist_t **vdevChildren;
2017a0c41d5SAlan Somers 	int result;
2027a0c41d5SAlan Somers 	u_int numChildren;
2037a0c41d5SAlan Somers 	std::list<Vdev> children;
2047a0c41d5SAlan Somers 
2057a0c41d5SAlan Somers 	if (m_poolConfig == NULL || m_config == NULL)
2067a0c41d5SAlan Somers 		return (children);
2077a0c41d5SAlan Somers 
2087a0c41d5SAlan Somers 	result = nvlist_lookup_nvlist_array(m_config,
2097a0c41d5SAlan Somers 	    ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren);
2107a0c41d5SAlan Somers 	if (result != 0)
2117a0c41d5SAlan Somers 		return (children);
2127a0c41d5SAlan Somers 
2137a0c41d5SAlan Somers 	for (u_int c = 0;c < numChildren; c++)
2147a0c41d5SAlan Somers 		children.push_back(Vdev(m_poolConfig, vdevChildren[c]));
2157a0c41d5SAlan Somers 
2167a0c41d5SAlan Somers 	return (children);
2177a0c41d5SAlan Somers }
2187a0c41d5SAlan Somers 
2197a0c41d5SAlan Somers Vdev
RootVdev()2207a0c41d5SAlan Somers Vdev::RootVdev()
2217a0c41d5SAlan Somers {
2227a0c41d5SAlan Somers 	nvlist_t *rootVdev;
2237a0c41d5SAlan Somers 
2247a0c41d5SAlan Somers 	if (m_poolConfig == NULL)
2257a0c41d5SAlan Somers 		return (NonexistentVdev);
2267a0c41d5SAlan Somers 
2277a0c41d5SAlan Somers 	if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE,
2287a0c41d5SAlan Somers 	    &rootVdev) != 0)
2297a0c41d5SAlan Somers 		return (NonexistentVdev);
2307a0c41d5SAlan Somers 	return (Vdev(m_poolConfig, rootVdev));
2317a0c41d5SAlan Somers }
2327a0c41d5SAlan Somers 
2337a0c41d5SAlan Somers /*
2347a0c41d5SAlan Somers  * Find our parent.  This requires doing a traversal of the config; we can't
2357a0c41d5SAlan Somers  * cache it as leaf vdevs may change their pool config location (spare,
2367a0c41d5SAlan Somers  * replacing, mirror, etc).
2377a0c41d5SAlan Somers  */
2387a0c41d5SAlan Somers Vdev
Parent()2397a0c41d5SAlan Somers Vdev::Parent()
2407a0c41d5SAlan Somers {
2417a0c41d5SAlan Somers 	std::list<Vdev> to_examine;
2427a0c41d5SAlan Somers 	std::list<Vdev> children;
2437a0c41d5SAlan Somers 	std::list<Vdev>::iterator children_it;
2447a0c41d5SAlan Somers 
2457a0c41d5SAlan Somers 	to_examine.push_back(RootVdev());
2467a0c41d5SAlan Somers 	for (;;) {
2477a0c41d5SAlan Somers 		if (to_examine.empty())
2487a0c41d5SAlan Somers 			return (NonexistentVdev);
2497a0c41d5SAlan Somers 		Vdev vd = to_examine.front();
2507a0c41d5SAlan Somers 		if (vd.DoesNotExist())
2517a0c41d5SAlan Somers 			return (NonexistentVdev);
2527a0c41d5SAlan Somers 		to_examine.pop_front();
2537a0c41d5SAlan Somers 		children = vd.Children();
2547a0c41d5SAlan Somers 		children_it = children.begin();
2557a0c41d5SAlan Somers 		for (;children_it != children.end(); children_it++) {
2567a0c41d5SAlan Somers 			Vdev child = *children_it;
2577a0c41d5SAlan Somers 
2587a0c41d5SAlan Somers 			if (child.GUID() == GUID())
2597a0c41d5SAlan Somers 				return (vd);
2607a0c41d5SAlan Somers 			to_examine.push_front(child);
2617a0c41d5SAlan Somers 		}
2627a0c41d5SAlan Somers 	}
2637a0c41d5SAlan Somers }
2647a0c41d5SAlan Somers 
2657a0c41d5SAlan Somers bool
IsAvailableSpare() const2667a0c41d5SAlan Somers Vdev::IsAvailableSpare() const
2677a0c41d5SAlan Somers {
2687a0c41d5SAlan Somers 	/* If we have a pool guid, we cannot be an available spare. */
2697a0c41d5SAlan Somers 	if (PoolGUID())
2707a0c41d5SAlan Somers 		return (false);
2717a0c41d5SAlan Somers 
2727a0c41d5SAlan Somers 	return (true);
2737a0c41d5SAlan Somers }
2747a0c41d5SAlan Somers 
2757a0c41d5SAlan Somers bool
IsSpare()2767a0c41d5SAlan Somers Vdev::IsSpare()
2777a0c41d5SAlan Somers {
2787a0c41d5SAlan Somers 	uint64_t spare;
2797a0c41d5SAlan Somers 	if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0)
2807a0c41d5SAlan Somers 		return (false);
2817a0c41d5SAlan Somers 	return (spare != 0);
2827a0c41d5SAlan Somers }
2837a0c41d5SAlan Somers 
2847a0c41d5SAlan Somers bool
IsActiveSpare() const2857a0c41d5SAlan Somers Vdev::IsActiveSpare() const
2867a0c41d5SAlan Somers {
2877a0c41d5SAlan Somers 	vdev_stat_t *vs;
2887a0c41d5SAlan Somers 	uint_t c;
2897a0c41d5SAlan Somers 
2907a0c41d5SAlan Somers 	if (m_poolConfig == NULL)
2917a0c41d5SAlan Somers 		return (false);
2927a0c41d5SAlan Somers 
2937a0c41d5SAlan Somers 	(void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS,
2947a0c41d5SAlan Somers 	    reinterpret_cast<uint64_t **>(&vs), &c);
2957a0c41d5SAlan Somers 	if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED)
2967a0c41d5SAlan Somers 		return (false);
2977a0c41d5SAlan Somers 	return (true);
2987a0c41d5SAlan Somers }
2997a0c41d5SAlan Somers 
3007a0c41d5SAlan Somers bool
IsResilvering() const3017a0c41d5SAlan Somers Vdev::IsResilvering() const
3027a0c41d5SAlan Somers {
3037a0c41d5SAlan Somers 	pool_scan_stat_t *ps = NULL;
3047a0c41d5SAlan Somers 	uint_t c;
3057a0c41d5SAlan Somers 
3067a0c41d5SAlan Somers 	if (State() != VDEV_STATE_HEALTHY)
3077a0c41d5SAlan Somers 		return (false);
3087a0c41d5SAlan Somers 
3097a0c41d5SAlan Somers 	(void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS,
3107a0c41d5SAlan Somers 	    reinterpret_cast<uint64_t **>(&ps), &c);
3117a0c41d5SAlan Somers 	if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER)
3127a0c41d5SAlan Somers 		return (false);
3137a0c41d5SAlan Somers 	return (true);
3147a0c41d5SAlan Somers }
3157a0c41d5SAlan Somers 
3167a0c41d5SAlan Somers string
GUIDString() const3177a0c41d5SAlan Somers Vdev::GUIDString() const
3187a0c41d5SAlan Somers {
3197a0c41d5SAlan Somers 	stringstream vdevGUIDString;
3207a0c41d5SAlan Somers 
3217a0c41d5SAlan Somers 	vdevGUIDString << GUID();
3227a0c41d5SAlan Somers 	return (vdevGUIDString.str());
3237a0c41d5SAlan Somers }
3247a0c41d5SAlan Somers 
3257a0c41d5SAlan Somers string
Name(zpool_handle_t * zhp,bool verbose) const3267a0c41d5SAlan Somers Vdev::Name(zpool_handle_t *zhp, bool verbose) const
3277a0c41d5SAlan Somers {
3287a0c41d5SAlan Somers 	return (zpool_vdev_name(g_zfsHandle, zhp, m_config,
3297a0c41d5SAlan Somers 	    verbose ? B_TRUE : B_FALSE));
3307a0c41d5SAlan Somers }
3317a0c41d5SAlan Somers 
3327a0c41d5SAlan Somers string
Path() const3337a0c41d5SAlan Somers Vdev::Path() const
3347a0c41d5SAlan Somers {
335*2a58b312SMartin Matuska 	const char *path(NULL);
3367a0c41d5SAlan Somers 
3377a0c41d5SAlan Somers 	if ((m_config != NULL)
3387a0c41d5SAlan Somers 	    && (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0))
3397a0c41d5SAlan Somers 		return (path);
3407a0c41d5SAlan Somers 
3417a0c41d5SAlan Somers 	return ("");
3427a0c41d5SAlan Somers }
3437a0c41d5SAlan Somers 
3447a0c41d5SAlan Somers string
PhysicalPath() const3457a0c41d5SAlan Somers Vdev::PhysicalPath() const
3467a0c41d5SAlan Somers {
347*2a58b312SMartin Matuska 	const char *path(NULL);
3487a0c41d5SAlan Somers 
3497a0c41d5SAlan Somers 	if ((m_config != NULL) && (nvlist_lookup_string(m_config,
3507a0c41d5SAlan Somers 				    ZPOOL_CONFIG_PHYS_PATH, &path) == 0))
3517a0c41d5SAlan Somers 		return (path);
3527a0c41d5SAlan Somers 
3537a0c41d5SAlan Somers 	return ("");
3547a0c41d5SAlan Somers }
355