Print this page
NEX-5736 implement autoreplace matching based on FRU slot number
NEX-6200 hot spares are not reactivated after reinserting into enclosure
NEX-9403 need to update FRU for spare and l2cache devices
NEX-9404 remove lofi autoreplace support from syseventd
NEX-9409 hotsparing doesn't work for vdevs without FRU
NEX-9424 zfs`vdev_online() needs better notification about state changes
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Portions contributed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-8986 assertion triggered in syseventd zfs_mod.so
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Marcel Telka <marcel@telka.sk>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Reviewed by: Igor Kozhukhov <igor@dilos.org>
6175 sdev can create bogus zvol directories
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jason King <jason.brian.king@gmail.com>
Approved by: Dan McDonald <danmcd@omniti.com>
6174 /dev/zvol does not show pool directories
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jason King <jason.brian.king@gmail.com>
Approved by: Dan McDonald <danmcd@omniti.com>
5997 FRU field not set during pool creation and never updated
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
Reviewed by: Richard Elling <richard.elling@gmail.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
6046 SPARC boot should support com.delphix:hole_birth
Reviewed by: Igor Kozhukhov <ikozhukhov@gmail.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
6041 SPARC boot should support LZ4
Reviewed by: Igor Kozhukhov <ikozhukhov@gmail.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
6044 SPARC zfs reader is using wrong size for objset_phys
Reviewed by: Igor Kozhukhov <ikozhukhov@gmail.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
backout 5997: breaks "zpool add"
5997 FRU field not set during pool creation and never updated
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
Reviewed by: Richard Elling <richard.elling@gmail.com>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-3474 CLONE - Port NEX-2591 FRU field not set during pool creation and never updated
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
NEX-860 Offlined vdevs are online after reboot
re #12684 rb4206 importing pool with autoreplace=on and "hole" vdevs crashes syseventd

*** 16,66 **** * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. ! * Copyright 2016 Nexenta Systems, Inc. All rights reserved. */ /* * ZFS syseventd module. * ! * The purpose of this module is to identify when devices are added to the ! * system, and appropriately online or replace the affected vdevs. * ! * When a device is added to the system: * ! * 1. Search for any vdevs whose devid matches that of the newly added ! * device. * ! * 2. If no vdevs are found, then search for any vdevs whose devfs path ! * matches that of the new device. ! * ! * 3. If no vdevs match by either method, then ignore the event. ! * ! * 4. Attempt to online the device with a flag to indicate that it should ! * be unspared when resilvering completes. If this succeeds, then the ! * same device was inserted and we should continue normally. ! * ! * 5. If the pool does not have the 'autoreplace' property set, attempt to ! * online the device again without the unspare flag, which will ! * generate a FMA fault. ! * ! * 6. If the pool has the 'autoreplace' property set, and the matching vdev ! * is a whole disk, then label the new disk and attempt a 'zpool ! * replace'. ! * ! * The module responds to EC_DEV_ADD events for both disks and lofi devices, ! * with the latter used for testing. The special ESC_ZFS_VDEV_CHECK event ! * indicates that a device failed to open during pool load, but the autoreplace ! * property was set. In this case, we deferred the associated FMA fault until ! * our module had a chance to process the autoreplace logic. If the device ! * could not be replaced, then the second online attempt will trigger the FMA ! * fault that we skipped earlier. */ #include <alloca.h> #include <devid.h> #include <fcntl.h> --- 16,60 ---- * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ + /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. ! * Copyright 2017 Nexenta Systems, Inc. */ /* * ZFS syseventd module. * ! * The purpose of this module is to process ZFS related events. * ! * EC_DEV_ADD ! * ESC_DISK Search for associated vdevs matching devid, physpath, ! * or FRU, and appropriately online or replace the device. * ! * EC_DEV_STATUS ! * ESC_DEV_DLE Device capacity dynamically changed. Process the change ! * according to 'autoexpand' property. * ! * EC_ZFS ! * ESC_ZFS_VDEV_CHECK This event indicates that a device failed to open during ! * pool load, but the autoreplace property was set. In ! * this case the associated FMA fault was deferred until ! * the module had a chance to process the autoreplace ! * logic. If the device could not be replaced, then the ! * second online attempt will trigger the FMA fault that ! * was skipped earlier. ! * ESC_ZFS_VDEV_ADD ! * ESC_ZFS_VDEV_ATTACH ! * ESC_ZFS_VDEV_CLEAR ! * ESC_ZFS_VDEV_ONLINE ! * ESC_ZFS_POOL_CREATE ! * ESC_ZFS_POOL_IMPORT All of the above events will trigger the update of ! * FRU for all associated devices. */ #include <alloca.h> #include <devid.h> #include <fcntl.h>
*** 68,98 **** #include <libsysevent.h> #include <libzfs.h> #include <limits.h> #include <stdlib.h> #include <string.h> - #include <syslog.h> #include <sys/list.h> #include <sys/sunddi.h> #include <sys/sysevent/eventdefs.h> #include <sys/sysevent/dev.h> #include <thread_pool.h> #include <unistd.h> #include "syseventd.h" #if defined(__i386) || defined(__amd64) ! #define PHYS_PATH ":q" ! #define RAW_SLICE "p0" #elif defined(__sparc) ! #define PHYS_PATH ":c" ! #define RAW_SLICE "s2" #else #error Unknown architecture #endif ! typedef void (*zfs_process_func_t)(zpool_handle_t *, nvlist_t *, boolean_t); libzfs_handle_t *g_zfshdl; list_t g_pool_list; tpool_t *g_tpool; boolean_t g_enumeration_done; thread_t g_zfs_tid; --- 62,92 ---- #include <libsysevent.h> #include <libzfs.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <sys/list.h> #include <sys/sunddi.h> + #include <sys/fs/zfs.h> #include <sys/sysevent/eventdefs.h> #include <sys/sysevent/dev.h> #include <thread_pool.h> #include <unistd.h> #include "syseventd.h" #if defined(__i386) || defined(__amd64) ! #define WD_MINOR ":q" #elif defined(__sparc) ! #define WD_MINOR ":c" #else #error Unknown architecture #endif ! #define DEVICE_PREFIX "/devices" + typedef void (*zfs_process_func_t)(zpool_handle_t *, nvlist_t *, const char *); + libzfs_handle_t *g_zfshdl; list_t g_pool_list; tpool_t *g_tpool; boolean_t g_enumeration_done; thread_t g_zfs_tid;
*** 129,338 **** } return (0); } /* ! * The device associated with the given vdev (either by devid or physical path) ! * has been added to the system. If 'isdisk' is set, then we only attempt a ! * replacement if it's a whole disk. This also implies that we should label the ! * disk first. ! * ! * First, we attempt to online the device (making sure to undo any spare ! * operation when finished). If this succeeds, then we're done. If it fails, ! * and the new state is VDEV_CANT_OPEN, it indicates that the device was opened, ! * but that the label was not what we expected. If the 'autoreplace' property ! * is not set, then we relabel the disk (if specified), and attempt a 'zpool ! * replace'. If the online is successful, but the new state is something else ! * (REMOVED or FAULTED), it indicates that we're out of sync or in some sort of ! * race, and we should avoid attempting to relabel the disk. */ static void ! zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t isdisk) { - char *path; vdev_state_t newstate; ! nvlist_t *nvroot, *newvd; uint64_t wholedisk = 0ULL; uint64_t offline = 0ULL; ! char *physpath = NULL; ! char rawpath[PATH_MAX], fullpath[PATH_MAX]; zpool_boot_label_t boot_type; uint64_t boot_size; - size_t len; ! if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &path) != 0) return; - (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_PHYS_PATH, &physpath); (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK, &wholedisk); (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_OFFLINE, &offline); ! /* ! * We should have a way to online a device by guid. With the current ! * interface, we are forced to chop off the 's0' for whole disks. ! */ ! (void) strlcpy(fullpath, path, sizeof (fullpath)); if (wholedisk) fullpath[strlen(fullpath) - 2] = '\0'; /* ! * Attempt to online the device. It would be nice to online this by ! * GUID, but the current interface only supports lookup by path. */ ! if (offline || ! (zpool_vdev_online(zhp, fullpath, ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE, &newstate) == 0 && ! (newstate == VDEV_STATE_HEALTHY || ! newstate == VDEV_STATE_DEGRADED))) return; /* ! * If the pool doesn't have the autoreplace property set, then attempt a ! * true online (without the unspare flag), which will trigger a FMA ! * fault. */ ! if (!zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOREPLACE, NULL) || ! (isdisk && !wholedisk)) { (void) zpool_vdev_online(zhp, fullpath, ZFS_ONLINE_FORCEFAULT, &newstate); return; } - if (isdisk) { /* ! * If this is a request to label a whole disk, then attempt to ! * write out the label. Before we can label the disk, we need ! * access to a raw node. Ideally, we'd like to walk the devinfo ! * tree and find a raw node from the corresponding parent node. ! * This is overly complicated, and since we know how we labeled ! * this device in the first place, we know it's save to switch ! * from /dev/dsk to /dev/rdsk and append the backup slice. * ! * If any part of this process fails, then do a force online to ! * trigger a ZFS fault for the device (and any hot spare ! * replacement). */ ! if (strncmp(path, ZFS_DISK_ROOTD, ! strlen(ZFS_DISK_ROOTD)) != 0) { ! (void) zpool_vdev_online(zhp, fullpath, ! ZFS_ONLINE_FORCEFAULT, &newstate); ! return; } ! (void) strlcpy(rawpath, path + 9, sizeof (rawpath)); ! len = strlen(rawpath); ! rawpath[len - 2] = '\0'; ! if (zpool_is_bootable(zhp)) boot_type = ZPOOL_COPY_BOOT_LABEL; else boot_type = ZPOOL_NO_BOOT_LABEL; boot_size = zpool_get_prop_int(zhp, ZPOOL_PROP_BOOTSIZE, NULL); ! if (zpool_label_disk(g_zfshdl, zhp, rawpath, ! boot_type, boot_size, NULL) != 0) { ! (void) zpool_vdev_online(zhp, fullpath, ! ZFS_ONLINE_FORCEFAULT, &newstate); return; } } /* ! * Cosntruct the root vdev to pass to zpool_vdev_attach(). While adding ! * the entire vdev structure is harmless, we construct a reduced set of ! * path/physpath/wholedisk to keep it simple. */ ! if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0) ! return; ! if (nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { ! nvlist_free(nvroot); ! return; ! } if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, VDEV_TYPE_DISK) != 0 || ! nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, path) != 0 || ! (physpath != NULL && nvlist_add_string(newvd, ! ZPOOL_CONFIG_PHYS_PATH, physpath) != 0) || nvlist_add_uint64(newvd, ZPOOL_CONFIG_WHOLE_DISK, wholedisk) != 0 || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 || ! nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &newvd, ! 1) != 0) { ! nvlist_free(newvd); ! nvlist_free(nvroot); ! return; } nvlist_free(newvd); - - (void) zpool_vdev_attach(zhp, fullpath, path, nvroot, B_TRUE); - nvlist_free(nvroot); - } /* * Utility functions to find a vdev matching given criteria. */ typedef struct dev_data { const char *dd_compare; const char *dd_prop; zfs_process_func_t dd_func; boolean_t dd_found; - boolean_t dd_isdisk; uint64_t dd_pool_guid; uint64_t dd_vdev_guid; } dev_data_t; static void zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data) { dev_data_t *dp = data; ! char *path; ! uint_t c, children; ! nvlist_t **child; ! size_t len; uint64_t guid; ! /* ! * First iterate over any children. ! */ if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN, ! &child, &children) == 0) { ! for (c = 0; c < children; c++) ! zfs_iter_vdev(zhp, child[c], data); ! return; } ! if (dp->dd_vdev_guid != 0) { ! if (nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID, ! &guid) != 0 || guid != dp->dd_vdev_guid) return; - } else if (dp->dd_compare != NULL) { - len = strlen(dp->dd_compare); ! if (nvlist_lookup_string(nvl, dp->dd_prop, &path) != 0 || ! strncmp(dp->dd_compare, path, len) != 0) return; ! /* ! * Normally, we want to have an exact match for the comparison ! * string. However, we allow substring matches in the following ! * cases: ! * ! * <path>: This is a devpath, and the target is one ! * of its children. ! * ! * <path/> This is a devid for a whole disk, and ! * the target is one of its children. ! */ ! if (path[len] != '\0' && path[len] != ':' && ! path[len - 1] != '/') return; - } ! (dp->dd_func)(zhp, nvl, dp->dd_isdisk); } void zfs_enable_ds(void *arg) { --- 123,354 ---- } return (0); } /* ! * The device associated with the given vdev (matched by devid, physical path, ! * or FRU) has been added to the system. */ static void ! zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, const char *newrawpath) { vdev_state_t newstate; ! nvlist_t *nvroot = NULL, *newvd = NULL; uint64_t wholedisk = 0ULL; uint64_t offline = 0ULL; ! boolean_t avail_spare, l2cache; ! const char *zc_type = ZPOOL_CONFIG_CHILDREN; ! char *devpath; /* current /dev path */ ! char *physpath; /* current /devices node */ ! char fullpath[PATH_MAX]; /* current /dev path without slice */ ! char fullphyspath[PATH_MAX]; /* full /devices phys path */ ! char newdevpath[PATH_MAX]; /* new /dev path */ ! char newphyspath[PATH_MAX]; /* new /devices node */ ! char diskname[PATH_MAX]; /* disk device without /dev and slice */ ! const char *adevid = NULL; /* devid to attach */ ! const char *adevpath; /* /dev path to attach */ ! const char *aphyspath = NULL; /* /devices node to attach */ zpool_boot_label_t boot_type; uint64_t boot_size; ! if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &devpath) != 0) return; (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_PHYS_PATH, &physpath); (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK, &wholedisk); (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_OFFLINE, &offline); ! /* Do nothing if vdev is explicitly marked offline */ ! if (offline) ! return; ! ! (void) strlcpy(fullpath, devpath, sizeof (fullpath)); ! /* Chop off slice for whole disks */ if (wholedisk) fullpath[strlen(fullpath) - 2] = '\0'; /* ! * Device could still have valid label, so first attempt to online the ! * device undoing any spare operation. If online succeeds and new state ! * is either HEALTHY or DEGRADED, we are done. */ ! if (zpool_vdev_online(zhp, fullpath, ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE, &newstate) == 0 && ! (newstate == VDEV_STATE_HEALTHY || newstate == VDEV_STATE_DEGRADED)) return; /* ! * If the pool doesn't have the autoreplace property set or this is a ! * non-whole disk vdev, there's nothing else we can do so attempt a true ! * online (without the unspare flag), which will trigger a FMA fault. */ ! if (zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOREPLACE, NULL) == 0 || ! !wholedisk) { (void) zpool_vdev_online(zhp, fullpath, ZFS_ONLINE_FORCEFAULT, &newstate); return; } /* ! * Attempt to replace the device. * ! * If newrawpath is set (not NULL), then we matched by FRU and need to ! * use new /dev and /devices paths for attach. ! * ! * First, construct the short disk name to label, chopping off any ! * leading /dev path and slice (which newrawpath doesn't include). */ ! if (newrawpath != NULL) { ! (void) strlcpy(diskname, newrawpath + ! strlen(ZFS_RDISK_ROOTD), sizeof (diskname)); ! } else { ! (void) strlcpy(diskname, fullpath + ! strlen(ZFS_DISK_ROOTD), sizeof (diskname)); } ! /* Write out the label */ if (zpool_is_bootable(zhp)) boot_type = ZPOOL_COPY_BOOT_LABEL; else boot_type = ZPOOL_NO_BOOT_LABEL; boot_size = zpool_get_prop_int(zhp, ZPOOL_PROP_BOOTSIZE, NULL); ! if (zpool_label_disk(g_zfshdl, zhp, diskname, boot_type, boot_size, ! NULL) != 0) { ! syseventd_print(9, "%s: failed to write the label\n", __func__); return; } + + /* Define "path" and "physpath" to be used for attach */ + if (newrawpath != NULL) { + /* Construct newdevpath from newrawpath */ + (void) snprintf(newdevpath, sizeof (newdevpath), "%s%s%s", + ZFS_DISK_ROOTD, newrawpath + strlen(ZFS_RDISK_ROOTD), + (boot_size > 0) ? "s1" : "s0"); + /* Use replacing vdev's "path" and "physpath" */ + adevpath = newdevpath; + /* Resolve /dev path to /devices node */ + aphyspath = realpath(newdevpath, newphyspath) + + strlen(DEVICE_PREFIX); + } else { + /* Use original vdev's "path" and "physpath" */ + adevpath = devpath; + aphyspath = physpath; } + /* Construct new devid */ + (void) snprintf(fullphyspath, sizeof (fullphyspath), "%s%s", + DEVICE_PREFIX, aphyspath); + adevid = devid_str_from_path(fullphyspath); + /* ! * Check if replaced vdev is "available" (not swapped in) spare ! * or l2cache device. */ ! (void) zpool_find_vdev(zhp, fullpath, &avail_spare, &l2cache, NULL, ! NULL); ! if (avail_spare) ! zc_type = ZPOOL_CONFIG_SPARES; ! else if (l2cache) ! zc_type = ZPOOL_CONFIG_L2CACHE; ! /* Construct the root vdev */ ! if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 || ! nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) ! goto fail; if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, VDEV_TYPE_DISK) != 0 || ! (adevid != NULL && ! nvlist_add_string(newvd, ZPOOL_CONFIG_DEVID, adevid) != 0) || ! nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, adevpath) != 0 || ! (aphyspath != NULL && ! nvlist_add_string(newvd, ZPOOL_CONFIG_PHYS_PATH, aphyspath) != 0) || nvlist_add_uint64(newvd, ZPOOL_CONFIG_WHOLE_DISK, wholedisk) != 0 || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 || ! nvlist_add_nvlist_array(nvroot, zc_type, &newvd, 1) != 0) ! goto fail; ! ! if (avail_spare || l2cache) { ! /* ! * For spares/l2cache, we need to explicitly remove the device ! * and add the new one. ! */ ! (void) zpool_vdev_remove(zhp, fullpath); ! (void) zpool_add(zhp, nvroot); ! } else { ! /* Do the replace for regular vdevs */ ! (void) zpool_vdev_attach(zhp, fullpath, adevpath, nvroot, ! B_TRUE); } + fail: + if (adevid != NULL) + devid_str_free((char *)adevid); nvlist_free(newvd); nvlist_free(nvroot); } /* * Utility functions to find a vdev matching given criteria. */ typedef struct dev_data { const char *dd_compare; const char *dd_prop; + const char *dd_devpath; zfs_process_func_t dd_func; + int (*dd_cmp_func)(libzfs_handle_t *, const char *, + const char *, size_t); boolean_t dd_found; uint64_t dd_pool_guid; uint64_t dd_vdev_guid; } dev_data_t; static void zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data) { dev_data_t *dp = data; ! boolean_t nested = B_FALSE; ! char *cmp_str; ! nvlist_t **cnvl, **snvl, **lnvl; ! uint_t i, nc, ns, nl; uint64_t guid; ! /* Iterate over child vdevs */ if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN, ! &cnvl, &nc) == 0) { ! for (i = 0; i < nc; i++) ! zfs_iter_vdev(zhp, cnvl[i], data); ! nested = B_TRUE; } + /* Iterate over spare vdevs */ + if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_SPARES, + &snvl, &ns) == 0) { + for (i = 0; i < ns; i++) + zfs_iter_vdev(zhp, snvl[i], data); + nested = B_TRUE; + } + /* Iterate over l2cache vdevs */ + if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_L2CACHE, + &lnvl, &nl) == 0) { + for (i = 0; i < nl; i++) + zfs_iter_vdev(zhp, lnvl[i], data); + nested = B_TRUE; + } ! if (nested) return; ! if (dp->dd_vdev_guid != 0 && (nvlist_lookup_uint64(nvl, ! ZPOOL_CONFIG_GUID, &guid) != 0 || guid != dp->dd_vdev_guid)) return; ! if (dp->dd_compare != NULL && (nvlist_lookup_string(nvl, dp->dd_prop, ! &cmp_str) != 0 || dp->dd_cmp_func(g_zfshdl, dp->dd_compare, cmp_str, ! strlen(dp->dd_compare)) != 0)) return; ! dp->dd_found = B_TRUE; ! (dp->dd_func)(zhp, nvl, dp->dd_devpath); } void zfs_enable_ds(void *arg) {
*** 379,524 **** zpool_close(zhp); return (0); } /* * Given a physical device path, iterate over all (pool, vdev) pairs which * correspond to the given path. */ static boolean_t ! devpath_iter(const char *devpath, zfs_process_func_t func, boolean_t wholedisk) { dev_data_t data = { 0 }; ! data.dd_compare = devpath; data.dd_func = func; data.dd_prop = ZPOOL_CONFIG_PHYS_PATH; data.dd_found = B_FALSE; ! data.dd_isdisk = wholedisk; (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); return (data.dd_found); } /* ! * Given a /devices path, lookup the corresponding devid for each minor node, ! * and find any vdevs with matching devids. Doing this straight up would be ! * rather inefficient, O(minor nodes * vdevs in system), so we take advantage of ! * the fact that each devid ends with "/<minornode>". Once we find any valid ! * minor node, we chop off the portion after the last slash, and then search for ! * matching vdevs, which is O(vdevs in system). */ static boolean_t ! devid_iter(const char *devpath, zfs_process_func_t func, boolean_t wholedisk) { ! size_t len = strlen(devpath) + sizeof ("/devices") + ! sizeof (PHYS_PATH) - 1; ! char *fullpath; ! int fd; ! ddi_devid_t devid; ! char *devidstr, *fulldevid; dev_data_t data = { 0 }; ! /* ! * Try to open a known minor node. ! */ ! fullpath = alloca(len); ! (void) snprintf(fullpath, len, "/devices%s%s", devpath, PHYS_PATH); ! if ((fd = open(fullpath, O_RDONLY)) < 0) ! return (B_FALSE); ! /* ! * Determine the devid as a string, with no trailing slash for the minor ! * node. ! */ ! if (devid_get(fd, &devid) != 0) { ! (void) close(fd); return (B_FALSE); ! } ! (void) close(fd); ! if ((devidstr = devid_str_encode(devid, NULL)) == NULL) { ! devid_free(devid); ! return (B_FALSE); ! } ! ! len = strlen(devidstr) + 2; ! fulldevid = alloca(len); ! (void) snprintf(fulldevid, len, "%s/", devidstr); ! ! data.dd_compare = fulldevid; data.dd_func = func; data.dd_prop = ZPOOL_CONFIG_DEVID; data.dd_found = B_FALSE; ! data.dd_isdisk = wholedisk; (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); devid_str_free(devidstr); - devid_free(devid); return (data.dd_found); } /* ! * This function is called when we receive a devfs add event. This can be ! * either a disk event or a lofi event, and the behavior is slightly different ! * depending on which it is. */ static int ! zfs_deliver_add(nvlist_t *nvl, boolean_t is_lofi) { ! char *devpath, *devname; ! char path[PATH_MAX], realpath[PATH_MAX]; ! char *colon, *raw; ! int ret; ! /* ! * The main unit of operation is the physical device path. For disks, ! * this is the device node, as all minor nodes are affected. For lofi ! * devices, this includes the minor path. Unfortunately, this isn't ! * represented in the DEV_PHYS_PATH for various reasons. ! */ ! if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devpath) != 0) return (-1); /* ! * If this is a lofi device, then also get the minor instance name. ! * Unfortunately, the current payload doesn't include an easy way to get ! * this information. So we cheat by resolving the 'dev_name' (which ! * refers to the raw device) and taking the portion between ':(*),raw'. */ ! (void) strlcpy(realpath, devpath, sizeof (realpath)); ! if (is_lofi) { ! if (nvlist_lookup_string(nvl, DEV_NAME, ! &devname) == 0 && ! (ret = resolvepath(devname, path, ! sizeof (path))) > 0) { ! path[ret] = '\0'; ! colon = strchr(path, ':'); ! if (colon != NULL) ! raw = strstr(colon + 1, ",raw"); ! if (colon != NULL && raw != NULL) { ! *raw = '\0'; ! (void) snprintf(realpath, ! sizeof (realpath), "%s%s", ! devpath, colon); ! *raw = ','; } - } - } - /* - * Iterate over all vdevs with a matching devid, and then those with a - * matching /devices path. For disks, we only want to pay attention to - * vdevs marked as whole disks. For lofi, we don't care (because we're - * matching an exact minor name). - */ - if (!devid_iter(realpath, zfs_process_add, !is_lofi)) - (void) devpath_iter(realpath, zfs_process_add, !is_lofi); - return (0); } /* * Called when we receive a VDEV_CHECK event, which indicates a device could not --- 395,531 ---- zpool_close(zhp); return (0); } /* + * Wrap strncmp() to be used as comparison function for devid_iter() and + * physpath_iter(). + */ + /* ARGSUSED */ + static int + strncmp_wrap(libzfs_handle_t *hdl, const char *a, const char *b, size_t len) + { + return (strncmp(a, b, len)); + } + + /* * Given a physical device path, iterate over all (pool, vdev) pairs which + * correspond to the given path's FRU. + */ + static boolean_t + devfru_iter(const char *devpath, const char *physpath, zfs_process_func_t func) + { + dev_data_t data = { 0 }; + const char *fru; + + /* + * Need to refresh the fru cache otherwise we won't find the newly + * inserted disk. + */ + libzfs_fru_refresh(g_zfshdl); + + fru = libzfs_fru_lookup(g_zfshdl, physpath); + if (fru == NULL) + return (B_FALSE); + + data.dd_compare = fru; + data.dd_func = func; + data.dd_cmp_func = libzfs_fru_cmp_slot; + data.dd_prop = ZPOOL_CONFIG_FRU; + data.dd_found = B_FALSE; + data.dd_devpath = devpath; + + (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); + + return (data.dd_found); + } + + /* + * Given a physical device path, iterate over all (pool, vdev) pairs which * correspond to the given path. */ + /*ARGSUSED*/ static boolean_t ! physpath_iter(const char *devpath, const char *physpath, ! zfs_process_func_t func) { dev_data_t data = { 0 }; ! data.dd_compare = physpath; data.dd_func = func; + data.dd_cmp_func = strncmp_wrap; data.dd_prop = ZPOOL_CONFIG_PHYS_PATH; data.dd_found = B_FALSE; ! data.dd_devpath = NULL; (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); return (data.dd_found); } /* ! * Given a devid, iterate over all (pool, vdev) pairs which correspond to the ! * given vdev. */ + /*ARGSUSED*/ static boolean_t ! devid_iter(const char *devpath, const char *physpath, zfs_process_func_t func) { ! char fullphyspath[PATH_MAX]; ! char *devidstr; ! char *s; dev_data_t data = { 0 }; ! /* Try to open a known minor node */ ! (void) snprintf(fullphyspath, sizeof (fullphyspath), "%s%s%s", ! DEVICE_PREFIX, physpath, WD_MINOR); ! devidstr = devid_str_from_path(fullphyspath); ! if (devidstr == NULL) return (B_FALSE); ! /* Chop off the minor node */ ! if ((s = strrchr(devidstr, '/')) != NULL) ! *(s + 1) = '\0'; ! data.dd_compare = devidstr; data.dd_func = func; + data.dd_cmp_func = strncmp_wrap; data.dd_prop = ZPOOL_CONFIG_DEVID; data.dd_found = B_FALSE; ! data.dd_devpath = NULL; (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); devid_str_free(devidstr); return (data.dd_found); } /* ! * This function is called when we receive a devfs add event. */ static int ! zfs_deliver_add(nvlist_t *nvl) { ! char *devpath, *physpath; ! if (nvlist_lookup_string(nvl, DEV_NAME, &devpath) != 0 || ! nvlist_lookup_string(nvl, DEV_PHYS_PATH, &physpath) != 0) return (-1); /* ! * Iterate over all vdevs with a matching devid, then those with a ! * matching /devices path, and finally those with a matching FRU slot ! * number, only paying attention to vdevs marked as whole disks. */ ! if (!devid_iter(devpath, physpath, zfs_process_add) && ! !physpath_iter(devpath, physpath, zfs_process_add) && ! !devfru_iter(devpath, physpath, zfs_process_add)) { ! syseventd_print(9, "%s: match failed devpath=%s physpath=%s\n", ! __func__, devpath, physpath); } return (0); } /* * Called when we receive a VDEV_CHECK event, which indicates a device could not
*** 535,563 **** nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &data.dd_vdev_guid) != 0 || data.dd_vdev_guid == 0) return (0); - data.dd_isdisk = B_TRUE; data.dd_func = zfs_process_add; (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); return (0); } - #define DEVICE_PREFIX "/devices" - static int zfsdle_vdev_online(zpool_handle_t *zhp, void *data) { char *devname = data; boolean_t avail_spare, l2cache; vdev_state_t newstate; nvlist_t *tgt; ! syseventd_print(9, "zfsdle_vdev_online: searching for %s in pool %s\n", devname, zpool_get_name(zhp)); if ((tgt = zpool_find_vdev_by_physpath(zhp, devname, &avail_spare, &l2cache, NULL)) != NULL) { char *path, fullpath[MAXPATHLEN]; --- 542,567 ---- nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &data.dd_vdev_guid) != 0 || data.dd_vdev_guid == 0) return (0); data.dd_func = zfs_process_add; (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); return (0); } static int zfsdle_vdev_online(zpool_handle_t *zhp, void *data) { char *devname = data; boolean_t avail_spare, l2cache; vdev_state_t newstate; nvlist_t *tgt; ! syseventd_print(9, "%s: searching for %s in pool %s\n", __func__, devname, zpool_get_name(zhp)); if ((tgt = zpool_find_vdev_by_physpath(zhp, devname, &avail_spare, &l2cache, NULL)) != NULL) { char *path, fullpath[MAXPATHLEN];
*** 579,591 **** */ (void) zpool_reopen(zhp); } if (zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) { ! syseventd_print(9, "zfsdle_vdev_online: setting device" ! " device %s to ONLINE state in pool %s.\n", ! fullpath, zpool_get_name(zhp)); if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL) (void) zpool_vdev_online(zhp, fullpath, 0, &newstate); } zpool_close(zhp); --- 583,595 ---- */ (void) zpool_reopen(zhp); } if (zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) { ! syseventd_print(9, "%s: setting device '%s' to ONLINE " ! "state in pool %s\n", __func__, fullpath, ! zpool_get_name(zhp)); if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL) (void) zpool_vdev_online(zhp, fullpath, 0, &newstate); } zpool_close(zhp);
*** 595,645 **** return (0); } /* * This function is called for each vdev of a pool for which any of the ! * following events was recieved: * - ESC_ZFS_vdev_add * - ESC_ZFS_vdev_attach * - ESC_ZFS_vdev_clear * - ESC_ZFS_vdev_online * - ESC_ZFS_pool_create * - ESC_ZFS_pool_import * It will update the vdevs FRU property if it is out of date. */ ! /*ARGSUSED2*/ static void ! zfs_update_vdev_fru(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t isdisk) { ! char *devpath, *cptr, *oldfru = NULL; const char *newfru; uint64_t vdev_guid; (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_GUID, &vdev_guid); ! (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_PHYS_PATH, &devpath); (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_FRU, &oldfru); ! /* remove :<slice> from devpath */ ! cptr = strrchr(devpath, ':'); if (cptr != NULL) *cptr = '\0'; ! newfru = libzfs_fru_lookup(g_zfshdl, devpath); if (newfru == NULL) { ! syseventd_print(9, "zfs_update_vdev_fru: no FRU for %s\n", ! devpath); return; } ! /* do nothing if the FRU hasn't changed */ if (oldfru != NULL && libzfs_fru_compare(g_zfshdl, oldfru, newfru)) { ! syseventd_print(9, "zfs_update_vdev_fru: FRU unchanged\n"); return; } ! syseventd_print(9, "zfs_update_vdev_fru: devpath = %s\n", devpath); ! syseventd_print(9, "zfs_update_vdev_fru: FRU = %s\n", newfru); (void) zpool_fru_set(zhp, vdev_guid, newfru); } /* --- 599,650 ---- return (0); } /* * This function is called for each vdev of a pool for which any of the ! * following events was received: * - ESC_ZFS_vdev_add * - ESC_ZFS_vdev_attach * - ESC_ZFS_vdev_clear * - ESC_ZFS_vdev_online * - ESC_ZFS_pool_create * - ESC_ZFS_pool_import * It will update the vdevs FRU property if it is out of date. */ ! /*ARGSUSED*/ static void ! zfs_update_vdev_fru(zpool_handle_t *zhp, nvlist_t *vdev, const char *devpath) { ! char *physpath, *cptr, *oldfru = NULL; const char *newfru; uint64_t vdev_guid; (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_GUID, &vdev_guid); ! (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_PHYS_PATH, &physpath); (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_FRU, &oldfru); ! /* Remove :<slice> from physpath */ ! cptr = strrchr(physpath, ':'); if (cptr != NULL) *cptr = '\0'; ! newfru = libzfs_fru_lookup(g_zfshdl, physpath); if (newfru == NULL) { ! syseventd_print(9, "%s: physpath=%s newFRU=<none>\n", __func__, ! physpath); return; } ! /* Do nothing if the FRU hasn't changed */ if (oldfru != NULL && libzfs_fru_compare(g_zfshdl, oldfru, newfru)) { ! syseventd_print(9, "%s: physpath=%s newFRU=<unchanged>\n", ! __func__, physpath); return; } ! syseventd_print(9, "%s: physpath=%s newFRU=%s\n", __func__, physpath, ! newfru); (void) zpool_fru_set(zhp, vdev_guid, newfru); } /*
*** 659,669 **** char *pname; zpool_handle_t *zhp; nvlist_t *config, *vdev; if (nvlist_lookup_string(nvl, "pool_name", &pname) != 0) { ! syseventd_print(9, "zfs_deliver_update: no pool name\n"); return (-1); } /* * If this event was triggered by a pool export or destroy we cannot --- 664,674 ---- char *pname; zpool_handle_t *zhp; nvlist_t *config, *vdev; if (nvlist_lookup_string(nvl, "pool_name", &pname) != 0) { ! syseventd_print(9, "%s: no pool name\n", __func__); return (-1); } /* * If this event was triggered by a pool export or destroy we cannot
*** 674,692 **** if (zhp == NULL) return (0); config = zpool_get_config(zhp, NULL); if (config == NULL) { ! syseventd_print(9, "zfs_deliver_update: " ! "failed to get pool config for %s\n", pname); zpool_close(zhp); return (-1); } if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdev) != 0) { ! syseventd_print(0, "zfs_deliver_update: " ! "failed to get vdev tree for %s\n", pname); zpool_close(zhp); return (-1); } libzfs_fru_refresh(g_zfshdl); --- 679,697 ---- if (zhp == NULL) return (0); config = zpool_get_config(zhp, NULL); if (config == NULL) { ! syseventd_print(9, "%s: failed to get pool config for %s\n", ! __func__, pname); zpool_close(zhp); return (-1); } if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdev) != 0) { ! syseventd_print(0, "%s: failed to get vdev tree for %s\n", ! __func__, pname); zpool_close(zhp); return (-1); } libzfs_fru_refresh(g_zfshdl);
*** 699,728 **** } int zfs_deliver_dle(nvlist_t *nvl) { ! char *devname; ! if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devname) != 0) { ! syseventd_print(9, "zfs_deliver_event: no physpath\n"); return (-1); } ! if (strncmp(devname, DEVICE_PREFIX, strlen(DEVICE_PREFIX)) != 0) { ! syseventd_print(9, "zfs_deliver_event: invalid " ! "device '%s'", devname); return (-1); } /* * We try to find the device using the physical * path that has been supplied. We need to strip off * the /devices prefix before starting our search. */ ! devname += strlen(DEVICE_PREFIX); ! if (zpool_iter(g_zfshdl, zfsdle_vdev_online, devname) != 1) { ! syseventd_print(9, "zfs_deliver_event: device '%s' not" ! " found\n", devname); return (1); } return (0); } --- 704,734 ---- } int zfs_deliver_dle(nvlist_t *nvl) { ! char *physpath; ! ! if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &physpath) != 0) { ! syseventd_print(9, "%s: no physpath\n", __func__); return (-1); } ! if (strncmp(physpath, DEVICE_PREFIX, strlen(DEVICE_PREFIX)) != 0) { ! syseventd_print(9, "%s: invalid device '%s'", __func__, ! physpath); return (-1); } /* * We try to find the device using the physical * path that has been supplied. We need to strip off * the /devices prefix before starting our search. */ ! physpath += strlen(DEVICE_PREFIX); ! if (zpool_iter(g_zfshdl, zfsdle_vdev_online, physpath) != 1) { ! syseventd_print(9, "%s: device '%s' not found\n", ! __func__, physpath); return (1); } return (0); }
*** 733,758 **** { const char *class = sysevent_get_class_name(ev); const char *subclass = sysevent_get_subclass_name(ev); nvlist_t *nvl; int ret; ! boolean_t is_lofi = B_FALSE, is_check = B_FALSE; ! boolean_t is_dle = B_FALSE, is_update = B_FALSE; if (strcmp(class, EC_DEV_ADD) == 0) { ! /* ! * We're mainly interested in disk additions, but we also listen ! * for new lofi devices, to allow for simplified testing. ! */ ! if (strcmp(subclass, ESC_DISK) == 0) ! is_lofi = B_FALSE; ! else if (strcmp(subclass, ESC_LOFI) == 0) ! is_lofi = B_TRUE; ! else return (0); - - is_check = B_FALSE; } else if (strcmp(class, EC_ZFS) == 0) { if (strcmp(subclass, ESC_ZFS_VDEV_CHECK) == 0) { /* * This event signifies that a device failed to open * during pool load, but the 'autoreplace' property was --- 739,756 ---- { const char *class = sysevent_get_class_name(ev); const char *subclass = sysevent_get_subclass_name(ev); nvlist_t *nvl; int ret; ! boolean_t is_check = B_FALSE; ! boolean_t is_dle = B_FALSE; ! boolean_t is_update = B_FALSE; if (strcmp(class, EC_DEV_ADD) == 0) { ! /* We're only interested in disk additions */ ! if (strcmp(subclass, ESC_DISK) != 0) return (0); } else if (strcmp(class, EC_ZFS) == 0) { if (strcmp(subclass, ESC_ZFS_VDEV_CHECK) == 0) { /* * This event signifies that a device failed to open * during pool load, but the 'autoreplace' property was
*** 786,796 **** else if (is_update) ret = zfs_deliver_update(nvl); else if (is_check) ret = zfs_deliver_check(nvl); else ! ret = zfs_deliver_add(nvl, is_lofi); nvlist_free(nvl); return (ret); } --- 784,794 ---- else if (is_update) ret = zfs_deliver_update(nvl); else if (is_check) ret = zfs_deliver_check(nvl); else ! ret = zfs_deliver_add(nvl); nvlist_free(nvl); return (ret); }