2 * Copyright (c) 2011, 2012, 2013, 2014, 2016 Spectra Logic Corporation
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 * Authors: Justin T. Gibbs (Spectra Logic Corporation)
36 #include <sys/cdefs.h>
38 #include <sys/fs/zfs.h>
39 #include <sys/vdev_impl.h>
45 * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
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>
62 #include "vdev_iterator.h"
63 #include "zfsd_event.h"
64 #include "case_file.h"
67 #include "zfsd_exception.h"
68 #include "zpool_list.h"
70 __FBSDID("$FreeBSD$");
71 /*============================ Namespace Control =============================*/
74 using DevdCtl::NVPairMap;
75 using std::stringstream;
77 /*=========================== Class Implementations ==========================*/
79 /*-------------------------------- DevfsEvent --------------------------------*/
81 //- DevfsEvent Static Public Methods -------------------------------------------
83 DevfsEvent::Builder(Event::Type type,
85 const string &eventString)
87 return (new DevfsEvent(type, nvPairs, eventString));
90 //- DevfsEvent Static Protected Methods ----------------------------------------
92 DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded)
94 pool_state_t poolState;
102 if (zpool_in_use(g_zfsHandle, devFd, &poolState,
103 &poolName, &b_inuse) == 0) {
104 nvlist_t *devLabel = NULL;
106 inUse = b_inuse == B_TRUE;
107 if (poolName != NULL)
110 nlabels = zpool_read_all_labels(devFd, &devLabel);
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.
119 if (nlabels != VDEV_LABELS || devLabel == NULL) {
120 nvlist_free(devLabel);
126 degraded = vdev.State() != VDEV_STATE_HEALTHY;
128 } catch (ZfsdException &exp) {
129 string devName = fdevname(devFd);
130 string devPath = _PATH_DEV + devName;
131 string context("DevfsEvent::ReadLabel: "
134 exp.GetString().insert(0, context);
136 nvlist_free(devLabel);
143 DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath,
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.
152 syslog(LOG_INFO, "Interrogating VDEV label for %s\n",
154 Vdev vdev(devConfig);
155 CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(),
157 if (caseFile != NULL)
158 return (caseFile->ReEvaluate(devPath, physPath, &vdev));
160 } catch (ZfsdException &exp) {
161 string context("DevfsEvent::OnlineByLabel: " + devPath + ": ");
163 exp.GetString().insert(0, context);
169 //- DevfsEvent Virtual Public Methods ------------------------------------------
171 DevfsEvent::DeepCopy() const
173 return (new DevfsEvent(*this));
177 DevfsEvent::Process() const
180 * We are only concerned with newly discovered
181 * devices that can be ZFS vdevs.
183 if (Value("type") != "CREATE" || !IsDiskDev())
186 /* Log the event since it is of interest. */
190 if (!DevPath(devPath))
193 int devFd(open(devPath.c_str(), O_RDONLY));
199 nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded));
202 bool havePhysPath(PhysicalPath(physPath));
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",
214 } else if (havePhysPath && IsWholeDev()) {
216 * TODO: attempt to resolve events using every casefile
217 * that matches this physpath
219 CaseFile *caseFile(CaseFile::Find(physPath));
220 if (caseFile != NULL) {
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(),
227 caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
230 if (devLabel != NULL)
231 nvlist_free(devLabel);
235 //- DevfsEvent Protected Methods -----------------------------------------------
236 DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
237 const string &eventString)
238 : DevdCtl::DevfsEvent(type, nvpairs, eventString)
242 DevfsEvent::DevfsEvent(const DevfsEvent &src)
243 : DevdCtl::DevfsEvent::DevfsEvent(src)
247 /*-------------------------------- GeomEvent --------------------------------*/
249 //- GeomEvent Static Public Methods -------------------------------------------
251 GeomEvent::Builder(Event::Type type,
253 const string &eventString)
255 return (new GeomEvent(type, nvPairs, eventString));
258 //- GeomEvent Virtual Public Methods ------------------------------------------
260 GeomEvent::DeepCopy() const
262 return (new GeomEvent(*this));
266 GeomEvent::Process() const
269 * We are only concerned with physical path changes, because those can
270 * be used to satisfy autoreplace operations
272 if (Value("type") != "GEOM::physpath" || !IsDiskDev())
275 /* Log the event since it is of interest. */
279 if (!DevPath(devPath))
283 bool havePhysPath(PhysicalPath(physPath));
290 * TODO: attempt to resolve events using every casefile
291 * that matches this physpath
293 CaseFile *caseFile(CaseFile::Find(physPath));
294 if (caseFile != NULL) {
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(),
301 caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
307 //- GeomEvent Protected Methods -----------------------------------------------
308 GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
309 const string &eventString)
310 : DevdCtl::GeomEvent(type, nvpairs, eventString)
314 GeomEvent::GeomEvent(const GeomEvent &src)
315 : DevdCtl::GeomEvent::GeomEvent(src)
320 /*--------------------------------- ZfsEvent ---------------------------------*/
321 //- ZfsEvent Static Public Methods ---------------------------------------------
323 ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
324 const string &eventString)
326 return (new ZfsEvent(type, nvpairs, eventString));
329 //- ZfsEvent Virtual Public Methods --------------------------------------------
331 ZfsEvent::DeepCopy() const
333 return (new ZfsEvent(*this));
337 ZfsEvent::Process() const
341 if (!Contains("class") && !Contains("type")) {
343 "ZfsEvent::Process: Missing class or type data.");
347 /* On config syncs, replay any queued events first. */
348 if (Value("type").find("misc.fs.zfs.config_sync") == 0) {
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
355 ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true);
356 CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
359 if (Value("type").find("misc.fs.zfs.") == 0) {
360 /* Configuration changes, resilver events, etc. */
365 if (!Contains("pool_guid") || !Contains("vdev_guid")) {
366 /* Only currently interested in Vdev related events. */
370 CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
371 if (caseFile != NULL) {
373 syslog(LOG_INFO, "Evaluating existing case file\n");
374 caseFile->ReEvaluate(*this);
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) {
383 msg << "No replicas available for pool " << poolGUID;
386 syslog(LOG_INFO, "%s", msg.str().c_str());
391 * Create a case file for this vdev, and have it
392 * evaluate the event.
394 ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
397 int priority = LOG_INFO;
398 msg << "ZfsEvent::Process: Event for unknown pool ";
399 msg << poolGUID << " ";
402 syslog(priority, "%s", msg.str().c_str());
406 nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID());
407 if (vdevConfig == NULL) {
409 int priority = LOG_INFO;
410 msg << "ZfsEvent::Process: Event for unknown vdev ";
411 msg << VdevGUID() << " ";
414 syslog(priority, "%s", msg.str().c_str());
418 Vdev vdev(zpl.front(), vdevConfig);
419 caseFile = &CaseFile::Create(vdev);
420 if (caseFile->ReEvaluate(*this) == false) {
422 int priority = LOG_INFO;
423 msg << "ZfsEvent::Process: Unconsumed event for vdev(";
424 msg << zpool_get_name(zpl.front()) << ",";
425 msg << vdev.GUID() << ") ";
428 syslog(priority, "%s", msg.str().c_str());
434 //- ZfsEvent Protected Methods -------------------------------------------------
435 ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
436 const string &eventString)
437 : DevdCtl::ZfsEvent(type, nvpairs, eventString)
441 ZfsEvent::ZfsEvent(const ZfsEvent &src)
442 : DevdCtl::ZfsEvent(src)
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
454 ZfsEvent::CleanupSpares() const
456 Guid poolGUID(PoolGUID());
457 ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
462 VdevIterator(hdl).Each(TryDetach, (void*)hdl);
467 ZfsEvent::ProcessPoolEvent() const
469 bool degradedDevice(false);
471 /* The pool is destroyed. Discard any open cases */
472 if (Value("type") == "misc.fs.zfs.pool_destroy") {
474 CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
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;
485 caseFile->ReEvaluate(*this);
487 else if (Value("type") == "misc.fs.zfs.resilver_finish")
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.
498 if (Value("type") == "misc.fs.zfs.vdev_remove"
499 && degradedDevice == false) {
501 /* See if any other cases can make use of this device. */
503 ZfsDaemon::RequestSystemRescan();
508 ZfsEvent::TryDetach(Vdev &vdev, void *cbArg)
512 * if this device is a spare, and its parent includes one healthy,
513 * non-spare child, then detach this device.
515 zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg));
517 if (vdev.IsSpare()) {
518 std::list<Vdev> siblings;
519 std::list<Vdev>::iterator siblings_it;
520 boolean_t cleanup = B_FALSE;
522 Vdev parent = vdev.Parent();
523 siblings = parent.Children();
525 /* Determine whether the parent should be cleaned up */
526 for (siblings_it = siblings.begin();
527 siblings_it != siblings.end();
529 Vdev sibling = *siblings_it;
531 if (!sibling.IsSpare() &&
532 sibling.State() == VDEV_STATE_HEALTHY) {
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());
546 /* Always return false, because there may be other spares to detach */