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