zfsd_event.cc revision 300906
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>
39300906Sasomers
40300906Sasomers#include <syslog.h>
41300906Sasomers
42300906Sasomers#include <libzfs.h>
43300906Sasomers/*
44300906Sasomers * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
45300906Sasomers * C++ flush methods
46300906Sasomers */
47300906Sasomers#undef   flush
48300906Sasomers
49300906Sasomers#include <list>
50300906Sasomers#include <map>
51300906Sasomers#include <sstream>
52300906Sasomers#include <string>
53300906Sasomers
54300906Sasomers#include <devdctl/guid.h>
55300906Sasomers#include <devdctl/event.h>
56300906Sasomers#include <devdctl/event_factory.h>
57300906Sasomers#include <devdctl/exception.h>
58300906Sasomers#include <devdctl/consumer.h>
59300906Sasomers
60300906Sasomers#include "callout.h"
61300906Sasomers#include "vdev_iterator.h"
62300906Sasomers#include "zfsd_event.h"
63300906Sasomers#include "case_file.h"
64300906Sasomers#include "vdev.h"
65300906Sasomers#include "zfsd.h"
66300906Sasomers#include "zfsd_exception.h"
67300906Sasomers#include "zpool_list.h"
68300906Sasomers
69300906Sasomers__FBSDID("$FreeBSD: head/cddl/usr.sbin/zfsd/zfsd_event.cc 300906 2016-05-28 17:43:40Z asomers $");
70300906Sasomers/*============================ Namespace Control =============================*/
71300906Sasomersusing DevdCtl::Event;
72300906Sasomersusing DevdCtl::Guid;
73300906Sasomersusing DevdCtl::NVPairMap;
74300906Sasomersusing std::stringstream;
75300906Sasomers
76300906Sasomers/*=========================== Class Implementations ==========================*/
77300906Sasomers
78300906Sasomers/*-------------------------------- DevfsEvent --------------------------------*/
79300906Sasomers
80300906Sasomers//- DevfsEvent Static Public Methods -------------------------------------------
81300906SasomersEvent *
82300906SasomersDevfsEvent::Builder(Event::Type type,
83300906Sasomers		    NVPairMap &nvPairs,
84300906Sasomers		    const string &eventString)
85300906Sasomers{
86300906Sasomers	return (new DevfsEvent(type, nvPairs, eventString));
87300906Sasomers}
88300906Sasomers
89300906Sasomers//- DevfsEvent Static Protected Methods ----------------------------------------
90300906Sasomersnvlist_t *
91300906SasomersDevfsEvent::ReadLabel(int devFd, bool &inUse, bool &degraded)
92300906Sasomers{
93300906Sasomers	pool_state_t poolState;
94300906Sasomers	char        *poolName;
95300906Sasomers	boolean_t    b_inuse;
96300906Sasomers
97300906Sasomers	inUse    = false;
98300906Sasomers	degraded = false;
99300906Sasomers	poolName = NULL;
100300906Sasomers	if (zpool_in_use(g_zfsHandle, devFd, &poolState,
101300906Sasomers			 &poolName, &b_inuse) == 0) {
102300906Sasomers		nvlist_t *devLabel;
103300906Sasomers
104300906Sasomers		inUse = b_inuse == B_TRUE;
105300906Sasomers		if (poolName != NULL)
106300906Sasomers			free(poolName);
107300906Sasomers
108300906Sasomers		if (zpool_read_label(devFd, &devLabel) != 0
109300906Sasomers		 || devLabel == NULL)
110300906Sasomers			return (NULL);
111300906Sasomers
112300906Sasomers		try {
113300906Sasomers			Vdev vdev(devLabel);
114300906Sasomers			degraded = vdev.State() != VDEV_STATE_HEALTHY;
115300906Sasomers			return (devLabel);
116300906Sasomers		} catch (ZfsdException &exp) {
117300906Sasomers			string devName = fdevname(devFd);
118300906Sasomers			string devPath = _PATH_DEV + devName;
119300906Sasomers			string context("DevfsEvent::ReadLabel: "
120300906Sasomers				     + devPath + ": ");
121300906Sasomers
122300906Sasomers			exp.GetString().insert(0, context);
123300906Sasomers			exp.Log();
124300906Sasomers		}
125300906Sasomers	}
126300906Sasomers	return (NULL);
127300906Sasomers}
128300906Sasomers
129300906Sasomersbool
130300906SasomersDevfsEvent::OnlineByLabel(const string &devPath, const string& physPath,
131300906Sasomers			      nvlist_t *devConfig)
132300906Sasomers{
133300906Sasomers	try {
134300906Sasomers		/*
135300906Sasomers		 * A device with ZFS label information has been
136300906Sasomers		 * inserted.  If it matches a device for which we
137300906Sasomers		 * have a case, see if we can solve that case.
138300906Sasomers		 */
139300906Sasomers		syslog(LOG_INFO, "Interrogating VDEV label for %s\n",
140300906Sasomers		       devPath.c_str());
141300906Sasomers		Vdev vdev(devConfig);
142300906Sasomers		CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(),
143300906Sasomers						  vdev.GUID()));
144300906Sasomers		if (caseFile != NULL)
145300906Sasomers			return (caseFile->ReEvaluate(devPath, physPath, &vdev));
146300906Sasomers
147300906Sasomers	} catch (ZfsdException &exp) {
148300906Sasomers		string context("DevfsEvent::OnlineByLabel: " + devPath + ": ");
149300906Sasomers
150300906Sasomers		exp.GetString().insert(0, context);
151300906Sasomers		exp.Log();
152300906Sasomers	}
153300906Sasomers	return (false);
154300906Sasomers}
155300906Sasomers
156300906Sasomers//- DevfsEvent Virtual Public Methods ------------------------------------------
157300906SasomersEvent *
158300906SasomersDevfsEvent::DeepCopy() const
159300906Sasomers{
160300906Sasomers	return (new DevfsEvent(*this));
161300906Sasomers}
162300906Sasomers
163300906Sasomersbool
164300906SasomersDevfsEvent::Process() const
165300906Sasomers{
166300906Sasomers	/*
167300906Sasomers	 * We are only concerned with newly discovered
168300906Sasomers	 * devices that can be ZFS vdevs.
169300906Sasomers	 */
170300906Sasomers	if (Value("type") != "CREATE" || !IsDiskDev())
171300906Sasomers		return (false);
172300906Sasomers
173300906Sasomers	/* Log the event since it is of interest. */
174300906Sasomers	Log(LOG_INFO);
175300906Sasomers
176300906Sasomers	string devPath;
177300906Sasomers	if (!DevPath(devPath))
178300906Sasomers		return (false);
179300906Sasomers
180300906Sasomers	int devFd(open(devPath.c_str(), O_RDONLY));
181300906Sasomers	if (devFd == -1)
182300906Sasomers		return (false);
183300906Sasomers
184300906Sasomers	bool inUse;
185300906Sasomers	bool degraded;
186300906Sasomers	nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded));
187300906Sasomers
188300906Sasomers	string physPath;
189300906Sasomers	bool havePhysPath(PhysicalPath(physPath));
190300906Sasomers
191300906Sasomers	string devName;
192300906Sasomers	DevName(devName);
193300906Sasomers	close(devFd);
194300906Sasomers
195300906Sasomers	if (inUse && devLabel != NULL) {
196300906Sasomers		OnlineByLabel(devPath, physPath, devLabel);
197300906Sasomers	} else if (degraded) {
198300906Sasomers		syslog(LOG_INFO, "%s is marked degraded.  Ignoring "
199300906Sasomers		       "as a replace by physical path candidate.\n",
200300906Sasomers		       devName.c_str());
201300906Sasomers	} else if (havePhysPath && IsWholeDev()) {
202300906Sasomers		/*
203300906Sasomers		 * TODO: attempt to resolve events using every casefile
204300906Sasomers		 * that matches this physpath
205300906Sasomers		 */
206300906Sasomers		CaseFile *caseFile(CaseFile::Find(physPath));
207300906Sasomers		if (caseFile != NULL) {
208300906Sasomers			syslog(LOG_INFO,
209300906Sasomers			       "Found CaseFile(%s:%s:%s) - ReEvaluating\n",
210300906Sasomers			       caseFile->PoolGUIDString().c_str(),
211300906Sasomers			       caseFile->VdevGUIDString().c_str(),
212300906Sasomers			       zpool_state_to_name(caseFile->VdevState(),
213300906Sasomers						   VDEV_AUX_NONE));
214300906Sasomers			caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
215300906Sasomers		}
216300906Sasomers	}
217300906Sasomers	if (devLabel != NULL)
218300906Sasomers		nvlist_free(devLabel);
219300906Sasomers	return (false);
220300906Sasomers}
221300906Sasomers
222300906Sasomers//- DevfsEvent Protected Methods -----------------------------------------------
223300906SasomersDevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
224300906Sasomers			       const string &eventString)
225300906Sasomers : DevdCtl::DevfsEvent(type, nvpairs, eventString)
226300906Sasomers{
227300906Sasomers}
228300906Sasomers
229300906SasomersDevfsEvent::DevfsEvent(const DevfsEvent &src)
230300906Sasomers : DevdCtl::DevfsEvent::DevfsEvent(src)
231300906Sasomers{
232300906Sasomers}
233300906Sasomers
234300906Sasomers/*-------------------------------- GeomEvent --------------------------------*/
235300906Sasomers
236300906Sasomers//- GeomEvent Static Public Methods -------------------------------------------
237300906SasomersEvent *
238300906SasomersGeomEvent::Builder(Event::Type type,
239300906Sasomers		   NVPairMap &nvPairs,
240300906Sasomers		   const string &eventString)
241300906Sasomers{
242300906Sasomers	return (new GeomEvent(type, nvPairs, eventString));
243300906Sasomers}
244300906Sasomers
245300906Sasomers//- GeomEvent Virtual Public Methods ------------------------------------------
246300906SasomersEvent *
247300906SasomersGeomEvent::DeepCopy() const
248300906Sasomers{
249300906Sasomers	return (new GeomEvent(*this));
250300906Sasomers}
251300906Sasomers
252300906Sasomersbool
253300906SasomersGeomEvent::Process() const
254300906Sasomers{
255300906Sasomers	/*
256300906Sasomers	 * We are only concerned with physical path changes, because those can
257300906Sasomers	 * be used to satisfy autoreplace operations
258300906Sasomers	 */
259300906Sasomers	if (Value("type") != "GEOM::physpath" || !IsDiskDev())
260300906Sasomers		return (false);
261300906Sasomers
262300906Sasomers	/* Log the event since it is of interest. */
263300906Sasomers	Log(LOG_INFO);
264300906Sasomers
265300906Sasomers	string devPath;
266300906Sasomers	if (!DevPath(devPath))
267300906Sasomers		return (false);
268300906Sasomers
269300906Sasomers	string physPath;
270300906Sasomers        bool havePhysPath(PhysicalPath(physPath));
271300906Sasomers
272300906Sasomers	string devName;
273300906Sasomers	DevName(devName);
274300906Sasomers
275300906Sasomers	if (havePhysPath) {
276300906Sasomers		/*
277300906Sasomers		 * TODO: attempt to resolve events using every casefile
278300906Sasomers		 * that matches this physpath
279300906Sasomers		 */
280300906Sasomers		CaseFile *caseFile(CaseFile::Find(physPath));
281300906Sasomers		if (caseFile != NULL) {
282300906Sasomers			syslog(LOG_INFO,
283300906Sasomers			       "Found CaseFile(%s:%s:%s) - ReEvaluating\n",
284300906Sasomers			       caseFile->PoolGUIDString().c_str(),
285300906Sasomers			       caseFile->VdevGUIDString().c_str(),
286300906Sasomers			       zpool_state_to_name(caseFile->VdevState(),
287300906Sasomers						   VDEV_AUX_NONE));
288300906Sasomers			caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
289300906Sasomers		}
290300906Sasomers	}
291300906Sasomers	return (false);
292300906Sasomers}
293300906Sasomers
294300906Sasomers//- GeomEvent Protected Methods -----------------------------------------------
295300906SasomersGeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
296300906Sasomers			       const string &eventString)
297300906Sasomers : DevdCtl::GeomEvent(type, nvpairs, eventString)
298300906Sasomers{
299300906Sasomers}
300300906Sasomers
301300906SasomersGeomEvent::GeomEvent(const GeomEvent &src)
302300906Sasomers : DevdCtl::GeomEvent::GeomEvent(src)
303300906Sasomers{
304300906Sasomers}
305300906Sasomers
306300906Sasomers
307300906Sasomers/*--------------------------------- ZfsEvent ---------------------------------*/
308300906Sasomers//- ZfsEvent Static Public Methods ---------------------------------------------
309300906SasomersDevdCtl::Event *
310300906SasomersZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
311300906Sasomers		  const string &eventString)
312300906Sasomers{
313300906Sasomers	return (new ZfsEvent(type, nvpairs, eventString));
314300906Sasomers}
315300906Sasomers
316300906Sasomers//- ZfsEvent Virtual Public Methods --------------------------------------------
317300906SasomersEvent *
318300906SasomersZfsEvent::DeepCopy() const
319300906Sasomers{
320300906Sasomers	return (new ZfsEvent(*this));
321300906Sasomers}
322300906Sasomers
323300906Sasomersbool
324300906SasomersZfsEvent::Process() const
325300906Sasomers{
326300906Sasomers	string logstr("");
327300906Sasomers
328300906Sasomers	if (!Contains("class") && !Contains("type")) {
329300906Sasomers		syslog(LOG_ERR,
330300906Sasomers		       "ZfsEvent::Process: Missing class or type data.");
331300906Sasomers		return (false);
332300906Sasomers	}
333300906Sasomers
334300906Sasomers	/* On config syncs, replay any queued events first. */
335300906Sasomers	if (Value("type").find("misc.fs.zfs.config_sync") == 0) {
336300906Sasomers		/*
337300906Sasomers		 * Even if saved events are unconsumed the second time
338300906Sasomers		 * around, drop them.  Any events that still can't be
339300906Sasomers		 * consumed are probably referring to vdevs or pools that
340300906Sasomers		 * no longer exist.
341300906Sasomers		 */
342300906Sasomers		ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true);
343300906Sasomers		CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
344300906Sasomers	}
345300906Sasomers
346300906Sasomers	if (Value("type").find("misc.fs.zfs.") == 0) {
347300906Sasomers		/* Configuration changes, resilver events, etc. */
348300906Sasomers		ProcessPoolEvent();
349300906Sasomers		return (false);
350300906Sasomers	}
351300906Sasomers
352300906Sasomers	if (!Contains("pool_guid") || !Contains("vdev_guid")) {
353300906Sasomers		/* Only currently interested in Vdev related events. */
354300906Sasomers		return (false);
355300906Sasomers	}
356300906Sasomers
357300906Sasomers	CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
358300906Sasomers	if (caseFile != NULL) {
359300906Sasomers		Log(LOG_INFO);
360300906Sasomers		syslog(LOG_INFO, "Evaluating existing case file\n");
361300906Sasomers		caseFile->ReEvaluate(*this);
362300906Sasomers		return (false);
363300906Sasomers	}
364300906Sasomers
365300906Sasomers	/* Skip events that can't be handled. */
366300906Sasomers	Guid poolGUID(PoolGUID());
367300906Sasomers	/* If there are no replicas for a pool, then it's not manageable. */
368300906Sasomers	if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) {
369300906Sasomers		stringstream msg;
370300906Sasomers		msg << "No replicas available for pool "  << poolGUID;
371300906Sasomers		msg << ", ignoring";
372300906Sasomers		Log(LOG_INFO);
373300906Sasomers		syslog(LOG_INFO, "%s", msg.str().c_str());
374300906Sasomers		return (false);
375300906Sasomers	}
376300906Sasomers
377300906Sasomers	/*
378300906Sasomers	 * Create a case file for this vdev, and have it
379300906Sasomers	 * evaluate the event.
380300906Sasomers	 */
381300906Sasomers	ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
382300906Sasomers	if (zpl.empty()) {
383300906Sasomers		stringstream msg;
384300906Sasomers		int priority = LOG_INFO;
385300906Sasomers		msg << "ZfsEvent::Process: Event for unknown pool ";
386300906Sasomers		msg << poolGUID << " ";
387300906Sasomers		msg << "queued";
388300906Sasomers		Log(LOG_INFO);
389300906Sasomers		syslog(priority, "%s", msg.str().c_str());
390300906Sasomers		return (true);
391300906Sasomers	}
392300906Sasomers
393300906Sasomers	nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID());
394300906Sasomers	if (vdevConfig == NULL) {
395300906Sasomers		stringstream msg;
396300906Sasomers		int priority = LOG_INFO;
397300906Sasomers		msg << "ZfsEvent::Process: Event for unknown vdev ";
398300906Sasomers		msg << VdevGUID() << " ";
399300906Sasomers		msg << "queued";
400300906Sasomers		Log(LOG_INFO);
401300906Sasomers		syslog(priority, "%s", msg.str().c_str());
402300906Sasomers		return (true);
403300906Sasomers	}
404300906Sasomers
405300906Sasomers	Vdev vdev(zpl.front(), vdevConfig);
406300906Sasomers	caseFile = &CaseFile::Create(vdev);
407300906Sasomers	if (caseFile->ReEvaluate(*this) == false) {
408300906Sasomers		stringstream msg;
409300906Sasomers		int priority = LOG_INFO;
410300906Sasomers		msg << "ZfsEvent::Process: Unconsumed event for vdev(";
411300906Sasomers		msg << zpool_get_name(zpl.front()) << ",";
412300906Sasomers		msg << vdev.GUID() << ") ";
413300906Sasomers		msg << "queued";
414300906Sasomers		Log(LOG_INFO);
415300906Sasomers		syslog(priority, "%s", msg.str().c_str());
416300906Sasomers		return (true);
417300906Sasomers	}
418300906Sasomers	return (false);
419300906Sasomers}
420300906Sasomers
421300906Sasomers//- ZfsEvent Protected Methods -------------------------------------------------
422300906SasomersZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
423300906Sasomers			   const string &eventString)
424300906Sasomers : DevdCtl::ZfsEvent(type, nvpairs, eventString)
425300906Sasomers{
426300906Sasomers}
427300906Sasomers
428300906SasomersZfsEvent::ZfsEvent(const ZfsEvent &src)
429300906Sasomers : DevdCtl::ZfsEvent(src)
430300906Sasomers{
431300906Sasomers}
432300906Sasomers
433300906Sasomers/*
434300906Sasomers * Sometimes the kernel won't detach a spare when it is no longer needed.  This
435300906Sasomers * can happen for example if a drive is removed, then either the pool is
436300906Sasomers * exported or the machine is powered off, then the drive is reinserted, then
437300906Sasomers * the machine is powered on or the pool is imported.  ZFSD must detach these
438300906Sasomers * spares itself.
439300906Sasomers */
440300906Sasomersvoid
441300906SasomersZfsEvent::CleanupSpares() const
442300906Sasomers{
443300906Sasomers	Guid poolGUID(PoolGUID());
444300906Sasomers	ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
445300906Sasomers	if (!zpl.empty()) {
446300906Sasomers		zpool_handle_t* hdl;
447300906Sasomers
448300906Sasomers		hdl = zpl.front();
449300906Sasomers		VdevIterator(hdl).Each(TryDetach, (void*)hdl);
450300906Sasomers	}
451300906Sasomers}
452300906Sasomers
453300906Sasomersvoid
454300906SasomersZfsEvent::ProcessPoolEvent() const
455300906Sasomers{
456300906Sasomers	bool degradedDevice(false);
457300906Sasomers
458300906Sasomers	/* The pool is destroyed.  Discard any open cases */
459300906Sasomers	if (Value("type") == "misc.fs.zfs.pool_destroy") {
460300906Sasomers		Log(LOG_INFO);
461300906Sasomers		CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
462300906Sasomers		return;
463300906Sasomers	}
464300906Sasomers
465300906Sasomers	CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
466300906Sasomers	if (caseFile != NULL) {
467300906Sasomers		if (caseFile->VdevState() != VDEV_STATE_UNKNOWN
468300906Sasomers		 && caseFile->VdevState() < VDEV_STATE_HEALTHY)
469300906Sasomers			degradedDevice = true;
470300906Sasomers
471300906Sasomers		Log(LOG_INFO);
472300906Sasomers		caseFile->ReEvaluate(*this);
473300906Sasomers	}
474300906Sasomers	else if (Value("type") == "misc.fs.zfs.resilver_finish")
475300906Sasomers	{
476300906Sasomers		/*
477300906Sasomers		 * It's possible to get a resilver_finish event with no
478300906Sasomers		 * corresponding casefile.  For example, if a damaged pool were
479300906Sasomers		 * exported, repaired, then reimported.
480300906Sasomers		 */
481300906Sasomers		Log(LOG_INFO);
482300906Sasomers		CleanupSpares();
483300906Sasomers	}
484300906Sasomers
485300906Sasomers	if (Value("type") == "misc.fs.zfs.vdev_remove"
486300906Sasomers	 && degradedDevice == false) {
487300906Sasomers
488300906Sasomers		/* See if any other cases can make use of this device. */
489300906Sasomers		Log(LOG_INFO);
490300906Sasomers		ZfsDaemon::RequestSystemRescan();
491300906Sasomers	}
492300906Sasomers}
493300906Sasomers
494300906Sasomersbool
495300906SasomersZfsEvent::TryDetach(Vdev &vdev, void *cbArg)
496300906Sasomers{
497300906Sasomers	/*
498300906Sasomers	 * Outline:
499300906Sasomers	 * if this device is a spare, and its parent includes one healthy,
500300906Sasomers	 * non-spare child, then detach this device.
501300906Sasomers	 */
502300906Sasomers	zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg));
503300906Sasomers
504300906Sasomers	if (vdev.IsSpare()) {
505300906Sasomers		std::list<Vdev> siblings;
506300906Sasomers		std::list<Vdev>::iterator siblings_it;
507300906Sasomers		boolean_t cleanup = B_FALSE;
508300906Sasomers
509300906Sasomers		Vdev parent = vdev.Parent();
510300906Sasomers		siblings = parent.Children();
511300906Sasomers
512300906Sasomers		/* Determine whether the parent should be cleaned up */
513300906Sasomers		for (siblings_it = siblings.begin();
514300906Sasomers		     siblings_it != siblings.end();
515300906Sasomers		     siblings_it++) {
516300906Sasomers			Vdev sibling = *siblings_it;
517300906Sasomers
518300906Sasomers			if (!sibling.IsSpare() &&
519300906Sasomers			     sibling.State() == VDEV_STATE_HEALTHY) {
520300906Sasomers				cleanup = B_TRUE;
521300906Sasomers				break;
522300906Sasomers			}
523300906Sasomers		}
524300906Sasomers
525300906Sasomers		if (cleanup) {
526300906Sasomers			syslog(LOG_INFO, "Detaching spare vdev %s from pool %s",
527300906Sasomers			       vdev.Path().c_str(), zpool_get_name(hdl));
528300906Sasomers			zpool_vdev_detach(hdl, vdev.Path().c_str());
529300906Sasomers		}
530300906Sasomers
531300906Sasomers	}
532300906Sasomers
533300906Sasomers	/* Always return false, because there may be other spares to detach */
534300906Sasomers	return (false);
535300906Sasomers}
536