xref: /freebsd-src/sys/contrib/openzfs/cmd/zed/agents/zfs_agents.c (revision 180f822596ecc49d3074dcc9dfea9628aae1d48d)
1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy  * CDDL HEADER START
3eda14cbcSMatt Macy  *
4eda14cbcSMatt Macy  * The contents of this file are subject to the terms of the
5eda14cbcSMatt Macy  * Common Development and Distribution License Version 1.0 (CDDL-1.0).
6eda14cbcSMatt Macy  * You can obtain a copy of the license from the top-level file
7eda14cbcSMatt Macy  * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
8eda14cbcSMatt Macy  * You may not use this file except in compliance with the license.
9eda14cbcSMatt Macy  *
10eda14cbcSMatt Macy  * CDDL HEADER END
11eda14cbcSMatt Macy  */
12eda14cbcSMatt Macy 
13eda14cbcSMatt Macy /*
14eda14cbcSMatt Macy  * Copyright (c) 2016, Intel Corporation.
15eda14cbcSMatt Macy  * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
16eda14cbcSMatt Macy  */
17eda14cbcSMatt Macy 
18eda14cbcSMatt Macy #include <libnvpair.h>
19eda14cbcSMatt Macy #include <libzfs.h>
20eda14cbcSMatt Macy #include <stddef.h>
21eda14cbcSMatt Macy #include <stdlib.h>
22eda14cbcSMatt Macy #include <string.h>
23eda14cbcSMatt Macy #include <sys/list.h>
24eda14cbcSMatt Macy #include <sys/time.h>
25eda14cbcSMatt Macy #include <sys/sysevent/eventdefs.h>
26eda14cbcSMatt Macy #include <sys/sysevent/dev.h>
27eda14cbcSMatt Macy #include <sys/fm/protocol.h>
28eda14cbcSMatt Macy #include <sys/fm/fs/zfs.h>
29eda14cbcSMatt Macy #include <pthread.h>
30eda14cbcSMatt Macy #include <unistd.h>
31eda14cbcSMatt Macy 
32eda14cbcSMatt Macy #include "zfs_agents.h"
33eda14cbcSMatt Macy #include "fmd_api.h"
34eda14cbcSMatt Macy #include "../zed_log.h"
35eda14cbcSMatt Macy 
36eda14cbcSMatt Macy /*
37eda14cbcSMatt Macy  * agent dispatch code
38eda14cbcSMatt Macy  */
39eda14cbcSMatt Macy 
40eda14cbcSMatt Macy static pthread_mutex_t	agent_lock = PTHREAD_MUTEX_INITIALIZER;
41eda14cbcSMatt Macy static pthread_cond_t	agent_cond = PTHREAD_COND_INITIALIZER;
42eda14cbcSMatt Macy static list_t		agent_events;	/* list of pending events */
43eda14cbcSMatt Macy static int		agent_exiting;
44eda14cbcSMatt Macy 
45eda14cbcSMatt Macy typedef struct agent_event {
46eda14cbcSMatt Macy 	char		ae_class[64];
47eda14cbcSMatt Macy 	char		ae_subclass[32];
48eda14cbcSMatt Macy 	nvlist_t	*ae_nvl;
49eda14cbcSMatt Macy 	list_node_t	ae_node;
50eda14cbcSMatt Macy } agent_event_t;
51eda14cbcSMatt Macy 
52eda14cbcSMatt Macy pthread_t g_agents_tid;
53eda14cbcSMatt Macy 
54eda14cbcSMatt Macy libzfs_handle_t *g_zfs_hdl;
55eda14cbcSMatt Macy 
56eda14cbcSMatt Macy /* guid search data */
57eda14cbcSMatt Macy typedef enum device_type {
58eda14cbcSMatt Macy 	DEVICE_TYPE_L2ARC,	/* l2arc device */
59eda14cbcSMatt Macy 	DEVICE_TYPE_SPARE,	/* spare device */
60eda14cbcSMatt Macy 	DEVICE_TYPE_PRIMARY	/* any primary pool storage device */
61eda14cbcSMatt Macy } device_type_t;
62eda14cbcSMatt Macy 
63eda14cbcSMatt Macy typedef struct guid_search {
64eda14cbcSMatt Macy 	uint64_t	gs_pool_guid;
65eda14cbcSMatt Macy 	uint64_t	gs_vdev_guid;
66eda14cbcSMatt Macy 	char		*gs_devid;
67eda14cbcSMatt Macy 	device_type_t	gs_vdev_type;
68eda14cbcSMatt Macy 	uint64_t	gs_vdev_expandtime;	/* vdev expansion time */
69eda14cbcSMatt Macy } guid_search_t;
70eda14cbcSMatt Macy 
71eda14cbcSMatt Macy /*
72eda14cbcSMatt Macy  * Walks the vdev tree recursively looking for a matching devid.
73eda14cbcSMatt Macy  * Returns B_TRUE as soon as a matching device is found, B_FALSE otherwise.
74eda14cbcSMatt Macy  */
75eda14cbcSMatt Macy static boolean_t
76eda14cbcSMatt Macy zfs_agent_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *arg)
77eda14cbcSMatt Macy {
78eda14cbcSMatt Macy 	guid_search_t *gsp = arg;
79eda14cbcSMatt Macy 	char *path = NULL;
80eda14cbcSMatt Macy 	uint_t c, children;
81eda14cbcSMatt Macy 	nvlist_t **child;
82eda14cbcSMatt Macy 
83eda14cbcSMatt Macy 	/*
84eda14cbcSMatt Macy 	 * First iterate over any children.
85eda14cbcSMatt Macy 	 */
86eda14cbcSMatt Macy 	if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
87eda14cbcSMatt Macy 	    &child, &children) == 0) {
88eda14cbcSMatt Macy 		for (c = 0; c < children; c++) {
89eda14cbcSMatt Macy 			if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
90eda14cbcSMatt Macy 				gsp->gs_vdev_type = DEVICE_TYPE_PRIMARY;
91eda14cbcSMatt Macy 				return (B_TRUE);
92eda14cbcSMatt Macy 			}
93eda14cbcSMatt Macy 		}
94eda14cbcSMatt Macy 	}
95eda14cbcSMatt Macy 	/*
96eda14cbcSMatt Macy 	 * Iterate over any spares and cache devices
97eda14cbcSMatt Macy 	 */
98eda14cbcSMatt Macy 	if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_SPARES,
99eda14cbcSMatt Macy 	    &child, &children) == 0) {
100eda14cbcSMatt Macy 		for (c = 0; c < children; c++) {
101eda14cbcSMatt Macy 			if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
102eda14cbcSMatt Macy 				gsp->gs_vdev_type = DEVICE_TYPE_L2ARC;
103eda14cbcSMatt Macy 				return (B_TRUE);
104eda14cbcSMatt Macy 			}
105eda14cbcSMatt Macy 		}
106eda14cbcSMatt Macy 	}
107eda14cbcSMatt Macy 	if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_L2CACHE,
108eda14cbcSMatt Macy 	    &child, &children) == 0) {
109eda14cbcSMatt Macy 		for (c = 0; c < children; c++) {
110eda14cbcSMatt Macy 			if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
111eda14cbcSMatt Macy 				gsp->gs_vdev_type = DEVICE_TYPE_SPARE;
112eda14cbcSMatt Macy 				return (B_TRUE);
113eda14cbcSMatt Macy 			}
114eda14cbcSMatt Macy 		}
115eda14cbcSMatt Macy 	}
116eda14cbcSMatt Macy 	/*
117eda14cbcSMatt Macy 	 * On a devid match, grab the vdev guid and expansion time, if any.
118eda14cbcSMatt Macy 	 */
119eda14cbcSMatt Macy 	if (gsp->gs_devid != NULL &&
120eda14cbcSMatt Macy 	    (nvlist_lookup_string(nvl, ZPOOL_CONFIG_DEVID, &path) == 0) &&
121eda14cbcSMatt Macy 	    (strcmp(gsp->gs_devid, path) == 0)) {
122eda14cbcSMatt Macy 		(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID,
123eda14cbcSMatt Macy 		    &gsp->gs_vdev_guid);
124eda14cbcSMatt Macy 		(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_EXPANSION_TIME,
125eda14cbcSMatt Macy 		    &gsp->gs_vdev_expandtime);
126eda14cbcSMatt Macy 		return (B_TRUE);
127eda14cbcSMatt Macy 	}
128eda14cbcSMatt Macy 
129eda14cbcSMatt Macy 	return (B_FALSE);
130eda14cbcSMatt Macy }
131eda14cbcSMatt Macy 
132eda14cbcSMatt Macy static int
133eda14cbcSMatt Macy zfs_agent_iter_pool(zpool_handle_t *zhp, void *arg)
134eda14cbcSMatt Macy {
135eda14cbcSMatt Macy 	guid_search_t *gsp = arg;
136eda14cbcSMatt Macy 	nvlist_t *config, *nvl;
137eda14cbcSMatt Macy 
138eda14cbcSMatt Macy 	/*
139eda14cbcSMatt Macy 	 * For each vdev in this pool, look for a match by devid
140eda14cbcSMatt Macy 	 */
141eda14cbcSMatt Macy 	if ((config = zpool_get_config(zhp, NULL)) != NULL) {
142eda14cbcSMatt Macy 		if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
143eda14cbcSMatt Macy 		    &nvl) == 0) {
144eda14cbcSMatt Macy 			(void) zfs_agent_iter_vdev(zhp, nvl, gsp);
145eda14cbcSMatt Macy 		}
146eda14cbcSMatt Macy 	}
147eda14cbcSMatt Macy 	/*
148eda14cbcSMatt Macy 	 * if a match was found then grab the pool guid
149eda14cbcSMatt Macy 	 */
150eda14cbcSMatt Macy 	if (gsp->gs_vdev_guid) {
151eda14cbcSMatt Macy 		(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
152eda14cbcSMatt Macy 		    &gsp->gs_pool_guid);
153eda14cbcSMatt Macy 	}
154eda14cbcSMatt Macy 
155eda14cbcSMatt Macy 	zpool_close(zhp);
156eda14cbcSMatt Macy 	return (gsp->gs_vdev_guid != 0);
157eda14cbcSMatt Macy }
158eda14cbcSMatt Macy 
159eda14cbcSMatt Macy void
160eda14cbcSMatt Macy zfs_agent_post_event(const char *class, const char *subclass, nvlist_t *nvl)
161eda14cbcSMatt Macy {
162eda14cbcSMatt Macy 	agent_event_t *event;
163eda14cbcSMatt Macy 
164eda14cbcSMatt Macy 	if (subclass == NULL)
165eda14cbcSMatt Macy 		subclass = "";
166eda14cbcSMatt Macy 
167eda14cbcSMatt Macy 	event = malloc(sizeof (agent_event_t));
168eda14cbcSMatt Macy 	if (event == NULL || nvlist_dup(nvl, &event->ae_nvl, 0) != 0) {
169eda14cbcSMatt Macy 		if (event)
170eda14cbcSMatt Macy 			free(event);
171eda14cbcSMatt Macy 		return;
172eda14cbcSMatt Macy 	}
173eda14cbcSMatt Macy 
174eda14cbcSMatt Macy 	if (strcmp(class, "sysevent.fs.zfs.vdev_check") == 0) {
175eda14cbcSMatt Macy 		class = EC_ZFS;
176eda14cbcSMatt Macy 		subclass = ESC_ZFS_VDEV_CHECK;
177eda14cbcSMatt Macy 	}
178eda14cbcSMatt Macy 
179eda14cbcSMatt Macy 	/*
180*180f8225SMatt Macy 	 * On Linux, we don't get the expected FM_RESOURCE_REMOVED ereport
181*180f8225SMatt Macy 	 * from the vdev_disk layer after a hot unplug. Fortunately we do
182*180f8225SMatt Macy 	 * get an EC_DEV_REMOVE from our disk monitor and it is a suitable
183eda14cbcSMatt Macy 	 * proxy so we remap it here for the benefit of the diagnosis engine.
184eda14cbcSMatt Macy 	 */
185eda14cbcSMatt Macy 	if ((strcmp(class, EC_DEV_REMOVE) == 0) &&
186eda14cbcSMatt Macy 	    (strcmp(subclass, ESC_DISK) == 0) &&
187eda14cbcSMatt Macy 	    (nvlist_exists(nvl, ZFS_EV_VDEV_GUID) ||
188eda14cbcSMatt Macy 	    nvlist_exists(nvl, DEV_IDENTIFIER))) {
189eda14cbcSMatt Macy 		nvlist_t *payload = event->ae_nvl;
190eda14cbcSMatt Macy 		struct timeval tv;
191eda14cbcSMatt Macy 		int64_t tod[2];
192eda14cbcSMatt Macy 		uint64_t pool_guid = 0, vdev_guid = 0;
193eda14cbcSMatt Macy 		guid_search_t search = { 0 };
194eda14cbcSMatt Macy 		device_type_t devtype = DEVICE_TYPE_PRIMARY;
195eda14cbcSMatt Macy 
196eda14cbcSMatt Macy 		class = "resource.fs.zfs.removed";
197eda14cbcSMatt Macy 		subclass = "";
198eda14cbcSMatt Macy 
199eda14cbcSMatt Macy 		(void) nvlist_add_string(payload, FM_CLASS, class);
200eda14cbcSMatt Macy 		(void) nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &pool_guid);
201eda14cbcSMatt Macy 		(void) nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &vdev_guid);
202eda14cbcSMatt Macy 
203eda14cbcSMatt Macy 		(void) gettimeofday(&tv, NULL);
204eda14cbcSMatt Macy 		tod[0] = tv.tv_sec;
205eda14cbcSMatt Macy 		tod[1] = tv.tv_usec;
206eda14cbcSMatt Macy 		(void) nvlist_add_int64_array(payload, FM_EREPORT_TIME, tod, 2);
207eda14cbcSMatt Macy 
208eda14cbcSMatt Macy 		/*
209eda14cbcSMatt Macy 		 * For multipath, spare and l2arc devices ZFS_EV_VDEV_GUID or
210eda14cbcSMatt Macy 		 * ZFS_EV_POOL_GUID may be missing so find them.
211eda14cbcSMatt Macy 		 */
212eda14cbcSMatt Macy 		(void) nvlist_lookup_string(nvl, DEV_IDENTIFIER,
213eda14cbcSMatt Macy 		    &search.gs_devid);
214eda14cbcSMatt Macy 		(void) zpool_iter(g_zfs_hdl, zfs_agent_iter_pool, &search);
215eda14cbcSMatt Macy 		pool_guid = search.gs_pool_guid;
216eda14cbcSMatt Macy 		vdev_guid = search.gs_vdev_guid;
217eda14cbcSMatt Macy 		devtype = search.gs_vdev_type;
218eda14cbcSMatt Macy 
219eda14cbcSMatt Macy 		/*
220eda14cbcSMatt Macy 		 * We want to avoid reporting "remove" events coming from
221eda14cbcSMatt Macy 		 * libudev for VDEVs which were expanded recently (10s) and
222eda14cbcSMatt Macy 		 * avoid activating spares in response to partitions being
223eda14cbcSMatt Macy 		 * deleted and created in rapid succession.
224eda14cbcSMatt Macy 		 */
225eda14cbcSMatt Macy 		if (search.gs_vdev_expandtime != 0 &&
226eda14cbcSMatt Macy 		    search.gs_vdev_expandtime + 10 > tv.tv_sec) {
227eda14cbcSMatt Macy 			zed_log_msg(LOG_INFO, "agent post event: ignoring '%s' "
228eda14cbcSMatt Macy 			    "for recently expanded device '%s'", EC_DEV_REMOVE,
229eda14cbcSMatt Macy 			    search.gs_devid);
230eda14cbcSMatt Macy 			goto out;
231eda14cbcSMatt Macy 		}
232eda14cbcSMatt Macy 
233eda14cbcSMatt Macy 		(void) nvlist_add_uint64(payload,
234eda14cbcSMatt Macy 		    FM_EREPORT_PAYLOAD_ZFS_POOL_GUID, pool_guid);
235eda14cbcSMatt Macy 		(void) nvlist_add_uint64(payload,
236eda14cbcSMatt Macy 		    FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID, vdev_guid);
237eda14cbcSMatt Macy 		switch (devtype) {
238eda14cbcSMatt Macy 		case DEVICE_TYPE_L2ARC:
239eda14cbcSMatt Macy 			(void) nvlist_add_string(payload,
240eda14cbcSMatt Macy 			    FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE,
241eda14cbcSMatt Macy 			    VDEV_TYPE_L2CACHE);
242eda14cbcSMatt Macy 			break;
243eda14cbcSMatt Macy 		case DEVICE_TYPE_SPARE:
244eda14cbcSMatt Macy 			(void) nvlist_add_string(payload,
245eda14cbcSMatt Macy 			    FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE, VDEV_TYPE_SPARE);
246eda14cbcSMatt Macy 			break;
247eda14cbcSMatt Macy 		case DEVICE_TYPE_PRIMARY:
248eda14cbcSMatt Macy 			(void) nvlist_add_string(payload,
249eda14cbcSMatt Macy 			    FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE, VDEV_TYPE_DISK);
250eda14cbcSMatt Macy 			break;
251eda14cbcSMatt Macy 		}
252eda14cbcSMatt Macy 
253eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "agent post event: mapping '%s' to '%s'",
254eda14cbcSMatt Macy 		    EC_DEV_REMOVE, class);
255eda14cbcSMatt Macy 	}
256eda14cbcSMatt Macy 
257eda14cbcSMatt Macy 	(void) strlcpy(event->ae_class, class, sizeof (event->ae_class));
258eda14cbcSMatt Macy 	(void) strlcpy(event->ae_subclass, subclass,
259eda14cbcSMatt Macy 	    sizeof (event->ae_subclass));
260eda14cbcSMatt Macy 
261eda14cbcSMatt Macy 	(void) pthread_mutex_lock(&agent_lock);
262eda14cbcSMatt Macy 	list_insert_tail(&agent_events, event);
263eda14cbcSMatt Macy 	(void) pthread_mutex_unlock(&agent_lock);
264eda14cbcSMatt Macy 
265eda14cbcSMatt Macy out:
266eda14cbcSMatt Macy 	(void) pthread_cond_signal(&agent_cond);
267eda14cbcSMatt Macy }
268eda14cbcSMatt Macy 
269eda14cbcSMatt Macy static void
270eda14cbcSMatt Macy zfs_agent_dispatch(const char *class, const char *subclass, nvlist_t *nvl)
271eda14cbcSMatt Macy {
272eda14cbcSMatt Macy 	/*
273eda14cbcSMatt Macy 	 * The diagnosis engine subscribes to the following events.
274eda14cbcSMatt Macy 	 * On illumos these subscriptions reside in:
275eda14cbcSMatt Macy 	 * 	/usr/lib/fm/fmd/plugins/zfs-diagnosis.conf
276eda14cbcSMatt Macy 	 */
277eda14cbcSMatt Macy 	if (strstr(class, "ereport.fs.zfs.") != NULL ||
278eda14cbcSMatt Macy 	    strstr(class, "resource.fs.zfs.") != NULL ||
279eda14cbcSMatt Macy 	    strcmp(class, "sysevent.fs.zfs.vdev_remove") == 0 ||
280eda14cbcSMatt Macy 	    strcmp(class, "sysevent.fs.zfs.vdev_remove_dev") == 0 ||
281eda14cbcSMatt Macy 	    strcmp(class, "sysevent.fs.zfs.pool_destroy") == 0) {
282eda14cbcSMatt Macy 		fmd_module_recv(fmd_module_hdl("zfs-diagnosis"), nvl, class);
283eda14cbcSMatt Macy 	}
284eda14cbcSMatt Macy 
285eda14cbcSMatt Macy 	/*
286eda14cbcSMatt Macy 	 * The retire agent subscribes to the following events.
287eda14cbcSMatt Macy 	 * On illumos these subscriptions reside in:
288eda14cbcSMatt Macy 	 * 	/usr/lib/fm/fmd/plugins/zfs-retire.conf
289eda14cbcSMatt Macy 	 *
290eda14cbcSMatt Macy 	 * NOTE: faults events come directly from our diagnosis engine
291eda14cbcSMatt Macy 	 * and will not pass through the zfs kernel module.
292eda14cbcSMatt Macy 	 */
293eda14cbcSMatt Macy 	if (strcmp(class, FM_LIST_SUSPECT_CLASS) == 0 ||
294eda14cbcSMatt Macy 	    strcmp(class, "resource.fs.zfs.removed") == 0 ||
295eda14cbcSMatt Macy 	    strcmp(class, "resource.fs.zfs.statechange") == 0 ||
296eda14cbcSMatt Macy 	    strcmp(class, "sysevent.fs.zfs.vdev_remove")  == 0) {
297eda14cbcSMatt Macy 		fmd_module_recv(fmd_module_hdl("zfs-retire"), nvl, class);
298eda14cbcSMatt Macy 	}
299eda14cbcSMatt Macy 
300eda14cbcSMatt Macy 	/*
301eda14cbcSMatt Macy 	 * The SLM module only consumes disk events and vdev check events
302eda14cbcSMatt Macy 	 *
303eda14cbcSMatt Macy 	 * NOTE: disk events come directly from disk monitor and will
304eda14cbcSMatt Macy 	 * not pass through the zfs kernel module.
305eda14cbcSMatt Macy 	 */
306eda14cbcSMatt Macy 	if (strstr(class, "EC_dev_") != NULL ||
307eda14cbcSMatt Macy 	    strcmp(class, EC_ZFS) == 0) {
308eda14cbcSMatt Macy 		(void) zfs_slm_event(class, subclass, nvl);
309eda14cbcSMatt Macy 	}
310eda14cbcSMatt Macy }
311eda14cbcSMatt Macy 
312eda14cbcSMatt Macy /*
313eda14cbcSMatt Macy  * Events are consumed and dispatched from this thread
314eda14cbcSMatt Macy  * An agent can also post an event so event list lock
315eda14cbcSMatt Macy  * is not held when calling an agent.
316eda14cbcSMatt Macy  * One event is consumed at a time.
317eda14cbcSMatt Macy  */
318eda14cbcSMatt Macy static void *
319eda14cbcSMatt Macy zfs_agent_consumer_thread(void *arg)
320eda14cbcSMatt Macy {
321eda14cbcSMatt Macy 	for (;;) {
322eda14cbcSMatt Macy 		agent_event_t *event;
323eda14cbcSMatt Macy 
324eda14cbcSMatt Macy 		(void) pthread_mutex_lock(&agent_lock);
325eda14cbcSMatt Macy 
326eda14cbcSMatt Macy 		/* wait for an event to show up */
327eda14cbcSMatt Macy 		while (!agent_exiting && list_is_empty(&agent_events))
328eda14cbcSMatt Macy 			(void) pthread_cond_wait(&agent_cond, &agent_lock);
329eda14cbcSMatt Macy 
330eda14cbcSMatt Macy 		if (agent_exiting) {
331eda14cbcSMatt Macy 			(void) pthread_mutex_unlock(&agent_lock);
332eda14cbcSMatt Macy 			zed_log_msg(LOG_INFO, "zfs_agent_consumer_thread: "
333eda14cbcSMatt Macy 			    "exiting");
334eda14cbcSMatt Macy 			return (NULL);
335eda14cbcSMatt Macy 		}
336eda14cbcSMatt Macy 
337eda14cbcSMatt Macy 		if ((event = (list_head(&agent_events))) != NULL) {
338eda14cbcSMatt Macy 			list_remove(&agent_events, event);
339eda14cbcSMatt Macy 
340eda14cbcSMatt Macy 			(void) pthread_mutex_unlock(&agent_lock);
341eda14cbcSMatt Macy 
342eda14cbcSMatt Macy 			/* dispatch to all event subscribers */
343eda14cbcSMatt Macy 			zfs_agent_dispatch(event->ae_class, event->ae_subclass,
344eda14cbcSMatt Macy 			    event->ae_nvl);
345eda14cbcSMatt Macy 
346eda14cbcSMatt Macy 			nvlist_free(event->ae_nvl);
347eda14cbcSMatt Macy 			free(event);
348eda14cbcSMatt Macy 			continue;
349eda14cbcSMatt Macy 		}
350eda14cbcSMatt Macy 
351eda14cbcSMatt Macy 		(void) pthread_mutex_unlock(&agent_lock);
352eda14cbcSMatt Macy 	}
353eda14cbcSMatt Macy 
354eda14cbcSMatt Macy 	return (NULL);
355eda14cbcSMatt Macy }
356eda14cbcSMatt Macy 
357eda14cbcSMatt Macy void
358eda14cbcSMatt Macy zfs_agent_init(libzfs_handle_t *zfs_hdl)
359eda14cbcSMatt Macy {
360eda14cbcSMatt Macy 	fmd_hdl_t *hdl;
361eda14cbcSMatt Macy 
362eda14cbcSMatt Macy 	g_zfs_hdl = zfs_hdl;
363eda14cbcSMatt Macy 
364eda14cbcSMatt Macy 	if (zfs_slm_init() != 0)
365eda14cbcSMatt Macy 		zed_log_die("Failed to initialize zfs slm");
366eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "Add Agent: init");
367eda14cbcSMatt Macy 
368eda14cbcSMatt Macy 	hdl = fmd_module_hdl("zfs-diagnosis");
369eda14cbcSMatt Macy 	_zfs_diagnosis_init(hdl);
370eda14cbcSMatt Macy 	if (!fmd_module_initialized(hdl))
371eda14cbcSMatt Macy 		zed_log_die("Failed to initialize zfs diagnosis");
372eda14cbcSMatt Macy 
373eda14cbcSMatt Macy 	hdl = fmd_module_hdl("zfs-retire");
374eda14cbcSMatt Macy 	_zfs_retire_init(hdl);
375eda14cbcSMatt Macy 	if (!fmd_module_initialized(hdl))
376eda14cbcSMatt Macy 		zed_log_die("Failed to initialize zfs retire");
377eda14cbcSMatt Macy 
378eda14cbcSMatt Macy 	list_create(&agent_events, sizeof (agent_event_t),
379eda14cbcSMatt Macy 	    offsetof(struct agent_event, ae_node));
380eda14cbcSMatt Macy 
381eda14cbcSMatt Macy 	if (pthread_create(&g_agents_tid, NULL, zfs_agent_consumer_thread,
382eda14cbcSMatt Macy 	    NULL) != 0) {
383eda14cbcSMatt Macy 		list_destroy(&agent_events);
384eda14cbcSMatt Macy 		zed_log_die("Failed to initialize agents");
385eda14cbcSMatt Macy 	}
386eda14cbcSMatt Macy }
387eda14cbcSMatt Macy 
388eda14cbcSMatt Macy void
389eda14cbcSMatt Macy zfs_agent_fini(void)
390eda14cbcSMatt Macy {
391eda14cbcSMatt Macy 	fmd_hdl_t *hdl;
392eda14cbcSMatt Macy 	agent_event_t *event;
393eda14cbcSMatt Macy 
394eda14cbcSMatt Macy 	agent_exiting = 1;
395eda14cbcSMatt Macy 	(void) pthread_cond_signal(&agent_cond);
396eda14cbcSMatt Macy 
397eda14cbcSMatt Macy 	/* wait for zfs_enum_pools thread to complete */
398eda14cbcSMatt Macy 	(void) pthread_join(g_agents_tid, NULL);
399eda14cbcSMatt Macy 
400eda14cbcSMatt Macy 	/* drain any pending events */
401eda14cbcSMatt Macy 	while ((event = (list_head(&agent_events))) != NULL) {
402eda14cbcSMatt Macy 		list_remove(&agent_events, event);
403eda14cbcSMatt Macy 		nvlist_free(event->ae_nvl);
404eda14cbcSMatt Macy 		free(event);
405eda14cbcSMatt Macy 	}
406eda14cbcSMatt Macy 
407eda14cbcSMatt Macy 	list_destroy(&agent_events);
408eda14cbcSMatt Macy 
409eda14cbcSMatt Macy 	if ((hdl = fmd_module_hdl("zfs-retire")) != NULL) {
410eda14cbcSMatt Macy 		_zfs_retire_fini(hdl);
411eda14cbcSMatt Macy 		fmd_hdl_unregister(hdl);
412eda14cbcSMatt Macy 	}
413eda14cbcSMatt Macy 	if ((hdl = fmd_module_hdl("zfs-diagnosis")) != NULL) {
414eda14cbcSMatt Macy 		_zfs_diagnosis_fini(hdl);
415eda14cbcSMatt Macy 		fmd_hdl_unregister(hdl);
416eda14cbcSMatt Macy 	}
417eda14cbcSMatt Macy 
418eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "Add Agent: fini");
419eda14cbcSMatt Macy 	zfs_slm_fini();
420eda14cbcSMatt Macy 
421eda14cbcSMatt Macy 	g_zfs_hdl = NULL;
422eda14cbcSMatt Macy }
423