1 /*- 2 * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * substantially similar to the "NO WARRANTY" disclaimer below 13 * ("Disclaimer") and any redistribution must be conditioned upon 14 * including a substantially similar Disclaimer requirement for further 15 * binary redistribution. 16 * 17 * NO WARRANTY 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGES. 29 * 30 * Authors: Justin T. Gibbs (Spectra Logic Corporation) 31 * 32 * $FreeBSD$ 33 */ 34 35 /** 36 * \file case_file.h 37 * 38 * CaseFile objects aggregate vdev faults that may require ZFSD action 39 * in order to maintain the health of a ZFS pool. 40 * 41 * Header requirements: 42 * 43 * #include <list> 44 * 45 * #include "callout.h" 46 * #include "zfsd_event.h" 47 */ 48 #ifndef _CASE_FILE_H_ 49 #define _CASE_FILE_H_ 50 51 /*=========================== Forward Declarations ===========================*/ 52 class CaseFile; 53 class Vdev; 54 55 /*============================= Class Definitions ============================*/ 56 /*------------------------------- CaseFileList -------------------------------*/ 57 /** 58 * CaseFileList is a specialization of the standard list STL container. 59 */ 60 typedef std::list< CaseFile *> CaseFileList; 61 62 /*--------------------------------- CaseFile ---------------------------------*/ 63 /** 64 * A CaseFile object is instantiated anytime a vdev for an active pool 65 * experiences an I/O error, is faulted by ZFS, or is determined to be 66 * missing/removed. 67 * 68 * A vdev may have at most one CaseFile. 69 * 70 * CaseFiles are retired when a vdev leaves an active pool configuration 71 * or an action is taken to resolve the issues recorded in the CaseFile. 72 * 73 * Logging a case against a vdev does not imply that an immediate action 74 * to resolve a fault is required or even desired. For example, a CaseFile 75 * must accumulate a number of I/O errors in order to flag a device as 76 * degraded. 77 * 78 * Vdev I/O errors are not recorded in ZFS label inforamation. For this 79 * reasons, CaseFile%%s with accumulated I/O error events are serialized 80 * to the file system so that they survive across boots. Currently all 81 * other fault types can be reconstructed from ZFS label information, so 82 * CaseFile%%s for missing, faulted, or degradded members are just recreated 83 * at ZFSD startup instead of being deserialized from the file system. 84 */ 85 class CaseFile 86 { 87 public: 88 /** 89 * \brief Find a CaseFile object by a vdev's pool/vdev GUID tuple. 90 * 91 * \param poolGUID Pool GUID for the vdev of the CaseFile to find. 92 * If InvalidGuid, then only match the vdev GUID 93 * instead of both pool and vdev GUIDs. 94 * \param vdevGUID Vdev GUID for the vdev of the CaseFile to find. 95 * 96 * \return If found, a pointer to a valid CaseFile object. 97 * Otherwise NULL. 98 */ 99 static CaseFile *Find(DevdCtl::Guid poolGUID, DevdCtl::Guid vdevGUID); 100 101 /** 102 * \brief Find multiple CaseFile objects by a vdev's pool/vdev 103 * GUID tuple (special case for spare vdevs) 104 * 105 * \param poolGUID Pool GUID for the vdev of the CaseFile to find. 106 * If InvalidGuid, then only match the vdev GUID 107 * instead of both pool and vdev GUIDs. 108 * \param vdevGUID Vdev GUID for the vdev of the CaseFile to find. 109 * \param caseList List of cases associated with the vdev. 110 */ 111 static void Find(DevdCtl::Guid poolGUID, DevdCtl::Guid vdevGUID, 112 CaseFileList &caseList); 113 114 /** 115 * \brief Find a CaseFile object by a vdev's current/last known 116 * physical path. 117 * 118 * \param physPath Physical path of the vdev of the CaseFile to find. 119 * 120 * \return If found, a pointer to a valid CaseFile object. 121 * Otherwise NULL. 122 */ 123 static CaseFile *Find(const string &physPath); 124 125 /** 126 * \brief ReEvaluate all open cases whose pool guid matches the argument 127 * 128 * \param poolGUID Only reevaluate cases for this pool 129 * \param event Try to consume this event with the casefile 130 */ 131 static void ReEvaluateByGuid(DevdCtl::Guid poolGUID, 132 const ZfsEvent &event); 133 134 /** 135 * \brief Create or return an existing active CaseFile for the 136 * specified vdev. 137 * 138 * \param vdev The vdev object for which to find/create a CaseFile. 139 * 140 * \return A reference to a valid CaseFile object. 141 */ 142 static CaseFile &Create(Vdev &vdev); 143 144 /** 145 * \brief Deserialize all serialized CaseFile objects found in 146 * the file system. 147 */ 148 static void DeSerialize(); 149 150 /** 151 * \brief returns true if there are no CaseFiles 152 */ 153 static bool Empty(); 154 155 /** 156 * \brief Emit syslog data on all active CaseFile%%s in the system. 157 */ 158 static void LogAll(); 159 160 /** 161 * \brief Destroy the in-core cache of CaseFile data. 162 * 163 * This routine does not disturb the on disk, serialized, CaseFile 164 * data. 165 */ 166 static void PurgeAll(); 167 168 DevdCtl::Guid PoolGUID() const; 169 DevdCtl::Guid VdevGUID() const; 170 vdev_state VdevState() const; 171 const string &PoolGUIDString() const; 172 const string &VdevGUIDString() const; 173 const string &PhysicalPath() const; 174 175 /** 176 * \brief Attempt to resolve this CaseFile using the disk 177 * resource at the given device/physical path/vdev object 178 * tuple. 179 * 180 * \param devPath The devfs path for the disk resource. 181 * \param physPath The physical path information reported by 182 * the disk resource. 183 * \param vdev If the disk contains ZFS label information, 184 * a pointer to the disk label's vdev object 185 * data. Otherwise NULL. 186 * 187 * \return True if this event was consumed by this CaseFile. 188 */ 189 bool ReEvaluate(const string &devPath, const string &physPath, 190 Vdev *vdev); 191 192 /** 193 * \brief Update this CaseFile in light of the provided ZfsEvent. 194 * 195 * Must be virtual so it can be overridden in the unit tests 196 * 197 * \param event The ZfsEvent to evaluate. 198 * 199 * \return True if this event was consumed by this CaseFile. 200 */ 201 virtual bool ReEvaluate(const ZfsEvent &event); 202 203 /** 204 * \brief Register an itimer callout for the given event, if necessary 205 */ 206 virtual void RegisterCallout(const DevdCtl::Event &event); 207 208 /** 209 * \brief Close a case if it is no longer relevant. 210 * 211 * This method deals with cases tracking soft errors. Soft errors 212 * will be discarded should a remove event occur within a short period 213 * of the soft errors being reported. We also discard the events 214 * if the vdev is marked degraded or failed. 215 * 216 * \return True if the case is closed. False otherwise. 217 */ 218 bool CloseIfSolved(); 219 220 /** 221 * \brief Emit data about this CaseFile via syslog(3). 222 */ 223 void Log(); 224 225 /** 226 * \brief Whether we should degrade this vdev 227 */ 228 bool ShouldDegrade() const; 229 230 /** 231 * \brief Whether we should fault this vdev 232 */ 233 bool ShouldFault() const; 234 235 /** 236 * \brief If this vdev is spare 237 */ 238 int IsSpare(); 239 240 protected: 241 enum { 242 /** 243 * The number of soft errors on a vdev required 244 * to transition a vdev from healthy to degraded 245 * status. 246 */ 247 ZFS_DEGRADE_IO_COUNT = 50 248 }; 249 250 static CalloutFunc_t OnGracePeriodEnded; 251 252 /** 253 * \brief scandir(3) filter function used to find files containing 254 * serialized CaseFile data. 255 * 256 * \param dirEntry Directory entry for the file to filter. 257 * 258 * \return Non-zero for a file to include in the selection, 259 * otherwise 0. 260 */ 261 static int DeSerializeSelector(const struct dirent *dirEntry); 262 263 /** 264 * \brief Given the name of a file containing serialized events from a 265 * CaseFile object, create/update an in-core CaseFile object 266 * representing the serialized data. 267 * 268 * \param fileName The name of a file containing serialized events 269 * from a CaseFile object. 270 */ 271 static void DeSerializeFile(const char *fileName); 272 273 /** Constructor. */ 274 CaseFile(const Vdev &vdev); 275 276 /** 277 * Destructor. 278 * Must be virtual so it can be subclassed in the unit tests 279 */ 280 virtual ~CaseFile(); 281 282 /** 283 * \brief Reload state for the vdev associated with this CaseFile. 284 * 285 * \return True if the refresh was successful. False if the system 286 * has no record of the pool or vdev for this CaseFile. 287 */ 288 virtual bool RefreshVdevState(); 289 290 /** 291 * \brief Free all events in the m_events list. 292 */ 293 void PurgeEvents(); 294 295 /** 296 * \brief Free all events in the m_tentativeEvents list. 297 */ 298 void PurgeTentativeEvents(); 299 300 /** 301 * \brief Commit to file system storage. 302 */ 303 void Serialize(); 304 305 /** 306 * \brief Retrieve event data from a serialization stream. 307 * 308 * \param caseStream The serializtion stream to parse. 309 */ 310 void DeSerialize(std::ifstream &caseStream); 311 312 /** 313 * \brief Serializes the supplied event list and writes it to fd 314 * 315 * \param prefix If not NULL, this prefix will be prepended to 316 * every event in the file. 317 */ 318 void SerializeEvList(const DevdCtl::EventList events, int fd, 319 const char* prefix=NULL) const; 320 321 /** 322 * \brief Unconditionally close a CaseFile. 323 */ 324 virtual void Close(); 325 326 /** 327 * \brief Callout callback invoked when the remove timer grace 328 * period expires. 329 * 330 * If no remove events are received prior to the grace period 331 * firing, then any tentative events are promoted and counted 332 * against the health of the vdev. 333 */ 334 void OnGracePeriodEnded(); 335 336 /** 337 * \brief Attempt to activate a spare on this case's pool. 338 * 339 * Call this whenever a pool becomes degraded. It will look for any 340 * spare devices and activate one to replace the casefile's vdev. It 341 * will _not_ close the casefile; that should only happen when the 342 * missing drive is replaced or the user promotes the spare. 343 * 344 * \return True if a spare was activated 345 */ 346 bool ActivateSpare(); 347 348 /** 349 * \brief replace a pool's vdev with another 350 * 351 * \param vdev_type The type of the new vdev. Usually either 352 * VDEV_TYPE_DISK or VDEV_TYPE_FILE 353 * \param path The file system path to the new vdev 354 * \param isspare Whether the new vdev is a spare 355 * 356 * \return true iff the replacement was successful 357 */ 358 bool Replace(const char* vdev_type, const char* path, bool isspare); 359 360 /** 361 * \brief Which vdev, if any, is replacing ours. 362 * 363 * \param zhp Pool handle state from the caller context 364 * 365 * \return the vdev that is currently replacing ours, 366 * or NonexistentVdev if there isn't one. 367 */ 368 Vdev BeingReplacedBy(zpool_handle_t *zhp); 369 370 /** 371 * \brief All CaseFiles being tracked by ZFSD. 372 */ 373 static CaseFileList s_activeCases; 374 375 /** 376 * \brief The file system path to serialized CaseFile data. 377 */ 378 static const string s_caseFilePath; 379 380 /** 381 * \brief The time ZFSD waits before promoting a tentative event 382 * into a permanent event. 383 */ 384 static const timeval s_removeGracePeriod; 385 386 /** 387 * \brief A list of soft error events counted against the health of 388 * a vdev. 389 */ 390 DevdCtl::EventList m_events; 391 392 /** 393 * \brief A list of soft error events waiting for a grace period 394 * expiration before being counted against the health of 395 * a vdev. 396 */ 397 DevdCtl::EventList m_tentativeEvents; 398 399 DevdCtl::Guid m_poolGUID; 400 DevdCtl::Guid m_vdevGUID; 401 vdev_state m_vdevState; 402 string m_poolGUIDString; 403 string m_vdevGUIDString; 404 string m_vdevPhysPath; 405 int m_is_spare; 406 407 /** 408 * \brief Callout activated when a grace period 409 */ 410 Callout m_tentativeTimer; 411 412 private: 413 nvlist_t *CaseVdev(zpool_handle_t *zhp) const; 414 }; 415 416 inline DevdCtl::Guid 417 CaseFile::PoolGUID() const 418 { 419 return (m_poolGUID); 420 } 421 422 inline DevdCtl::Guid 423 CaseFile::VdevGUID() const 424 { 425 return (m_vdevGUID); 426 } 427 428 inline vdev_state 429 CaseFile::VdevState() const 430 { 431 return (m_vdevState); 432 } 433 434 inline const string & 435 CaseFile::PoolGUIDString() const 436 { 437 return (m_poolGUIDString); 438 } 439 440 inline const string & 441 CaseFile::VdevGUIDString() const 442 { 443 return (m_vdevGUIDString); 444 } 445 446 inline const string & 447 CaseFile::PhysicalPath() const 448 { 449 return (m_vdevPhysPath); 450 } 451 452 #endif /* _CASE_FILE_H_ */ 453