xref: /freebsd/cddl/usr.sbin/zfsd/case_file.h (revision 7ef62cebc2f965b0f640263e179276928885e33d)
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