Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/cddl/usr.sbin/zfsd/case_file.h
105585 views
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 ===========================*/
50
class CaseFile;
51
class Vdev;
52
53
/*============================= Class Definitions ============================*/
54
/*------------------------------- CaseFileList -------------------------------*/
55
/**
56
* CaseFileList is a specialization of the standard list STL container.
57
*/
58
typedef 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
*/
83
class CaseFile
84
{
85
public:
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
243
protected:
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
418
private:
419
nvlist_t *CaseVdev(zpool_handle_t *zhp) const;
420
};
421
422
inline DevdCtl::Guid
423
CaseFile::PoolGUID() const
424
{
425
return (m_poolGUID);
426
}
427
428
inline DevdCtl::Guid
429
CaseFile::VdevGUID() const
430
{
431
return (m_vdevGUID);
432
}
433
434
inline vdev_state
435
CaseFile::VdevState() const
436
{
437
return (m_vdevState);
438
}
439
440
inline const string &
441
CaseFile::PoolGUIDString() const
442
{
443
return (m_poolGUIDString);
444
}
445
446
inline const string &
447
CaseFile::VdevGUIDString() const
448
{
449
return (m_vdevGUIDString);
450
}
451
452
inline const string &
453
CaseFile::PhysicalPath() const
454
{
455
return (m_vdevPhysPath);
456
}
457
458
#endif /* _CASE_FILE_H_ */
459
460