zfsd_event.cc revision 326298
1300906Sasomers/*-
2300906Sasomers * Copyright (c) 2011, 2012, 2013, 2014, 2016 Spectra Logic Corporation
3300906Sasomers * All rights reserved.
4300906Sasomers *
5300906Sasomers * Redistribution and use in source and binary forms, with or without
6300906Sasomers * modification, are permitted provided that the following conditions
7300906Sasomers * are met:
8300906Sasomers * 1. Redistributions of source code must retain the above copyright
9300906Sasomers *    notice, this list of conditions, and the following disclaimer,
10300906Sasomers *    without modification.
11300906Sasomers * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12300906Sasomers *    substantially similar to the "NO WARRANTY" disclaimer below
13300906Sasomers *    ("Disclaimer") and any redistribution must be conditioned upon
14300906Sasomers *    including a substantially similar Disclaimer requirement for further
15300906Sasomers *    binary redistribution.
16300906Sasomers *
17300906Sasomers * NO WARRANTY
18300906Sasomers * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19300906Sasomers * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20300906Sasomers * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21300906Sasomers * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22300906Sasomers * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23300906Sasomers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24300906Sasomers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25300906Sasomers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26300906Sasomers * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27300906Sasomers * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28300906Sasomers * POSSIBILITY OF SUCH DAMAGES.
29300906Sasomers *
30300906Sasomers * Authors: Justin T. Gibbs     (Spectra Logic Corporation)
31300906Sasomers */
32300906Sasomers
33300906Sasomers/**
34300906Sasomers * \file zfsd_event.cc
35300906Sasomers */
36300906Sasomers#include <sys/cdefs.h>
37300906Sasomers#include <sys/time.h>
38300906Sasomers#include <sys/fs/zfs.h>
39326298Sasomers#include <sys/vdev_impl.h>
40300906Sasomers
41300906Sasomers#include <syslog.h>
42300906Sasomers
43300906Sasomers#include <libzfs.h>
44300906Sasomers/*
45300906Sasomers * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
46300906Sasomers * C++ flush methods
47300906Sasomers */
48300906Sasomers#undef   flush
49300906Sasomers
50300906Sasomers#include <list>
51300906Sasomers#include <map>
52300906Sasomers#include <sstream>
53300906Sasomers#include <string>
54300906Sasomers
55300906Sasomers#include <devdctl/guid.h>
56300906Sasomers#include <devdctl/event.h>
57300906Sasomers#include <devdctl/event_factory.h>
58300906Sasomers#include <devdctl/exception.h>
59300906Sasomers#include <devdctl/consumer.h>
60300906Sasomers
61300906Sasomers#include "callout.h"
62300906Sasomers#include "vdev_iterator.h"
63300906Sasomers#include "zfsd_event.h"
64300906Sasomers#include "case_file.h"
65300906Sasomers#include "vdev.h"
66300906Sasomers#include "zfsd.h"
67300906Sasomers#include "zfsd_exception.h"
68300906Sasomers#include "zpool_list.h"
69300906Sasomers
70300906Sasomers__FBSDID("$FreeBSD: stable/11/cddl/usr.sbin/zfsd/zfsd_event.cc 326298 2017-11-28 00:19:04Z asomers $");
71300906Sasomers/*============================ Namespace Control =============================*/
72300906Sasomersusing DevdCtl::Event;
73300906Sasomersusing DevdCtl::Guid;
74300906Sasomersusing DevdCtl::NVPairMap;
75300906Sasomersusing std::stringstream;
76300906Sasomers
77300906Sasomers/*=========================== Class Implementations ==========================*/
78300906Sasomers
79300906Sasomers/*-------------------------------- DevfsEvent --------------------------------*/
80300906Sasomers
81300906Sasomers//- DevfsEvent Static Public Methods -------------------------------------------
82300906SasomersEvent *
83300906SasomersDevfsEvent::Builder(Event::Type type,
84300906Sasomers		    NVPairMap &nvPairs,
85300906Sasomers		    const string &eventString)
86300906Sasomers{
87300906Sasomers	return (new DevfsEvent(type, nvPairs, eventString));
88300906Sasomers}
89300906Sasomers
90300906Sasomers//- DevfsEvent Static Protected Methods ----------------------------------------
91300906Sasomersnvlist_t *
92300906SasomersDevfsEvent::ReadLabel(int devFd, bool &inUse, bool &degraded)
93300906Sasomers{
94300906Sasomers	pool_state_t poolState;
95300906Sasomers	char        *poolName;
96300906Sasomers	boolean_t    b_inuse;
97326298Sasomers	int          nlabels;
98300906Sasomers
99300906Sasomers	inUse    = false;
100300906Sasomers	degraded = false;
101300906Sasomers	poolName = NULL;
102300906Sasomers	if (zpool_in_use(g_zfsHandle, devFd, &poolState,
103300906Sasomers			 &poolName, &b_inuse) == 0) {
104326298Sasomers		nvlist_t *devLabel = NULL;
105300906Sasomers
106300906Sasomers		inUse = b_inuse == B_TRUE;
107300906Sasomers		if (poolName != NULL)
108300906Sasomers			free(poolName);
109300906Sasomers
110326298Sasomers		nlabels = zpool_read_all_labels(devFd, &devLabel);
111326298Sasomers		/*
112326298Sasomers		 * If we find a disk with fewer than the maximum number of
113326298Sasomers		 * labels, it might be the whole disk of a partitioned disk
114326298Sasomers		 * where ZFS resides on a partition.  In that case, we should do
115326298Sasomers		 * nothing and wait for the partition to appear.  Or, the disk
116326298Sasomers		 * might be damaged.  In that case, zfsd should do nothing and
117326298Sasomers		 * wait for the sysadmin to decide.
118326298Sasomers		 */
119326298Sasomers		if (nlabels != VDEV_LABELS || devLabel == NULL) {
120326298Sasomers			nvlist_free(devLabel);
121300906Sasomers			return (NULL);
122326298Sasomers		}
123300906Sasomers
124300906Sasomers		try {
125300906Sasomers			Vdev vdev(devLabel);
126300906Sasomers			degraded = vdev.State() != VDEV_STATE_HEALTHY;
127300906Sasomers			return (devLabel);
128300906Sasomers		} catch (ZfsdException &exp) {
129300906Sasomers			string devName = fdevname(devFd);
130300906Sasomers			string devPath = _PATH_DEV + devName;
131300906Sasomers			string context("DevfsEvent::ReadLabel: "
132300906Sasomers				     + devPath + ": ");
133300906Sasomers
134300906Sasomers			exp.GetString().insert(0, context);
135300906Sasomers			exp.Log();
136326298Sasomers			nvlist_free(devLabel);
137300906Sasomers		}
138300906Sasomers	}
139300906Sasomers	return (NULL);
140300906Sasomers}
141300906Sasomers
142300906Sasomersbool
143300906SasomersDevfsEvent::OnlineByLabel(const string &devPath, const string& physPath,
144300906Sasomers			      nvlist_t *devConfig)
145300906Sasomers{
146300906Sasomers	try {
147300906Sasomers		/*
148300906Sasomers		 * A device with ZFS label information has been
149300906Sasomers		 * inserted.  If it matches a device for which we
150300906Sasomers		 * have a case, see if we can solve that case.
151300906Sasomers		 */
152300906Sasomers		syslog(LOG_INFO, "Interrogating VDEV label for %s\n",
153300906Sasomers		       devPath.c_str());
154300906Sasomers		Vdev vdev(devConfig);
155300906Sasomers		CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(),
156300906Sasomers						  vdev.GUID()));
157300906Sasomers		if (caseFile != NULL)
158300906Sasomers			return (caseFile->ReEvaluate(devPath, physPath, &vdev));
159300906Sasomers
160300906Sasomers	} catch (ZfsdException &exp) {
161300906Sasomers		string context("DevfsEvent::OnlineByLabel: " + devPath + ": ");
162300906Sasomers
163300906Sasomers		exp.GetString().insert(0, context);
164300906Sasomers		exp.Log();
165300906Sasomers	}
166300906Sasomers	return (false);
167300906Sasomers}
168300906Sasomers
169300906Sasomers//- DevfsEvent Virtual Public Methods ------------------------------------------
170300906SasomersEvent *
171300906SasomersDevfsEvent::DeepCopy() const
172300906Sasomers{
173300906Sasomers	return (new DevfsEvent(*this));
174300906Sasomers}
175300906Sasomers
176300906Sasomersbool
177300906SasomersDevfsEvent::Process() const
178300906Sasomers{
179300906Sasomers	/*
180300906Sasomers	 * We are only concerned with newly discovered
181300906Sasomers	 * devices that can be ZFS vdevs.
182300906Sasomers	 */
183300906Sasomers	if (Value("type") != "CREATE" || !IsDiskDev())
184300906Sasomers		return (false);
185300906Sasomers
186300906Sasomers	/* Log the event since it is of interest. */
187300906Sasomers	Log(LOG_INFO);
188300906Sasomers
189300906Sasomers	string devPath;
190300906Sasomers	if (!DevPath(devPath))
191300906Sasomers		return (false);
192300906Sasomers
193300906Sasomers	int devFd(open(devPath.c_str(), O_RDONLY));
194300906Sasomers	if (devFd == -1)
195300906Sasomers		return (false);
196300906Sasomers
197300906Sasomers	bool inUse;
198300906Sasomers	bool degraded;
199300906Sasomers	nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded));
200300906Sasomers
201300906Sasomers	string physPath;
202300906Sasomers	bool havePhysPath(PhysicalPath(physPath));
203300906Sasomers
204300906Sasomers	string devName;
205300906Sasomers	DevName(devName);
206300906Sasomers	close(devFd);
207300906Sasomers
208300906Sasomers	if (inUse && devLabel != NULL) {
209300906Sasomers		OnlineByLabel(devPath, physPath, devLabel);
210300906Sasomers	} else if (degraded) {
211300906Sasomers		syslog(LOG_INFO, "%s is marked degraded.  Ignoring "
212300906Sasomers		       "as a replace by physical path candidate.\n",
213300906Sasomers		       devName.c_str());
214300906Sasomers	} else if (havePhysPath && IsWholeDev()) {
215300906Sasomers		/*
216300906Sasomers		 * TODO: attempt to resolve events using every casefile
217300906Sasomers		 * that matches this physpath
218300906Sasomers		 */
219300906Sasomers		CaseFile *caseFile(CaseFile::Find(physPath));
220300906Sasomers		if (caseFile != NULL) {
221300906Sasomers			syslog(LOG_INFO,
222300906Sasomers			       "Found CaseFile(%s:%s:%s) - ReEvaluating\n",
223300906Sasomers			       caseFile->PoolGUIDString().c_str(),
224300906Sasomers			       caseFile->VdevGUIDString().c_str(),
225300906Sasomers			       zpool_state_to_name(caseFile->VdevState(),
226300906Sasomers						   VDEV_AUX_NONE));
227300906Sasomers			caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
228300906Sasomers		}
229300906Sasomers	}
230300906Sasomers	if (devLabel != NULL)
231300906Sasomers		nvlist_free(devLabel);
232300906Sasomers	return (false);
233300906Sasomers}
234300906Sasomers
235300906Sasomers//- DevfsEvent Protected Methods -----------------------------------------------
236300906SasomersDevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
237300906Sasomers			       const string &eventString)
238300906Sasomers : DevdCtl::DevfsEvent(type, nvpairs, eventString)
239300906Sasomers{
240300906Sasomers}
241300906Sasomers
242300906SasomersDevfsEvent::DevfsEvent(const DevfsEvent &src)
243300906Sasomers : DevdCtl::DevfsEvent::DevfsEvent(src)
244300906Sasomers{
245300906Sasomers}
246300906Sasomers
247300906Sasomers/*-------------------------------- GeomEvent --------------------------------*/
248300906Sasomers
249300906Sasomers//- GeomEvent Static Public Methods -------------------------------------------
250300906SasomersEvent *
251300906SasomersGeomEvent::Builder(Event::Type type,
252300906Sasomers		   NVPairMap &nvPairs,
253300906Sasomers		   const string &eventString)
254300906Sasomers{
255300906Sasomers	return (new GeomEvent(type, nvPairs, eventString));
256300906Sasomers}
257300906Sasomers
258300906Sasomers//- GeomEvent Virtual Public Methods ------------------------------------------
259300906SasomersEvent *
260300906SasomersGeomEvent::DeepCopy() const
261300906Sasomers{
262300906Sasomers	return (new GeomEvent(*this));
263300906Sasomers}
264300906Sasomers
265300906Sasomersbool
266300906SasomersGeomEvent::Process() const
267300906Sasomers{
268300906Sasomers	/*
269300906Sasomers	 * We are only concerned with physical path changes, because those can
270300906Sasomers	 * be used to satisfy autoreplace operations
271300906Sasomers	 */
272300906Sasomers	if (Value("type") != "GEOM::physpath" || !IsDiskDev())
273300906Sasomers		return (false);
274300906Sasomers
275300906Sasomers	/* Log the event since it is of interest. */
276300906Sasomers	Log(LOG_INFO);
277300906Sasomers
278300906Sasomers	string devPath;
279300906Sasomers	if (!DevPath(devPath))
280300906Sasomers		return (false);
281300906Sasomers
282300906Sasomers	string physPath;
283300906Sasomers        bool havePhysPath(PhysicalPath(physPath));
284300906Sasomers
285300906Sasomers	string devName;
286300906Sasomers	DevName(devName);
287300906Sasomers
288300906Sasomers	if (havePhysPath) {
289300906Sasomers		/*
290300906Sasomers		 * TODO: attempt to resolve events using every casefile
291300906Sasomers		 * that matches this physpath
292300906Sasomers		 */
293300906Sasomers		CaseFile *caseFile(CaseFile::Find(physPath));
294300906Sasomers		if (caseFile != NULL) {
295300906Sasomers			syslog(LOG_INFO,
296300906Sasomers			       "Found CaseFile(%s:%s:%s) - ReEvaluating\n",
297300906Sasomers			       caseFile->PoolGUIDString().c_str(),
298300906Sasomers			       caseFile->VdevGUIDString().c_str(),
299300906Sasomers			       zpool_state_to_name(caseFile->VdevState(),
300300906Sasomers						   VDEV_AUX_NONE));
301300906Sasomers			caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
302300906Sasomers		}
303300906Sasomers	}
304300906Sasomers	return (false);
305300906Sasomers}
306300906Sasomers
307300906Sasomers//- GeomEvent Protected Methods -----------------------------------------------
308300906SasomersGeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
309300906Sasomers			       const string &eventString)
310300906Sasomers : DevdCtl::GeomEvent(type, nvpairs, eventString)
311300906Sasomers{
312300906Sasomers}
313300906Sasomers
314300906SasomersGeomEvent::GeomEvent(const GeomEvent &src)
315300906Sasomers : DevdCtl::GeomEvent::GeomEvent(src)
316300906Sasomers{
317300906Sasomers}
318300906Sasomers
319300906Sasomers
320300906Sasomers/*--------------------------------- ZfsEvent ---------------------------------*/
321300906Sasomers//- ZfsEvent Static Public Methods ---------------------------------------------
322300906SasomersDevdCtl::Event *
323300906SasomersZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
324300906Sasomers		  const string &eventString)
325300906Sasomers{
326300906Sasomers	return (new ZfsEvent(type, nvpairs, eventString));
327300906Sasomers}
328300906Sasomers
329300906Sasomers//- ZfsEvent Virtual Public Methods --------------------------------------------
330300906SasomersEvent *
331300906SasomersZfsEvent::DeepCopy() const
332300906Sasomers{
333300906Sasomers	return (new ZfsEvent(*this));
334300906Sasomers}
335300906Sasomers
336300906Sasomersbool
337300906SasomersZfsEvent::Process() const
338300906Sasomers{
339300906Sasomers	string logstr("");
340300906Sasomers
341300906Sasomers	if (!Contains("class") && !Contains("type")) {
342300906Sasomers		syslog(LOG_ERR,
343300906Sasomers		       "ZfsEvent::Process: Missing class or type data.");
344300906Sasomers		return (false);
345300906Sasomers	}
346300906Sasomers
347300906Sasomers	/* On config syncs, replay any queued events first. */
348300906Sasomers	if (Value("type").find("misc.fs.zfs.config_sync") == 0) {
349300906Sasomers		/*
350300906Sasomers		 * Even if saved events are unconsumed the second time
351300906Sasomers		 * around, drop them.  Any events that still can't be
352300906Sasomers		 * consumed are probably referring to vdevs or pools that
353300906Sasomers		 * no longer exist.
354300906Sasomers		 */
355300906Sasomers		ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true);
356300906Sasomers		CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
357300906Sasomers	}
358300906Sasomers
359300906Sasomers	if (Value("type").find("misc.fs.zfs.") == 0) {
360300906Sasomers		/* Configuration changes, resilver events, etc. */
361300906Sasomers		ProcessPoolEvent();
362300906Sasomers		return (false);
363300906Sasomers	}
364300906Sasomers
365300906Sasomers	if (!Contains("pool_guid") || !Contains("vdev_guid")) {
366300906Sasomers		/* Only currently interested in Vdev related events. */
367300906Sasomers		return (false);
368300906Sasomers	}
369300906Sasomers
370300906Sasomers	CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
371300906Sasomers	if (caseFile != NULL) {
372300906Sasomers		Log(LOG_INFO);
373300906Sasomers		syslog(LOG_INFO, "Evaluating existing case file\n");
374300906Sasomers		caseFile->ReEvaluate(*this);
375300906Sasomers		return (false);
376300906Sasomers	}
377300906Sasomers
378300906Sasomers	/* Skip events that can't be handled. */
379300906Sasomers	Guid poolGUID(PoolGUID());
380300906Sasomers	/* If there are no replicas for a pool, then it's not manageable. */
381300906Sasomers	if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) {
382300906Sasomers		stringstream msg;
383300906Sasomers		msg << "No replicas available for pool "  << poolGUID;
384300906Sasomers		msg << ", ignoring";
385300906Sasomers		Log(LOG_INFO);
386300906Sasomers		syslog(LOG_INFO, "%s", msg.str().c_str());
387300906Sasomers		return (false);
388300906Sasomers	}
389300906Sasomers
390300906Sasomers	/*
391300906Sasomers	 * Create a case file for this vdev, and have it
392300906Sasomers	 * evaluate the event.
393300906Sasomers	 */
394300906Sasomers	ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
395300906Sasomers	if (zpl.empty()) {
396300906Sasomers		stringstream msg;
397300906Sasomers		int priority = LOG_INFO;
398300906Sasomers		msg << "ZfsEvent::Process: Event for unknown pool ";
399300906Sasomers		msg << poolGUID << " ";
400300906Sasomers		msg << "queued";
401300906Sasomers		Log(LOG_INFO);
402300906Sasomers		syslog(priority, "%s", msg.str().c_str());
403300906Sasomers		return (true);
404300906Sasomers	}
405300906Sasomers
406300906Sasomers	nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID());
407300906Sasomers	if (vdevConfig == NULL) {
408300906Sasomers		stringstream msg;
409300906Sasomers		int priority = LOG_INFO;
410300906Sasomers		msg << "ZfsEvent::Process: Event for unknown vdev ";
411300906Sasomers		msg << VdevGUID() << " ";
412300906Sasomers		msg << "queued";
413300906Sasomers		Log(LOG_INFO);
414300906Sasomers		syslog(priority, "%s", msg.str().c_str());
415300906Sasomers		return (true);
416300906Sasomers	}
417300906Sasomers
418300906Sasomers	Vdev vdev(zpl.front(), vdevConfig);
419300906Sasomers	caseFile = &CaseFile::Create(vdev);
420300906Sasomers	if (caseFile->ReEvaluate(*this) == false) {
421300906Sasomers		stringstream msg;
422300906Sasomers		int priority = LOG_INFO;
423300906Sasomers		msg << "ZfsEvent::Process: Unconsumed event for vdev(";
424300906Sasomers		msg << zpool_get_name(zpl.front()) << ",";
425300906Sasomers		msg << vdev.GUID() << ") ";
426300906Sasomers		msg << "queued";
427300906Sasomers		Log(LOG_INFO);
428300906Sasomers		syslog(priority, "%s", msg.str().c_str());
429300906Sasomers		return (true);
430300906Sasomers	}
431300906Sasomers	return (false);
432300906Sasomers}
433300906Sasomers
434300906Sasomers//- ZfsEvent Protected Methods -------------------------------------------------
435300906SasomersZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
436300906Sasomers			   const string &eventString)
437300906Sasomers : DevdCtl::ZfsEvent(type, nvpairs, eventString)
438300906Sasomers{
439300906Sasomers}
440300906Sasomers
441300906SasomersZfsEvent::ZfsEvent(const ZfsEvent &src)
442300906Sasomers : DevdCtl::ZfsEvent(src)
443300906Sasomers{
444300906Sasomers}
445300906Sasomers
446300906Sasomers/*
447300906Sasomers * Sometimes the kernel won't detach a spare when it is no longer needed.  This
448300906Sasomers * can happen for example if a drive is removed, then either the pool is
449300906Sasomers * exported or the machine is powered off, then the drive is reinserted, then
450300906Sasomers * the machine is powered on or the pool is imported.  ZFSD must detach these
451300906Sasomers * spares itself.
452300906Sasomers */
453300906Sasomersvoid
454300906SasomersZfsEvent::CleanupSpares() const
455300906Sasomers{
456300906Sasomers	Guid poolGUID(PoolGUID());
457300906Sasomers	ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
458300906Sasomers	if (!zpl.empty()) {
459300906Sasomers		zpool_handle_t* hdl;
460300906Sasomers
461300906Sasomers		hdl = zpl.front();
462300906Sasomers		VdevIterator(hdl).Each(TryDetach, (void*)hdl);
463300906Sasomers	}
464300906Sasomers}
465300906Sasomers
466300906Sasomersvoid
467300906SasomersZfsEvent::ProcessPoolEvent() const
468300906Sasomers{
469300906Sasomers	bool degradedDevice(false);
470300906Sasomers
471300906Sasomers	/* The pool is destroyed.  Discard any open cases */
472300906Sasomers	if (Value("type") == "misc.fs.zfs.pool_destroy") {
473300906Sasomers		Log(LOG_INFO);
474300906Sasomers		CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
475300906Sasomers		return;
476300906Sasomers	}
477300906Sasomers
478300906Sasomers	CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
479300906Sasomers	if (caseFile != NULL) {
480300906Sasomers		if (caseFile->VdevState() != VDEV_STATE_UNKNOWN
481300906Sasomers		 && caseFile->VdevState() < VDEV_STATE_HEALTHY)
482300906Sasomers			degradedDevice = true;
483300906Sasomers
484300906Sasomers		Log(LOG_INFO);
485300906Sasomers		caseFile->ReEvaluate(*this);
486300906Sasomers	}
487300906Sasomers	else if (Value("type") == "misc.fs.zfs.resilver_finish")
488300906Sasomers	{
489300906Sasomers		/*
490300906Sasomers		 * It's possible to get a resilver_finish event with no
491300906Sasomers		 * corresponding casefile.  For example, if a damaged pool were
492300906Sasomers		 * exported, repaired, then reimported.
493300906Sasomers		 */
494300906Sasomers		Log(LOG_INFO);
495300906Sasomers		CleanupSpares();
496300906Sasomers	}
497300906Sasomers
498300906Sasomers	if (Value("type") == "misc.fs.zfs.vdev_remove"
499300906Sasomers	 && degradedDevice == false) {
500300906Sasomers
501300906Sasomers		/* See if any other cases can make use of this device. */
502300906Sasomers		Log(LOG_INFO);
503300906Sasomers		ZfsDaemon::RequestSystemRescan();
504300906Sasomers	}
505300906Sasomers}
506300906Sasomers
507300906Sasomersbool
508300906SasomersZfsEvent::TryDetach(Vdev &vdev, void *cbArg)
509300906Sasomers{
510300906Sasomers	/*
511300906Sasomers	 * Outline:
512300906Sasomers	 * if this device is a spare, and its parent includes one healthy,
513300906Sasomers	 * non-spare child, then detach this device.
514300906Sasomers	 */
515300906Sasomers	zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg));
516300906Sasomers
517300906Sasomers	if (vdev.IsSpare()) {
518300906Sasomers		std::list<Vdev> siblings;
519300906Sasomers		std::list<Vdev>::iterator siblings_it;
520300906Sasomers		boolean_t cleanup = B_FALSE;
521300906Sasomers
522300906Sasomers		Vdev parent = vdev.Parent();
523300906Sasomers		siblings = parent.Children();
524300906Sasomers
525300906Sasomers		/* Determine whether the parent should be cleaned up */
526300906Sasomers		for (siblings_it = siblings.begin();
527300906Sasomers		     siblings_it != siblings.end();
528300906Sasomers		     siblings_it++) {
529300906Sasomers			Vdev sibling = *siblings_it;
530300906Sasomers
531300906Sasomers			if (!sibling.IsSpare() &&
532300906Sasomers			     sibling.State() == VDEV_STATE_HEALTHY) {
533300906Sasomers				cleanup = B_TRUE;
534300906Sasomers				break;
535300906Sasomers			}
536300906Sasomers		}
537300906Sasomers
538300906Sasomers		if (cleanup) {
539300906Sasomers			syslog(LOG_INFO, "Detaching spare vdev %s from pool %s",
540300906Sasomers			       vdev.Path().c_str(), zpool_get_name(hdl));
541300906Sasomers			zpool_vdev_detach(hdl, vdev.Path().c_str());
542300906Sasomers		}
543300906Sasomers
544300906Sasomers	}
545300906Sasomers
546300906Sasomers	/* Always return false, because there may be other spares to detach */
547300906Sasomers	return (false);
548300906Sasomers}
549