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