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