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