xref: /freebsd/cddl/usr.sbin/zfsd/zfsd_event.cc (revision 40427cca7a9ae77b095936fb1954417c290cfb17)
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 }
546