]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - cddl/usr.sbin/zfsd/zfsd_event.cc
MFV r323678: file 5.32
[FreeBSD/FreeBSD.git] / cddl / usr.sbin / zfsd / zfsd_event.cc
1 /*-
2  * Copyright (c) 2011, 2012, 2013, 2014, 2016 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 zfsd_event.cc
35  */
36 #include <sys/cdefs.h>
37 #include <sys/time.h>
38 #include <sys/fs/zfs.h>
39 #include <sys/vdev_impl.h>
40
41 #include <syslog.h>
42
43 #include <libzfs.h>
44 /* 
45  * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
46  * C++ flush methods
47  */
48 #undef   flush
49
50 #include <list>
51 #include <map>
52 #include <sstream>
53 #include <string>
54
55 #include <devdctl/guid.h>
56 #include <devdctl/event.h>
57 #include <devdctl/event_factory.h>
58 #include <devdctl/exception.h>
59 #include <devdctl/consumer.h>
60
61 #include "callout.h"
62 #include "vdev_iterator.h"
63 #include "zfsd_event.h"
64 #include "case_file.h"
65 #include "vdev.h"
66 #include "zfsd.h"
67 #include "zfsd_exception.h"
68 #include "zpool_list.h"
69
70 __FBSDID("$FreeBSD$");
71 /*============================ Namespace Control =============================*/
72 using DevdCtl::Event;
73 using DevdCtl::Guid;
74 using DevdCtl::NVPairMap;
75 using std::stringstream;
76
77 /*=========================== Class Implementations ==========================*/
78
79 /*-------------------------------- DevfsEvent --------------------------------*/
80
81 //- DevfsEvent Static Public Methods -------------------------------------------
82 Event *
83 DevfsEvent::Builder(Event::Type type,
84                     NVPairMap &nvPairs,
85                     const string &eventString)
86 {
87         return (new DevfsEvent(type, nvPairs, eventString));
88 }
89
90 //- DevfsEvent Static Protected Methods ----------------------------------------
91 nvlist_t *
92 DevfsEvent::ReadLabel(int devFd, bool &inUse, bool &degraded)
93 {
94         pool_state_t poolState;
95         char        *poolName;
96         boolean_t    b_inuse;
97         int          nlabels;
98
99         inUse    = false;
100         degraded = false;
101         poolName = NULL;
102         if (zpool_in_use(g_zfsHandle, devFd, &poolState,
103                          &poolName, &b_inuse) == 0) {
104                 nvlist_t *devLabel;
105
106                 inUse = b_inuse == B_TRUE;
107                 if (poolName != NULL)
108                         free(poolName);
109
110                 nlabels = zpool_read_all_labels(devFd, &devLabel);
111                 /*
112                  * If we find a disk with fewer than the maximum number of
113                  * labels, it might be the whole disk of a partitioned disk
114                  * where ZFS resides on a partition.  In that case, we should do
115                  * nothing and wait for the partition to appear.  Or, the disk
116                  * might be damaged.  In that case, zfsd should do nothing and
117                  * wait for the sysadmin to decide.
118                  */
119                 if (nlabels != VDEV_LABELS || devLabel == NULL)
120                         return (NULL);
121
122                 try {
123                         Vdev vdev(devLabel);
124                         degraded = vdev.State() != VDEV_STATE_HEALTHY;
125                         return (devLabel);
126                 } catch (ZfsdException &exp) {
127                         string devName = fdevname(devFd);
128                         string devPath = _PATH_DEV + devName;
129                         string context("DevfsEvent::ReadLabel: "
130                                      + devPath + ": ");
131
132                         exp.GetString().insert(0, context);
133                         exp.Log();
134                 }
135         }
136         return (NULL);
137 }
138
139 bool
140 DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath,
141                               nvlist_t *devConfig)
142 {
143         try {
144                 /*
145                  * A device with ZFS label information has been
146                  * inserted.  If it matches a device for which we
147                  * have a case, see if we can solve that case.
148                  */
149                 syslog(LOG_INFO, "Interrogating VDEV label for %s\n",
150                        devPath.c_str());
151                 Vdev vdev(devConfig);
152                 CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(),
153                                                   vdev.GUID()));
154                 if (caseFile != NULL)
155                         return (caseFile->ReEvaluate(devPath, physPath, &vdev));
156
157         } catch (ZfsdException &exp) {
158                 string context("DevfsEvent::OnlineByLabel: " + devPath + ": ");
159
160                 exp.GetString().insert(0, context);
161                 exp.Log();
162         }
163         return (false);
164 }
165
166 //- DevfsEvent Virtual Public Methods ------------------------------------------
167 Event *
168 DevfsEvent::DeepCopy() const
169 {
170         return (new DevfsEvent(*this));
171 }
172
173 bool
174 DevfsEvent::Process() const
175 {
176         /*
177          * We are only concerned with newly discovered
178          * devices that can be ZFS vdevs.
179          */
180         if (Value("type") != "CREATE" || !IsDiskDev())
181                 return (false);
182
183         /* Log the event since it is of interest. */
184         Log(LOG_INFO);
185
186         string devPath;
187         if (!DevPath(devPath))
188                 return (false);
189
190         int devFd(open(devPath.c_str(), O_RDONLY));
191         if (devFd == -1)
192                 return (false);
193
194         bool inUse;
195         bool degraded;
196         nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded));
197
198         string physPath;
199         bool havePhysPath(PhysicalPath(physPath));
200
201         string devName;
202         DevName(devName);
203         close(devFd);
204
205         if (inUse && devLabel != NULL) {
206                 OnlineByLabel(devPath, physPath, devLabel);
207         } else if (degraded) {
208                 syslog(LOG_INFO, "%s is marked degraded.  Ignoring "
209                        "as a replace by physical path candidate.\n",
210                        devName.c_str());
211         } else if (havePhysPath && IsWholeDev()) {
212                 /*
213                  * TODO: attempt to resolve events using every casefile
214                  * that matches this physpath
215                  */
216                 CaseFile *caseFile(CaseFile::Find(physPath));
217                 if (caseFile != NULL) {
218                         syslog(LOG_INFO,
219                                "Found CaseFile(%s:%s:%s) - ReEvaluating\n",
220                                caseFile->PoolGUIDString().c_str(),
221                                caseFile->VdevGUIDString().c_str(),
222                                zpool_state_to_name(caseFile->VdevState(),
223                                                    VDEV_AUX_NONE));
224                         caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
225                 }
226         }
227         if (devLabel != NULL)
228                 nvlist_free(devLabel);
229         return (false);
230 }
231
232 //- DevfsEvent Protected Methods -----------------------------------------------
233 DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
234                                const string &eventString)
235  : DevdCtl::DevfsEvent(type, nvpairs, eventString)
236 {
237 }
238
239 DevfsEvent::DevfsEvent(const DevfsEvent &src)
240  : DevdCtl::DevfsEvent::DevfsEvent(src)
241 {
242 }
243
244 /*-------------------------------- GeomEvent --------------------------------*/
245
246 //- GeomEvent Static Public Methods -------------------------------------------
247 Event *
248 GeomEvent::Builder(Event::Type type,
249                    NVPairMap &nvPairs,
250                    const string &eventString)
251 {
252         return (new GeomEvent(type, nvPairs, eventString));
253 }
254
255 //- GeomEvent Virtual Public Methods ------------------------------------------
256 Event *
257 GeomEvent::DeepCopy() const
258 {
259         return (new GeomEvent(*this));
260 }
261  
262 bool
263 GeomEvent::Process() const
264 {
265         /*
266          * We are only concerned with physical path changes, because those can
267          * be used to satisfy autoreplace operations
268          */
269         if (Value("type") != "GEOM::physpath" || !IsDiskDev())
270                 return (false);
271
272         /* Log the event since it is of interest. */
273         Log(LOG_INFO);
274
275         string devPath;
276         if (!DevPath(devPath))
277                 return (false);
278
279         string physPath;
280         bool havePhysPath(PhysicalPath(physPath));
281
282         string devName;
283         DevName(devName);
284
285         if (havePhysPath) {
286                 /* 
287                  * TODO: attempt to resolve events using every casefile
288                  * that matches this physpath
289                  */
290                 CaseFile *caseFile(CaseFile::Find(physPath));
291                 if (caseFile != NULL) {
292                         syslog(LOG_INFO,
293                                "Found CaseFile(%s:%s:%s) - ReEvaluating\n",
294                                caseFile->PoolGUIDString().c_str(),
295                                caseFile->VdevGUIDString().c_str(),
296                                zpool_state_to_name(caseFile->VdevState(),
297                                                    VDEV_AUX_NONE));
298                         caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
299                 }
300         }
301         return (false);
302 }
303
304 //- GeomEvent Protected Methods -----------------------------------------------
305 GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
306                                const string &eventString)
307  : DevdCtl::GeomEvent(type, nvpairs, eventString)
308 {
309 }
310
311 GeomEvent::GeomEvent(const GeomEvent &src)
312  : DevdCtl::GeomEvent::GeomEvent(src)
313 {
314 }
315
316
317 /*--------------------------------- ZfsEvent ---------------------------------*/
318 //- ZfsEvent Static Public Methods ---------------------------------------------
319 DevdCtl::Event *
320 ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
321                   const string &eventString)
322 {
323         return (new ZfsEvent(type, nvpairs, eventString));
324 }
325
326 //- ZfsEvent Virtual Public Methods --------------------------------------------
327 Event *
328 ZfsEvent::DeepCopy() const
329 {
330         return (new ZfsEvent(*this));
331 }
332
333 bool
334 ZfsEvent::Process() const
335 {
336         string logstr("");
337
338         if (!Contains("class") && !Contains("type")) {
339                 syslog(LOG_ERR,
340                        "ZfsEvent::Process: Missing class or type data.");
341                 return (false);
342         }
343
344         /* On config syncs, replay any queued events first. */
345         if (Value("type").find("misc.fs.zfs.config_sync") == 0) {
346                 /*
347                  * Even if saved events are unconsumed the second time
348                  * around, drop them.  Any events that still can't be
349                  * consumed are probably referring to vdevs or pools that
350                  * no longer exist.
351                  */
352                 ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true);
353                 CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
354         }
355
356         if (Value("type").find("misc.fs.zfs.") == 0) {
357                 /* Configuration changes, resilver events, etc. */
358                 ProcessPoolEvent();
359                 return (false);
360         }
361
362         if (!Contains("pool_guid") || !Contains("vdev_guid")) {
363                 /* Only currently interested in Vdev related events. */
364                 return (false);
365         }
366
367         CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
368         if (caseFile != NULL) {
369                 Log(LOG_INFO);
370                 syslog(LOG_INFO, "Evaluating existing case file\n");
371                 caseFile->ReEvaluate(*this);
372                 return (false);
373         }
374
375         /* Skip events that can't be handled. */
376         Guid poolGUID(PoolGUID());
377         /* If there are no replicas for a pool, then it's not manageable. */
378         if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) {
379                 stringstream msg;
380                 msg << "No replicas available for pool "  << poolGUID;
381                 msg << ", ignoring";
382                 Log(LOG_INFO);
383                 syslog(LOG_INFO, "%s", msg.str().c_str());
384                 return (false);
385         }
386
387         /*
388          * Create a case file for this vdev, and have it
389          * evaluate the event.
390          */
391         ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
392         if (zpl.empty()) {
393                 stringstream msg;
394                 int priority = LOG_INFO;
395                 msg << "ZfsEvent::Process: Event for unknown pool ";
396                 msg << poolGUID << " ";
397                 msg << "queued";
398                 Log(LOG_INFO);
399                 syslog(priority, "%s", msg.str().c_str());
400                 return (true);
401         }
402
403         nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID());
404         if (vdevConfig == NULL) {
405                 stringstream msg;
406                 int priority = LOG_INFO;
407                 msg << "ZfsEvent::Process: Event for unknown vdev ";
408                 msg << VdevGUID() << " ";
409                 msg << "queued";
410                 Log(LOG_INFO);
411                 syslog(priority, "%s", msg.str().c_str());
412                 return (true);
413         }
414
415         Vdev vdev(zpl.front(), vdevConfig);
416         caseFile = &CaseFile::Create(vdev);
417         if (caseFile->ReEvaluate(*this) == false) {
418                 stringstream msg;
419                 int priority = LOG_INFO;
420                 msg << "ZfsEvent::Process: Unconsumed event for vdev(";
421                 msg << zpool_get_name(zpl.front()) << ",";
422                 msg << vdev.GUID() << ") ";
423                 msg << "queued";
424                 Log(LOG_INFO);
425                 syslog(priority, "%s", msg.str().c_str());
426                 return (true);
427         }
428         return (false);
429 }
430
431 //- ZfsEvent Protected Methods -------------------------------------------------
432 ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
433                            const string &eventString)
434  : DevdCtl::ZfsEvent(type, nvpairs, eventString)
435 {
436 }
437
438 ZfsEvent::ZfsEvent(const ZfsEvent &src)
439  : DevdCtl::ZfsEvent(src)
440 {
441 }
442
443 /*
444  * Sometimes the kernel won't detach a spare when it is no longer needed.  This
445  * can happen for example if a drive is removed, then either the pool is
446  * exported or the machine is powered off, then the drive is reinserted, then
447  * the machine is powered on or the pool is imported.  ZFSD must detach these
448  * spares itself.
449  */
450 void
451 ZfsEvent::CleanupSpares() const
452 {
453         Guid poolGUID(PoolGUID());
454         ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
455         if (!zpl.empty()) {
456                 zpool_handle_t* hdl;
457
458                 hdl = zpl.front();
459                 VdevIterator(hdl).Each(TryDetach, (void*)hdl);
460         }
461 }
462
463 void
464 ZfsEvent::ProcessPoolEvent() const
465 {
466         bool degradedDevice(false);
467
468         /* The pool is destroyed.  Discard any open cases */
469         if (Value("type") == "misc.fs.zfs.pool_destroy") {
470                 Log(LOG_INFO);
471                 CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
472                 return;
473         }
474
475         CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
476         if (caseFile != NULL) {
477                 if (caseFile->VdevState() != VDEV_STATE_UNKNOWN
478                  && caseFile->VdevState() < VDEV_STATE_HEALTHY)
479                         degradedDevice = true;
480
481                 Log(LOG_INFO);
482                 caseFile->ReEvaluate(*this);
483         }
484         else if (Value("type") == "misc.fs.zfs.resilver_finish")
485         {
486                 /*
487                  * It's possible to get a resilver_finish event with no
488                  * corresponding casefile.  For example, if a damaged pool were
489                  * exported, repaired, then reimported.
490                  */
491                 Log(LOG_INFO);
492                 CleanupSpares();
493         }
494
495         if (Value("type") == "misc.fs.zfs.vdev_remove"
496          && degradedDevice == false) {
497
498                 /* See if any other cases can make use of this device. */
499                 Log(LOG_INFO);
500                 ZfsDaemon::RequestSystemRescan();
501         }
502 }
503
504 bool
505 ZfsEvent::TryDetach(Vdev &vdev, void *cbArg)
506 {
507         /*
508          * Outline:
509          * if this device is a spare, and its parent includes one healthy,
510          * non-spare child, then detach this device.
511          */
512         zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg));
513
514         if (vdev.IsSpare()) {
515                 std::list<Vdev> siblings;
516                 std::list<Vdev>::iterator siblings_it;
517                 boolean_t cleanup = B_FALSE;
518
519                 Vdev parent = vdev.Parent();
520                 siblings = parent.Children();
521
522                 /* Determine whether the parent should be cleaned up */
523                 for (siblings_it = siblings.begin();
524                      siblings_it != siblings.end();
525                      siblings_it++) {
526                         Vdev sibling = *siblings_it;
527
528                         if (!sibling.IsSpare() &&
529                              sibling.State() == VDEV_STATE_HEALTHY) {
530                                 cleanup = B_TRUE;
531                                 break;
532                         }
533                 }
534
535                 if (cleanup) {
536                         syslog(LOG_INFO, "Detaching spare vdev %s from pool %s",
537                                vdev.Path().c_str(), zpool_get_name(hdl));
538                         zpool_vdev_detach(hdl, vdev.Path().c_str());
539                 }
540
541         }
542
543         /* Always return false, because there may be other spares to detach */
544         return (false);
545 }