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 ===========================*/
52class CaseFile;
53class Vdev;
54
55/*============================= Class Definitions ============================*/
56/*------------------------------- CaseFileList -------------------------------*/
57/**
58 * CaseFileList is a specialization of the standard list STL container.
59 */
60typedef 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 */
85class CaseFile
86{
87public:
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 a CaseFile object by a vdev's current/last known
103	 *        physical path.
104	 *
105	 * \param physPath  Physical path of the vdev of the CaseFile to find.
106	 *
107	 * \return  If found, a pointer to a valid CaseFile object.
108	 *          Otherwise NULL.
109	 */
110	static CaseFile *Find(const string &physPath);
111
112	/**
113	 * \brief ReEvaluate all open cases whose pool guid matches the argument
114	 *
115	 * \param poolGUID	Only reevaluate cases for this pool
116	 * \param event		Try to consume this event with the casefile
117	 */
118	static void ReEvaluateByGuid(DevdCtl::Guid poolGUID,
119				     const ZfsEvent &event);
120
121	/**
122	 * \brief Create or return an existing active CaseFile for the
123	 *        specified vdev.
124	 *
125	 * \param vdev  The vdev object for which to find/create a CaseFile.
126	 *
127	 * \return  A reference to a valid CaseFile object.
128	 */
129	static CaseFile &Create(Vdev &vdev);
130
131	/**
132	 * \brief Deserialize all serialized CaseFile objects found in
133	 *        the file system.
134	 */
135	static void      DeSerialize();
136
137	/**
138	 * \brief returns true if there are no CaseFiles
139	 */
140	static bool	Empty();
141
142	/**
143	 * \brief Emit syslog data on all active CaseFile%%s in the system.
144	 */
145	static void      LogAll();
146
147	/**
148	 * \brief Destroy the in-core cache of CaseFile data.
149	 *
150	 * This routine does not disturb the on disk, serialized, CaseFile
151	 * data.
152	 */
153	static void      PurgeAll();
154
155	DevdCtl::Guid PoolGUID()       const;
156	DevdCtl::Guid VdevGUID()       const;
157	vdev_state    VdevState()      const;
158	const string &PoolGUIDString() const;
159	const string &VdevGUIDString() const;
160	const string &PhysicalPath()   const;
161
162	/**
163	 * \brief Attempt to resolve this CaseFile using the disk
164	 *        resource at the given device/physical path/vdev object
165	 *        tuple.
166	 *
167	 * \param devPath   The devfs path for the disk resource.
168	 * \param physPath  The physical path information reported by
169	 *                  the disk resource.
170	 * \param vdev      If the disk contains ZFS label information,
171	 *                  a pointer to the disk label's vdev object
172	 *                  data.  Otherwise NULL.
173	 *
174	 * \return  True if this event was consumed by this CaseFile.
175	 */
176	bool ReEvaluate(const string &devPath, const string &physPath,
177			Vdev *vdev);
178
179	/**
180	 * \brief Update this CaseFile in light of the provided ZfsEvent.
181	 *
182	 * Must be virtual so it can be overridden in the unit tests
183	 *
184	 * \param event  The ZfsEvent to evaluate.
185	 *
186	 * \return  True if this event was consumed by this CaseFile.
187	 */
188	virtual bool ReEvaluate(const ZfsEvent &event);
189
190	/**
191	 * \brief Register an itimer callout for the given event, if necessary
192	 */
193	virtual void RegisterCallout(const DevdCtl::Event &event);
194
195	/**
196	 * \brief Close a case if it is no longer relevant.
197	 *
198	 * This method deals with cases tracking soft errors.  Soft errors
199	 * will be discarded should a remove event occur within a short period
200	 * of the soft errors being reported.  We also discard the events
201	 * if the vdev is marked degraded or failed.
202	 *
203	 * \return  True if the case is closed.  False otherwise.
204	 */
205	bool CloseIfSolved();
206
207	/**
208	 * \brief Emit data about this CaseFile via syslog(3).
209	 */
210	void Log();
211
212	/**
213	 * \brief Whether we should degrade this vdev
214	 */
215	bool ShouldDegrade() const;
216
217	/**
218	 * \brief Whether we should fault this vdev
219	 */
220	bool ShouldFault() const;
221
222protected:
223	enum {
224		/**
225		 * The number of soft errors on a vdev required
226		 * to transition a vdev from healthy to degraded
227		 * status.
228		 */
229		ZFS_DEGRADE_IO_COUNT = 50
230	};
231
232	static CalloutFunc_t OnGracePeriodEnded;
233
234	/**
235	 * \brief scandir(3) filter function used to find files containing
236	 *        serialized CaseFile data.
237	 *
238	 * \param dirEntry  Directory entry for the file to filter.
239	 *
240	 * \return  Non-zero for a file to include in the selection,
241	 *          otherwise 0.
242	 */
243	static int  DeSerializeSelector(const struct dirent *dirEntry);
244
245	/**
246	 * \brief Given the name of a file containing serialized events from a
247	 *        CaseFile object, create/update an in-core CaseFile object
248	 *        representing the serialized data.
249	 *
250	 * \param fileName  The name of a file containing serialized events
251	 *                  from a CaseFile object.
252	 */
253	static void DeSerializeFile(const char *fileName);
254
255	/** Constructor. */
256	CaseFile(const Vdev &vdev);
257
258	/**
259	 * Destructor.
260	 * Must be virtual so it can be subclassed in the unit tests
261	 */
262	virtual ~CaseFile();
263
264	/**
265	 * \brief Reload state for the vdev associated with this CaseFile.
266	 *
267	 * \return  True if the refresh was successful.  False if the system
268	 *          has no record of the pool or vdev for this CaseFile.
269	 */
270	virtual bool RefreshVdevState();
271
272	/**
273	 * \brief Free all events in the m_events list.
274	 */
275	void PurgeEvents();
276
277	/**
278	 * \brief Free all events in the m_tentativeEvents list.
279	 */
280	void PurgeTentativeEvents();
281
282	/**
283	 * \brief Commit to file system storage.
284	 */
285	void Serialize();
286
287	/**
288	 * \brief Retrieve event data from a serialization stream.
289	 *
290	 * \param caseStream  The serializtion stream to parse.
291	 */
292	void DeSerialize(std::ifstream &caseStream);
293
294	/**
295	 * \brief Serializes the supplied event list and writes it to fd
296	 *
297	 * \param prefix  If not NULL, this prefix will be prepended to
298	 *                every event in the file.
299	 */
300	void SerializeEvList(const DevdCtl::EventList events, int fd,
301			     const char* prefix=NULL) const;
302
303	/**
304	 * \brief Unconditionally close a CaseFile.
305	 */
306	virtual void Close();
307
308	/**
309	 * \brief Callout callback invoked when the remove timer grace
310	 *        period expires.
311	 *
312	 * If no remove events are received prior to the grace period
313	 * firing, then any tentative events are promoted and counted
314	 * against the health of the vdev.
315	 */
316	void OnGracePeriodEnded();
317
318	/**
319	 * \brief Attempt to activate a spare on this case's pool.
320	 *
321	 * Call this whenever a pool becomes degraded.  It will look for any
322	 * spare devices and activate one to replace the casefile's vdev.  It
323	 * will _not_ close the casefile; that should only happen when the
324	 * missing drive is replaced or the user promotes the spare.
325	 *
326	 * \return True if a spare was activated
327	 */
328	bool ActivateSpare();
329
330	/**
331	 * \brief replace a pool's vdev with another
332	 *
333	 * \param vdev_type   The type of the new vdev.  Usually either
334	 *                    VDEV_TYPE_DISK or VDEV_TYPE_FILE
335	 * \param path        The file system path to the new vdev
336	 * \param isspare     Whether the new vdev is a spare
337	 *
338	 * \return            true iff the replacement was successful
339	 */
340	bool Replace(const char* vdev_type, const char* path, bool isspare);
341
342	/**
343	 * \brief Which vdev, if any, is replacing ours.
344	 *
345	 * \param zhp		Pool handle state from the caller context
346	 *
347	 * \return		the vdev that is currently replacing ours,
348	 *			or NonexistentVdev if there isn't one.
349	 */
350	Vdev BeingReplacedBy(zpool_handle_t *zhp);
351
352	/**
353	 * \brief All CaseFiles being tracked by ZFSD.
354	 */
355	static CaseFileList  s_activeCases;
356
357	/**
358	 * \brief The file system path to serialized CaseFile data.
359	 */
360	static const string  s_caseFilePath;
361
362	/**
363	 * \brief The time ZFSD waits before promoting a tentative event
364	 *        into a permanent event.
365	 */
366	static const timeval s_removeGracePeriod;
367
368	/**
369	 * \brief A list of soft error events counted against the health of
370	 *        a vdev.
371	 */
372	DevdCtl::EventList m_events;
373
374	/**
375	 * \brief A list of soft error events waiting for a grace period
376	 *        expiration before being counted against the health of
377	 *        a vdev.
378	 */
379	DevdCtl::EventList m_tentativeEvents;
380
381	DevdCtl::Guid	   m_poolGUID;
382	DevdCtl::Guid	   m_vdevGUID;
383	vdev_state	   m_vdevState;
384	string		   m_poolGUIDString;
385	string		   m_vdevGUIDString;
386	string		   m_vdevPhysPath;
387
388	/**
389	 * \brief Callout activated when a grace period
390	 */
391	Callout		  m_tentativeTimer;
392
393private:
394	nvlist_t	*CaseVdev(zpool_handle_t *zhp)	const;
395};
396
397inline DevdCtl::Guid
398CaseFile::PoolGUID() const
399{
400	return (m_poolGUID);
401}
402
403inline DevdCtl::Guid
404CaseFile::VdevGUID() const
405{
406	return (m_vdevGUID);
407}
408
409inline vdev_state
410CaseFile::VdevState() const
411{
412	return (m_vdevState);
413}
414
415inline const string &
416CaseFile::PoolGUIDString() const
417{
418	return (m_poolGUIDString);
419}
420
421inline const string &
422CaseFile::VdevGUIDString() const
423{
424	return (m_vdevGUIDString);
425}
426
427inline const string &
428CaseFile::PhysicalPath() const
429{
430	return (m_vdevPhysPath);
431}
432
433#endif /* _CASE_FILE_H_ */
434