Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 11 Oct 2013 22:44:15 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r256357 - projects/zfsd/head/cddl/sbin/zfsd
Message-ID:  <201310112244.r9BMiFrs064972@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Fri Oct 11 22:44:15 2013
New Revision: 256357
URL: http://svnweb.freebsd.org/changeset/base/256357

Log:
  zfsd will now try to activate a spare, if one is available, when a resource
  dissapears.  Other functionality, such as detaching the spare when the
  original device returns, is TBD.
  
  	cddl/sbin/zfsd/vdev_iterator.h
  	cddl/sbin/zfsd/vdev_iterator.cc
  	cddl/sbin/zfsd/zpool_list.cc
  	cddl/sbin/zfsd/dev_ctl_event.h
  	cddl/sbin/zfsd/case_file.h
  	cddl/sbin/zfsd/case_file.cc
  	cddl/sbin/zfsd/vdev.h
  	cddl/sbin/zfsd/vdev.cc
  		Created a new Guid class that can have a None value.
  		Modified the Vdev class to be able to represent available
  		spares, which do not have pool or vdev guids.
  
  	cddl/sbin/zfsd/case_file.h
  	cddl/sbin/zfsd/case_file.cc
  		Abstract device replacement into the CaseFile::Replace
  		method, which is used by both ActivateSpare and replace by
  		physical path.
  		Try to active a hotspare whenever a vdev dissappears.
  
  Submitted by:	asomers
  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/dev_ctl_event.cc
  projects/zfsd/head/cddl/sbin/zfsd/dev_ctl_event.h
  projects/zfsd/head/cddl/sbin/zfsd/vdev.cc
  projects/zfsd/head/cddl/sbin/zfsd/vdev.h
  projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.cc
  projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.h
  projects/zfsd/head/cddl/sbin/zfsd/zpool_list.cc

Modified: projects/zfsd/head/cddl/sbin/zfsd/case_file.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/case_file.cc	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/case_file.cc	Fri Oct 11 22:44:15 2013	(r256357)
@@ -66,8 +66,9 @@ const string  CaseFile::s_caseFilePath =
 const timeval CaseFile::s_removeGracePeriod = { 60 /*sec*/, 0 /*usec*/};
 
 //- CaseFile Static Public Methods ---------------------------------------------
+
 CaseFile *
-CaseFile::Find(uint64_t poolGUID, uint64_t vdevGUID)
+CaseFile::Find(Guid poolGUID, Guid vdevGUID)
 {
 	for (CaseFileList::iterator curCase = s_activeCases.begin();
 	     curCase != s_activeCases.end(); curCase++) {
@@ -163,16 +164,17 @@ CaseFile::RefreshVdevState()
 {
 	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
 	if (zpl.empty()) {
-		syslog(LOG_INFO,
-		       "CaseFile::RefreshVdevState: Unknown pool for "
-		       "Vdev(%ju,%ju).\n",
-		       m_poolGUID, m_vdevGUID);
-		return (false);
+		stringstream msg;
+		msg << "CaseFile::RefreshVdevState: Unknown pool for Vdev(";
+		msg << m_poolGUID << "," << m_vdevGUID << ").";
+		syslog(LOG_INFO, msg.str().c_str());
+			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());
@@ -288,55 +290,7 @@ CaseFile::ReEvaluate(const string &devPa
 		return (/*consumed*/false);
 	}
 
-	/*
-	 * Build a root vdev/leaf vdev configuration suitable for
-	 * zpool_vdev_attach. Only enough data for the kernel to find
-	 * the device (i.e. type and disk device node path) are needed.
-	 */
-	nvlist_t *nvroot(NULL);
-	nvlist_t *newvd(NULL);
-	if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0
-	 || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) {
-		syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: "
-		       "Unable to allocate configuration data.\n",
-		       zpool_get_name(pool), VdevGUIDString().c_str());
-		if (nvroot != NULL)
-			nvlist_free(nvroot);
-		return (/*consumed*/false);
-	}
-
-	if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, VDEV_TYPE_DISK) != 0
-	 || nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, devPath.c_str()) != 0
-	 || 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) by physical path: "
-		       "Unable to initialize configuration data.\n",
-		       zpool_get_name(pool), VdevGUIDString().c_str());
-		nvlist_free(newvd);
-		nvlist_free(nvroot);
-		return (/*consumed*/true);
-	}
-
-	/* Data was copied when added to the root vdev. */
-	nvlist_free(newvd);
-
-	if (zpool_vdev_attach(pool, VdevGUIDString().c_str(),
-			      devPath.c_str(), nvroot,
-			      /*replace*/B_TRUE) != 0) {
-		syslog(LOG_ERR,
-		       "Replace vdev(%s/%s) by physical path(attach): %s: %s\n",
-		       zpool_get_name(pool), VdevGUIDString().c_str(),
-		       libzfs_error_action(g_zfsHandle),
-		       libzfs_error_description(g_zfsHandle));
-	} else {
-		syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n",
-		       zpool_get_name(pool), VdevGUIDString().c_str(),
-		       devPath.c_str());
-	}
-	nvlist_free(nvroot);
-
-	return (true);
+	return (Replace(VDEV_TYPE_DISK, devPath.c_str()));
 }
 
 bool
@@ -385,6 +339,9 @@ CaseFile::ReEvaluate(const ZfsEvent &eve
 		 */
 		PurgeTentativeEvents();
 
+		/* Try to activate spares if they are available */
+		ActivateSpare();
+
 		/*
 		 * Rescan the drives in the system to see if a recent
 		 * drive arrival can be used to solve this case.
@@ -405,6 +362,83 @@ CaseFile::ReEvaluate(const ZfsEvent &eve
 	return (consumed || closed);
 }
 
+
+bool
+CaseFile::ActivateSpare() {
+	nvlist_t *config, *nvroot;
+	nvlist_t **spares;
+	zpool_handle_t *zhp;
+	char *devPath, *vdev_type;
+	const char* poolname;
+	unsigned nspares, i;
+
+	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
+	if (zpl.empty()) {
+		syslog(LOG_ERR, "CaseFile::Replace: could not find pool for "
+		    "pool_guid %ju", (uint64_t)m_poolGUID);
+		return (false);
+	}
+	zhp = zpl.front();
+	poolname = zpool_get_name(zhp);
+	config = zpool_get_config(zhp, NULL);
+	if (config == NULL) {
+		syslog(LOG_ERR,
+		    "ActivateSpare: Could not find pool config for pool %s",
+		    poolname);
+		return (false);
+	}
+	if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0){
+		syslog(LOG_ERR,
+		    "ActivateSpare: Could not find vdev tree for pool %s",
+		    poolname);
+		return (false);
+	}
+	nspares = 0;
+	nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares,
+	    &nspares);
+	if (nspares == 0) {
+		/* The pool has no spares configured */
+		return (false);
+	}
+	for (i = 0; i < nspares; i++) {
+		vdev_stat_t *vs;
+		unsigned nstats;
+
+		if (nvlist_lookup_uint64_array(spares[i],
+		    ZPOOL_CONFIG_VDEV_STATS, (uint64_t**)&vs, &nstats) != 0) {
+			syslog(LOG_ERR, "ActivateSpare: Could not find vdev "
+			    "stats for pool %s, spare %d",
+			    poolname, i);
+			return (false);
+		}
+
+		if ( (vs->vs_aux != VDEV_AUX_SPARED)
+		    && (vs->vs_state == VDEV_STATE_HEALTHY)) {
+			/* We found a usable spare */
+			break;
+		}
+	}
+
+	if (i == nspares) {
+		/* No available spares were found */
+		return (false);
+	}
+
+	if (nvlist_lookup_string(spares[i], ZPOOL_CONFIG_PATH, &devPath) != 0){
+		syslog(LOG_ERR, "ActivateSpare: Cannot determine the path of "
+		    "pool %s, spare %d", poolname, i);
+		return (false);
+	}
+
+	if (nvlist_lookup_string(spares[i], ZPOOL_CONFIG_TYPE, &vdev_type)!= 0){
+		syslog(LOG_ERR, "ActivateSpare: Cannot determine the vdev type "
+		    "of pool %s, spare %d", poolname, i);
+		return (false);
+	}
+
+	return (Replace(vdev_type, devPath));
+}
+
 void
 CaseFile::RegisterCallout(const DevCtlEvent &event)
 {   
@@ -519,7 +553,7 @@ CaseFile::DeSerializeFile(const char *fi
 
 		sscanf(fileName, "pool_%ju_vdev_%ju.case",
 		       &poolGUID, &vdevGUID);
-		existingCaseFile = Find(poolGUID, vdevGUID);
+		existingCaseFile = Find(Guid(poolGUID), Guid(vdevGUID));
 		if (existingCaseFile != NULL) {
 			/*
 			 * If the vdev is already degraded or faulted,
@@ -756,7 +790,7 @@ CaseFile::OnGracePeriodEnded()
 		}
 
 		/* Degrade the vdev and close the case. */
-		if (zpool_vdev_degrade(zpl.front(), m_vdevGUID,
+		if (zpool_vdev_degrade(zpl.front(), (uint64_t)m_vdevGUID,
 				       VDEV_AUX_ERR_EXCEEDED) == 0) {
 			Close();
 			return;
@@ -764,3 +798,69 @@ CaseFile::OnGracePeriodEnded()
 	}
 	Serialize();
 }
+
+bool
+CaseFile::Replace(const char* vdev_type, const char* path) {
+	nvlist_t *nvroot, *newvd;
+	zpool_handle_t *zhp;
+	const char* poolname;
+
+	/* Figure out what pool we're working on */
+	ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
+	if (zpl.empty()) {
+		syslog(LOG_ERR, "CaseFile::Replace: could not find pool for "
+		    "pool_guid %ju", (uint64_t)m_poolGUID);
+		return (false);
+	}
+	zhp = zpl.front();
+	poolname = zpool_get_name(zhp);
+
+	/*
+	 * Build a root vdev/leaf vdev configuration suitable for
+	 * zpool_vdev_attach. Only enough data for the kernel to find
+	 * the device (i.e. type and disk device node path) are needed.
+	 */
+	nvroot = NULL;
+	newvd = NULL;
+
+	if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0
+	 || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) {
+		syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: "
+		       "Unable to allocate configuration data.\n",
+		       poolname, VdevGUIDString().c_str());
+		if (nvroot != NULL)
+			nvlist_free(nvroot);
+		return (false);
+	}
+	if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, vdev_type) != 0
+	 || nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, path) != 0
+	 || 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) by physical path: "
+		       "Unable to initialize configuration data.\n",
+		       poolname, VdevGUIDString().c_str());
+		nvlist_free(newvd);
+		nvlist_free(nvroot);
+		return (true);
+	}
+
+	/* 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) by physical path(attach): %s: %s\n",
+		       poolname, VdevGUIDString().c_str(),
+		       libzfs_error_action(g_zfsHandle),
+		       libzfs_error_description(g_zfsHandle));
+	} else {
+		syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n",
+		       poolname, VdevGUIDString().c_str(),
+		       path);
+	}
+	nvlist_free(nvroot);
+
+	return (true);
+}

Modified: projects/zfsd/head/cddl/sbin/zfsd/case_file.h
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/case_file.h	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/case_file.h	Fri Oct 11 22:44:15 2013	(r256357)
@@ -95,7 +95,7 @@ public:
 	 * \return  If found, a pointer to a valid CaseFile object.
 	 *          Otherwise NULL.
 	 */
-	static CaseFile *Find(uint64_t poolGUID, uint64_t vdevGUID);
+	static CaseFile *Find(Guid poolGUID, Guid vdevGUID);
 
 	/**
 	 * \brief Find a CaseFile object by a vdev's current/last known
@@ -137,8 +137,8 @@ public:
 	 */
 	static void      PurgeAll();
 
-	uint64_t      PoolGUID()       const;
-	uint64_t      VdevGUID()       const;
+	Guid	      PoolGUID()       const;
+	Guid	      VdevGUID()       const;
 	vdev_state    VdevState()      const;
 	const string &PoolGUIDString() const;
 	const string &VdevGUIDString() const;
@@ -279,6 +279,30 @@ protected:
 	void OnGracePeriodEnded();
 
 	/**
+	 * \brief Attempt to activate a spare on this case's pool.
+	 *
+	 * Call this whenever a pool becomes degraded.  It will look for any
+	 * spare devices and activate one to replace the casefile's vdev.  It
+	 * will _not_ close the casefile; that should only happen when the
+	 * missing drive is replaced or the user promotes the spare.
+	 *
+	 * \return True if a spare was activated
+	 */
+	bool ActivateSpare();
+
+	/**
+	 * \brief replace a pool's vdev with another
+	 *
+	 * \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
+	 *
+	 * \return            true iff the replacement was successful
+	 *
+	 */
+	bool Replace(const char* vdev_type, const char* path);
+
+	/**
 	 * \brief All CaseFiles being tracked by ZFSD.
 	 */
 	static CaseFileList  s_activeCases;
@@ -307,8 +331,8 @@ protected:
 	 */
 	DevCtlEventList m_tentativeEvents;
 
-	uint64_t	m_poolGUID;
-	uint64_t	m_vdevGUID;
+	Guid		m_poolGUID;
+	Guid		m_vdevGUID;
 	vdev_state	m_vdevState;
 	string		m_poolGUIDString;
 	string		m_vdevGUIDString;
@@ -320,13 +344,13 @@ protected:
 	Callout		m_tentativeTimer;
 };
 
-inline uint64_t
+inline Guid
 CaseFile::PoolGUID() const
 {
 	return (m_poolGUID);
 }
 
-inline uint64_t
+inline Guid
 CaseFile::VdevGUID() const
 {
 	return (m_vdevGUID);

Modified: projects/zfsd/head/cddl/sbin/zfsd/dev_ctl_event.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/dev_ctl_event.cc	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/dev_ctl_event.cc	Fri Oct 11 22:44:15 2013	(r256357)
@@ -163,8 +163,11 @@ DevCtlEvent::CreateEvent(const string &e
 
 	EventFactoryKey key(type, nvpairs["system"]);
 	EventFactoryRegistry::iterator foundMethod(s_factoryRegistry.find(key));
-	if (foundMethod == s_factoryRegistry.end())
+	if (foundMethod == s_factoryRegistry.end()) {
+		syslog(LOG_INFO, "DevCtlEvent::CreateEvent: unhandled event %s",
+		    eventString.c_str());
 		return (NULL);
+	}
 	return ((foundMethod->second)(type, nvpairs, eventString));
 }
 
@@ -633,11 +636,13 @@ ZfsEvent::Process() const
 	}
 
 	/* Skip events that can't be handled. */
-	uint64_t poolGUID(PoolGUID());
+	Guid poolGUID(PoolGUID());
 	/* If there are no replicas for a pool, then it's not manageable. */
 	if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) {
-		syslog(LOG_INFO, "No replicas available for pool %ju"
-		    ", ignoring\n", (uintmax_t)poolGUID);
+		stringstream msg;
+		msg << "No replicas available for pool "  << poolGUID;
+		msg << ", ignoring";
+		syslog(LOG_INFO, msg.str().c_str());
 		return;
 	}
 
@@ -647,21 +652,25 @@ ZfsEvent::Process() const
 	 */
 	ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
 	if (zpl.empty()) {
+		stringstream msg;
 		bool queued = ZfsDaemon::SaveEvent(*this);
 		int priority = queued ? LOG_INFO : LOG_ERR;
-		syslog(priority,
-		    "ZfsEvent::Process: Event for unknown pool %ju %s",
-		    (uintmax_t)poolGUID, queued ? "queued" : "dropped");
+		msg << "ZfsEvent::Process: Event for unknown pool ";
+		msg << poolGUID << " ";
+		msg << (queued ? "queued" : "dropped");
+		syslog(priority, msg.str().c_str());
 		return;
 	}
 
 	nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID());
 	if (vdevConfig == NULL) {
+		stringstream msg;
 		bool queued = ZfsDaemon::SaveEvent(*this);
 		int priority = queued ? LOG_INFO : LOG_ERR;
-		syslog(priority,
-		    "ZfsEvent::Process: Event for unknown vdev %ju %s",
-		    (uintmax_t)poolGUID, queued ? "queued" : "dropped");
+		msg << "ZfsEvent::Process: Event for unknown vdev ";
+		msg << VdevGUID() << " ";
+		msg << (queued ? "queued" : "dropped");
+		syslog(priority, msg.str().c_str());
 		return;
 	}
 
@@ -679,8 +688,18 @@ ZfsEvent::ZfsEvent(DevCtlEvent::Type typ
 	 * These are zero on conversion failure as will happen if
 	 * Value returns the empty string.
 	 */
-	m_poolGUID = (uint64_t)strtoumax(Value("pool_guid").c_str(), NULL, 0);
-	m_vdevGUID = (uint64_t)strtoumax(Value("vdev_guid").c_str(), NULL, 0);
+	if (Contains("pool_guid")) {
+		m_poolGUID = (uint64_t)strtoumax(Value("pool_guid").c_str(),
+		    NULL, 0);
+	}
+	else
+		m_poolGUID = Guid();
+	if (Contains("vdev_guid")) {
+		m_vdevGUID = (uint64_t)strtoumax(Value("vdev_guid").c_str(),
+		    NULL, 0);
+	}
+	else
+		m_vdevGUID = Guid();
 }
 
 ZfsEvent::ZfsEvent(const ZfsEvent &src)

Modified: projects/zfsd/head/cddl/sbin/zfsd/dev_ctl_event.h
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/dev_ctl_event.h	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/dev_ctl_event.h	Fri Oct 11 22:44:15 2013	(r256357)
@@ -49,6 +49,8 @@
 #include <sys/fs/zfs.h>
 #include <libzfs.h>
 
+#include "vdev.h"
+
 /*============================ Namespace Control =============================*/
 using std::map;
 using std::pair;
@@ -473,8 +475,8 @@ public:
 	virtual void Process()		const;
 
 	const string &PoolName()	const;
-	uint64_t      PoolGUID()	const;
-	uint64_t      VdevGUID()	const;
+	Guid	      PoolGUID()	const;
+	Guid	      VdevGUID()	const;
 
 protected:
 	/** Constructor */
@@ -485,8 +487,8 @@ protected:
 
 	void ProcessPoolEvent()		const;
 
-	uint64_t m_poolGUID;
-	uint64_t m_vdevGUID;
+	Guid	 m_poolGUID;
+	Guid	 m_vdevGUID;
 };
 
 //- ZfsEvent Inline Public Methods --------------------------------------------
@@ -497,13 +499,13 @@ ZfsEvent::PoolName() const
 	return (Value("subsystem"));
 }
 
-inline uint64_t
+inline Guid
 ZfsEvent::PoolGUID() const
 {
 	return (m_poolGUID);
 }
 
-inline uint64_t
+inline Guid
 ZfsEvent::VdevGUID() const
 {
 	return (m_vdevGUID);

Modified: projects/zfsd/head/cddl/sbin/zfsd/vdev.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/vdev.cc	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/vdev.cc	Fri Oct 11 22:44:15 2013	(r256357)
@@ -50,51 +50,76 @@ __FBSDID("$FreeBSD$");
 using std::stringstream;
 
 /*=========================== Class Implementations ==========================*/
+/*----------------------------------- Guid -----------------------------------*/
+std::ostream& operator<< (std::ostream& out, Guid g){
+	if (g.isValid())
+		out << (uint64_t) g;
+	else
+		out << "None";
+	return (out);
+}
+
+
 /*----------------------------------- Vdev -----------------------------------*/
 Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config)
  : m_poolConfig(zpool_get_config(pool, NULL)),
    m_config(config)
 {
+	uint64_t raw_guid;
 	if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID,
-				 &m_poolGUID) != 0)
+				 &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, &m_vdevGUID) != 0)
+	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;
 }
 
 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,
-				 &m_poolGUID) != 0)
+				 &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, &m_vdevGUID) != 0)
+	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;
 }
 
 Vdev::Vdev(nvlist_t *labelConfig)
  : m_poolConfig(labelConfig)
 {
+	uint64_t raw_guid;
 	if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_POOL_GUID,
-				 &m_poolGUID) != 0)
-		throw ZfsdException("Unable to extract pool GUID "
-				    "from vdev label data.");
+				 &raw_guid) != 0)
+		m_vdevGUID = Guid();
+	else
+		m_poolGUID = raw_guid;
 
 	if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_GUID,
-				 &m_vdevGUID) != 0)
+				 &raw_guid) != 0)
 		throw ZfsdException("Unable to extract vdev GUID "
 				    "from vdev label data.");
-	m_config = VdevIterator(labelConfig).Find(m_vdevGUID);
-	if (m_config == NULL)
-		throw ZfsdException("Unable to find vdev config "
-				    "within vdev label data.");
+	m_vdevGUID = raw_guid;
+
+	try {
+		m_config = VdevIterator(labelConfig).Find(m_vdevGUID);
+	} catch (const ZfsdException &exp) {
+		/*
+		 * When reading a spare's label, it is normal not to find
+		 * a list of vdevs
+		 */
+		m_config = NULL;
+	}
 }
 
 vdev_state
@@ -103,6 +128,18 @@ Vdev::State() const
 	vdev_stat_t *vs;
 	uint_t       vsc;
 
+	if (m_config == NULL) {
+		/*
+		 * If we couldn't find the list of vdevs, that normally means
+		 * that this is an available hotspare.  In that case, we will
+		 * presume it to be healthy.  Even if this spare had formerly
+		 * been in use, been degraded, and been replaced, the act of
+		 * replacement wipes the degraded bit from the label.  So we
+		 * have no choice but to presume that it is healthy.
+		 */
+		return (VDEV_STATE_HEALTHY);
+	}
+
 	if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS,
 					(uint64_t **)&vs, &vsc) == 0)
 		return (static_cast<vdev_state>(vs->vs_state));
@@ -136,7 +173,8 @@ Vdev::Path() const
 {
 	char *path(NULL);
 
-	if (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0)
+	if ((m_config != NULL)
+	    && (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0))
 		return (path);
 
 	return ("");
@@ -147,7 +185,8 @@ Vdev::PhysicalPath() const
 {
 	char *path(NULL);
 
-	if (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PHYS_PATH, &path) == 0)
+	if ((m_config != NULL) && (nvlist_lookup_string(m_config,
+				    ZPOOL_CONFIG_PHYS_PATH, &path) == 0))
 		return (path);
 
 	return ("");

Modified: projects/zfsd/head/cddl/sbin/zfsd/vdev.h
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/vdev.h	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/vdev.h	Fri Oct 11 22:44:15 2013	(r256357)
@@ -40,11 +40,63 @@
 #ifndef	_VDEV_H_
 #define	_VDEV_H_
 
+#include <ostream>
 #include <string>
 
 #include <sys/fs/zfs.h>
 #include <libzfs.h>
 
+
+/**
+ * \brief Object that represents guids.
+ *
+ * It can generally be manipulated as a uint64_t, but with a special value
+ * "None" that does not equal any valid guid.
+ *
+ * As of this writing, spa_generate_guid() in spa_misc.c explicitly refuses to
+ * return a guid of 0.  So this class uses 0 as a flag value for "None".  In the
+ * future, if 0 is allowed to be a valid guid, the implementation of this class
+ * must change.
+ */
+class Guid
+{
+public:
+	/* Constructors */
+	Guid(uint64_t guid) : m_GUID(guid) {};
+	Guid() 				{ m_GUID = NONE_FLAG; };
+
+	/* Assignment */
+	Guid& operator=(const uint64_t& other) {
+		m_GUID = other;
+		return (*this);
+	};
+
+	/* Test the validity of this guid. */
+	bool isValid() const		{ return ((bool)m_GUID);	};
+
+	/* Comparison to other Guid operators */
+	bool operator==(const Guid& other) const {
+		return (m_GUID == other.m_GUID);
+	};
+	bool operator!=(const Guid& other) const {
+		return (m_GUID != other.m_GUID);
+	};
+
+	/* Integer conversion operators */
+	operator uint64_t() const	{ return (m_GUID);		};
+	operator bool() const		{ return (m_GUID != NONE_FLAG);	};
+
+protected:
+	const static uint64_t NONE_FLAG = 0;
+	/* The stored value.  0 is a flag for "None" */
+	uint64_t  m_GUID;
+};
+
+
+/** Convert the GUID into its string representation */
+std::ostream& operator<< (std::ostream& out, Guid g);
+
+
 /**
  * \brief Wrapper class for a vdev's name/value configuration list
  *        simplifying access to commonly used vdev attributes.
@@ -93,8 +145,8 @@ public:
 	 */
 	Vdev(nvlist_t *vdevConfig);
 
-	uint64_t	 GUID()		const;
-	uint64_t	 PoolGUID()	const;
+	Guid		 GUID()		const;
+	Guid		 PoolGUID()	const;
 	vdev_state	 State()	const;
 	std::string	 Path()		const;
 	std::string	 PhysicalPath()	const;
@@ -103,19 +155,19 @@ public:
 	nvlist_t	*Config()	const;
 
 private:
-	uint64_t  m_poolGUID;
-	uint64_t  m_vdevGUID;
+	Guid	  m_poolGUID;
+	Guid	  m_vdevGUID;
 	nvlist_t *m_poolConfig;
 	nvlist_t *m_config;
 };
 
-inline uint64_t
+inline Guid
 Vdev::PoolGUID() const
 {
 	return (m_poolGUID);
 }
 
-inline uint64_t
+inline Guid
 Vdev::GUID() const
 {
 	return (m_vdevGUID);

Modified: projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.cc	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.cc	Fri Oct 11 22:44:15 2013	(r256357)
@@ -126,7 +126,7 @@ VdevIterator::Each(VdevCallback_t *callB
 }
 
 nvlist_t *
-VdevIterator::Find(uint64_t vdevGUID)
+VdevIterator::Find(Guid vdevGUID)
 {
 	nvlist_t *vdevConfig;
 

Modified: projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.h
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.h	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.h	Fri Oct 11 22:44:15 2013	(r256357)
@@ -97,7 +97,7 @@ public:
 	 * Upon return, the VdevIterator's cursor points to the vdev just
 	 * past the returned vdev or end() if no matching vdev is found.
 	 */
-	nvlist_t *Find(uint64_t vdevGUID);
+	nvlist_t *Find(Guid vdevGUID);
 
 	/**
 	 * \brief Perform the specified operation on each leaf member of

Modified: projects/zfsd/head/cddl/sbin/zfsd/zpool_list.cc
==============================================================================
--- projects/zfsd/head/cddl/sbin/zfsd/zpool_list.cc	Fri Oct 11 22:19:45 2013	(r256356)
+++ projects/zfsd/head/cddl/sbin/zfsd/zpool_list.cc	Fri Oct 11 22:44:15 2013	(r256357)
@@ -35,6 +35,7 @@
  *
  * Implementation of the ZpoolList class.
  */
+#include "vdev.h"
 #include "zpool_list.h"
 #include "zfsd.h"
 
@@ -50,13 +51,13 @@ bool
 ZpoolList::ZpoolByGUID(zpool_handle_t *pool, nvlist_t *poolConfig,
 			   void *cbArg)
 {
-	uint64_t *desiredPoolGUID(static_cast<uint64_t *>(cbArg));
+	Guid *desiredPoolGUID(static_cast<Guid *>(cbArg));
 	uint64_t poolGUID;
 
 	/* We are only intested in the pool that matches our pool GUID. */
 	return (nvlist_lookup_uint64(poolConfig, ZPOOL_CONFIG_POOL_GUID,
 				     &poolGUID) == 0
-	     && poolGUID == *desiredPoolGUID);
+	     && poolGUID == (uint64_t)*desiredPoolGUID);
 }
 
 bool



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