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 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
222 protected:
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
393 private:
394 nvlist_t *CaseVdev(zpool_handle_t *zhp) const;
395 };
396
397 inline DevdCtl::Guid
PoolGUID()398 CaseFile::PoolGUID() const
399 {
400 return (m_poolGUID);
401 }
402
403 inline DevdCtl::Guid
VdevGUID()404 CaseFile::VdevGUID() const
405 {
406 return (m_vdevGUID);
407 }
408
409 inline vdev_state
VdevState()410 CaseFile::VdevState() const
411 {
412 return (m_vdevState);
413 }
414
415 inline const string &
PoolGUIDString()416 CaseFile::PoolGUIDString() const
417 {
418 return (m_poolGUIDString);
419 }
420
421 inline const string &
VdevGUIDString()422 CaseFile::VdevGUIDString() const
423 {
424 return (m_vdevGUIDString);
425 }
426
427 inline const string &
PhysicalPath()428 CaseFile::PhysicalPath() const
429 {
430 return (m_vdevPhysPath);
431 }
432
433 #endif /* _CASE_FILE_H_ */
434