]> granicus.if.org Git - zfs/blob - cmd/zed/agents/zfs_mod.c
systemd encryption key support
[zfs] / cmd / zed / agents / zfs_mod.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2012 by Delphix. All rights reserved.
24  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25  * Copyright (c) 2016, 2017, Intel Corporation.
26  * Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
27  */
28
29 /*
30  * ZFS syseventd module.
31  *
32  * file origin: openzfs/usr/src/cmd/syseventd/modules/zfs_mod/zfs_mod.c
33  *
34  * The purpose of this module is to identify when devices are added to the
35  * system, and appropriately online or replace the affected vdevs.
36  *
37  * When a device is added to the system:
38  *
39  *      1. Search for any vdevs whose devid matches that of the newly added
40  *         device.
41  *
42  *      2. If no vdevs are found, then search for any vdevs whose udev path
43  *         matches that of the new device.
44  *
45  *      3. If no vdevs match by either method, then ignore the event.
46  *
47  *      4. Attempt to online the device with a flag to indicate that it should
48  *         be unspared when resilvering completes.  If this succeeds, then the
49  *         same device was inserted and we should continue normally.
50  *
51  *      5. If the pool does not have the 'autoreplace' property set, attempt to
52  *         online the device again without the unspare flag, which will
53  *         generate a FMA fault.
54  *
55  *      6. If the pool has the 'autoreplace' property set, and the matching vdev
56  *         is a whole disk, then label the new disk and attempt a 'zpool
57  *         replace'.
58  *
59  * The module responds to EC_DEV_ADD events.  The special ESC_ZFS_VDEV_CHECK
60  * event indicates that a device failed to open during pool load, but the
61  * autoreplace property was set.  In this case, we deferred the associated
62  * FMA fault until our module had a chance to process the autoreplace logic.
63  * If the device could not be replaced, then the second online attempt will
64  * trigger the FMA fault that we skipped earlier.
65  *
66  * ZFS on Linux porting notes:
67  *      Linux udev provides a disk insert for both the disk and the partition
68  *
69  */
70
71 #include <ctype.h>
72 #include <devid.h>
73 #include <fcntl.h>
74 #include <libnvpair.h>
75 #include <libzfs.h>
76 #include <libzutil.h>
77 #include <limits.h>
78 #include <stddef.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <syslog.h>
82 #include <sys/list.h>
83 #include <sys/sunddi.h>
84 #include <sys/sysevent/eventdefs.h>
85 #include <sys/sysevent/dev.h>
86 #include <thread_pool.h>
87 #include <pthread.h>
88 #include <unistd.h>
89 #include <errno.h>
90 #include "zfs_agents.h"
91 #include "../zed_log.h"
92
93 #define DEV_BYID_PATH   "/dev/disk/by-id/"
94 #define DEV_BYPATH_PATH "/dev/disk/by-path/"
95 #define DEV_BYVDEV_PATH "/dev/disk/by-vdev/"
96
97 typedef void (*zfs_process_func_t)(zpool_handle_t *, nvlist_t *, boolean_t);
98
99 libzfs_handle_t *g_zfshdl;
100 list_t g_pool_list;     /* list of unavailable pools at initialization */
101 list_t g_device_list;   /* list of disks with asynchronous label request */
102 tpool_t *g_tpool;
103 boolean_t g_enumeration_done;
104 pthread_t g_zfs_tid;    /* zfs_enum_pools() thread */
105
106 typedef struct unavailpool {
107         zpool_handle_t  *uap_zhp;
108         list_node_t     uap_node;
109 } unavailpool_t;
110
111 typedef struct pendingdev {
112         char            pd_physpath[128];
113         list_node_t     pd_node;
114 } pendingdev_t;
115
116 static int
117 zfs_toplevel_state(zpool_handle_t *zhp)
118 {
119         nvlist_t *nvroot;
120         vdev_stat_t *vs;
121         unsigned int c;
122
123         verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
124             ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
125         verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
126             (uint64_t **)&vs, &c) == 0);
127         return (vs->vs_state);
128 }
129
130 static int
131 zfs_unavail_pool(zpool_handle_t *zhp, void *data)
132 {
133         zed_log_msg(LOG_INFO, "zfs_unavail_pool: examining '%s' (state %d)",
134             zpool_get_name(zhp), (int)zfs_toplevel_state(zhp));
135
136         if (zfs_toplevel_state(zhp) < VDEV_STATE_DEGRADED) {
137                 unavailpool_t *uap;
138                 uap = malloc(sizeof (unavailpool_t));
139                 uap->uap_zhp = zhp;
140                 list_insert_tail((list_t *)data, uap);
141         } else {
142                 zpool_close(zhp);
143         }
144         return (0);
145 }
146
147 /*
148  * Two stage replace on Linux
149  * since we get disk notifications
150  * we can wait for partitioned disk slice to show up!
151  *
152  * First stage tags the disk, initiates async partitioning, and returns
153  * Second stage finds the tag and proceeds to ZFS labeling/replace
154  *
155  * disk-add --> label-disk + tag-disk --> partition-add --> zpool_vdev_attach
156  *
157  * 1. physical match with no fs, no partition
158  *      tag it top, partition disk
159  *
160  * 2. physical match again, see partion and tag
161  *
162  */
163
164 /*
165  * The device associated with the given vdev (either by devid or physical path)
166  * has been added to the system.  If 'isdisk' is set, then we only attempt a
167  * replacement if it's a whole disk.  This also implies that we should label the
168  * disk first.
169  *
170  * First, we attempt to online the device (making sure to undo any spare
171  * operation when finished).  If this succeeds, then we're done.  If it fails,
172  * and the new state is VDEV_CANT_OPEN, it indicates that the device was opened,
173  * but that the label was not what we expected.  If the 'autoreplace' property
174  * is enabled, then we relabel the disk (if specified), and attempt a 'zpool
175  * replace'.  If the online is successful, but the new state is something else
176  * (REMOVED or FAULTED), it indicates that we're out of sync or in some sort of
177  * race, and we should avoid attempting to relabel the disk.
178  *
179  * Also can arrive here from a ESC_ZFS_VDEV_CHECK event
180  */
181 static void
182 zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
183 {
184         char *path;
185         vdev_state_t newstate;
186         nvlist_t *nvroot, *newvd;
187         pendingdev_t *device;
188         uint64_t wholedisk = 0ULL;
189         uint64_t offline = 0ULL;
190         uint64_t guid = 0ULL;
191         char *physpath = NULL, *new_devid = NULL, *enc_sysfs_path = NULL;
192         char rawpath[PATH_MAX], fullpath[PATH_MAX];
193         char devpath[PATH_MAX];
194         int ret;
195         int is_dm = 0;
196         int is_sd = 0;
197         uint_t c;
198         vdev_stat_t *vs;
199
200         if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &path) != 0)
201                 return;
202
203         /* Skip healthy disks */
204         verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_VDEV_STATS,
205             (uint64_t **)&vs, &c) == 0);
206         if (vs->vs_state == VDEV_STATE_HEALTHY) {
207                 zed_log_msg(LOG_INFO, "%s: %s is already healthy, skip it.",
208                     __func__, path);
209                 return;
210         }
211
212         (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_PHYS_PATH, &physpath);
213         (void) nvlist_lookup_string(vdev, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
214             &enc_sysfs_path);
215         (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK, &wholedisk);
216         (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_OFFLINE, &offline);
217         (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_GUID, &guid);
218
219         if (offline)
220                 return;  /* don't intervene if it was taken offline */
221
222         is_dm = zfs_dev_is_dm(path);
223         zed_log_msg(LOG_INFO, "zfs_process_add: pool '%s' vdev '%s', phys '%s'"
224             " wholedisk %d, dm %d (%llu)", zpool_get_name(zhp), path,
225             physpath ? physpath : "NULL", wholedisk, is_dm,
226             (long long unsigned int)guid);
227
228         /*
229          * The VDEV guid is preferred for identification (gets passed in path)
230          */
231         if (guid != 0) {
232                 (void) snprintf(fullpath, sizeof (fullpath), "%llu",
233                     (long long unsigned int)guid);
234         } else {
235                 /*
236                  * otherwise use path sans partition suffix for whole disks
237                  */
238                 (void) strlcpy(fullpath, path, sizeof (fullpath));
239                 if (wholedisk) {
240                         char *spath = zfs_strip_partition(fullpath);
241                         if (!spath) {
242                                 zed_log_msg(LOG_INFO, "%s: Can't alloc",
243                                     __func__);
244                                 return;
245                         }
246
247                         (void) strlcpy(fullpath, spath, sizeof (fullpath));
248                         free(spath);
249                 }
250         }
251
252         /*
253          * Attempt to online the device.
254          */
255         if (zpool_vdev_online(zhp, fullpath,
256             ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE, &newstate) == 0 &&
257             (newstate == VDEV_STATE_HEALTHY ||
258             newstate == VDEV_STATE_DEGRADED)) {
259                 zed_log_msg(LOG_INFO, "  zpool_vdev_online: vdev %s is %s",
260                     fullpath, (newstate == VDEV_STATE_HEALTHY) ?
261                     "HEALTHY" : "DEGRADED");
262                 return;
263         }
264
265         /*
266          * vdev_id alias rule for using scsi_debug devices (FMA automated
267          * testing)
268          */
269         if (physpath != NULL && strcmp("scsidebug", physpath) == 0)
270                 is_sd = 1;
271
272         /*
273          * If the pool doesn't have the autoreplace property set, then use
274          * vdev online to trigger a FMA fault by posting an ereport.
275          */
276         if (!zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOREPLACE, NULL) ||
277             !(wholedisk || is_dm) || (physpath == NULL)) {
278                 (void) zpool_vdev_online(zhp, fullpath, ZFS_ONLINE_FORCEFAULT,
279                     &newstate);
280                 zed_log_msg(LOG_INFO, "Pool's autoreplace is not enabled or "
281                     "not a whole disk for '%s'", fullpath);
282                 return;
283         }
284
285         /*
286          * Convert physical path into its current device node.  Rawpath
287          * needs to be /dev/disk/by-vdev for a scsi_debug device since
288          * /dev/disk/by-path will not be present.
289          */
290         (void) snprintf(rawpath, sizeof (rawpath), "%s%s",
291             is_sd ? DEV_BYVDEV_PATH : DEV_BYPATH_PATH, physpath);
292
293         if (realpath(rawpath, devpath) == NULL && !is_dm) {
294                 zed_log_msg(LOG_INFO, "  realpath: %s failed (%s)",
295                     rawpath, strerror(errno));
296
297                 (void) zpool_vdev_online(zhp, fullpath, ZFS_ONLINE_FORCEFAULT,
298                     &newstate);
299
300                 zed_log_msg(LOG_INFO, "  zpool_vdev_online: %s FORCEFAULT (%s)",
301                     fullpath, libzfs_error_description(g_zfshdl));
302                 return;
303         }
304
305         /* Only autoreplace bad disks */
306         if ((vs->vs_state != VDEV_STATE_DEGRADED) &&
307             (vs->vs_state != VDEV_STATE_FAULTED) &&
308             (vs->vs_state != VDEV_STATE_CANT_OPEN)) {
309                 return;
310         }
311
312         nvlist_lookup_string(vdev, "new_devid", &new_devid);
313
314         if (is_dm) {
315                 /* Don't label device mapper or multipath disks. */
316         } else if (!labeled) {
317                 /*
318                  * we're auto-replacing a raw disk, so label it first
319                  */
320                 char *leafname;
321
322                 /*
323                  * If this is a request to label a whole disk, then attempt to
324                  * write out the label.  Before we can label the disk, we need
325                  * to map the physical string that was matched on to the under
326                  * lying device node.
327                  *
328                  * If any part of this process fails, then do a force online
329                  * to trigger a ZFS fault for the device (and any hot spare
330                  * replacement).
331                  */
332                 leafname = strrchr(devpath, '/') + 1;
333
334                 /*
335                  * If this is a request to label a whole disk, then attempt to
336                  * write out the label.
337                  */
338                 if (zpool_label_disk(g_zfshdl, zhp, leafname) != 0) {
339                         zed_log_msg(LOG_INFO, "  zpool_label_disk: could not "
340                             "label '%s' (%s)", leafname,
341                             libzfs_error_description(g_zfshdl));
342
343                         (void) zpool_vdev_online(zhp, fullpath,
344                             ZFS_ONLINE_FORCEFAULT, &newstate);
345                         return;
346                 }
347
348                 /*
349                  * The disk labeling is asynchronous on Linux. Just record
350                  * this label request and return as there will be another
351                  * disk add event for the partition after the labeling is
352                  * completed.
353                  */
354                 device = malloc(sizeof (pendingdev_t));
355                 (void) strlcpy(device->pd_physpath, physpath,
356                     sizeof (device->pd_physpath));
357                 list_insert_tail(&g_device_list, device);
358
359                 zed_log_msg(LOG_INFO, "  zpool_label_disk: async '%s' (%llu)",
360                     leafname, (u_longlong_t)guid);
361
362                 return; /* resumes at EC_DEV_ADD.ESC_DISK for partition */
363
364         } else /* labeled */ {
365                 boolean_t found = B_FALSE;
366                 /*
367                  * match up with request above to label the disk
368                  */
369                 for (device = list_head(&g_device_list); device != NULL;
370                     device = list_next(&g_device_list, device)) {
371                         if (strcmp(physpath, device->pd_physpath) == 0) {
372                                 list_remove(&g_device_list, device);
373                                 free(device);
374                                 found = B_TRUE;
375                                 break;
376                         }
377                         zed_log_msg(LOG_INFO, "zpool_label_disk: %s != %s",
378                             physpath, device->pd_physpath);
379                 }
380                 if (!found) {
381                         /* unexpected partition slice encountered */
382                         zed_log_msg(LOG_INFO, "labeled disk %s unexpected here",
383                             fullpath);
384                         (void) zpool_vdev_online(zhp, fullpath,
385                             ZFS_ONLINE_FORCEFAULT, &newstate);
386                         return;
387                 }
388
389                 zed_log_msg(LOG_INFO, "  zpool_label_disk: resume '%s' (%llu)",
390                     physpath, (u_longlong_t)guid);
391
392                 (void) snprintf(devpath, sizeof (devpath), "%s%s",
393                     DEV_BYID_PATH, new_devid);
394         }
395
396         /*
397          * Construct the root vdev to pass to zpool_vdev_attach().  While adding
398          * the entire vdev structure is harmless, we construct a reduced set of
399          * path/physpath/wholedisk to keep it simple.
400          */
401         if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0) {
402                 zed_log_msg(LOG_WARNING, "zfs_mod: nvlist_alloc out of memory");
403                 return;
404         }
405         if (nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) {
406                 zed_log_msg(LOG_WARNING, "zfs_mod: nvlist_alloc out of memory");
407                 nvlist_free(nvroot);
408                 return;
409         }
410
411         if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, VDEV_TYPE_DISK) != 0 ||
412             nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, path) != 0 ||
413             nvlist_add_string(newvd, ZPOOL_CONFIG_DEVID, new_devid) != 0 ||
414             (physpath != NULL && nvlist_add_string(newvd,
415             ZPOOL_CONFIG_PHYS_PATH, physpath) != 0) ||
416             (enc_sysfs_path != NULL && nvlist_add_string(newvd,
417             ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH, enc_sysfs_path) != 0) ||
418             nvlist_add_uint64(newvd, ZPOOL_CONFIG_WHOLE_DISK, wholedisk) != 0 ||
419             nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 ||
420             nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &newvd,
421             1) != 0) {
422                 zed_log_msg(LOG_WARNING, "zfs_mod: unable to add nvlist pairs");
423                 nvlist_free(newvd);
424                 nvlist_free(nvroot);
425                 return;
426         }
427
428         nvlist_free(newvd);
429
430         /*
431          * Wait for udev to verify the links exist, then auto-replace
432          * the leaf disk at same physical location.
433          */
434         if (zpool_label_disk_wait(path, 3000) != 0) {
435                 zed_log_msg(LOG_WARNING, "zfs_mod: expected replacement "
436                     "disk %s is missing", path);
437                 nvlist_free(nvroot);
438                 return;
439         }
440
441         ret = zpool_vdev_attach(zhp, fullpath, path, nvroot, B_TRUE);
442
443         zed_log_msg(LOG_INFO, "  zpool_vdev_replace: %s with %s (%s)",
444             fullpath, path, (ret == 0) ? "no errors" :
445             libzfs_error_description(g_zfshdl));
446
447         nvlist_free(nvroot);
448 }
449
450 /*
451  * Utility functions to find a vdev matching given criteria.
452  */
453 typedef struct dev_data {
454         const char              *dd_compare;
455         const char              *dd_prop;
456         zfs_process_func_t      dd_func;
457         boolean_t               dd_found;
458         boolean_t               dd_islabeled;
459         uint64_t                dd_pool_guid;
460         uint64_t                dd_vdev_guid;
461         const char              *dd_new_devid;
462 } dev_data_t;
463
464 static void
465 zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data)
466 {
467         dev_data_t *dp = data;
468         char *path = NULL;
469         uint_t c, children;
470         nvlist_t **child;
471
472         /*
473          * First iterate over any children.
474          */
475         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
476             &child, &children) == 0) {
477                 for (c = 0; c < children; c++)
478                         zfs_iter_vdev(zhp, child[c], data);
479         }
480
481         /*
482          * Iterate over any spares and cache devices
483          */
484         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_SPARES,
485             &child, &children) == 0) {
486                 for (c = 0; c < children; c++)
487                         zfs_iter_vdev(zhp, child[c], data);
488         }
489         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_L2CACHE,
490             &child, &children) == 0) {
491                 for (c = 0; c < children; c++)
492                         zfs_iter_vdev(zhp, child[c], data);
493         }
494
495         /* once a vdev was matched and processed there is nothing left to do */
496         if (dp->dd_found)
497                 return;
498
499         /*
500          * Match by GUID if available otherwise fallback to devid or physical
501          */
502         if (dp->dd_vdev_guid != 0) {
503                 uint64_t guid;
504
505                 if (nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID,
506                     &guid) != 0 || guid != dp->dd_vdev_guid) {
507                         return;
508                 }
509                 zed_log_msg(LOG_INFO, "  zfs_iter_vdev: matched on %llu", guid);
510                 dp->dd_found = B_TRUE;
511
512         } else if (dp->dd_compare != NULL) {
513                 /*
514                  * NOTE: On Linux there is an event for partition, so unlike
515                  * illumos, substring matching is not required to accommodate
516                  * the partition suffix. An exact match will be present in
517                  * the dp->dd_compare value.
518                  */
519                 if (nvlist_lookup_string(nvl, dp->dd_prop, &path) != 0 ||
520                     strcmp(dp->dd_compare, path) != 0)
521                         return;
522
523                 zed_log_msg(LOG_INFO, "  zfs_iter_vdev: matched %s on %s",
524                     dp->dd_prop, path);
525                 dp->dd_found = B_TRUE;
526
527                 /* pass the new devid for use by replacing code */
528                 if (dp->dd_new_devid != NULL) {
529                         (void) nvlist_add_string(nvl, "new_devid",
530                             dp->dd_new_devid);
531                 }
532         }
533
534         (dp->dd_func)(zhp, nvl, dp->dd_islabeled);
535 }
536
537 void
538 zfs_enable_ds(void *arg)
539 {
540         unavailpool_t *pool = (unavailpool_t *)arg;
541
542         (void) zpool_enable_datasets(pool->uap_zhp, NULL, 0);
543         zpool_close(pool->uap_zhp);
544         free(pool);
545 }
546
547 static int
548 zfs_iter_pool(zpool_handle_t *zhp, void *data)
549 {
550         nvlist_t *config, *nvl;
551         dev_data_t *dp = data;
552         uint64_t pool_guid;
553         unavailpool_t *pool;
554
555         zed_log_msg(LOG_INFO, "zfs_iter_pool: evaluating vdevs on %s (by %s)",
556             zpool_get_name(zhp), dp->dd_vdev_guid ? "GUID" : dp->dd_prop);
557
558         /*
559          * For each vdev in this pool, look for a match to apply dd_func
560          */
561         if ((config = zpool_get_config(zhp, NULL)) != NULL) {
562                 if (dp->dd_pool_guid == 0 ||
563                     (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
564                     &pool_guid) == 0 && pool_guid == dp->dd_pool_guid)) {
565                         (void) nvlist_lookup_nvlist(config,
566                             ZPOOL_CONFIG_VDEV_TREE, &nvl);
567                         zfs_iter_vdev(zhp, nvl, data);
568                 }
569         }
570
571         /*
572          * if this pool was originally unavailable,
573          * then enable its datasets asynchronously
574          */
575         if (g_enumeration_done)  {
576                 for (pool = list_head(&g_pool_list); pool != NULL;
577                     pool = list_next(&g_pool_list, pool)) {
578
579                         if (strcmp(zpool_get_name(zhp),
580                             zpool_get_name(pool->uap_zhp)))
581                                 continue;
582                         if (zfs_toplevel_state(zhp) >= VDEV_STATE_DEGRADED) {
583                                 list_remove(&g_pool_list, pool);
584                                 (void) tpool_dispatch(g_tpool, zfs_enable_ds,
585                                     pool);
586                                 break;
587                         }
588                 }
589         }
590
591         zpool_close(zhp);
592         return (dp->dd_found);  /* cease iteration after a match */
593 }
594
595 /*
596  * Given a physical device location, iterate over all
597  * (pool, vdev) pairs which correspond to that location.
598  */
599 static boolean_t
600 devphys_iter(const char *physical, const char *devid, zfs_process_func_t func,
601     boolean_t is_slice)
602 {
603         dev_data_t data = { 0 };
604
605         data.dd_compare = physical;
606         data.dd_func = func;
607         data.dd_prop = ZPOOL_CONFIG_PHYS_PATH;
608         data.dd_found = B_FALSE;
609         data.dd_islabeled = is_slice;
610         data.dd_new_devid = devid;      /* used by auto replace code */
611
612         (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data);
613
614         return (data.dd_found);
615 }
616
617 /*
618  * Given a device identifier, find any vdevs with a matching devid.
619  * On Linux we can match devid directly which is always a whole disk.
620  */
621 static boolean_t
622 devid_iter(const char *devid, zfs_process_func_t func, boolean_t is_slice)
623 {
624         dev_data_t data = { 0 };
625
626         data.dd_compare = devid;
627         data.dd_func = func;
628         data.dd_prop = ZPOOL_CONFIG_DEVID;
629         data.dd_found = B_FALSE;
630         data.dd_islabeled = is_slice;
631         data.dd_new_devid = devid;
632
633         (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data);
634
635         return (data.dd_found);
636 }
637
638 /*
639  * Handle a EC_DEV_ADD.ESC_DISK event.
640  *
641  * illumos
642  *      Expects: DEV_PHYS_PATH string in schema
643  *      Matches: vdev's ZPOOL_CONFIG_PHYS_PATH or ZPOOL_CONFIG_DEVID
644  *
645  *      path: '/dev/dsk/c0t1d0s0' (persistent)
646  *     devid: 'id1,sd@SATA_____Hitachi_HDS72101______JP2940HZ3H74MC/a'
647  * phys_path: '/pci@0,0/pci103c,1609@11/disk@1,0:a'
648  *
649  * linux
650  *      provides: DEV_PHYS_PATH and DEV_IDENTIFIER strings in schema
651  *      Matches: vdev's ZPOOL_CONFIG_PHYS_PATH or ZPOOL_CONFIG_DEVID
652  *
653  *      path: '/dev/sdc1' (not persistent)
654  *     devid: 'ata-SAMSUNG_HD204UI_S2HGJD2Z805891-part1'
655  * phys_path: 'pci-0000:04:00.0-sas-0x4433221106000000-lun-0'
656  */
657 static int
658 zfs_deliver_add(nvlist_t *nvl, boolean_t is_lofi)
659 {
660         char *devpath = NULL, *devid;
661         boolean_t is_slice;
662
663         /*
664          * Expecting a devid string and an optional physical location
665          */
666         if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &devid) != 0)
667                 return (-1);
668
669         (void) nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devpath);
670
671         is_slice = (nvlist_lookup_boolean(nvl, DEV_IS_PART) == 0);
672
673         zed_log_msg(LOG_INFO, "zfs_deliver_add: adding %s (%s) (is_slice %d)",
674             devid, devpath ? devpath : "NULL", is_slice);
675
676         /*
677          * Iterate over all vdevs looking for a match in the folllowing order:
678          * 1. ZPOOL_CONFIG_DEVID (identifies the unique disk)
679          * 2. ZPOOL_CONFIG_PHYS_PATH (identifies disk physical location).
680          *
681          * For disks, we only want to pay attention to vdevs marked as whole
682          * disks or are a multipath device.
683          */
684         if (!devid_iter(devid, zfs_process_add, is_slice) && devpath != NULL)
685                 (void) devphys_iter(devpath, devid, zfs_process_add, is_slice);
686
687         return (0);
688 }
689
690 /*
691  * Called when we receive a VDEV_CHECK event, which indicates a device could not
692  * be opened during initial pool open, but the autoreplace property was set on
693  * the pool.  In this case, we treat it as if it were an add event.
694  */
695 static int
696 zfs_deliver_check(nvlist_t *nvl)
697 {
698         dev_data_t data = { 0 };
699
700         if (nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID,
701             &data.dd_pool_guid) != 0 ||
702             nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID,
703             &data.dd_vdev_guid) != 0 ||
704             data.dd_vdev_guid == 0)
705                 return (0);
706
707         zed_log_msg(LOG_INFO, "zfs_deliver_check: pool '%llu', vdev %llu",
708             data.dd_pool_guid, data.dd_vdev_guid);
709
710         data.dd_func = zfs_process_add;
711
712         (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data);
713
714         return (0);
715 }
716
717 static int
718 zfsdle_vdev_online(zpool_handle_t *zhp, void *data)
719 {
720         char *devname = data;
721         boolean_t avail_spare, l2cache;
722         nvlist_t *tgt;
723         int error;
724
725         zed_log_msg(LOG_INFO, "zfsdle_vdev_online: searching for '%s' in '%s'",
726             devname, zpool_get_name(zhp));
727
728         if ((tgt = zpool_find_vdev_by_physpath(zhp, devname,
729             &avail_spare, &l2cache, NULL)) != NULL) {
730                 char *path, fullpath[MAXPATHLEN];
731                 uint64_t wholedisk;
732
733                 error = nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &path);
734                 if (error) {
735                         zpool_close(zhp);
736                         return (0);
737                 }
738
739                 error = nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK,
740                     &wholedisk);
741                 if (error)
742                         wholedisk = 0;
743
744                 if (wholedisk) {
745                         path = strrchr(path, '/');
746                         if (path != NULL) {
747                                 path = zfs_strip_partition(path + 1);
748                                 if (path == NULL) {
749                                         zpool_close(zhp);
750                                         return (0);
751                                 }
752                         } else {
753                                 zpool_close(zhp);
754                                 return (0);
755                         }
756
757                         (void) strlcpy(fullpath, path, sizeof (fullpath));
758                         free(path);
759
760                         /*
761                          * We need to reopen the pool associated with this
762                          * device so that the kernel can update the size of
763                          * the expanded device.  When expanding there is no
764                          * need to restart the scrub from the beginning.
765                          */
766                         boolean_t scrub_restart = B_FALSE;
767                         (void) zpool_reopen_one(zhp, &scrub_restart);
768                 } else {
769                         (void) strlcpy(fullpath, path, sizeof (fullpath));
770                 }
771
772                 if (zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) {
773                         vdev_state_t newstate;
774
775                         if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL) {
776                                 error = zpool_vdev_online(zhp, fullpath, 0,
777                                     &newstate);
778                                 zed_log_msg(LOG_INFO, "zfsdle_vdev_online: "
779                                     "setting device '%s' to ONLINE state "
780                                     "in pool '%s': %d", fullpath,
781                                     zpool_get_name(zhp), error);
782                         }
783                 }
784                 zpool_close(zhp);
785                 return (1);
786         }
787         zpool_close(zhp);
788         return (0);
789 }
790
791 /*
792  * This function handles the ESC_DEV_DLE device change event.  Use the
793  * provided vdev guid when looking up a disk or partition, when the guid
794  * is not present assume the entire disk is owned by ZFS and append the
795  * expected -part1 partition information then lookup by physical path.
796  */
797 static int
798 zfs_deliver_dle(nvlist_t *nvl)
799 {
800         char *devname, name[MAXPATHLEN];
801         uint64_t guid;
802
803         if (nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &guid) == 0) {
804                 sprintf(name, "%llu", (u_longlong_t)guid);
805         } else if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devname) == 0) {
806                 strlcpy(name, devname, MAXPATHLEN);
807                 zfs_append_partition(name, MAXPATHLEN);
808         } else {
809                 zed_log_msg(LOG_INFO, "zfs_deliver_dle: no guid or physpath");
810         }
811
812         if (zpool_iter(g_zfshdl, zfsdle_vdev_online, name) != 1) {
813                 zed_log_msg(LOG_INFO, "zfs_deliver_dle: device '%s' not "
814                     "found", name);
815                 return (1);
816         }
817
818         return (0);
819 }
820
821 /*
822  * syseventd daemon module event handler
823  *
824  * Handles syseventd daemon zfs device related events:
825  *
826  *      EC_DEV_ADD.ESC_DISK
827  *      EC_DEV_STATUS.ESC_DEV_DLE
828  *      EC_ZFS.ESC_ZFS_VDEV_CHECK
829  *
830  * Note: assumes only one thread active at a time (not thread safe)
831  */
832 static int
833 zfs_slm_deliver_event(const char *class, const char *subclass, nvlist_t *nvl)
834 {
835         int ret;
836         boolean_t is_lofi = B_FALSE, is_check = B_FALSE, is_dle = B_FALSE;
837
838         if (strcmp(class, EC_DEV_ADD) == 0) {
839                 /*
840                  * We're mainly interested in disk additions, but we also listen
841                  * for new loop devices, to allow for simplified testing.
842                  */
843                 if (strcmp(subclass, ESC_DISK) == 0)
844                         is_lofi = B_FALSE;
845                 else if (strcmp(subclass, ESC_LOFI) == 0)
846                         is_lofi = B_TRUE;
847                 else
848                         return (0);
849
850                 is_check = B_FALSE;
851         } else if (strcmp(class, EC_ZFS) == 0 &&
852             strcmp(subclass, ESC_ZFS_VDEV_CHECK) == 0) {
853                 /*
854                  * This event signifies that a device failed to open
855                  * during pool load, but the 'autoreplace' property was
856                  * set, so we should pretend it's just been added.
857                  */
858                 is_check = B_TRUE;
859         } else if (strcmp(class, EC_DEV_STATUS) == 0 &&
860             strcmp(subclass, ESC_DEV_DLE) == 0) {
861                 is_dle = B_TRUE;
862         } else {
863                 return (0);
864         }
865
866         if (is_dle)
867                 ret = zfs_deliver_dle(nvl);
868         else if (is_check)
869                 ret = zfs_deliver_check(nvl);
870         else
871                 ret = zfs_deliver_add(nvl, is_lofi);
872
873         return (ret);
874 }
875
876 /*ARGSUSED*/
877 static void *
878 zfs_enum_pools(void *arg)
879 {
880         (void) zpool_iter(g_zfshdl, zfs_unavail_pool, (void *)&g_pool_list);
881         /*
882          * Linux - instead of using a thread pool, each list entry
883          * will spawn a thread when an unavailable pool transitions
884          * to available. zfs_slm_fini will wait for these threads.
885          */
886         g_enumeration_done = B_TRUE;
887         return (NULL);
888 }
889
890 /*
891  * called from zed daemon at startup
892  *
893  * sent messages from zevents or udev monitor
894  *
895  * For now, each agent has it's own libzfs instance
896  */
897 int
898 zfs_slm_init()
899 {
900         if ((g_zfshdl = libzfs_init()) == NULL)
901                 return (-1);
902
903         /*
904          * collect a list of unavailable pools (asynchronously,
905          * since this can take a while)
906          */
907         list_create(&g_pool_list, sizeof (struct unavailpool),
908             offsetof(struct unavailpool, uap_node));
909
910         if (pthread_create(&g_zfs_tid, NULL, zfs_enum_pools, NULL) != 0) {
911                 list_destroy(&g_pool_list);
912                 libzfs_fini(g_zfshdl);
913                 return (-1);
914         }
915
916         list_create(&g_device_list, sizeof (struct pendingdev),
917             offsetof(struct pendingdev, pd_node));
918
919         return (0);
920 }
921
922 void
923 zfs_slm_fini()
924 {
925         unavailpool_t *pool;
926         pendingdev_t *device;
927
928         /* wait for zfs_enum_pools thread to complete */
929         (void) pthread_join(g_zfs_tid, NULL);
930         /* destroy the thread pool */
931         if (g_tpool != NULL) {
932                 tpool_wait(g_tpool);
933                 tpool_destroy(g_tpool);
934         }
935
936         while ((pool = (list_head(&g_pool_list))) != NULL) {
937                 list_remove(&g_pool_list, pool);
938                 zpool_close(pool->uap_zhp);
939                 free(pool);
940         }
941         list_destroy(&g_pool_list);
942
943         while ((device = (list_head(&g_device_list))) != NULL) {
944                 list_remove(&g_device_list, device);
945                 free(device);
946         }
947         list_destroy(&g_device_list);
948
949         libzfs_fini(g_zfshdl);
950 }
951
952 void
953 zfs_slm_event(const char *class, const char *subclass, nvlist_t *nvl)
954 {
955         zed_log_msg(LOG_INFO, "zfs_slm_event: %s.%s", class, subclass);
956         (void) zfs_slm_deliver_event(class, subclass, nvl);
957 }