Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 14 Oct 2013 23:31:18 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r256476 - projects/zfsd/head/cddl/sbin/zfsd
Message-ID:  <201310142331.r9ENVIVR034376@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Mon Oct 14 23:31:18 2013
New Revision: 256476
URL: http://svnweb.freebsd.org/changeset/base/256476

Log:
  Improve zfsd's spare handling.
  
  	Zfsd can now:
  	- Spare broken spares, whether a case file is created for the
  	  original drive or for the previous spare.
  	- Avoid sparing spares that are either resilvering or healthy.  This
  	  enables failover of multiple devices in a pool.  It also means
  	  that zfsd doesn't require more spares than are actually needed to
  	  make a pool whole again.
  
  	cddl/sbin/zfsd/vdev.h:
  	cddl/sbin/zfsd/vdev.cc:
  	- Add NonexistentVdev, a singleton instance of Vdev that represents
  	  a vdev that doesn't exist.  Supporting this are new methods:
  	  - Vdev::Vdev(), an empty copy constructor.
  	  - Vdev::DoesNotExist(), which returns true if the vdev doesn't
  	    exist.
  	- Add vdev tree methods of use for purposes for which VdevIterator
  	  is not appropriate:
  	  - Vdev::Children(), which returns a list of the vdev's children.
  	  - Vdev::RootVdev(), which returns the root vdev of the vdev's pool.
  	  - Vdev::Parent(), which returns the vdev's parent.  This works by
  	    traversing the tree until it finds a vdev whose children include
  	    the caller object.
  	- Add several useful vdev instance methods:
  	  - Vdev::IsAvailableSpare(), which returns whether the vdev is an
  	    unassigned and usable spare.
  	  - Vdev::Name(), which returns the vdev's name; it can be made to
  	    return a "verbose" name too (e.g. "spare" vs. "spare-1").
  	  - Vdev::IsSpare(), Vdev::IsActiveSpare(), and Vdev::IsResilvering().
  	- While I'm here, refactor Vdev::Vdev(*) so that common
  	  initialization code only exists in one place.
  
  	cddl/sbin/zfsd/case_file.h:
  	cddl/sbin/zfsd/case_file.cc:
  	- Add CaseFile::BeingReplacedBy(), which returns the device
  	  replacing the current device, if one exists.  Performing this
  	  requires finding the vdev's parent and checking the state of its
  	  children.
  	- Change CaseFile::Replace() to allow the caller to specify whether
  	  the case's vdev is being replaced by a spare.  This enables
  	  Replace() to detect the appropriate device to perform the actual
  	  replace on.
  	- Add CaseFile::CaseVdev() to obtain a Vdev for the case file, and
  	  use it in several places that perform the operation.
  
  	cddl/sbin/zfsd/case_file.cc:
  	- Improve logging for case file evaluation so that the action chosen
  	  is always logged.
  	- While I'm here, make zpool lookups consistent and always generate
  	  a zpool_handle_t * local to the function where it's needed.
  
  	cddl/sbin/zfsd/vdev.h:
  	cddl/sbin/zfsd/zfsd_exception.cc:
  	- Add <list>, now required because of Vdev::Children() in vdev.h.
  
  Submitted by:	will
  Approved by:	ken (mentor)
  Sponsored by:	Spectra Logic Corporation

Modified:
  projects/zfsd/head/cddl/sbin/zfsd/case_file.cc
  projects/zfsd/head/cddl/sbin/zfsd/case_file.h
  projects/zfsd/head/cddl/sbin/zfsd/vdev.cc
  projects/zfsd/head/cddl/sbin/zfsd/vdev.h
  projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.cc

Modified: projects/zfsd/head/cddl/sbin/zfsd/case_file.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/case_file.cc	Mon Oct 14 23:24:53 2013	(r256475)
+++ projects/zfsd/head/cddl/sbin/zfsd/case_file.cc	Mon Oct 14 23:31:18 2013	(r256476)
@@ -233,27 +233,16 @@ bool
 CaseFile::RefreshVdevState()
 {
 	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
-	if (zpl.empty()) {
-		stringstream msg;
-		msg << "CaseFile::RefreshVdevState: Unknown pool for Vdev(";
-		msg << m_poolGUID << "," << m_vdevGUID << ").";
-		syslog(LOG_INFO, "%s", msg.str().c_str());
-			return (false);
-	}
+	zpool_handle_t *casePool(zpl.empty() ? NULL : zpl.front());
+	if (casePool == NULL)
+		return (false);
 
-	zpool_handle_t *casePool(zpl.front());
-	nvlist_t       *vdevConfig = VdevIterator(casePool).Find(VdevGUID());
-	if (vdevConfig == NULL) {
-		stringstream msg;
-		syslog(LOG_INFO,
-		       "CaseFile::RefreshVdevState: Unknown Vdev(%s,%s).\n",
-		       PoolGUIDString().c_str(), PoolGUIDString().c_str());
+	Vdev vd(casePool, CaseVdev(casePool));
+	if (vd.DoesNotExist())
 		return (false);
-	}
-	Vdev caseVdev(casePool, vdevConfig);
 
-	m_vdevState    = caseVdev.State();
-	m_vdevPhysPath = caseVdev.PhysicalPath();
+	m_vdevState    = vd.State();
+	m_vdevPhysPath = vd.PhysicalPath();
 	return (true);
 }
 
@@ -261,8 +250,9 @@ bool
 CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev)
 {
 	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
+	zpool_handle_t *pool(zpl.empty() ? NULL : zpl.front());
 
-	if (zpl.empty() || !RefreshVdevState()) {
+	if (pool == NULL || !RefreshVdevState()) {
 		/*
 		 * The pool or vdev for this case file is no longer
 		 * part of the configuration.  This can happen
@@ -283,7 +273,6 @@ CaseFile::ReEvaluate(const string &devPa
 		 */
 		return (/*consumed*/false);
 	}
-	zpool_handle_t *pool(zpl.front());
 
 	if (VdevState() > VDEV_STATE_CANT_OPEN) {
 		/*
@@ -292,6 +281,8 @@ CaseFile::ReEvaluate(const string &devPa
 		 * use a newly inserted spare to replace a degraded
 		 * or faulted device.
 		 */
+		syslog(LOG_INFO, "CaseFile::ReEvaluate(%s,%s): Pool/Vdev ignored",
+		    PoolGUIDString().c_str(), VdevGUIDString().c_str());
 		return (/*consumed*/false);
 	}
 
@@ -360,7 +351,10 @@ CaseFile::ReEvaluate(const string &devPa
 		return (/*consumed*/false);
 	}
 
-	return (Replace(VDEV_TYPE_DISK, devPath.c_str()));
+	syslog(LOG_INFO, "CaseFile::ReEvaluate(%s/%s): Replacing with %s",
+	    PoolGUIDString().c_str(), VdevGUIDString().c_str(),
+	    devPath.c_str());
+	return (Replace(VDEV_TYPE_DISK, devPath.c_str(), /*isspare*/false));
 }
 
 bool
@@ -378,6 +372,7 @@ CaseFile::ReEvaluate(const ZfsEvent &eve
 		return (/*consumed*/true);
 	}
 	else if (event.Value("type") == "misc.fs.zfs.config_sync") {
+		RefreshVdevState();
 		if (VdevState() < VDEV_STATE_HEALTHY)
 			consumed = ActivateSpare();
 	}
@@ -460,27 +455,22 @@ CaseFile::ReEvaluate(const ZfsEvent &eve
 }
 
 
-/*
- * TODO: ensure that we don't activate a spare for a vdev that is already being
- * replaced by another spare.
- */
 bool
 CaseFile::ActivateSpare() {
 	nvlist_t	*config, *nvroot;
 	nvlist_t       **spares;
-	zpool_handle_t	*zhp;
 	char		*devPath, *vdev_type;
 	const char	*poolname;
 	u_int		 nspares, i;
 	int		 error;
 
 	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
-	if (zpl.empty()) {
+	zpool_handle_t	*zhp(zpl.empty() ? NULL : zpl.front());
+	if (zhp == NULL) {
 		syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool "
 		       "for pool_guid %"PRIu64".", (uint64_t)m_poolGUID);
 		return (false);
 	}
-	zhp = zpl.front();
 	poolname = zpool_get_name(zhp);
 	config = zpool_get_config(zhp, NULL);
 	if (config == NULL) {
@@ -545,7 +535,7 @@ CaseFile::ActivateSpare() {
 		return (false);
 	}
 
-	return (Replace(vdev_type, devPath));
+	return (Replace(vdev_type, devPath, /*isspare*/true));
 }
 
 void
@@ -909,13 +899,15 @@ CaseFile::OnGracePeriodEnded()
 {
 	bool should_fault, should_degrade;
 	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
+	zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front());
+
 	m_events.splice(m_events.begin(), m_tentativeEvents);
 	should_fault = ShouldFault();
 	should_degrade = ShouldDegrade();
 
 	if (should_fault || should_degrade) {
-		if (zpl.empty()
-		 || (VdevIterator(zpl.front()).Find(m_vdevGUID)) == NULL) {
+		if (zhp == NULL
+		 || (VdevIterator(zhp).Find(m_vdevGUID)) == NULL) {
 			/*
 			 * Either the pool no longer exists
 			 * or this vdev is no longer a member of
@@ -930,7 +922,7 @@ CaseFile::OnGracePeriodEnded()
 	/* A fault condition has priority over a degrade condition */
 	if (ShouldFault()) {
 		/* Fault the vdev and close the case. */
-		if (zpool_vdev_fault(zpl.front(), (uint64_t)m_vdevGUID,
+		if (zpool_vdev_fault(zhp, (uint64_t)m_vdevGUID,
 				       VDEV_AUX_ERR_EXCEEDED) == 0) {
 			syslog(LOG_INFO, "Faulting vdev(%s/%s)",
 			       PoolGUIDString().c_str(),
@@ -948,7 +940,7 @@ CaseFile::OnGracePeriodEnded()
 	}
 	else if (ShouldDegrade()) {
 		/* Degrade the vdev and close the case. */
-		if (zpool_vdev_degrade(zpl.front(), (uint64_t)m_vdevGUID,
+		if (zpool_vdev_degrade(zhp, (uint64_t)m_vdevGUID,
 				       VDEV_AUX_ERR_EXCEEDED) == 0) {
 			syslog(LOG_INFO, "Degrading vdev(%s/%s)",
 			       PoolGUIDString().c_str(),
@@ -967,22 +959,86 @@ CaseFile::OnGracePeriodEnded()
 	Serialize();
 }
 
+Vdev
+CaseFile::BeingReplacedBy(zpool_handle_t *zhp) {
+	Vdev vd(zhp, CaseVdev(zhp));
+	std::list<Vdev> children;
+	std::list<Vdev>::iterator children_it;
+
+	Vdev parent(vd.Parent());
+	Vdev replacing(NonexistentVdev);
+
+	/*
+	 * To determine whether we are being replaced by another spare that
+	 * is still working, then make sure that it is currently spared and
+	 * that the spare is either resilvering or healthy.  If any of these
+	 * conditions fail, then we are not being replaced by a spare.
+	 *
+	 * If the spare is healthy, then the case file should be closed very
+	 * soon after this check.
+	 */
+	if (parent.DoesNotExist()
+	 || parent.Name(zhp, /*verbose*/false) != "spare")
+		return (NonexistentVdev);
+
+	children = parent.Children();
+	children_it = children.begin();
+	for (;children_it != children.end(); children_it++) {
+		Vdev child = *children_it;
+
+		/* Skip our vdev. */
+		if (child.GUID() == VdevGUID())
+			continue;
+		/*
+		 * Accept the first child that doesn't match our GUID, or
+		 * any resilvering/healthy device if one exists.
+		 */
+		if (replacing.DoesNotExist() || child.IsResilvering()
+		 || child.State() == VDEV_STATE_HEALTHY)
+			replacing = child;
+	}
+
+	return (replacing);
+}
+
 bool
-CaseFile::Replace(const char* vdev_type, const char* path) {
+CaseFile::Replace(const char* vdev_type, const char* path, bool isspare) {
 	nvlist_t *nvroot, *newvd;
-	zpool_handle_t *zhp;
-	const char* poolname;
+	const char *poolname;
+	string oldstr(VdevGUIDString());
 	bool retval = true;
 
 	/* Figure out what pool we're working on */
 	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
-	if (zpl.empty()) {
+	zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front());
+	if (zhp == NULL) {
 		syslog(LOG_ERR, "CaseFile::Replace: could not find pool for "
 		       "pool_guid %"PRIu64".", (uint64_t)m_poolGUID);
 		return (false);
 	}
-	zhp = zpl.front();
 	poolname = zpool_get_name(zhp);
+	Vdev vd(zhp, CaseVdev(zhp));
+	Vdev replaced(BeingReplacedBy(zhp));
+
+	if (!vd.IsSpare() && !replaced.DoesNotExist()) {
+		/* If we are already being replaced by a working spare, pass. */
+		if (replaced.IsResilvering()
+		 || replaced.State() == VDEV_STATE_HEALTHY) {
+			syslog(LOG_INFO, "CaseFile::Replace(%s->%s): already "
+			    "replaced", VdevGUIDString().c_str(), path);
+			return (/*consumed*/false);
+		}
+		/*
+		 * If we have already been replaced by a spare, but that spare
+		 * is broken, we must spare the spare, not the original device.
+		 */
+		if (isspare) {
+			oldstr = replaced.GUIDString();
+			syslog(LOG_INFO, "CaseFile::Replace(%s->%s): sparing "
+			    "broken spare %s instead", VdevGUIDString().c_str(),
+			    path, oldstr.c_str());
+		}
+	}
 
 	/*
 	 * Build a root vdev/leaf vdev configuration suitable for
@@ -994,9 +1050,8 @@ CaseFile::Replace(const char* vdev_type,
 
 	if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0
 	 || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) {
-		syslog(LOG_ERR, "Replace vdev(%s/%s): "
-		       "Unable to allocate configuration data.\n",
-		       poolname, VdevGUIDString().c_str());
+		syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to allocate "
+		    "configuration data.", poolname, oldstr.c_str());
 		if (nvroot != NULL)
 			nvlist_free(nvroot);
 		return (false);
@@ -1006,9 +1061,8 @@ CaseFile::Replace(const char* vdev_type,
 	 || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0
 	 || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
 				    &newvd, 1) != 0) {
-		syslog(LOG_ERR, "Replace vdev(%s/%s): "
-		       "Unable to initialize configuration data.\n",
-		       poolname, VdevGUIDString().c_str());
+		syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to initialize "
+		    "configuration data.", poolname, oldstr.c_str());
 		nvlist_free(newvd);
 		nvlist_free(nvroot);
 		return (true);
@@ -1017,19 +1071,15 @@ CaseFile::Replace(const char* vdev_type,
 	/* Data was copied when added to the root vdev. */
 	nvlist_free(newvd);
 
-	if (zpool_vdev_attach(zhp, VdevGUIDString().c_str(),
-			      path, nvroot, /*replace*/B_TRUE) != 0) {
-		syslog(LOG_ERR,
-		       "Replace vdev(%s/%s): %s: %s\n",
-		       poolname, VdevGUIDString().c_str(),
-		       libzfs_error_action(g_zfsHandle),
-		       libzfs_error_description(g_zfsHandle));
-		retval = false;
-	} else {
+	retval = (zpool_vdev_attach(zhp, oldstr.c_str(), path, nvroot,
+	    /*replace*/B_TRUE) == 0);
+	if (retval)
 		syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n",
-		       poolname, VdevGUIDString().c_str(),
-		       path);
-	}
+		    poolname, oldstr.c_str(), path);
+	else
+		syslog(LOG_ERR, "Replace vdev(%s/%s): %s: %s\n",
+		    poolname, oldstr.c_str(), libzfs_error_action(g_zfsHandle),
+		    libzfs_error_description(g_zfsHandle));
 	nvlist_free(nvroot);
 
 	return (retval);
@@ -1062,3 +1112,9 @@ CaseFile::ShouldFault() const
 	return (std::count_if(m_events.begin(), m_events.end(),
 	    		      IsIOEvent) > ZFS_DEGRADE_IO_COUNT);
 }
+
+nvlist_t *
+CaseFile::CaseVdev(zpool_handle_t *zhp) const
+{
+	return (VdevIterator(zhp).Find(VdevGUID()));
+}

Modified: projects/zfsd/head/cddl/sbin/zfsd/case_file.h
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/case_file.h	Mon Oct 14 23:24:53 2013	(r256475)
+++ projects/zfsd/head/cddl/sbin/zfsd/case_file.h	Mon Oct 14 23:31:18 2013	(r256476)
@@ -326,10 +326,21 @@ protected:
 	 * \param vdev_type   The type of the new vdev.  Usually either
 	 *                    VDEV_TYPE_DISK or VDEV_TYPE_FILE
 	 * \param path        The file system path to the new vdev
+	 * \param isspare     Whether the new vdev is a spare
 	 *
 	 * \return            true iff the replacement was successful
 	 */
-	bool Replace(const char* vdev_type, const char* path);
+	bool Replace(const char* vdev_type, const char* path, bool isspare);
+
+	/**
+	 * \brief Which vdev, if any, is replacing ours.
+	 *
+	 * \param zhp		Pool handle state from the caller context
+	 *
+	 * \return		the vdev that is currently replacing ours,
+	 *			or NonexistentVdev if there isn't one.
+	 */
+	Vdev BeingReplacedBy(zpool_handle_t *zhp);
 
 	/**
 	 * \brief All CaseFiles being tracked by ZFSD.
@@ -371,6 +382,9 @@ protected:
 	 * \brief Callout activated when a grace period
 	 */
 	Callout		  m_tentativeTimer;
+
+private:
+	nvlist_t	*CaseVdev(zpool_handle_t *zhp)	const;
 };
 
 inline DevCtl::Guid

Modified: projects/zfsd/head/cddl/sbin/zfsd/vdev.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/vdev.cc	Mon Oct 14 23:24:53 2013	(r256475)
+++ projects/zfsd/head/cddl/sbin/zfsd/vdev.cc	Mon Oct 14 23:31:18 2013	(r256476)
@@ -37,6 +37,7 @@
  *
  * Implementation of the Vdev class.
  */
+#include <syslog.h>
 #include <sys/cdefs.h>
 #include <sys/fs/zfs.h>
 
@@ -58,68 +59,76 @@
 #include "vdev_iterator.h"
 #include "zfsd.h"
 #include "zfsd_exception.h"
+#include "zpool_list.h"
 
 __FBSDID("$FreeBSD$");
 /*============================ Namespace Control =============================*/
 using std::string;
 using std::stringstream;
 
+//- Special objects -----------------------------------------------------------
+Vdev NonexistentVdev;
+
+//- Vdev Inline Public Methods ------------------------------------------------
 /*=========================== Class Implementations ==========================*/
 /*----------------------------------- Vdev -----------------------------------*/
-Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config)
- : m_poolConfig(zpool_get_config(pool, NULL)),
-   m_config(config)
+
+/* Special constructor for NonexistentVdev. */
+Vdev::Vdev()
+ : m_poolConfig(NULL),
+   m_config(NULL)
+{}
+
+bool
+Vdev::VdevLookupPoolGuid()
 {
-	uint64_t raw_guid;
-	if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID,
-				 &raw_guid) != 0)
-		throw ZfsdException("Unable to extract pool GUID "
-				    "from pool handle.");
-	m_poolGUID = raw_guid;
+	uint64_t guid;
+	if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid))
+		return (false);
+	m_poolGUID = guid;
+	return (true);
+}
 
-	if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &raw_guid) != 0)
+void
+Vdev::VdevLookupGuid()
+{
+	uint64_t guid;
+	if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0)
 		throw ZfsdException("Unable to extract vdev GUID "
 				    "from vdev config data.");
-	m_vdevGUID = raw_guid;
+	m_vdevGUID = guid;
+}
+
+Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config)
+ : m_poolConfig(zpool_get_config(pool, NULL)),
+   m_config(config)
+{
+	if (!VdevLookupPoolGuid())
+		throw ZfsdException("Can't extract pool GUID from handle.");
+	VdevLookupGuid();
 }
 
 Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config)
  : m_poolConfig(poolConfig),
    m_config(config)
 {
-	uint64_t raw_guid;
-	if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID,
-				 &raw_guid) != 0)
-		throw ZfsdException("Unable to extract pool GUID "
-				    "from pool handle.");
-	m_poolGUID = raw_guid;
-
-	if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &raw_guid) != 0)
-		throw ZfsdException("Unable to extract vdev GUID "
-				    "from vdev config data.");
-	m_vdevGUID = raw_guid;
+	if (!VdevLookupPoolGuid())
+		throw ZfsdException("Can't extract pool GUID from config.");
+	VdevLookupGuid();
 }
 
 Vdev::Vdev(nvlist_t *labelConfig)
- : m_poolConfig(labelConfig)
+ : m_poolConfig(labelConfig),
+   m_config(labelConfig)
 {
-	uint64_t raw_guid;
-
 	/*
 	 * Spares do not have a Pool GUID.  Tolerate its absence.
 	 * Code accessing this Vdev in a context where the Pool GUID is
 	 * required will find it invalid (as it is upon Vdev construction)
 	 * and act accordingly.
 	 */
-	if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_POOL_GUID,
-				 &raw_guid) == 0)
-		m_poolGUID = raw_guid;
-
-	if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_GUID,
-				 &raw_guid) != 0)
-		throw ZfsdException("Unable to extract vdev GUID "
-				    "from vdev label data.");
-	m_vdevGUID = raw_guid;
+	(void) VdevLookupPoolGuid();
+	VdevLookupGuid();
 
 	try {
 		m_config = VdevIterator(labelConfig).Find(m_vdevGUID);
@@ -172,6 +181,125 @@ Vdev::State() const
 	return (VDEV_STATE_HEALTHY);
 }
 
+std::list<Vdev>
+Vdev::Children()
+{
+	nvlist_t **vdevChildren;
+	int result;
+	u_int numChildren;
+	std::list<Vdev> children;
+
+	if (m_poolConfig == NULL || m_config == NULL)
+		return (children);
+
+	result = nvlist_lookup_nvlist_array(m_config,
+	    ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren);
+	if (result != 0)
+		return (children);
+
+	for (u_int c = 0;c < numChildren; c++)
+		children.push_back(Vdev(m_poolConfig, vdevChildren[c]));
+
+	return (children);
+}
+
+Vdev
+Vdev::RootVdev()
+{
+	nvlist_t *rootVdev;
+
+	if (m_poolConfig == NULL)
+		return (NonexistentVdev);
+
+	if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE,
+	    &rootVdev) != 0)
+		return (NonexistentVdev);
+	return (Vdev(m_poolConfig, rootVdev));
+}
+
+/*
+ * Find our parent.  This requires doing a traversal of the config; we can't
+ * cache it as leaf vdevs may change their pool config location (spare,
+ * replacing, mirror, etc).
+ */
+Vdev
+Vdev::Parent()
+{
+	std::list<Vdev> to_examine;
+	std::list<Vdev> children;
+	std::list<Vdev>::iterator children_it;
+
+	to_examine.push_back(RootVdev());
+	for (;;) {
+		if (to_examine.empty())
+			return (NonexistentVdev);
+		Vdev vd = to_examine.front();
+		if (vd.DoesNotExist())
+			return (NonexistentVdev);
+		to_examine.pop_front();
+		children = vd.Children();
+		children_it = children.begin();
+		for (;children_it != children.end(); children_it++) {
+			Vdev child = *children_it;
+
+			if (child.GUID() == GUID())
+				return (vd);
+			to_examine.push_front(child);
+		}
+	}
+}
+
+bool
+Vdev::IsAvailableSpare() const
+{
+	/* If we have a pool guid, we cannot be an available spare. */
+	if (PoolGUID())
+		return (false);
+
+	return (true);
+}
+
+bool
+Vdev::IsSpare()
+{
+	uint64_t spare;
+	if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0)
+		return (false);
+	return (spare != 0);
+}
+
+bool
+Vdev::IsActiveSpare() const
+{
+	vdev_stat_t *vs;
+	uint_t c;
+
+	if (m_poolConfig == NULL)
+		return (false);
+
+	(void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS,
+	    reinterpret_cast<uint64_t **>(&vs), &c);
+	if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED)
+		return (false);
+	return (true);
+}
+
+bool
+Vdev::IsResilvering() const
+{
+	pool_scan_stat_t *ps = NULL;
+	uint_t c;
+
+	if (State() != VDEV_STATE_HEALTHY)
+		return (false);
+
+	(void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS,
+	    reinterpret_cast<uint64_t **>(&ps), &c);
+	if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER)
+		return (false);
+	return (true);
+}
+
 string
 Vdev::GUIDString() const
 {
@@ -182,6 +310,13 @@ Vdev::GUIDString() const
 }
 
 string
+Vdev::Name(zpool_handle_t *zhp, bool verbose) const
+{
+	return (zpool_vdev_name(g_zfsHandle, zhp, m_config,
+	    verbose ? B_TRUE : B_FALSE));
+}
+
+string
 Vdev::Path() const
 {
 	char *path(NULL);

Modified: projects/zfsd/head/cddl/sbin/zfsd/vdev.h
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/vdev.h	Mon Oct 14 23:24:53 2013	(r256475)
+++ projects/zfsd/head/cddl/sbin/zfsd/vdev.h	Mon Oct 14 23:31:18 2013	(r256476)
@@ -40,6 +40,7 @@
  * Header requirements:
  *
  *    #include <string>
+ *    #include <list>
  *
  *    #include <devctl/guid.h>
  */
@@ -103,6 +104,17 @@ public:
 	 */
 	Vdev(nvlist_t *vdevConfig);
 
+	/**
+	 * \brief No-op copy constructor for nonexistent vdevs.
+	 */
+	Vdev();
+	bool			DoesNotExist()	const;
+
+	/**
+	 * \brief Return a list of the vdev's children.
+	 */
+	std::list<Vdev>		 Children();
+
 	virtual DevCtl::Guid	 GUID()		const;
 	virtual DevCtl::Guid	 PoolGUID()	const;
 	virtual vdev_state	 State()	const;
@@ -111,14 +123,26 @@ public:
 	std::string		 GUIDString()	const;
 	nvlist_t		*PoolConfig()	const;
 	nvlist_t		*Config()	const;
+	Vdev			 Parent();
+	Vdev			 RootVdev();
+	std::string		 Name(zpool_handle_t *, bool verbose)	const;
+	bool			 IsSpare();
+	bool			 IsAvailableSpare()	const;
+	bool			 IsActiveSpare()	const;
+	bool			 IsResilvering()	const;
 
 private:
-	DevCtl::Guid m_poolGUID;
-	DevCtl::Guid m_vdevGUID;
-	nvlist_t    *m_poolConfig;
-	nvlist_t    *m_config;
+	void			 VdevLookupGuid();
+	bool			 VdevLookupPoolGuid();
+	DevCtl::Guid		 m_poolGUID;
+	DevCtl::Guid		 m_vdevGUID;
+	nvlist_t    		*m_poolConfig;
+	nvlist_t    		*m_config;
 };
 
+//- Special objects -----------------------------------------------------------
+extern Vdev NonexistentVdev;
+
 //- Vdev Inline Public Methods ------------------------------------------------
 inline DevCtl::Guid
 Vdev::PoolGUID() const
@@ -144,4 +168,10 @@ Vdev::Config() const
 	return (m_config);
 }
 
+inline bool
+Vdev::DoesNotExist() const
+{
+	return (m_config == NULL);
+}
+
 #endif /* _VDEV_H_ */

Modified: projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.cc	Mon Oct 14 23:24:53 2013	(r256475)
+++ projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.cc	Mon Oct 14 23:31:18 2013	(r256476)
@@ -41,6 +41,7 @@
 #include <syslog.h>
 
 #include <string>
+#include <list>
 #include <sstream>
 
 #include <devctl/exception.h>



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201310142331.r9ENVIVR034376>