xref: /freebsd-src/sys/contrib/openzfs/cmd/zed/zed_disk_event.c (revision e3aa18ad71782a73d3dd9dd3d526bbd2b607ca16)
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, 2017, Intel Corporation.
15eda14cbcSMatt Macy  */
16eda14cbcSMatt Macy 
17eda14cbcSMatt Macy #ifdef HAVE_LIBUDEV
18eda14cbcSMatt Macy 
19eda14cbcSMatt Macy #include <errno.h>
20eda14cbcSMatt Macy #include <fcntl.h>
21eda14cbcSMatt Macy #include <libnvpair.h>
22eda14cbcSMatt Macy #include <libudev.h>
23eda14cbcSMatt Macy #include <libzfs.h>
24eda14cbcSMatt Macy #include <libzutil.h>
25eda14cbcSMatt Macy #include <pthread.h>
26eda14cbcSMatt Macy #include <stdlib.h>
27eda14cbcSMatt Macy #include <string.h>
28eda14cbcSMatt Macy 
29eda14cbcSMatt Macy #include <sys/sysevent/eventdefs.h>
30eda14cbcSMatt Macy #include <sys/sysevent/dev.h>
31eda14cbcSMatt Macy 
32eda14cbcSMatt Macy #include "zed_log.h"
33eda14cbcSMatt Macy #include "zed_disk_event.h"
34eda14cbcSMatt Macy #include "agents/zfs_agents.h"
35eda14cbcSMatt Macy 
36eda14cbcSMatt Macy /*
37eda14cbcSMatt Macy  * Portions of ZED need to see disk events for disks belonging to ZFS pools.
38eda14cbcSMatt Macy  * A libudev monitor is established to monitor block device actions and pass
39eda14cbcSMatt Macy  * them on to internal ZED logic modules.  Initially, zfs_mod.c is the only
40eda14cbcSMatt Macy  * consumer and is the Linux equivalent for the illumos syseventd ZFS SLM
41eda14cbcSMatt Macy  * module responsible for handling disk events for ZFS.
42eda14cbcSMatt Macy  */
43eda14cbcSMatt Macy 
44eda14cbcSMatt Macy pthread_t g_mon_tid;
45eda14cbcSMatt Macy struct udev *g_udev;
46eda14cbcSMatt Macy struct udev_monitor *g_mon;
47eda14cbcSMatt Macy 
48eda14cbcSMatt Macy 
49eda14cbcSMatt Macy #define	DEV_BYID_PATH	"/dev/disk/by-id/"
50eda14cbcSMatt Macy 
51eda14cbcSMatt Macy /* 64MB is minimum usable disk for ZFS */
52eda14cbcSMatt Macy #define	MINIMUM_SECTORS		131072
53eda14cbcSMatt Macy 
54eda14cbcSMatt Macy 
55eda14cbcSMatt Macy /*
56eda14cbcSMatt Macy  * Post disk event to SLM module
57eda14cbcSMatt Macy  *
58eda14cbcSMatt Macy  * occurs in the context of monitor thread
59eda14cbcSMatt Macy  */
60eda14cbcSMatt Macy static void
61eda14cbcSMatt Macy zed_udev_event(const char *class, const char *subclass, nvlist_t *nvl)
62eda14cbcSMatt Macy {
63eda14cbcSMatt Macy 	char *strval;
64eda14cbcSMatt Macy 	uint64_t numval;
65eda14cbcSMatt Macy 
66eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "zed_disk_event:");
67eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "\tclass: %s", class);
68eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "\tsubclass: %s", subclass);
69eda14cbcSMatt Macy 	if (nvlist_lookup_string(nvl, DEV_NAME, &strval) == 0)
70eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %s", DEV_NAME, strval);
71eda14cbcSMatt Macy 	if (nvlist_lookup_string(nvl, DEV_PATH, &strval) == 0)
72eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PATH, strval);
73eda14cbcSMatt Macy 	if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &strval) == 0)
74eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %s", DEV_IDENTIFIER, strval);
757cd22ac4SMartin Matuska 	if (nvlist_lookup_boolean(nvl, DEV_IS_PART) == B_TRUE)
767cd22ac4SMartin Matuska 		zed_log_msg(LOG_INFO, "\t%s: B_TRUE", DEV_IS_PART);
77eda14cbcSMatt Macy 	if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &strval) == 0)
78eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PHYS_PATH, strval);
79eda14cbcSMatt Macy 	if (nvlist_lookup_uint64(nvl, DEV_SIZE, &numval) == 0)
80eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %llu", DEV_SIZE, numval);
81eda14cbcSMatt Macy 	if (nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &numval) == 0)
82eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %llu", ZFS_EV_POOL_GUID, numval);
83eda14cbcSMatt Macy 	if (nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &numval) == 0)
84eda14cbcSMatt Macy 		zed_log_msg(LOG_INFO, "\t%s: %llu", ZFS_EV_VDEV_GUID, numval);
85eda14cbcSMatt Macy 
86eda14cbcSMatt Macy 	(void) zfs_agent_post_event(class, subclass, nvl);
87eda14cbcSMatt Macy }
88eda14cbcSMatt Macy 
89eda14cbcSMatt Macy /*
90eda14cbcSMatt Macy  * dev_event_nvlist: place event schema into an nv pair list
91eda14cbcSMatt Macy  *
92eda14cbcSMatt Macy  * NAME			VALUE (example)
93eda14cbcSMatt Macy  * --------------	--------------------------------------------------------
94eda14cbcSMatt Macy  * DEV_NAME		/dev/sdl
95eda14cbcSMatt Macy  * DEV_PATH		/devices/pci0000:00/0000:00:03.0/0000:04:00.0/host0/...
96eda14cbcSMatt Macy  * DEV_IDENTIFIER	ata-Hitachi_HTS725050A9A362_100601PCG420VLJ37DMC
97eda14cbcSMatt Macy  * DEV_PHYS_PATH	pci-0000:04:00.0-sas-0x4433221101000000-lun-0
98eda14cbcSMatt Macy  * DEV_IS_PART		---
99eda14cbcSMatt Macy  * DEV_SIZE		500107862016
100eda14cbcSMatt Macy  * ZFS_EV_POOL_GUID	17523635698032189180
101eda14cbcSMatt Macy  * ZFS_EV_VDEV_GUID	14663607734290803088
102eda14cbcSMatt Macy  */
103eda14cbcSMatt Macy static nvlist_t *
104eda14cbcSMatt Macy dev_event_nvlist(struct udev_device *dev)
105eda14cbcSMatt Macy {
106eda14cbcSMatt Macy 	nvlist_t *nvl;
107eda14cbcSMatt Macy 	char strval[128];
108eda14cbcSMatt Macy 	const char *value, *path;
109eda14cbcSMatt Macy 	uint64_t guid;
110eda14cbcSMatt Macy 
111eda14cbcSMatt Macy 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
112eda14cbcSMatt Macy 		return (NULL);
113eda14cbcSMatt Macy 
114eda14cbcSMatt Macy 	if (zfs_device_get_devid(dev, strval, sizeof (strval)) == 0)
115eda14cbcSMatt Macy 		(void) nvlist_add_string(nvl, DEV_IDENTIFIER, strval);
116eda14cbcSMatt Macy 	if (zfs_device_get_physical(dev, strval, sizeof (strval)) == 0)
117eda14cbcSMatt Macy 		(void) nvlist_add_string(nvl, DEV_PHYS_PATH, strval);
118eda14cbcSMatt Macy 	if ((path = udev_device_get_devnode(dev)) != NULL)
119eda14cbcSMatt Macy 		(void) nvlist_add_string(nvl, DEV_NAME, path);
120eda14cbcSMatt Macy 	if ((value = udev_device_get_devpath(dev)) != NULL)
121eda14cbcSMatt Macy 		(void) nvlist_add_string(nvl, DEV_PATH, value);
122eda14cbcSMatt Macy 	value = udev_device_get_devtype(dev);
123eda14cbcSMatt Macy 	if ((value != NULL && strcmp("partition", value) == 0) ||
124eda14cbcSMatt Macy 	    (udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER")
125eda14cbcSMatt Macy 	    != NULL)) {
126eda14cbcSMatt Macy 		(void) nvlist_add_boolean(nvl, DEV_IS_PART);
127eda14cbcSMatt Macy 	}
128eda14cbcSMatt Macy 	if ((value = udev_device_get_sysattr_value(dev, "size")) != NULL) {
129eda14cbcSMatt Macy 		uint64_t numval = DEV_BSIZE;
130eda14cbcSMatt Macy 
131eda14cbcSMatt Macy 		numval *= strtoull(value, NULL, 10);
132eda14cbcSMatt Macy 		(void) nvlist_add_uint64(nvl, DEV_SIZE, numval);
133eda14cbcSMatt Macy 	}
134eda14cbcSMatt Macy 
135eda14cbcSMatt Macy 	/*
136eda14cbcSMatt Macy 	 * Grab the pool and vdev guids from blkid cache
137eda14cbcSMatt Macy 	 */
138eda14cbcSMatt Macy 	value = udev_device_get_property_value(dev, "ID_FS_UUID");
139eda14cbcSMatt Macy 	if (value != NULL && (guid = strtoull(value, NULL, 10)) != 0)
140eda14cbcSMatt Macy 		(void) nvlist_add_uint64(nvl, ZFS_EV_POOL_GUID, guid);
141eda14cbcSMatt Macy 
142eda14cbcSMatt Macy 	value = udev_device_get_property_value(dev, "ID_FS_UUID_SUB");
143eda14cbcSMatt Macy 	if (value != NULL && (guid = strtoull(value, NULL, 10)) != 0)
144eda14cbcSMatt Macy 		(void) nvlist_add_uint64(nvl, ZFS_EV_VDEV_GUID, guid);
145eda14cbcSMatt Macy 
146eda14cbcSMatt Macy 	/*
147eda14cbcSMatt Macy 	 * Either a vdev guid or a devid must be present for matching
148eda14cbcSMatt Macy 	 */
149eda14cbcSMatt Macy 	if (!nvlist_exists(nvl, DEV_IDENTIFIER) &&
150eda14cbcSMatt Macy 	    !nvlist_exists(nvl, ZFS_EV_VDEV_GUID)) {
151eda14cbcSMatt Macy 		nvlist_free(nvl);
152eda14cbcSMatt Macy 		return (NULL);
153eda14cbcSMatt Macy 	}
154eda14cbcSMatt Macy 
155eda14cbcSMatt Macy 	return (nvl);
156eda14cbcSMatt Macy }
157eda14cbcSMatt Macy 
158eda14cbcSMatt Macy /*
159eda14cbcSMatt Macy  *  Listen for block device uevents
160eda14cbcSMatt Macy  */
161eda14cbcSMatt Macy static void *
162eda14cbcSMatt Macy zed_udev_monitor(void *arg)
163eda14cbcSMatt Macy {
164eda14cbcSMatt Macy 	struct udev_monitor *mon = arg;
165eda14cbcSMatt Macy 	char *tmp, *tmp2;
166eda14cbcSMatt Macy 
167eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "Waiting for new udev disk events...");
168eda14cbcSMatt Macy 
169eda14cbcSMatt Macy 	while (1) {
170eda14cbcSMatt Macy 		struct udev_device *dev;
171eda14cbcSMatt Macy 		const char *action, *type, *part, *sectors;
172eda14cbcSMatt Macy 		const char *bus, *uuid;
173eda14cbcSMatt Macy 		const char *class, *subclass;
174eda14cbcSMatt Macy 		nvlist_t *nvl;
175eda14cbcSMatt Macy 		boolean_t is_zfs = B_FALSE;
176eda14cbcSMatt Macy 
177eda14cbcSMatt Macy 		/* allow a cancellation while blocked (recvmsg) */
178eda14cbcSMatt Macy 		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
179eda14cbcSMatt Macy 
180eda14cbcSMatt Macy 		/* blocks at recvmsg until an event occurs */
181eda14cbcSMatt Macy 		if ((dev = udev_monitor_receive_device(mon)) == NULL) {
182eda14cbcSMatt Macy 			zed_log_msg(LOG_WARNING, "zed_udev_monitor: receive "
183eda14cbcSMatt Macy 			    "device error %d", errno);
184eda14cbcSMatt Macy 			continue;
185eda14cbcSMatt Macy 		}
186eda14cbcSMatt Macy 
187eda14cbcSMatt Macy 		/* allow all steps to complete before a cancellation */
188eda14cbcSMatt Macy 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
189eda14cbcSMatt Macy 
190eda14cbcSMatt Macy 		/*
191eda14cbcSMatt Macy 		 * Strongly typed device is the preferred filter
192eda14cbcSMatt Macy 		 */
193eda14cbcSMatt Macy 		type = udev_device_get_property_value(dev, "ID_FS_TYPE");
194eda14cbcSMatt Macy 		if (type != NULL && type[0] != '\0') {
195eda14cbcSMatt Macy 			if (strcmp(type, "zfs_member") == 0) {
196eda14cbcSMatt Macy 				is_zfs = B_TRUE;
197eda14cbcSMatt Macy 			} else {
198eda14cbcSMatt Macy 				/* not ours, so skip */
199eda14cbcSMatt Macy 				zed_log_msg(LOG_INFO, "zed_udev_monitor: skip "
200eda14cbcSMatt Macy 				    "%s (in use by %s)",
201eda14cbcSMatt Macy 				    udev_device_get_devnode(dev), type);
202eda14cbcSMatt Macy 				udev_device_unref(dev);
203eda14cbcSMatt Macy 				continue;
204eda14cbcSMatt Macy 			}
205eda14cbcSMatt Macy 		}
206eda14cbcSMatt Macy 
207eda14cbcSMatt Macy 		/*
208eda14cbcSMatt Macy 		 * if this is a disk and it is partitioned, then the
209eda14cbcSMatt Macy 		 * zfs label will reside in a DEVTYPE=partition and
210eda14cbcSMatt Macy 		 * we can skip passing this event
211eda14cbcSMatt Macy 		 */
212eda14cbcSMatt Macy 		type = udev_device_get_property_value(dev, "DEVTYPE");
213eda14cbcSMatt Macy 		part = udev_device_get_property_value(dev,
214eda14cbcSMatt Macy 		    "ID_PART_TABLE_TYPE");
215eda14cbcSMatt Macy 		if (type != NULL && type[0] != '\0' &&
216eda14cbcSMatt Macy 		    strcmp(type, "disk") == 0 &&
217eda14cbcSMatt Macy 		    part != NULL && part[0] != '\0') {
218c03c5b1cSMartin Matuska 			zed_log_msg(LOG_INFO,
219c03c5b1cSMartin Matuska 			    "%s: skip %s since it has a %s partition already",
220c03c5b1cSMartin Matuska 			    __func__,
221c03c5b1cSMartin Matuska 			    udev_device_get_property_value(dev, "DEVNAME"),
222c03c5b1cSMartin Matuska 			    part);
223eda14cbcSMatt Macy 			/* skip and wait for partition event */
224eda14cbcSMatt Macy 			udev_device_unref(dev);
225eda14cbcSMatt Macy 			continue;
226eda14cbcSMatt Macy 		}
227eda14cbcSMatt Macy 
228eda14cbcSMatt Macy 		/*
229eda14cbcSMatt Macy 		 * ignore small partitions
230eda14cbcSMatt Macy 		 */
231eda14cbcSMatt Macy 		sectors = udev_device_get_property_value(dev,
232eda14cbcSMatt Macy 		    "ID_PART_ENTRY_SIZE");
233eda14cbcSMatt Macy 		if (sectors == NULL)
234eda14cbcSMatt Macy 			sectors = udev_device_get_sysattr_value(dev, "size");
235eda14cbcSMatt Macy 		if (sectors != NULL &&
236eda14cbcSMatt Macy 		    strtoull(sectors, NULL, 10) < MINIMUM_SECTORS) {
237c03c5b1cSMartin Matuska 			zed_log_msg(LOG_INFO,
238c03c5b1cSMartin Matuska 			    "%s: %s sectors %s < %llu (minimum)",
239c03c5b1cSMartin Matuska 			    __func__,
240c03c5b1cSMartin Matuska 			    udev_device_get_property_value(dev, "DEVNAME"),
241c03c5b1cSMartin Matuska 			    sectors, MINIMUM_SECTORS);
242eda14cbcSMatt Macy 			udev_device_unref(dev);
243eda14cbcSMatt Macy 			continue;
244eda14cbcSMatt Macy 		}
245eda14cbcSMatt Macy 
246eda14cbcSMatt Macy 		/*
247eda14cbcSMatt Macy 		 * If the blkid probe didn't find ZFS, then a persistent
248eda14cbcSMatt Macy 		 * device id string is required in the message schema
249eda14cbcSMatt Macy 		 * for matching with vdevs. Preflight here for expected
250eda14cbcSMatt Macy 		 * udev information.
251eda14cbcSMatt Macy 		 */
252eda14cbcSMatt Macy 		bus = udev_device_get_property_value(dev, "ID_BUS");
253eda14cbcSMatt Macy 		uuid = udev_device_get_property_value(dev, "DM_UUID");
254eda14cbcSMatt Macy 		if (!is_zfs && (bus == NULL && uuid == NULL)) {
255eda14cbcSMatt Macy 			zed_log_msg(LOG_INFO, "zed_udev_monitor: %s no devid "
256eda14cbcSMatt Macy 			    "source", udev_device_get_devnode(dev));
257eda14cbcSMatt Macy 			udev_device_unref(dev);
258eda14cbcSMatt Macy 			continue;
259eda14cbcSMatt Macy 		}
260eda14cbcSMatt Macy 
261eda14cbcSMatt Macy 		action = udev_device_get_action(dev);
262eda14cbcSMatt Macy 		if (strcmp(action, "add") == 0) {
263eda14cbcSMatt Macy 			class = EC_DEV_ADD;
264eda14cbcSMatt Macy 			subclass = ESC_DISK;
265eda14cbcSMatt Macy 		} else if (strcmp(action, "remove") == 0) {
266eda14cbcSMatt Macy 			class = EC_DEV_REMOVE;
267eda14cbcSMatt Macy 			subclass = ESC_DISK;
268eda14cbcSMatt Macy 		} else if (strcmp(action, "change") == 0) {
269eda14cbcSMatt Macy 			class = EC_DEV_STATUS;
270eda14cbcSMatt Macy 			subclass = ESC_DEV_DLE;
271eda14cbcSMatt Macy 		} else {
272eda14cbcSMatt Macy 			zed_log_msg(LOG_WARNING, "zed_udev_monitor: %s unknown",
273eda14cbcSMatt Macy 			    action);
274eda14cbcSMatt Macy 			udev_device_unref(dev);
275eda14cbcSMatt Macy 			continue;
276eda14cbcSMatt Macy 		}
277eda14cbcSMatt Macy 
278eda14cbcSMatt Macy 		/*
279eda14cbcSMatt Macy 		 * Special case an EC_DEV_ADD for multipath devices
280eda14cbcSMatt Macy 		 *
281eda14cbcSMatt Macy 		 * When a multipath device is created, udev reports the
282eda14cbcSMatt Macy 		 * following:
283eda14cbcSMatt Macy 		 *
284eda14cbcSMatt Macy 		 * 1.	"add" event of the dm device for the multipath device
285eda14cbcSMatt Macy 		 *	(like /dev/dm-3).
286eda14cbcSMatt Macy 		 * 2.	"change" event to create the actual multipath device
287eda14cbcSMatt Macy 		 *	symlink (like /dev/mapper/mpatha).  The event also
288eda14cbcSMatt Macy 		 *	passes back the relevant DM vars we care about, like
289eda14cbcSMatt Macy 		 *	DM_UUID.
290eda14cbcSMatt Macy 		 * 3.	Another "change" event identical to #2 (that we ignore).
291eda14cbcSMatt Macy 		 *
292eda14cbcSMatt Macy 		 * To get the behavior we want, we treat the "change" event
293eda14cbcSMatt Macy 		 * in #2 as a "add" event; as if "/dev/mapper/mpatha" was
294eda14cbcSMatt Macy 		 * a new disk being added.
295eda14cbcSMatt Macy 		 */
296eda14cbcSMatt Macy 		if (strcmp(class, EC_DEV_STATUS) == 0 &&
297eda14cbcSMatt Macy 		    udev_device_get_property_value(dev, "DM_UUID") &&
298eda14cbcSMatt Macy 		    udev_device_get_property_value(dev, "MPATH_SBIN_PATH")) {
299eda14cbcSMatt Macy 			tmp = (char *)udev_device_get_devnode(dev);
300eda14cbcSMatt Macy 			tmp2 = zfs_get_underlying_path(tmp);
301eda14cbcSMatt Macy 			if (tmp && tmp2 && (strcmp(tmp, tmp2) != 0)) {
302eda14cbcSMatt Macy 				/*
303eda14cbcSMatt Macy 				 * We have a real underlying device, which
304eda14cbcSMatt Macy 				 * means that this multipath "change" event is
305eda14cbcSMatt Macy 				 * an "add" event.
306eda14cbcSMatt Macy 				 *
307eda14cbcSMatt Macy 				 * If the multipath device and the underlying
308eda14cbcSMatt Macy 				 * dev are the same name (i.e. /dev/dm-5), then
309eda14cbcSMatt Macy 				 * there is no real underlying disk for this
310eda14cbcSMatt Macy 				 * multipath device, and so this "change" event
311eda14cbcSMatt Macy 				 * really is a multipath removal.
312eda14cbcSMatt Macy 				 */
313eda14cbcSMatt Macy 				class = EC_DEV_ADD;
314eda14cbcSMatt Macy 				subclass = ESC_DISK;
315eda14cbcSMatt Macy 			} else {
316eda14cbcSMatt Macy 				tmp = (char *)
317eda14cbcSMatt Macy 				    udev_device_get_property_value(dev,
318eda14cbcSMatt Macy 				    "DM_NR_VALID_PATHS");
319eda14cbcSMatt Macy 				/* treat as a multipath remove */
320eda14cbcSMatt Macy 				if (tmp != NULL && strcmp(tmp, "0") == 0) {
321eda14cbcSMatt Macy 					class = EC_DEV_REMOVE;
322eda14cbcSMatt Macy 					subclass = ESC_DISK;
323eda14cbcSMatt Macy 				}
324eda14cbcSMatt Macy 			}
325eda14cbcSMatt Macy 			free(tmp2);
326eda14cbcSMatt Macy 		}
327eda14cbcSMatt Macy 
328eda14cbcSMatt Macy 		/*
329eda14cbcSMatt Macy 		 * Special case an EC_DEV_ADD for scsi_debug devices
330eda14cbcSMatt Macy 		 *
331eda14cbcSMatt Macy 		 * These devices require a udevadm trigger command after
332eda14cbcSMatt Macy 		 * creation in order to register the vdev_id scsidebug alias
333eda14cbcSMatt Macy 		 * rule (adds a persistent path (phys_path) used for fault
334eda14cbcSMatt Macy 		 * management automated tests in the ZFS test suite.
335eda14cbcSMatt Macy 		 *
336eda14cbcSMatt Macy 		 * After udevadm trigger command, event registers as a "change"
337eda14cbcSMatt Macy 		 * event but needs to instead be handled as another "add" event
338eda14cbcSMatt Macy 		 * to allow for disk labeling and partitioning to occur.
339eda14cbcSMatt Macy 		 */
340eda14cbcSMatt Macy 		if (strcmp(class, EC_DEV_STATUS) == 0 &&
341eda14cbcSMatt Macy 		    udev_device_get_property_value(dev, "ID_VDEV") &&
342eda14cbcSMatt Macy 		    udev_device_get_property_value(dev, "ID_MODEL")) {
343eda14cbcSMatt Macy 			const char *id_model, *id_model_sd = "scsi_debug";
344eda14cbcSMatt Macy 
345eda14cbcSMatt Macy 			id_model = udev_device_get_property_value(dev,
346eda14cbcSMatt Macy 			    "ID_MODEL");
347eda14cbcSMatt Macy 			if (strcmp(id_model, id_model_sd) == 0) {
348eda14cbcSMatt Macy 				class = EC_DEV_ADD;
349eda14cbcSMatt Macy 				subclass = ESC_DISK;
350eda14cbcSMatt Macy 			}
351eda14cbcSMatt Macy 		}
352eda14cbcSMatt Macy 
353eda14cbcSMatt Macy 		if ((nvl = dev_event_nvlist(dev)) != NULL) {
354eda14cbcSMatt Macy 			zed_udev_event(class, subclass, nvl);
355eda14cbcSMatt Macy 			nvlist_free(nvl);
356eda14cbcSMatt Macy 		}
357eda14cbcSMatt Macy 
358eda14cbcSMatt Macy 		udev_device_unref(dev);
359eda14cbcSMatt Macy 	}
360eda14cbcSMatt Macy 
361eda14cbcSMatt Macy 	return (NULL);
362eda14cbcSMatt Macy }
363eda14cbcSMatt Macy 
364eda14cbcSMatt Macy int
365*e3aa18adSMartin Matuska zed_disk_event_init(void)
366eda14cbcSMatt Macy {
367eda14cbcSMatt Macy 	int fd, fflags;
368eda14cbcSMatt Macy 
369eda14cbcSMatt Macy 	if ((g_udev = udev_new()) == NULL) {
370eda14cbcSMatt Macy 		zed_log_msg(LOG_WARNING, "udev_new failed (%d)", errno);
371eda14cbcSMatt Macy 		return (-1);
372eda14cbcSMatt Macy 	}
373eda14cbcSMatt Macy 
374eda14cbcSMatt Macy 	/* Set up a udev monitor for block devices */
375eda14cbcSMatt Macy 	g_mon = udev_monitor_new_from_netlink(g_udev, "udev");
376eda14cbcSMatt Macy 	udev_monitor_filter_add_match_subsystem_devtype(g_mon, "block", "disk");
377eda14cbcSMatt Macy 	udev_monitor_filter_add_match_subsystem_devtype(g_mon, "block",
378eda14cbcSMatt Macy 	    "partition");
379eda14cbcSMatt Macy 	udev_monitor_enable_receiving(g_mon);
380eda14cbcSMatt Macy 
381eda14cbcSMatt Macy 	/* Make sure monitoring socket is blocking */
382eda14cbcSMatt Macy 	fd = udev_monitor_get_fd(g_mon);
383eda14cbcSMatt Macy 	if ((fflags = fcntl(fd, F_GETFL)) & O_NONBLOCK)
384eda14cbcSMatt Macy 		(void) fcntl(fd, F_SETFL, fflags & ~O_NONBLOCK);
385eda14cbcSMatt Macy 
386eda14cbcSMatt Macy 	/* spawn a thread to monitor events */
387eda14cbcSMatt Macy 	if (pthread_create(&g_mon_tid, NULL, zed_udev_monitor, g_mon) != 0) {
388eda14cbcSMatt Macy 		udev_monitor_unref(g_mon);
389eda14cbcSMatt Macy 		udev_unref(g_udev);
390eda14cbcSMatt Macy 		zed_log_msg(LOG_WARNING, "pthread_create failed");
391eda14cbcSMatt Macy 		return (-1);
392eda14cbcSMatt Macy 	}
393eda14cbcSMatt Macy 
39416038816SMartin Matuska 	pthread_setname_np(g_mon_tid, "udev monitor");
395eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "zed_disk_event_init");
396eda14cbcSMatt Macy 
397eda14cbcSMatt Macy 	return (0);
398eda14cbcSMatt Macy }
399eda14cbcSMatt Macy 
400eda14cbcSMatt Macy void
401*e3aa18adSMartin Matuska zed_disk_event_fini(void)
402eda14cbcSMatt Macy {
403eda14cbcSMatt Macy 	/* cancel monitor thread at recvmsg() */
404eda14cbcSMatt Macy 	(void) pthread_cancel(g_mon_tid);
405eda14cbcSMatt Macy 	(void) pthread_join(g_mon_tid, NULL);
406eda14cbcSMatt Macy 
407eda14cbcSMatt Macy 	/* cleanup udev resources */
408eda14cbcSMatt Macy 	udev_monitor_unref(g_mon);
409eda14cbcSMatt Macy 	udev_unref(g_udev);
410eda14cbcSMatt Macy 
411eda14cbcSMatt Macy 	zed_log_msg(LOG_INFO, "zed_disk_event_fini");
412eda14cbcSMatt Macy }
413eda14cbcSMatt Macy 
414eda14cbcSMatt Macy #else
415eda14cbcSMatt Macy 
416eda14cbcSMatt Macy #include "zed_disk_event.h"
417eda14cbcSMatt Macy 
418eda14cbcSMatt Macy int
419*e3aa18adSMartin Matuska zed_disk_event_init(void)
420eda14cbcSMatt Macy {
421eda14cbcSMatt Macy 	return (0);
422eda14cbcSMatt Macy }
423eda14cbcSMatt Macy 
424eda14cbcSMatt Macy void
425*e3aa18adSMartin Matuska zed_disk_event_fini(void)
426eda14cbcSMatt Macy {
427eda14cbcSMatt Macy }
428eda14cbcSMatt Macy 
429eda14cbcSMatt Macy #endif /* HAVE_LIBUDEV */
430