/*-1* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions, and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* substantially similar to the "NO WARRANTY" disclaimer below12* ("Disclaimer") and any redistribution must be conditioned upon13* including a substantially similar Disclaimer requirement for further14* binary redistribution.15*16* NO WARRANTY17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS18* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT19* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR20* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT21* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,25* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING26* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27* POSSIBILITY OF SUCH DAMAGES.28*29* Authors: Justin T. Gibbs (Spectra Logic Corporation)30*/3132/**33* \file case_file.h34*35* CaseFile objects aggregate vdev faults that may require ZFSD action36* in order to maintain the health of a ZFS pool.37*38* Header requirements:39*40* #include <list>41*42* #include "callout.h"43* #include "zfsd_event.h"44*/45#ifndef _CASE_FILE_H_46#define _CASE_FILE_H_4748/*=========================== Forward Declarations ===========================*/49class CaseFile;50class Vdev;5152/*============================= Class Definitions ============================*/53/*------------------------------- CaseFileList -------------------------------*/54/**55* CaseFileList is a specialization of the standard list STL container.56*/57typedef std::list< CaseFile *> CaseFileList;5859/*--------------------------------- CaseFile ---------------------------------*/60/**61* A CaseFile object is instantiated anytime a vdev for an active pool62* experiences an I/O error, is faulted by ZFS, or is determined to be63* missing/removed.64*65* A vdev may have at most one CaseFile.66*67* CaseFiles are retired when a vdev leaves an active pool configuration68* or an action is taken to resolve the issues recorded in the CaseFile.69*70* Logging a case against a vdev does not imply that an immediate action71* to resolve a fault is required or even desired. For example, a CaseFile72* must accumulate a number of I/O errors in order to flag a device as73* degraded.74*75* Vdev I/O errors are not recorded in ZFS label inforamation. For this76* reasons, CaseFile%%s with accumulated I/O error events are serialized77* to the file system so that they survive across boots. Currently all78* other fault types can be reconstructed from ZFS label information, so79* CaseFile%%s for missing, faulted, or degradded members are just recreated80* at ZFSD startup instead of being deserialized from the file system.81*/82class CaseFile83{84public:85/**86* \brief Find a CaseFile object by a vdev's pool/vdev GUID tuple.87*88* \param poolGUID Pool GUID for the vdev of the CaseFile to find.89* If InvalidGuid, then only match the vdev GUID90* instead of both pool and vdev GUIDs.91* \param vdevGUID Vdev GUID for the vdev of the CaseFile to find.92*93* \return If found, a pointer to a valid CaseFile object.94* Otherwise NULL.95*/96static CaseFile *Find(DevdCtl::Guid poolGUID, DevdCtl::Guid vdevGUID);9798/**99* \brief Find multiple CaseFile objects by a vdev's pool/vdev100* GUID tuple (special case for spare vdevs)101*102* \param poolGUID Pool GUID for the vdev of the CaseFile to find.103* If InvalidGuid, then only match the vdev GUID104* instead of both pool and vdev GUIDs.105* \param vdevGUID Vdev GUID for the vdev of the CaseFile to find.106* \param caseList List of cases associated with the vdev.107*/108static void Find(DevdCtl::Guid poolGUID, DevdCtl::Guid vdevGUID,109CaseFileList &caseList);110111/**112* \brief Find a CaseFile object by a vdev's current/last known113* physical path.114*115* \param physPath Physical path of the vdev of the CaseFile to find.116*117* \return If found, a pointer to a valid CaseFile object.118* Otherwise NULL.119*/120static CaseFile *Find(const string &physPath);121122/**123* \brief ReEvaluate all open cases whose pool guid matches the argument124*125* \param poolGUID Only reevaluate cases for this pool126* \param event Try to consume this event with the casefile127*/128static void ReEvaluateByGuid(DevdCtl::Guid poolGUID,129const ZfsEvent &event);130131/**132* \brief Create or return an existing active CaseFile for the133* specified vdev.134*135* \param vdev The vdev object for which to find/create a CaseFile.136*137* \return A reference to a valid CaseFile object.138*/139static CaseFile &Create(Vdev &vdev);140141/**142* \brief Deserialize all serialized CaseFile objects found in143* the file system.144*/145static void DeSerialize();146147/**148* \brief returns true if there are no CaseFiles149*/150static bool Empty();151152/**153* \brief Emit syslog data on all active CaseFile%%s in the system.154*/155static void LogAll();156157/**158* \brief Destroy the in-core cache of CaseFile data.159*160* This routine does not disturb the on disk, serialized, CaseFile161* data.162*/163static void PurgeAll();164165DevdCtl::Guid PoolGUID() const;166DevdCtl::Guid VdevGUID() const;167vdev_state VdevState() const;168const string &PoolGUIDString() const;169const string &VdevGUIDString() const;170const string &PhysicalPath() const;171172/**173* \brief Attempt to resolve this CaseFile using the disk174* resource at the given device/physical path/vdev object175* tuple.176*177* \param devPath The devfs path for the disk resource.178* \param physPath The physical path information reported by179* the disk resource.180* \param vdev If the disk contains ZFS label information,181* a pointer to the disk label's vdev object182* data. Otherwise NULL.183*184* \return True if this event was consumed by this CaseFile.185*/186bool ReEvaluate(const string &devPath, const string &physPath,187Vdev *vdev);188189/**190* \brief Update this CaseFile in light of the provided ZfsEvent.191*192* Must be virtual so it can be overridden in the unit tests193*194* \param event The ZfsEvent to evaluate.195*196* \return True if this event was consumed by this CaseFile.197*/198virtual bool ReEvaluate(const ZfsEvent &event);199200/**201* \brief Register an itimer callout for the given event, if necessary202*/203virtual void RegisterCallout(const DevdCtl::Event &event);204205/**206* \brief Close a case if it is no longer relevant.207*208* This method deals with cases tracking soft errors. Soft errors209* will be discarded should a remove event occur within a short period210* of the soft errors being reported. We also discard the events211* if the vdev is marked degraded or failed.212*213* \return True if the case is closed. False otherwise.214*/215bool CloseIfSolved();216217/**218* \brief Emit data about this CaseFile via syslog(3).219*/220void Log();221222/**223* \brief Whether we should degrade this vdev224*/225bool ShouldDegrade() const;226227/**228* \brief Whether we should fault this vdev229*/230bool ShouldFault() const;231232/**233* \brief If this vdev is spare234*/235int IsSpare();236237/**238* \brief Get case vdev's specified property239*/240int GetVdevProp(vdev_prop_t) const;241242protected:243enum {244/*245* Use these defaults if we can't get the corresponding vdev246* prop or if the prop is not set247*/248/**249* The number of soft errors on a vdev required250* to transition a vdev from healthy to degraded251* status252*/253DEFAULT_ZFS_DEGRADE_IO_COUNT = 50,254/**255* The number of delay errors on a vdev required to fault it256*/257DEFAULT_ZFS_FAULT_SLOW_IO_COUNT = 8,258};259260static CalloutFunc_t OnGracePeriodEnded;261262/**263* \brief scandir(3) filter function used to find files containing264* serialized CaseFile data.265*266* \param dirEntry Directory entry for the file to filter.267*268* \return Non-zero for a file to include in the selection,269* otherwise 0.270*/271static int DeSerializeSelector(const struct dirent *dirEntry);272273/**274* \brief Given the name of a file containing serialized events from a275* CaseFile object, create/update an in-core CaseFile object276* representing the serialized data.277*278* \param fileName The name of a file containing serialized events279* from a CaseFile object.280*/281static void DeSerializeFile(const char *fileName);282283/** Constructor. */284CaseFile(const Vdev &vdev);285286/**287* Destructor.288* Must be virtual so it can be subclassed in the unit tests289*/290virtual ~CaseFile();291292/**293* \brief Reload state for the vdev associated with this CaseFile.294*295* \return True if the refresh was successful. False if the system296* has no record of the pool or vdev for this CaseFile.297*/298virtual bool RefreshVdevState();299300/**301* \brief Free all events in the m_events list.302*/303void PurgeEvents();304305/**306* \brief Free all events in the m_tentativeEvents list.307*/308void PurgeTentativeEvents();309310/**311* \brief Commit to file system storage.312*/313void Serialize();314315/**316* \brief Retrieve event data from a serialization stream.317*318* \param caseStream The serializtion stream to parse.319*/320void DeSerialize(std::ifstream &caseStream);321322/**323* \brief Serializes the supplied event list and writes it to fd324*325* \param prefix If not NULL, this prefix will be prepended to326* every event in the file.327*/328void SerializeEvList(const DevdCtl::EventList events, int fd,329const char* prefix=NULL) const;330331/**332* \brief Unconditionally close a CaseFile.333*/334virtual void Close();335336/**337* \brief Callout callback invoked when the remove timer grace338* period expires.339*340* If no remove events are received prior to the grace period341* firing, then any tentative events are promoted and counted342* against the health of the vdev.343*/344void OnGracePeriodEnded();345346/**347* \brief Attempt to activate a spare on this case's pool.348*349* Call this whenever a pool becomes degraded. It will look for any350* spare devices and activate one to replace the casefile's vdev. It351* will _not_ close the casefile; that should only happen when the352* missing drive is replaced or the user promotes the spare.353*354* \return True if a spare was activated355*/356bool ActivateSpare();357358/**359* \brief replace a pool's vdev with another360*361* \param vdev_type The type of the new vdev. Usually either362* VDEV_TYPE_DISK or VDEV_TYPE_FILE363* \param path The file system path to the new vdev364* \param isspare Whether the new vdev is a spare365*366* \return true iff the replacement was successful367*/368bool Replace(const char* vdev_type, const char* path, bool isspare);369370/**371* \brief Which vdev, if any, is replacing ours.372*373* \param zhp Pool handle state from the caller context374*375* \return the vdev that is currently replacing ours,376* or NonexistentVdev if there isn't one.377*/378Vdev BeingReplacedBy(zpool_handle_t *zhp);379380/**381* \brief All CaseFiles being tracked by ZFSD.382*/383static CaseFileList s_activeCases;384385/**386* \brief The file system path to serialized CaseFile data.387*/388static const string s_caseFilePath;389390/**391* \brief A list of soft error events counted against the health of392* a vdev.393*/394DevdCtl::EventList m_events;395396/**397* \brief A list of soft error events waiting for a grace period398* expiration before being counted against the health of399* a vdev.400*/401DevdCtl::EventList m_tentativeEvents;402403DevdCtl::Guid m_poolGUID;404DevdCtl::Guid m_vdevGUID;405vdev_state m_vdevState;406string m_poolGUIDString;407string m_vdevGUIDString;408string m_vdevPhysPath;409string m_vdevName;410int m_is_spare;411412/**413* \brief Callout activated when a grace period414*/415Callout m_tentativeTimer;416417private:418nvlist_t *CaseVdev(zpool_handle_t *zhp) const;419};420421inline DevdCtl::Guid422CaseFile::PoolGUID() const423{424return (m_poolGUID);425}426427inline DevdCtl::Guid428CaseFile::VdevGUID() const429{430return (m_vdevGUID);431}432433inline vdev_state434CaseFile::VdevState() const435{436return (m_vdevState);437}438439inline const string &440CaseFile::PoolGUIDString() const441{442return (m_poolGUIDString);443}444445inline const string &446CaseFile::VdevGUIDString() const447{448return (m_vdevGUIDString);449}450451inline const string &452CaseFile::PhysicalPath() const453{454return (m_vdevPhysPath);455}456457#endif /* _CASE_FILE_H_ */458459460