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