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 */ 52c7046f76SMartin Matuska #define MINIMUM_SECTORS 131072ULL 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 { 632a58b312SMartin Matuska const 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); 81c7046f76SMartin Matuska if (nvlist_lookup_uint64(nvl, DEV_PARENT_SIZE, &numval) == 0) 82c7046f76SMartin Matuska zed_log_msg(LOG_INFO, "\t%s: %llu", DEV_PARENT_SIZE, numval); 83eda14cbcSMatt Macy if (nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &numval) == 0) 84eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %llu", ZFS_EV_POOL_GUID, numval); 85eda14cbcSMatt Macy if (nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &numval) == 0) 86eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "\t%s: %llu", ZFS_EV_VDEV_GUID, numval); 87eda14cbcSMatt Macy 88eda14cbcSMatt Macy (void) zfs_agent_post_event(class, subclass, nvl); 89eda14cbcSMatt Macy } 90eda14cbcSMatt Macy 91eda14cbcSMatt Macy /* 92eda14cbcSMatt Macy * dev_event_nvlist: place event schema into an nv pair list 93eda14cbcSMatt Macy * 94eda14cbcSMatt Macy * NAME VALUE (example) 95eda14cbcSMatt Macy * -------------- -------------------------------------------------------- 96eda14cbcSMatt Macy * DEV_NAME /dev/sdl 97eda14cbcSMatt Macy * DEV_PATH /devices/pci0000:00/0000:00:03.0/0000:04:00.0/host0/... 98eda14cbcSMatt Macy * DEV_IDENTIFIER ata-Hitachi_HTS725050A9A362_100601PCG420VLJ37DMC 99eda14cbcSMatt Macy * DEV_PHYS_PATH pci-0000:04:00.0-sas-0x4433221101000000-lun-0 100eda14cbcSMatt Macy * DEV_IS_PART --- 101eda14cbcSMatt Macy * DEV_SIZE 500107862016 102eda14cbcSMatt Macy * ZFS_EV_POOL_GUID 17523635698032189180 103eda14cbcSMatt Macy * ZFS_EV_VDEV_GUID 14663607734290803088 104eda14cbcSMatt Macy */ 105eda14cbcSMatt Macy static nvlist_t * 106eda14cbcSMatt Macy dev_event_nvlist(struct udev_device *dev) 107eda14cbcSMatt Macy { 108eda14cbcSMatt Macy nvlist_t *nvl; 109eda14cbcSMatt Macy char strval[128]; 110eda14cbcSMatt Macy const char *value, *path; 111eda14cbcSMatt Macy uint64_t guid; 112eda14cbcSMatt Macy 113eda14cbcSMatt Macy if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 114eda14cbcSMatt Macy return (NULL); 115eda14cbcSMatt Macy 116eda14cbcSMatt Macy if (zfs_device_get_devid(dev, strval, sizeof (strval)) == 0) 117eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_IDENTIFIER, strval); 118eda14cbcSMatt Macy if (zfs_device_get_physical(dev, strval, sizeof (strval)) == 0) 119eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_PHYS_PATH, strval); 120eda14cbcSMatt Macy if ((path = udev_device_get_devnode(dev)) != NULL) 121eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_NAME, path); 122eda14cbcSMatt Macy if ((value = udev_device_get_devpath(dev)) != NULL) 123eda14cbcSMatt Macy (void) nvlist_add_string(nvl, DEV_PATH, value); 124eda14cbcSMatt Macy value = udev_device_get_devtype(dev); 125eda14cbcSMatt Macy if ((value != NULL && strcmp("partition", value) == 0) || 126eda14cbcSMatt Macy (udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER") 127eda14cbcSMatt Macy != NULL)) { 128eda14cbcSMatt Macy (void) nvlist_add_boolean(nvl, DEV_IS_PART); 129eda14cbcSMatt Macy } 130eda14cbcSMatt Macy if ((value = udev_device_get_sysattr_value(dev, "size")) != NULL) { 131eda14cbcSMatt Macy uint64_t numval = DEV_BSIZE; 132eda14cbcSMatt Macy 133eda14cbcSMatt Macy numval *= strtoull(value, NULL, 10); 134eda14cbcSMatt Macy (void) nvlist_add_uint64(nvl, DEV_SIZE, numval); 135c7046f76SMartin Matuska 136c7046f76SMartin Matuska /* 137c7046f76SMartin Matuska * If the device has a parent, then get the parent block 138c7046f76SMartin Matuska * device's size as well. For example, /dev/sda1's parent 139c7046f76SMartin Matuska * is /dev/sda. 140c7046f76SMartin Matuska */ 141c7046f76SMartin Matuska struct udev_device *parent_dev = udev_device_get_parent(dev); 142*5c65a0a9SMartin Matuska if (parent_dev != NULL && 143*5c65a0a9SMartin Matuska (value = udev_device_get_sysattr_value(parent_dev, "size")) 144c7046f76SMartin Matuska != NULL) { 145c7046f76SMartin Matuska uint64_t numval = DEV_BSIZE; 146c7046f76SMartin Matuska 147c7046f76SMartin Matuska numval *= strtoull(value, NULL, 10); 148c7046f76SMartin Matuska (void) nvlist_add_uint64(nvl, DEV_PARENT_SIZE, numval); 149c7046f76SMartin Matuska } 150eda14cbcSMatt Macy } 151eda14cbcSMatt Macy 152eda14cbcSMatt Macy /* 153eda14cbcSMatt Macy * Grab the pool and vdev guids from blkid cache 154eda14cbcSMatt Macy */ 155eda14cbcSMatt Macy value = udev_device_get_property_value(dev, "ID_FS_UUID"); 156eda14cbcSMatt Macy if (value != NULL && (guid = strtoull(value, NULL, 10)) != 0) 157eda14cbcSMatt Macy (void) nvlist_add_uint64(nvl, ZFS_EV_POOL_GUID, guid); 158eda14cbcSMatt Macy 159eda14cbcSMatt Macy value = udev_device_get_property_value(dev, "ID_FS_UUID_SUB"); 160eda14cbcSMatt Macy if (value != NULL && (guid = strtoull(value, NULL, 10)) != 0) 161eda14cbcSMatt Macy (void) nvlist_add_uint64(nvl, ZFS_EV_VDEV_GUID, guid); 162eda14cbcSMatt Macy 163eda14cbcSMatt Macy /* 164eda14cbcSMatt Macy * Either a vdev guid or a devid must be present for matching 165eda14cbcSMatt Macy */ 166eda14cbcSMatt Macy if (!nvlist_exists(nvl, DEV_IDENTIFIER) && 167eda14cbcSMatt Macy !nvlist_exists(nvl, ZFS_EV_VDEV_GUID)) { 168eda14cbcSMatt Macy nvlist_free(nvl); 169eda14cbcSMatt Macy return (NULL); 170eda14cbcSMatt Macy } 171eda14cbcSMatt Macy 172eda14cbcSMatt Macy return (nvl); 173eda14cbcSMatt Macy } 174eda14cbcSMatt Macy 175eda14cbcSMatt Macy /* 176eda14cbcSMatt Macy * Listen for block device uevents 177eda14cbcSMatt Macy */ 178eda14cbcSMatt Macy static void * 179eda14cbcSMatt Macy zed_udev_monitor(void *arg) 180eda14cbcSMatt Macy { 181eda14cbcSMatt Macy struct udev_monitor *mon = arg; 1822a58b312SMartin Matuska const char *tmp; 1832a58b312SMartin Matuska char *tmp2; 184eda14cbcSMatt Macy 185eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "Waiting for new udev disk events..."); 186eda14cbcSMatt Macy 187eda14cbcSMatt Macy while (1) { 188eda14cbcSMatt Macy struct udev_device *dev; 189eda14cbcSMatt Macy const char *action, *type, *part, *sectors; 190271171e0SMartin Matuska const char *bus, *uuid, *devpath; 191eda14cbcSMatt Macy const char *class, *subclass; 192eda14cbcSMatt Macy nvlist_t *nvl; 193eda14cbcSMatt Macy boolean_t is_zfs = B_FALSE; 194eda14cbcSMatt Macy 195eda14cbcSMatt Macy /* allow a cancellation while blocked (recvmsg) */ 196eda14cbcSMatt Macy pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 197eda14cbcSMatt Macy 198eda14cbcSMatt Macy /* blocks at recvmsg until an event occurs */ 199eda14cbcSMatt Macy if ((dev = udev_monitor_receive_device(mon)) == NULL) { 200eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "zed_udev_monitor: receive " 201eda14cbcSMatt Macy "device error %d", errno); 202eda14cbcSMatt Macy continue; 203eda14cbcSMatt Macy } 204eda14cbcSMatt Macy 205eda14cbcSMatt Macy /* allow all steps to complete before a cancellation */ 206eda14cbcSMatt Macy pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 207eda14cbcSMatt Macy 208eda14cbcSMatt Macy /* 209eda14cbcSMatt Macy * Strongly typed device is the preferred filter 210eda14cbcSMatt Macy */ 211eda14cbcSMatt Macy type = udev_device_get_property_value(dev, "ID_FS_TYPE"); 212eda14cbcSMatt Macy if (type != NULL && type[0] != '\0') { 213eda14cbcSMatt Macy if (strcmp(type, "zfs_member") == 0) { 214eda14cbcSMatt Macy is_zfs = B_TRUE; 215eda14cbcSMatt Macy } else { 216eda14cbcSMatt Macy /* not ours, so skip */ 217eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_udev_monitor: skip " 218eda14cbcSMatt Macy "%s (in use by %s)", 219eda14cbcSMatt Macy udev_device_get_devnode(dev), type); 220eda14cbcSMatt Macy udev_device_unref(dev); 221eda14cbcSMatt Macy continue; 222eda14cbcSMatt Macy } 223eda14cbcSMatt Macy } 224eda14cbcSMatt Macy 225eda14cbcSMatt Macy /* 226eda14cbcSMatt Macy * if this is a disk and it is partitioned, then the 227eda14cbcSMatt Macy * zfs label will reside in a DEVTYPE=partition and 228eda14cbcSMatt Macy * we can skip passing this event 229271171e0SMartin Matuska * 230271171e0SMartin Matuska * Special case: Blank disks are sometimes reported with 231271171e0SMartin Matuska * an erroneous 'atari' partition, and should not be 232271171e0SMartin Matuska * excluded from being used as an autoreplace disk: 233271171e0SMartin Matuska * 234271171e0SMartin Matuska * https://github.com/openzfs/zfs/issues/13497 235eda14cbcSMatt Macy */ 236eda14cbcSMatt Macy type = udev_device_get_property_value(dev, "DEVTYPE"); 237eda14cbcSMatt Macy part = udev_device_get_property_value(dev, 238eda14cbcSMatt Macy "ID_PART_TABLE_TYPE"); 239eda14cbcSMatt Macy if (type != NULL && type[0] != '\0' && 240eda14cbcSMatt Macy strcmp(type, "disk") == 0 && 241eda14cbcSMatt Macy part != NULL && part[0] != '\0') { 242271171e0SMartin Matuska const char *devname = 243271171e0SMartin Matuska udev_device_get_property_value(dev, "DEVNAME"); 244271171e0SMartin Matuska 245271171e0SMartin Matuska if (strcmp(part, "atari") == 0) { 246c03c5b1cSMartin Matuska zed_log_msg(LOG_INFO, 247271171e0SMartin Matuska "%s: %s is reporting an atari partition, " 248271171e0SMartin Matuska "but we're going to assume it's a false " 249271171e0SMartin Matuska "positive and still use it (issue #13497)", 250271171e0SMartin Matuska __func__, devname); 251271171e0SMartin Matuska } else { 252271171e0SMartin Matuska zed_log_msg(LOG_INFO, 253271171e0SMartin Matuska "%s: skip %s since it has a %s partition " 254271171e0SMartin Matuska "already", __func__, devname, part); 255eda14cbcSMatt Macy /* skip and wait for partition event */ 256eda14cbcSMatt Macy udev_device_unref(dev); 257eda14cbcSMatt Macy continue; 258eda14cbcSMatt Macy } 259271171e0SMartin Matuska } 260eda14cbcSMatt Macy 261eda14cbcSMatt Macy /* 262eda14cbcSMatt Macy * ignore small partitions 263eda14cbcSMatt Macy */ 264eda14cbcSMatt Macy sectors = udev_device_get_property_value(dev, 265eda14cbcSMatt Macy "ID_PART_ENTRY_SIZE"); 266eda14cbcSMatt Macy if (sectors == NULL) 267eda14cbcSMatt Macy sectors = udev_device_get_sysattr_value(dev, "size"); 268eda14cbcSMatt Macy if (sectors != NULL && 269eda14cbcSMatt Macy strtoull(sectors, NULL, 10) < MINIMUM_SECTORS) { 270c03c5b1cSMartin Matuska zed_log_msg(LOG_INFO, 271c03c5b1cSMartin Matuska "%s: %s sectors %s < %llu (minimum)", 272c03c5b1cSMartin Matuska __func__, 273c03c5b1cSMartin Matuska udev_device_get_property_value(dev, "DEVNAME"), 274c03c5b1cSMartin Matuska sectors, MINIMUM_SECTORS); 275eda14cbcSMatt Macy udev_device_unref(dev); 276eda14cbcSMatt Macy continue; 277eda14cbcSMatt Macy } 278eda14cbcSMatt Macy 279eda14cbcSMatt Macy /* 280eda14cbcSMatt Macy * If the blkid probe didn't find ZFS, then a persistent 281eda14cbcSMatt Macy * device id string is required in the message schema 282eda14cbcSMatt Macy * for matching with vdevs. Preflight here for expected 283eda14cbcSMatt Macy * udev information. 284271171e0SMartin Matuska * 285271171e0SMartin Matuska * Special case: 286271171e0SMartin Matuska * NVMe devices don't have ID_BUS set (at least on RHEL 7-8), 287271171e0SMartin Matuska * but they are valid for autoreplace. Add a special case for 288271171e0SMartin Matuska * them by searching for "/nvme/" in the udev DEVPATH: 289271171e0SMartin Matuska * 290271171e0SMartin Matuska * DEVPATH=/devices/pci0000:00/0000:00:1e.0/nvme/nvme2/nvme2n1 291eda14cbcSMatt Macy */ 292eda14cbcSMatt Macy bus = udev_device_get_property_value(dev, "ID_BUS"); 293eda14cbcSMatt Macy uuid = udev_device_get_property_value(dev, "DM_UUID"); 294271171e0SMartin Matuska devpath = udev_device_get_devpath(dev); 295271171e0SMartin Matuska if (!is_zfs && (bus == NULL && uuid == NULL && 296271171e0SMartin Matuska strstr(devpath, "/nvme/") == NULL)) { 297eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_udev_monitor: %s no devid " 298eda14cbcSMatt Macy "source", udev_device_get_devnode(dev)); 299eda14cbcSMatt Macy udev_device_unref(dev); 300eda14cbcSMatt Macy continue; 301eda14cbcSMatt Macy } 302eda14cbcSMatt Macy 303eda14cbcSMatt Macy action = udev_device_get_action(dev); 304eda14cbcSMatt Macy if (strcmp(action, "add") == 0) { 305eda14cbcSMatt Macy class = EC_DEV_ADD; 306eda14cbcSMatt Macy subclass = ESC_DISK; 307eda14cbcSMatt Macy } else if (strcmp(action, "remove") == 0) { 308eda14cbcSMatt Macy class = EC_DEV_REMOVE; 309eda14cbcSMatt Macy subclass = ESC_DISK; 310eda14cbcSMatt Macy } else if (strcmp(action, "change") == 0) { 311eda14cbcSMatt Macy class = EC_DEV_STATUS; 312eda14cbcSMatt Macy subclass = ESC_DEV_DLE; 313eda14cbcSMatt Macy } else { 314eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "zed_udev_monitor: %s unknown", 315eda14cbcSMatt Macy action); 316eda14cbcSMatt Macy udev_device_unref(dev); 317eda14cbcSMatt Macy continue; 318eda14cbcSMatt Macy } 319eda14cbcSMatt Macy 320eda14cbcSMatt Macy /* 321eda14cbcSMatt Macy * Special case an EC_DEV_ADD for multipath devices 322eda14cbcSMatt Macy * 323eda14cbcSMatt Macy * When a multipath device is created, udev reports the 324eda14cbcSMatt Macy * following: 325eda14cbcSMatt Macy * 326eda14cbcSMatt Macy * 1. "add" event of the dm device for the multipath device 327eda14cbcSMatt Macy * (like /dev/dm-3). 328eda14cbcSMatt Macy * 2. "change" event to create the actual multipath device 329eda14cbcSMatt Macy * symlink (like /dev/mapper/mpatha). The event also 330eda14cbcSMatt Macy * passes back the relevant DM vars we care about, like 331eda14cbcSMatt Macy * DM_UUID. 332eda14cbcSMatt Macy * 3. Another "change" event identical to #2 (that we ignore). 333eda14cbcSMatt Macy * 334eda14cbcSMatt Macy * To get the behavior we want, we treat the "change" event 335eda14cbcSMatt Macy * in #2 as a "add" event; as if "/dev/mapper/mpatha" was 336eda14cbcSMatt Macy * a new disk being added. 337eda14cbcSMatt Macy */ 338eda14cbcSMatt Macy if (strcmp(class, EC_DEV_STATUS) == 0 && 339eda14cbcSMatt Macy udev_device_get_property_value(dev, "DM_UUID") && 340eda14cbcSMatt Macy udev_device_get_property_value(dev, "MPATH_SBIN_PATH")) { 3412a58b312SMartin Matuska tmp = udev_device_get_devnode(dev); 342eda14cbcSMatt Macy tmp2 = zfs_get_underlying_path(tmp); 343eda14cbcSMatt Macy if (tmp && tmp2 && (strcmp(tmp, tmp2) != 0)) { 344eda14cbcSMatt Macy /* 345eda14cbcSMatt Macy * We have a real underlying device, which 346eda14cbcSMatt Macy * means that this multipath "change" event is 347eda14cbcSMatt Macy * an "add" event. 348eda14cbcSMatt Macy * 349eda14cbcSMatt Macy * If the multipath device and the underlying 350eda14cbcSMatt Macy * dev are the same name (i.e. /dev/dm-5), then 351eda14cbcSMatt Macy * there is no real underlying disk for this 352eda14cbcSMatt Macy * multipath device, and so this "change" event 353eda14cbcSMatt Macy * really is a multipath removal. 354eda14cbcSMatt Macy */ 355eda14cbcSMatt Macy class = EC_DEV_ADD; 356eda14cbcSMatt Macy subclass = ESC_DISK; 357eda14cbcSMatt Macy } else { 3582a58b312SMartin Matuska tmp = udev_device_get_property_value(dev, 359eda14cbcSMatt Macy "DM_NR_VALID_PATHS"); 360eda14cbcSMatt Macy /* treat as a multipath remove */ 361eda14cbcSMatt Macy if (tmp != NULL && strcmp(tmp, "0") == 0) { 362eda14cbcSMatt Macy class = EC_DEV_REMOVE; 363eda14cbcSMatt Macy subclass = ESC_DISK; 364eda14cbcSMatt Macy } 365eda14cbcSMatt Macy } 366eda14cbcSMatt Macy free(tmp2); 367eda14cbcSMatt Macy } 368eda14cbcSMatt Macy 369eda14cbcSMatt Macy /* 370eda14cbcSMatt Macy * Special case an EC_DEV_ADD for scsi_debug devices 371eda14cbcSMatt Macy * 372eda14cbcSMatt Macy * These devices require a udevadm trigger command after 373eda14cbcSMatt Macy * creation in order to register the vdev_id scsidebug alias 374eda14cbcSMatt Macy * rule (adds a persistent path (phys_path) used for fault 375eda14cbcSMatt Macy * management automated tests in the ZFS test suite. 376eda14cbcSMatt Macy * 377eda14cbcSMatt Macy * After udevadm trigger command, event registers as a "change" 378eda14cbcSMatt Macy * event but needs to instead be handled as another "add" event 379eda14cbcSMatt Macy * to allow for disk labeling and partitioning to occur. 380eda14cbcSMatt Macy */ 381eda14cbcSMatt Macy if (strcmp(class, EC_DEV_STATUS) == 0 && 382eda14cbcSMatt Macy udev_device_get_property_value(dev, "ID_VDEV") && 383eda14cbcSMatt Macy udev_device_get_property_value(dev, "ID_MODEL")) { 384eda14cbcSMatt Macy const char *id_model, *id_model_sd = "scsi_debug"; 385eda14cbcSMatt Macy 386eda14cbcSMatt Macy id_model = udev_device_get_property_value(dev, 387eda14cbcSMatt Macy "ID_MODEL"); 388eda14cbcSMatt Macy if (strcmp(id_model, id_model_sd) == 0) { 389eda14cbcSMatt Macy class = EC_DEV_ADD; 390eda14cbcSMatt Macy subclass = ESC_DISK; 391eda14cbcSMatt Macy } 392eda14cbcSMatt Macy } 393eda14cbcSMatt Macy 394eda14cbcSMatt Macy if ((nvl = dev_event_nvlist(dev)) != NULL) { 395eda14cbcSMatt Macy zed_udev_event(class, subclass, nvl); 396eda14cbcSMatt Macy nvlist_free(nvl); 397eda14cbcSMatt Macy } 398eda14cbcSMatt Macy 399eda14cbcSMatt Macy udev_device_unref(dev); 400eda14cbcSMatt Macy } 401eda14cbcSMatt Macy 402eda14cbcSMatt Macy return (NULL); 403eda14cbcSMatt Macy } 404eda14cbcSMatt Macy 405eda14cbcSMatt Macy int 406e3aa18adSMartin Matuska zed_disk_event_init(void) 407eda14cbcSMatt Macy { 408eda14cbcSMatt Macy int fd, fflags; 409eda14cbcSMatt Macy 410eda14cbcSMatt Macy if ((g_udev = udev_new()) == NULL) { 411eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "udev_new failed (%d)", errno); 412eda14cbcSMatt Macy return (-1); 413eda14cbcSMatt Macy } 414eda14cbcSMatt Macy 415eda14cbcSMatt Macy /* Set up a udev monitor for block devices */ 416eda14cbcSMatt Macy g_mon = udev_monitor_new_from_netlink(g_udev, "udev"); 417eda14cbcSMatt Macy udev_monitor_filter_add_match_subsystem_devtype(g_mon, "block", "disk"); 418eda14cbcSMatt Macy udev_monitor_filter_add_match_subsystem_devtype(g_mon, "block", 419eda14cbcSMatt Macy "partition"); 420eda14cbcSMatt Macy udev_monitor_enable_receiving(g_mon); 421eda14cbcSMatt Macy 422eda14cbcSMatt Macy /* Make sure monitoring socket is blocking */ 423eda14cbcSMatt Macy fd = udev_monitor_get_fd(g_mon); 424eda14cbcSMatt Macy if ((fflags = fcntl(fd, F_GETFL)) & O_NONBLOCK) 425eda14cbcSMatt Macy (void) fcntl(fd, F_SETFL, fflags & ~O_NONBLOCK); 426eda14cbcSMatt Macy 427eda14cbcSMatt Macy /* spawn a thread to monitor events */ 428eda14cbcSMatt Macy if (pthread_create(&g_mon_tid, NULL, zed_udev_monitor, g_mon) != 0) { 429eda14cbcSMatt Macy udev_monitor_unref(g_mon); 430eda14cbcSMatt Macy udev_unref(g_udev); 431eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, "pthread_create failed"); 432eda14cbcSMatt Macy return (-1); 433eda14cbcSMatt Macy } 434eda14cbcSMatt Macy 43516038816SMartin Matuska pthread_setname_np(g_mon_tid, "udev monitor"); 436eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_disk_event_init"); 437eda14cbcSMatt Macy 438eda14cbcSMatt Macy return (0); 439eda14cbcSMatt Macy } 440eda14cbcSMatt Macy 441eda14cbcSMatt Macy void 442e3aa18adSMartin Matuska zed_disk_event_fini(void) 443eda14cbcSMatt Macy { 444eda14cbcSMatt Macy /* cancel monitor thread at recvmsg() */ 445eda14cbcSMatt Macy (void) pthread_cancel(g_mon_tid); 446eda14cbcSMatt Macy (void) pthread_join(g_mon_tid, NULL); 447eda14cbcSMatt Macy 448eda14cbcSMatt Macy /* cleanup udev resources */ 449eda14cbcSMatt Macy udev_monitor_unref(g_mon); 450eda14cbcSMatt Macy udev_unref(g_udev); 451eda14cbcSMatt Macy 452eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "zed_disk_event_fini"); 453eda14cbcSMatt Macy } 454eda14cbcSMatt Macy 455eda14cbcSMatt Macy #else 456eda14cbcSMatt Macy 457eda14cbcSMatt Macy #include "zed_disk_event.h" 458eda14cbcSMatt Macy 459eda14cbcSMatt Macy int 460e3aa18adSMartin Matuska zed_disk_event_init(void) 461eda14cbcSMatt Macy { 462eda14cbcSMatt Macy return (0); 463eda14cbcSMatt Macy } 464eda14cbcSMatt Macy 465eda14cbcSMatt Macy void 466e3aa18adSMartin Matuska zed_disk_event_fini(void) 467eda14cbcSMatt Macy { 468eda14cbcSMatt Macy } 469eda14cbcSMatt Macy 470eda14cbcSMatt Macy #endif /* HAVE_LIBUDEV */ 471