1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2eda14cbcSMatt Macy /*
3eda14cbcSMatt Macy * CDDL HEADER START
4eda14cbcSMatt Macy *
5eda14cbcSMatt Macy * The contents of this file are subject to the terms of the
6eda14cbcSMatt Macy * Common Development and Distribution License Version 1.0 (CDDL-1.0).
7eda14cbcSMatt Macy * You can obtain a copy of the license from the top-level file
8eda14cbcSMatt Macy * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
9eda14cbcSMatt Macy * You may not use this file except in compliance with the license.
10eda14cbcSMatt Macy *
11eda14cbcSMatt Macy * CDDL HEADER END
12eda14cbcSMatt Macy */
13eda14cbcSMatt Macy
14eda14cbcSMatt Macy /*
15eda14cbcSMatt Macy * Copyright (c) 2016, 2017, Intel Corporation.
16eda14cbcSMatt Macy */
17eda14cbcSMatt Macy
18eda14cbcSMatt Macy #ifdef HAVE_LIBUDEV
19eda14cbcSMatt Macy
20eda14cbcSMatt Macy #include <errno.h>
21eda14cbcSMatt Macy #include <fcntl.h>
22eda14cbcSMatt Macy #include <libnvpair.h>
23eda14cbcSMatt Macy #include <libudev.h>
24eda14cbcSMatt Macy #include <libzfs.h>
25eda14cbcSMatt Macy #include <libzutil.h>
26eda14cbcSMatt Macy #include <pthread.h>
27eda14cbcSMatt Macy #include <stdlib.h>
28eda14cbcSMatt Macy #include <string.h>
29eda14cbcSMatt Macy
30eda14cbcSMatt Macy #include <sys/sysevent/eventdefs.h>
31eda14cbcSMatt Macy #include <sys/sysevent/dev.h>
32eda14cbcSMatt Macy
33eda14cbcSMatt Macy #include "zed_log.h"
34eda14cbcSMatt Macy #include "zed_disk_event.h"
35eda14cbcSMatt Macy #include "agents/zfs_agents.h"
36eda14cbcSMatt Macy
37eda14cbcSMatt Macy /*
38eda14cbcSMatt Macy * Portions of ZED need to see disk events for disks belonging to ZFS pools.
39eda14cbcSMatt Macy * A libudev monitor is established to monitor block device actions and pass
40eda14cbcSMatt Macy * them on to internal ZED logic modules. Initially, zfs_mod.c is the only
41eda14cbcSMatt Macy * consumer and is the Linux equivalent for the illumos syseventd ZFS SLM
42eda14cbcSMatt Macy * module responsible for handling disk events for ZFS.
43eda14cbcSMatt Macy */
44eda14cbcSMatt Macy
45eda14cbcSMatt Macy pthread_t g_mon_tid;
46eda14cbcSMatt Macy struct udev *g_udev;
47eda14cbcSMatt Macy struct udev_monitor *g_mon;
48eda14cbcSMatt Macy
49eda14cbcSMatt Macy
50eda14cbcSMatt Macy #define DEV_BYID_PATH "/dev/disk/by-id/"
51eda14cbcSMatt Macy
52eda14cbcSMatt Macy /* 64MB is minimum usable disk for ZFS */
53c7046f76SMartin Matuska #define MINIMUM_SECTORS 131072ULL
54eda14cbcSMatt Macy
55eda14cbcSMatt Macy
56eda14cbcSMatt Macy /*
57eda14cbcSMatt Macy * Post disk event to SLM module
58eda14cbcSMatt Macy *
59eda14cbcSMatt Macy * occurs in the context of monitor thread
60eda14cbcSMatt Macy */
61eda14cbcSMatt Macy static void
zed_udev_event(const char * class,const char * subclass,nvlist_t * nvl)62eda14cbcSMatt Macy zed_udev_event(const char *class, const char *subclass, nvlist_t *nvl)
63eda14cbcSMatt Macy {
642a58b312SMartin Matuska const char *strval;
65eda14cbcSMatt Macy uint64_t numval;
66eda14cbcSMatt Macy
67eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_disk_event:");
68eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\tclass: %s", class);
69eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\tsubclass: %s", subclass);
70eda14cbcSMatt Macy if (nvlist_lookup_string(nvl, DEV_NAME, &strval) == 0)
71eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %s", DEV_NAME, strval);
72eda14cbcSMatt Macy if (nvlist_lookup_string(nvl, DEV_PATH, &strval) == 0)
73eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PATH, strval);
74eda14cbcSMatt Macy if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &strval) == 0)
75eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %s", DEV_IDENTIFIER, strval);
767cd22ac4SMartin Matuska if (nvlist_lookup_boolean(nvl, DEV_IS_PART) == B_TRUE)
777cd22ac4SMartin Matuska zed_log_msg(LOG_INFO, "\t%s: B_TRUE", DEV_IS_PART);
78eda14cbcSMatt Macy if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &strval) == 0)
79eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PHYS_PATH, strval);
80eda14cbcSMatt Macy if (nvlist_lookup_uint64(nvl, DEV_SIZE, &numval) == 0)
81eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %llu", DEV_SIZE, numval);
82c7046f76SMartin Matuska if (nvlist_lookup_uint64(nvl, DEV_PARENT_SIZE, &numval) == 0)
83c7046f76SMartin Matuska zed_log_msg(LOG_INFO, "\t%s: %llu", DEV_PARENT_SIZE, numval);
84eda14cbcSMatt Macy if (nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &numval) == 0)
85eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %llu", ZFS_EV_POOL_GUID, numval);
86eda14cbcSMatt Macy if (nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &numval) == 0)
87eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %llu", ZFS_EV_VDEV_GUID, numval);
88eda14cbcSMatt Macy
89eda14cbcSMatt Macy (void) zfs_agent_post_event(class, subclass, nvl);
90eda14cbcSMatt Macy }
91eda14cbcSMatt Macy
92eda14cbcSMatt Macy /*
93eda14cbcSMatt Macy * dev_event_nvlist: place event schema into an nv pair list
94eda14cbcSMatt Macy *
95eda14cbcSMatt Macy * NAME VALUE (example)
96eda14cbcSMatt Macy * -------------- --------------------------------------------------------
97eda14cbcSMatt Macy * DEV_NAME /dev/sdl
98eda14cbcSMatt Macy * DEV_PATH /devices/pci0000:00/0000:00:03.0/0000:04:00.0/host0/...
99eda14cbcSMatt Macy * DEV_IDENTIFIER ata-Hitachi_HTS725050A9A362_100601PCG420VLJ37DMC
100eda14cbcSMatt Macy * DEV_PHYS_PATH pci-0000:04:00.0-sas-0x4433221101000000-lun-0
101eda14cbcSMatt Macy * DEV_IS_PART ---
102eda14cbcSMatt Macy * DEV_SIZE 500107862016
103eda14cbcSMatt Macy * ZFS_EV_POOL_GUID 17523635698032189180
104eda14cbcSMatt Macy * ZFS_EV_VDEV_GUID 14663607734290803088
105eda14cbcSMatt Macy */
106eda14cbcSMatt Macy static nvlist_t *
dev_event_nvlist(struct udev_device * dev)107eda14cbcSMatt Macy dev_event_nvlist(struct udev_device *dev)
108eda14cbcSMatt Macy {
109eda14cbcSMatt Macy nvlist_t *nvl;
110eda14cbcSMatt Macy char strval[128];
111eda14cbcSMatt Macy const char *value, *path;
112eda14cbcSMatt Macy uint64_t guid;
113eda14cbcSMatt Macy
114eda14cbcSMatt Macy if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
115eda14cbcSMatt Macy return (NULL);
116eda14cbcSMatt Macy
117eda14cbcSMatt Macy if (zfs_device_get_devid(dev, strval, sizeof (strval)) == 0)
118eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_IDENTIFIER, strval);
119eda14cbcSMatt Macy if (zfs_device_get_physical(dev, strval, sizeof (strval)) == 0)
120eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_PHYS_PATH, strval);
121eda14cbcSMatt Macy if ((path = udev_device_get_devnode(dev)) != NULL)
122eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_NAME, path);
123eda14cbcSMatt Macy if ((value = udev_device_get_devpath(dev)) != NULL)
124eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_PATH, value);
125eda14cbcSMatt Macy value = udev_device_get_devtype(dev);
126eda14cbcSMatt Macy if ((value != NULL && strcmp("partition", value) == 0) ||
127eda14cbcSMatt Macy (udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER")
128eda14cbcSMatt Macy != NULL)) {
129eda14cbcSMatt Macy (void) nvlist_add_boolean(nvl, DEV_IS_PART);
130eda14cbcSMatt Macy }
131eda14cbcSMatt Macy if ((value = udev_device_get_sysattr_value(dev, "size")) != NULL) {
132eda14cbcSMatt Macy uint64_t numval = DEV_BSIZE;
133eda14cbcSMatt Macy
134eda14cbcSMatt Macy numval *= strtoull(value, NULL, 10);
135eda14cbcSMatt Macy (void) nvlist_add_uint64(nvl, DEV_SIZE, numval);
136c7046f76SMartin Matuska
137c7046f76SMartin Matuska /*
138c7046f76SMartin Matuska * If the device has a parent, then get the parent block
139c7046f76SMartin Matuska * device's size as well. For example, /dev/sda1's parent
140c7046f76SMartin Matuska * is /dev/sda.
141c7046f76SMartin Matuska */
142c7046f76SMartin Matuska struct udev_device *parent_dev = udev_device_get_parent(dev);
1435c65a0a9SMartin Matuska if (parent_dev != NULL &&
1445c65a0a9SMartin Matuska (value = udev_device_get_sysattr_value(parent_dev, "size"))
145c7046f76SMartin Matuska != NULL) {
146c7046f76SMartin Matuska uint64_t numval = DEV_BSIZE;
147c7046f76SMartin Matuska
148c7046f76SMartin Matuska numval *= strtoull(value, NULL, 10);
149c7046f76SMartin Matuska (void) nvlist_add_uint64(nvl, DEV_PARENT_SIZE, numval);
150c7046f76SMartin Matuska }
151eda14cbcSMatt Macy }
152eda14cbcSMatt Macy
153eda14cbcSMatt Macy /*
154eda14cbcSMatt Macy * Grab the pool and vdev guids from blkid cache
155eda14cbcSMatt Macy */
156eda14cbcSMatt Macy value = udev_device_get_property_value(dev, "ID_FS_UUID");
157eda14cbcSMatt Macy if (value != NULL && (guid = strtoull(value, NULL, 10)) != 0)
158eda14cbcSMatt Macy (void) nvlist_add_uint64(nvl, ZFS_EV_POOL_GUID, guid);
159eda14cbcSMatt Macy
160eda14cbcSMatt Macy value = udev_device_get_property_value(dev, "ID_FS_UUID_SUB");
161eda14cbcSMatt Macy if (value != NULL && (guid = strtoull(value, NULL, 10)) != 0)
162eda14cbcSMatt Macy (void) nvlist_add_uint64(nvl, ZFS_EV_VDEV_GUID, guid);
163eda14cbcSMatt Macy
164eda14cbcSMatt Macy /*
165eda14cbcSMatt Macy * Either a vdev guid or a devid must be present for matching
166eda14cbcSMatt Macy */
167eda14cbcSMatt Macy if (!nvlist_exists(nvl, DEV_IDENTIFIER) &&
168eda14cbcSMatt Macy !nvlist_exists(nvl, ZFS_EV_VDEV_GUID)) {
169eda14cbcSMatt Macy nvlist_free(nvl);
170eda14cbcSMatt Macy return (NULL);
171eda14cbcSMatt Macy }
172eda14cbcSMatt Macy
173eda14cbcSMatt Macy return (nvl);
174eda14cbcSMatt Macy }
175eda14cbcSMatt Macy
176eda14cbcSMatt Macy /*
177eda14cbcSMatt Macy * Listen for block device uevents
178eda14cbcSMatt Macy */
179eda14cbcSMatt Macy static void *
zed_udev_monitor(void * arg)180eda14cbcSMatt Macy zed_udev_monitor(void *arg)
181eda14cbcSMatt Macy {
182eda14cbcSMatt Macy struct udev_monitor *mon = arg;
1832a58b312SMartin Matuska const char *tmp;
1842a58b312SMartin Matuska char *tmp2;
185eda14cbcSMatt Macy
186eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "Waiting for new udev disk events...");
187eda14cbcSMatt Macy
188eda14cbcSMatt Macy while (1) {
189eda14cbcSMatt Macy struct udev_device *dev;
190eda14cbcSMatt Macy const char *action, *type, *part, *sectors;
191271171e0SMartin Matuska const char *bus, *uuid, *devpath;
192eda14cbcSMatt Macy const char *class, *subclass;
193eda14cbcSMatt Macy nvlist_t *nvl;
194eda14cbcSMatt Macy boolean_t is_zfs = B_FALSE;
195eda14cbcSMatt Macy
196eda14cbcSMatt Macy /* allow a cancellation while blocked (recvmsg) */
197eda14cbcSMatt Macy pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
198eda14cbcSMatt Macy
199eda14cbcSMatt Macy /* blocks at recvmsg until an event occurs */
200eda14cbcSMatt Macy if ((dev = udev_monitor_receive_device(mon)) == NULL) {
201eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "zed_udev_monitor: receive "
202eda14cbcSMatt Macy "device error %d", errno);
203eda14cbcSMatt Macy continue;
204eda14cbcSMatt Macy }
205eda14cbcSMatt Macy
206eda14cbcSMatt Macy /* allow all steps to complete before a cancellation */
207eda14cbcSMatt Macy pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
208eda14cbcSMatt Macy
209eda14cbcSMatt Macy /*
210eda14cbcSMatt Macy * Strongly typed device is the preferred filter
211eda14cbcSMatt Macy */
212eda14cbcSMatt Macy type = udev_device_get_property_value(dev, "ID_FS_TYPE");
213eda14cbcSMatt Macy if (type != NULL && type[0] != '\0') {
214eda14cbcSMatt Macy if (strcmp(type, "zfs_member") == 0) {
215eda14cbcSMatt Macy is_zfs = B_TRUE;
216eda14cbcSMatt Macy } else {
217eda14cbcSMatt Macy /* not ours, so skip */
218eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_udev_monitor: skip "
219eda14cbcSMatt Macy "%s (in use by %s)",
220eda14cbcSMatt Macy udev_device_get_devnode(dev), type);
221eda14cbcSMatt Macy udev_device_unref(dev);
222eda14cbcSMatt Macy continue;
223eda14cbcSMatt Macy }
224eda14cbcSMatt Macy }
225eda14cbcSMatt Macy
226eda14cbcSMatt Macy /*
227eda14cbcSMatt Macy * if this is a disk and it is partitioned, then the
228eda14cbcSMatt Macy * zfs label will reside in a DEVTYPE=partition and
229eda14cbcSMatt Macy * we can skip passing this event
230271171e0SMartin Matuska *
231271171e0SMartin Matuska * Special case: Blank disks are sometimes reported with
232271171e0SMartin Matuska * an erroneous 'atari' partition, and should not be
233271171e0SMartin Matuska * excluded from being used as an autoreplace disk:
234271171e0SMartin Matuska *
235271171e0SMartin Matuska * https://github.com/openzfs/zfs/issues/13497
236eda14cbcSMatt Macy */
237eda14cbcSMatt Macy type = udev_device_get_property_value(dev, "DEVTYPE");
238eda14cbcSMatt Macy part = udev_device_get_property_value(dev,
239eda14cbcSMatt Macy "ID_PART_TABLE_TYPE");
240eda14cbcSMatt Macy if (type != NULL && type[0] != '\0' &&
241eda14cbcSMatt Macy strcmp(type, "disk") == 0 &&
242eda14cbcSMatt Macy part != NULL && part[0] != '\0') {
243271171e0SMartin Matuska const char *devname =
244271171e0SMartin Matuska udev_device_get_property_value(dev, "DEVNAME");
245271171e0SMartin Matuska
246271171e0SMartin Matuska if (strcmp(part, "atari") == 0) {
247c03c5b1cSMartin Matuska zed_log_msg(LOG_INFO,
248271171e0SMartin Matuska "%s: %s is reporting an atari partition, "
249271171e0SMartin Matuska "but we're going to assume it's a false "
250271171e0SMartin Matuska "positive and still use it (issue #13497)",
251271171e0SMartin Matuska __func__, devname);
252271171e0SMartin Matuska } else {
253271171e0SMartin Matuska zed_log_msg(LOG_INFO,
254271171e0SMartin Matuska "%s: skip %s since it has a %s partition "
255271171e0SMartin Matuska "already", __func__, devname, part);
256eda14cbcSMatt Macy /* skip and wait for partition event */
257eda14cbcSMatt Macy udev_device_unref(dev);
258eda14cbcSMatt Macy continue;
259eda14cbcSMatt Macy }
260271171e0SMartin Matuska }
261eda14cbcSMatt Macy
262eda14cbcSMatt Macy /*
263eda14cbcSMatt Macy * ignore small partitions
264eda14cbcSMatt Macy */
265eda14cbcSMatt Macy sectors = udev_device_get_property_value(dev,
266eda14cbcSMatt Macy "ID_PART_ENTRY_SIZE");
267eda14cbcSMatt Macy if (sectors == NULL)
268eda14cbcSMatt Macy sectors = udev_device_get_sysattr_value(dev, "size");
269eda14cbcSMatt Macy if (sectors != NULL &&
270eda14cbcSMatt Macy strtoull(sectors, NULL, 10) < MINIMUM_SECTORS) {
271c03c5b1cSMartin Matuska zed_log_msg(LOG_INFO,
272c03c5b1cSMartin Matuska "%s: %s sectors %s < %llu (minimum)",
273c03c5b1cSMartin Matuska __func__,
274c03c5b1cSMartin Matuska udev_device_get_property_value(dev, "DEVNAME"),
275c03c5b1cSMartin Matuska sectors, MINIMUM_SECTORS);
276eda14cbcSMatt Macy udev_device_unref(dev);
277eda14cbcSMatt Macy continue;
278eda14cbcSMatt Macy }
279eda14cbcSMatt Macy
280eda14cbcSMatt Macy /*
281eda14cbcSMatt Macy * If the blkid probe didn't find ZFS, then a persistent
282eda14cbcSMatt Macy * device id string is required in the message schema
283eda14cbcSMatt Macy * for matching with vdevs. Preflight here for expected
284eda14cbcSMatt Macy * udev information.
285271171e0SMartin Matuska *
286271171e0SMartin Matuska * Special case:
287271171e0SMartin Matuska * NVMe devices don't have ID_BUS set (at least on RHEL 7-8),
288271171e0SMartin Matuska * but they are valid for autoreplace. Add a special case for
289271171e0SMartin Matuska * them by searching for "/nvme/" in the udev DEVPATH:
290271171e0SMartin Matuska *
291271171e0SMartin Matuska * DEVPATH=/devices/pci0000:00/0000:00:1e.0/nvme/nvme2/nvme2n1
292eda14cbcSMatt Macy */
293eda14cbcSMatt Macy bus = udev_device_get_property_value(dev, "ID_BUS");
294eda14cbcSMatt Macy uuid = udev_device_get_property_value(dev, "DM_UUID");
295271171e0SMartin Matuska devpath = udev_device_get_devpath(dev);
296271171e0SMartin Matuska if (!is_zfs && (bus == NULL && uuid == NULL &&
297271171e0SMartin Matuska strstr(devpath, "/nvme/") == NULL)) {
298eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_udev_monitor: %s no devid "
299eda14cbcSMatt Macy "source", udev_device_get_devnode(dev));
300eda14cbcSMatt Macy udev_device_unref(dev);
301eda14cbcSMatt Macy continue;
302eda14cbcSMatt Macy }
303eda14cbcSMatt Macy
304eda14cbcSMatt Macy action = udev_device_get_action(dev);
305eda14cbcSMatt Macy if (strcmp(action, "add") == 0) {
306eda14cbcSMatt Macy class = EC_DEV_ADD;
307eda14cbcSMatt Macy subclass = ESC_DISK;
308eda14cbcSMatt Macy } else if (strcmp(action, "remove") == 0) {
309eda14cbcSMatt Macy class = EC_DEV_REMOVE;
310eda14cbcSMatt Macy subclass = ESC_DISK;
311eda14cbcSMatt Macy } else if (strcmp(action, "change") == 0) {
312eda14cbcSMatt Macy class = EC_DEV_STATUS;
313eda14cbcSMatt Macy subclass = ESC_DEV_DLE;
314eda14cbcSMatt Macy } else {
315eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "zed_udev_monitor: %s unknown",
316eda14cbcSMatt Macy action);
317eda14cbcSMatt Macy udev_device_unref(dev);
318eda14cbcSMatt Macy continue;
319eda14cbcSMatt Macy }
320eda14cbcSMatt Macy
321eda14cbcSMatt Macy /*
322eda14cbcSMatt Macy * Special case an EC_DEV_ADD for multipath devices
323eda14cbcSMatt Macy *
324eda14cbcSMatt Macy * When a multipath device is created, udev reports the
325eda14cbcSMatt Macy * following:
326eda14cbcSMatt Macy *
327eda14cbcSMatt Macy * 1. "add" event of the dm device for the multipath device
328eda14cbcSMatt Macy * (like /dev/dm-3).
329eda14cbcSMatt Macy * 2. "change" event to create the actual multipath device
330eda14cbcSMatt Macy * symlink (like /dev/mapper/mpatha). The event also
331eda14cbcSMatt Macy * passes back the relevant DM vars we care about, like
332eda14cbcSMatt Macy * DM_UUID.
333eda14cbcSMatt Macy * 3. Another "change" event identical to #2 (that we ignore).
334eda14cbcSMatt Macy *
335eda14cbcSMatt Macy * To get the behavior we want, we treat the "change" event
336eda14cbcSMatt Macy * in #2 as a "add" event; as if "/dev/mapper/mpatha" was
337eda14cbcSMatt Macy * a new disk being added.
338eda14cbcSMatt Macy */
339eda14cbcSMatt Macy if (strcmp(class, EC_DEV_STATUS) == 0 &&
340eda14cbcSMatt Macy udev_device_get_property_value(dev, "DM_UUID") &&
341eda14cbcSMatt Macy udev_device_get_property_value(dev, "MPATH_SBIN_PATH")) {
3422a58b312SMartin Matuska tmp = udev_device_get_devnode(dev);
343eda14cbcSMatt Macy tmp2 = zfs_get_underlying_path(tmp);
344eda14cbcSMatt Macy if (tmp && tmp2 && (strcmp(tmp, tmp2) != 0)) {
345eda14cbcSMatt Macy /*
346eda14cbcSMatt Macy * We have a real underlying device, which
347eda14cbcSMatt Macy * means that this multipath "change" event is
348eda14cbcSMatt Macy * an "add" event.
349eda14cbcSMatt Macy *
350eda14cbcSMatt Macy * If the multipath device and the underlying
351eda14cbcSMatt Macy * dev are the same name (i.e. /dev/dm-5), then
352eda14cbcSMatt Macy * there is no real underlying disk for this
353eda14cbcSMatt Macy * multipath device, and so this "change" event
354eda14cbcSMatt Macy * really is a multipath removal.
355eda14cbcSMatt Macy */
356eda14cbcSMatt Macy class = EC_DEV_ADD;
357eda14cbcSMatt Macy subclass = ESC_DISK;
358eda14cbcSMatt Macy } else {
3592a58b312SMartin Matuska tmp = udev_device_get_property_value(dev,
360eda14cbcSMatt Macy "DM_NR_VALID_PATHS");
361eda14cbcSMatt Macy /* treat as a multipath remove */
362eda14cbcSMatt Macy if (tmp != NULL && strcmp(tmp, "0") == 0) {
363eda14cbcSMatt Macy class = EC_DEV_REMOVE;
364eda14cbcSMatt Macy subclass = ESC_DISK;
365eda14cbcSMatt Macy }
366eda14cbcSMatt Macy }
367eda14cbcSMatt Macy free(tmp2);
368eda14cbcSMatt Macy }
369eda14cbcSMatt Macy
370eda14cbcSMatt Macy /*
371eda14cbcSMatt Macy * Special case an EC_DEV_ADD for scsi_debug devices
372eda14cbcSMatt Macy *
373eda14cbcSMatt Macy * These devices require a udevadm trigger command after
374eda14cbcSMatt Macy * creation in order to register the vdev_id scsidebug alias
375eda14cbcSMatt Macy * rule (adds a persistent path (phys_path) used for fault
376eda14cbcSMatt Macy * management automated tests in the ZFS test suite.
377eda14cbcSMatt Macy *
378eda14cbcSMatt Macy * After udevadm trigger command, event registers as a "change"
379eda14cbcSMatt Macy * event but needs to instead be handled as another "add" event
380eda14cbcSMatt Macy * to allow for disk labeling and partitioning to occur.
381eda14cbcSMatt Macy */
382eda14cbcSMatt Macy if (strcmp(class, EC_DEV_STATUS) == 0 &&
383eda14cbcSMatt Macy udev_device_get_property_value(dev, "ID_VDEV") &&
384eda14cbcSMatt Macy udev_device_get_property_value(dev, "ID_MODEL")) {
385eda14cbcSMatt Macy const char *id_model, *id_model_sd = "scsi_debug";
386eda14cbcSMatt Macy
387eda14cbcSMatt Macy id_model = udev_device_get_property_value(dev,
388eda14cbcSMatt Macy "ID_MODEL");
389eda14cbcSMatt Macy if (strcmp(id_model, id_model_sd) == 0) {
390eda14cbcSMatt Macy class = EC_DEV_ADD;
391eda14cbcSMatt Macy subclass = ESC_DISK;
392eda14cbcSMatt Macy }
393eda14cbcSMatt Macy }
394eda14cbcSMatt Macy
395eda14cbcSMatt Macy if ((nvl = dev_event_nvlist(dev)) != NULL) {
396eda14cbcSMatt Macy zed_udev_event(class, subclass, nvl);
397eda14cbcSMatt Macy nvlist_free(nvl);
398eda14cbcSMatt Macy }
399eda14cbcSMatt Macy
400eda14cbcSMatt Macy udev_device_unref(dev);
401eda14cbcSMatt Macy }
402eda14cbcSMatt Macy
403eda14cbcSMatt Macy return (NULL);
404eda14cbcSMatt Macy }
405eda14cbcSMatt Macy
406eda14cbcSMatt Macy int
zed_disk_event_init(void)407e3aa18adSMartin Matuska zed_disk_event_init(void)
408eda14cbcSMatt Macy {
409eda14cbcSMatt Macy int fd, fflags;
410eda14cbcSMatt Macy
411eda14cbcSMatt Macy if ((g_udev = udev_new()) == NULL) {
412eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "udev_new failed (%d)", errno);
413eda14cbcSMatt Macy return (-1);
414eda14cbcSMatt Macy }
415eda14cbcSMatt Macy
416eda14cbcSMatt Macy /* Set up a udev monitor for block devices */
417eda14cbcSMatt Macy g_mon = udev_monitor_new_from_netlink(g_udev, "udev");
418eda14cbcSMatt Macy udev_monitor_filter_add_match_subsystem_devtype(g_mon, "block", "disk");
419eda14cbcSMatt Macy udev_monitor_filter_add_match_subsystem_devtype(g_mon, "block",
420eda14cbcSMatt Macy "partition");
421eda14cbcSMatt Macy udev_monitor_enable_receiving(g_mon);
422eda14cbcSMatt Macy
423eda14cbcSMatt Macy /* Make sure monitoring socket is blocking */
424eda14cbcSMatt Macy fd = udev_monitor_get_fd(g_mon);
425eda14cbcSMatt Macy if ((fflags = fcntl(fd, F_GETFL)) & O_NONBLOCK)
426eda14cbcSMatt Macy (void) fcntl(fd, F_SETFL, fflags & ~O_NONBLOCK);
427eda14cbcSMatt Macy
428eda14cbcSMatt Macy /* spawn a thread to monitor events */
429eda14cbcSMatt Macy if (pthread_create(&g_mon_tid, NULL, zed_udev_monitor, g_mon) != 0) {
430eda14cbcSMatt Macy udev_monitor_unref(g_mon);
431eda14cbcSMatt Macy udev_unref(g_udev);
432eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "pthread_create failed");
433eda14cbcSMatt Macy return (-1);
434eda14cbcSMatt Macy }
435eda14cbcSMatt Macy
43616038816SMartin Matuska pthread_setname_np(g_mon_tid, "udev monitor");
437eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_disk_event_init");
438eda14cbcSMatt Macy
439eda14cbcSMatt Macy return (0);
440eda14cbcSMatt Macy }
441eda14cbcSMatt Macy
442eda14cbcSMatt Macy void
zed_disk_event_fini(void)443e3aa18adSMartin Matuska zed_disk_event_fini(void)
444eda14cbcSMatt Macy {
445eda14cbcSMatt Macy /* cancel monitor thread at recvmsg() */
446eda14cbcSMatt Macy (void) pthread_cancel(g_mon_tid);
447eda14cbcSMatt Macy (void) pthread_join(g_mon_tid, NULL);
448eda14cbcSMatt Macy
449eda14cbcSMatt Macy /* cleanup udev resources */
450eda14cbcSMatt Macy udev_monitor_unref(g_mon);
451eda14cbcSMatt Macy udev_unref(g_udev);
452eda14cbcSMatt Macy
453eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_disk_event_fini");
454eda14cbcSMatt Macy }
455eda14cbcSMatt Macy
456eda14cbcSMatt Macy #else
457eda14cbcSMatt Macy
458eda14cbcSMatt Macy #include "zed_disk_event.h"
459eda14cbcSMatt Macy
460eda14cbcSMatt Macy int
zed_disk_event_init(void)461e3aa18adSMartin Matuska zed_disk_event_init(void)
462eda14cbcSMatt Macy {
463eda14cbcSMatt Macy return (0);
464eda14cbcSMatt Macy }
465eda14cbcSMatt Macy
466eda14cbcSMatt Macy void
zed_disk_event_fini(void)467e3aa18adSMartin Matuska zed_disk_event_fini(void)
468eda14cbcSMatt Macy {
469eda14cbcSMatt Macy }
470eda14cbcSMatt Macy
471eda14cbcSMatt Macy #endif /* HAVE_LIBUDEV */
472