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