From owner-svn-src-projects@FreeBSD.ORG Mon Oct 14 23:31:19 2013 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 9958BF0B; Mon, 14 Oct 2013 23:31:19 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 775632FB8; Mon, 14 Oct 2013 23:31:19 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9ENVJHV034381; Mon, 14 Oct 2013 23:31:19 GMT (envelope-from asomers@svn.freebsd.org) Received: (from asomers@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9ENVIVR034376; Mon, 14 Oct 2013 23:31:18 GMT (envelope-from asomers@svn.freebsd.org) Message-Id: <201310142331.r9ENVIVR034376@svn.freebsd.org> From: Alan Somers Date: Mon, 14 Oct 2013 23:31:18 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r256476 - projects/zfsd/head/cddl/sbin/zfsd X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 14 Oct 2013 23:31:19 -0000 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 , 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 children; + std::list::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 #include #include @@ -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::Children() +{ + nvlist_t **vdevChildren; + int result; + u_int numChildren; + std::list 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 to_examine; + std::list children; + std::list::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(&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(&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 + * #include * * #include */ @@ -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 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 #include +#include #include #include