The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/cmd/zed/agents/zfs_agents.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 Version 1.0 (CDDL-1.0).
    6  * You can obtain a copy of the license from the top-level file
    7  * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
    8  * You may not use this file except in compliance with the license.
    9  *
   10  * CDDL HEADER END
   11  */
   12 
   13 /*
   14  * Copyright (c) 2016, Intel Corporation.
   15  * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
   16  * Copyright (c) 2021 Hewlett Packard Enterprise Development LP
   17  */
   18 
   19 #include <libnvpair.h>
   20 #include <libzfs.h>
   21 #include <stddef.h>
   22 #include <stdlib.h>
   23 #include <string.h>
   24 #include <sys/list.h>
   25 #include <sys/time.h>
   26 #include <sys/sysevent/eventdefs.h>
   27 #include <sys/sysevent/dev.h>
   28 #include <sys/fm/protocol.h>
   29 #include <sys/fm/fs/zfs.h>
   30 #include <pthread.h>
   31 #include <unistd.h>
   32 
   33 #include "zfs_agents.h"
   34 #include "fmd_api.h"
   35 #include "../zed_log.h"
   36 
   37 /*
   38  * agent dispatch code
   39  */
   40 
   41 static pthread_mutex_t  agent_lock = PTHREAD_MUTEX_INITIALIZER;
   42 static pthread_cond_t   agent_cond = PTHREAD_COND_INITIALIZER;
   43 static list_t           agent_events;   /* list of pending events */
   44 static int              agent_exiting;
   45 
   46 typedef struct agent_event {
   47         char            ae_class[64];
   48         char            ae_subclass[32];
   49         nvlist_t        *ae_nvl;
   50         list_node_t     ae_node;
   51 } agent_event_t;
   52 
   53 pthread_t g_agents_tid;
   54 
   55 libzfs_handle_t *g_zfs_hdl;
   56 
   57 /* guid search data */
   58 typedef enum device_type {
   59         DEVICE_TYPE_L2ARC,      /* l2arc device */
   60         DEVICE_TYPE_SPARE,      /* spare device */
   61         DEVICE_TYPE_PRIMARY     /* any primary pool storage device */
   62 } device_type_t;
   63 
   64 typedef struct guid_search {
   65         uint64_t        gs_pool_guid;
   66         uint64_t        gs_vdev_guid;
   67         char            *gs_devid;
   68         device_type_t   gs_vdev_type;
   69         uint64_t        gs_vdev_expandtime;     /* vdev expansion time */
   70 } guid_search_t;
   71 
   72 /*
   73  * Walks the vdev tree recursively looking for a matching devid.
   74  * Returns B_TRUE as soon as a matching device is found, B_FALSE otherwise.
   75  */
   76 static boolean_t
   77 zfs_agent_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *arg)
   78 {
   79         guid_search_t *gsp = arg;
   80         char *path = NULL;
   81         uint_t c, children;
   82         nvlist_t **child;
   83         uint64_t vdev_guid;
   84 
   85         /*
   86          * First iterate over any children.
   87          */
   88         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
   89             &child, &children) == 0) {
   90                 for (c = 0; c < children; c++) {
   91                         if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
   92                                 gsp->gs_vdev_type = DEVICE_TYPE_PRIMARY;
   93                                 return (B_TRUE);
   94                         }
   95                 }
   96         }
   97         /*
   98          * Iterate over any spares and cache devices
   99          */
  100         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_SPARES,
  101             &child, &children) == 0) {
  102                 for (c = 0; c < children; c++) {
  103                         if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
  104                                 gsp->gs_vdev_type = DEVICE_TYPE_SPARE;
  105                                 return (B_TRUE);
  106                         }
  107                 }
  108         }
  109         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_L2CACHE,
  110             &child, &children) == 0) {
  111                 for (c = 0; c < children; c++) {
  112                         if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
  113                                 gsp->gs_vdev_type = DEVICE_TYPE_L2ARC;
  114                                 return (B_TRUE);
  115                         }
  116                 }
  117         }
  118         /*
  119          * On a devid match, grab the vdev guid and expansion time, if any.
  120          */
  121         if (gsp->gs_devid != NULL &&
  122             (nvlist_lookup_string(nvl, ZPOOL_CONFIG_DEVID, &path) == 0) &&
  123             (strcmp(gsp->gs_devid, path) == 0)) {
  124                 (void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID,
  125                     &gsp->gs_vdev_guid);
  126                 (void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_EXPANSION_TIME,
  127                     &gsp->gs_vdev_expandtime);
  128                 return (B_TRUE);
  129         }
  130         /*
  131          * Otherwise, on a vdev guid match, grab the devid and expansion
  132          * time. The devid might be missing on removal since its not part
  133          * of blkid cache and L2ARC VDEV does not contain pool guid in its
  134          * blkid, so this is a special case for L2ARC VDEV.
  135          */
  136         else if (gsp->gs_vdev_guid != 0 && gsp->gs_devid == NULL &&
  137             nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID, &vdev_guid) == 0 &&
  138             gsp->gs_vdev_guid == vdev_guid) {
  139                 (void) nvlist_lookup_string(nvl, ZPOOL_CONFIG_DEVID,
  140                     &gsp->gs_devid);
  141                 (void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_EXPANSION_TIME,
  142                     &gsp->gs_vdev_expandtime);
  143                 return (B_TRUE);
  144         }
  145 
  146         return (B_FALSE);
  147 }
  148 
  149 static int
  150 zfs_agent_iter_pool(zpool_handle_t *zhp, void *arg)
  151 {
  152         guid_search_t *gsp = arg;
  153         nvlist_t *config, *nvl;
  154 
  155         /*
  156          * For each vdev in this pool, look for a match by devid
  157          */
  158         if ((config = zpool_get_config(zhp, NULL)) != NULL) {
  159                 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
  160                     &nvl) == 0) {
  161                         (void) zfs_agent_iter_vdev(zhp, nvl, gsp);
  162                 }
  163         }
  164         /*
  165          * if a match was found then grab the pool guid
  166          */
  167         if (gsp->gs_vdev_guid && gsp->gs_devid) {
  168                 (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
  169                     &gsp->gs_pool_guid);
  170         }
  171 
  172         zpool_close(zhp);
  173         return (gsp->gs_devid != NULL && gsp->gs_vdev_guid != 0);
  174 }
  175 
  176 void
  177 zfs_agent_post_event(const char *class, const char *subclass, nvlist_t *nvl)
  178 {
  179         agent_event_t *event;
  180 
  181         if (subclass == NULL)
  182                 subclass = "";
  183 
  184         event = malloc(sizeof (agent_event_t));
  185         if (event == NULL || nvlist_dup(nvl, &event->ae_nvl, 0) != 0) {
  186                 if (event)
  187                         free(event);
  188                 return;
  189         }
  190 
  191         if (strcmp(class, "sysevent.fs.zfs.vdev_check") == 0) {
  192                 class = EC_ZFS;
  193                 subclass = ESC_ZFS_VDEV_CHECK;
  194         }
  195 
  196         /*
  197          * On Linux, we don't get the expected FM_RESOURCE_REMOVED ereport
  198          * from the vdev_disk layer after a hot unplug. Fortunately we do
  199          * get an EC_DEV_REMOVE from our disk monitor and it is a suitable
  200          * proxy so we remap it here for the benefit of the diagnosis engine.
  201          * Starting in OpenZFS 2.0, we do get FM_RESOURCE_REMOVED from the spa
  202          * layer. Processing multiple FM_RESOURCE_REMOVED events is not harmful.
  203          */
  204         if ((strcmp(class, EC_DEV_REMOVE) == 0) &&
  205             (strcmp(subclass, ESC_DISK) == 0) &&
  206             (nvlist_exists(nvl, ZFS_EV_VDEV_GUID) ||
  207             nvlist_exists(nvl, DEV_IDENTIFIER))) {
  208                 nvlist_t *payload = event->ae_nvl;
  209                 struct timeval tv;
  210                 int64_t tod[2];
  211                 uint64_t pool_guid = 0, vdev_guid = 0;
  212                 guid_search_t search = { 0 };
  213                 device_type_t devtype = DEVICE_TYPE_PRIMARY;
  214                 char *devid = NULL;
  215 
  216                 class = "resource.fs.zfs.removed";
  217                 subclass = "";
  218 
  219                 (void) nvlist_add_string(payload, FM_CLASS, class);
  220                 (void) nvlist_lookup_string(nvl, DEV_IDENTIFIER, &devid);
  221                 (void) nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &pool_guid);
  222                 (void) nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &vdev_guid);
  223 
  224                 (void) gettimeofday(&tv, NULL);
  225                 tod[0] = tv.tv_sec;
  226                 tod[1] = tv.tv_usec;
  227                 (void) nvlist_add_int64_array(payload, FM_EREPORT_TIME, tod, 2);
  228 
  229                 /*
  230                  * If devid is missing but vdev_guid is available, find devid
  231                  * and pool_guid from vdev_guid.
  232                  * For multipath, spare and l2arc devices ZFS_EV_VDEV_GUID or
  233                  * ZFS_EV_POOL_GUID may be missing so find them.
  234                  */
  235                 if (devid == NULL || pool_guid == 0 || vdev_guid == 0) {
  236                         if (devid == NULL)
  237                                 search.gs_vdev_guid = vdev_guid;
  238                         else
  239                                 search.gs_devid = devid;
  240                         zpool_iter(g_zfs_hdl, zfs_agent_iter_pool, &search);
  241                         if (devid == NULL)
  242                                 devid = search.gs_devid;
  243                         if (pool_guid == 0)
  244                                 pool_guid = search.gs_pool_guid;
  245                         if (vdev_guid == 0)
  246                                 vdev_guid = search.gs_vdev_guid;
  247                         devtype = search.gs_vdev_type;
  248                 }
  249 
  250                 /*
  251                  * We want to avoid reporting "remove" events coming from
  252                  * libudev for VDEVs which were expanded recently (10s) and
  253                  * avoid activating spares in response to partitions being
  254                  * deleted and created in rapid succession.
  255                  */
  256                 if (search.gs_vdev_expandtime != 0 &&
  257                     search.gs_vdev_expandtime + 10 > tv.tv_sec) {
  258                         zed_log_msg(LOG_INFO, "agent post event: ignoring '%s' "
  259                             "for recently expanded device '%s'", EC_DEV_REMOVE,
  260                             devid);
  261                         fnvlist_free(payload);
  262                         free(event);
  263                         goto out;
  264                 }
  265 
  266                 (void) nvlist_add_uint64(payload,
  267                     FM_EREPORT_PAYLOAD_ZFS_POOL_GUID, pool_guid);
  268                 (void) nvlist_add_uint64(payload,
  269                     FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID, vdev_guid);
  270                 switch (devtype) {
  271                 case DEVICE_TYPE_L2ARC:
  272                         (void) nvlist_add_string(payload,
  273                             FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE,
  274                             VDEV_TYPE_L2CACHE);
  275                         break;
  276                 case DEVICE_TYPE_SPARE:
  277                         (void) nvlist_add_string(payload,
  278                             FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE, VDEV_TYPE_SPARE);
  279                         break;
  280                 case DEVICE_TYPE_PRIMARY:
  281                         (void) nvlist_add_string(payload,
  282                             FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE, VDEV_TYPE_DISK);
  283                         break;
  284                 }
  285 
  286                 zed_log_msg(LOG_INFO, "agent post event: mapping '%s' to '%s'",
  287                     EC_DEV_REMOVE, class);
  288         }
  289 
  290         (void) strlcpy(event->ae_class, class, sizeof (event->ae_class));
  291         (void) strlcpy(event->ae_subclass, subclass,
  292             sizeof (event->ae_subclass));
  293 
  294         (void) pthread_mutex_lock(&agent_lock);
  295         list_insert_tail(&agent_events, event);
  296         (void) pthread_mutex_unlock(&agent_lock);
  297 
  298 out:
  299         (void) pthread_cond_signal(&agent_cond);
  300 }
  301 
  302 static void
  303 zfs_agent_dispatch(const char *class, const char *subclass, nvlist_t *nvl)
  304 {
  305         /*
  306          * The diagnosis engine subscribes to the following events.
  307          * On illumos these subscriptions reside in:
  308          *      /usr/lib/fm/fmd/plugins/zfs-diagnosis.conf
  309          */
  310         if (strstr(class, "ereport.fs.zfs.") != NULL ||
  311             strstr(class, "resource.fs.zfs.") != NULL ||
  312             strcmp(class, "sysevent.fs.zfs.vdev_remove") == 0 ||
  313             strcmp(class, "sysevent.fs.zfs.vdev_remove_dev") == 0 ||
  314             strcmp(class, "sysevent.fs.zfs.pool_destroy") == 0) {
  315                 fmd_module_recv(fmd_module_hdl("zfs-diagnosis"), nvl, class);
  316         }
  317 
  318         /*
  319          * The retire agent subscribes to the following events.
  320          * On illumos these subscriptions reside in:
  321          *      /usr/lib/fm/fmd/plugins/zfs-retire.conf
  322          *
  323          * NOTE: faults events come directly from our diagnosis engine
  324          * and will not pass through the zfs kernel module.
  325          */
  326         if (strcmp(class, FM_LIST_SUSPECT_CLASS) == 0 ||
  327             strcmp(class, "resource.fs.zfs.removed") == 0 ||
  328             strcmp(class, "resource.fs.zfs.statechange") == 0 ||
  329             strcmp(class, "sysevent.fs.zfs.vdev_remove")  == 0) {
  330                 fmd_module_recv(fmd_module_hdl("zfs-retire"), nvl, class);
  331         }
  332 
  333         /*
  334          * The SLM module only consumes disk events and vdev check events
  335          *
  336          * NOTE: disk events come directly from disk monitor and will
  337          * not pass through the zfs kernel module.
  338          */
  339         if (strstr(class, "EC_dev_") != NULL ||
  340             strcmp(class, EC_ZFS) == 0) {
  341                 (void) zfs_slm_event(class, subclass, nvl);
  342         }
  343 }
  344 
  345 /*
  346  * Events are consumed and dispatched from this thread
  347  * An agent can also post an event so event list lock
  348  * is not held when calling an agent.
  349  * One event is consumed at a time.
  350  */
  351 static void *
  352 zfs_agent_consumer_thread(void *arg)
  353 {
  354         (void) arg;
  355 
  356         for (;;) {
  357                 agent_event_t *event;
  358 
  359                 (void) pthread_mutex_lock(&agent_lock);
  360 
  361                 /* wait for an event to show up */
  362                 while (!agent_exiting && list_is_empty(&agent_events))
  363                         (void) pthread_cond_wait(&agent_cond, &agent_lock);
  364 
  365                 if (agent_exiting) {
  366                         (void) pthread_mutex_unlock(&agent_lock);
  367                         zed_log_msg(LOG_INFO, "zfs_agent_consumer_thread: "
  368                             "exiting");
  369                         return (NULL);
  370                 }
  371 
  372                 if ((event = (list_head(&agent_events))) != NULL) {
  373                         list_remove(&agent_events, event);
  374 
  375                         (void) pthread_mutex_unlock(&agent_lock);
  376 
  377                         /* dispatch to all event subscribers */
  378                         zfs_agent_dispatch(event->ae_class, event->ae_subclass,
  379                             event->ae_nvl);
  380 
  381                         nvlist_free(event->ae_nvl);
  382                         free(event);
  383                         continue;
  384                 }
  385 
  386                 (void) pthread_mutex_unlock(&agent_lock);
  387         }
  388 
  389         return (NULL);
  390 }
  391 
  392 void
  393 zfs_agent_init(libzfs_handle_t *zfs_hdl)
  394 {
  395         fmd_hdl_t *hdl;
  396 
  397         g_zfs_hdl = zfs_hdl;
  398 
  399         if (zfs_slm_init() != 0)
  400                 zed_log_die("Failed to initialize zfs slm");
  401         zed_log_msg(LOG_INFO, "Add Agent: init");
  402 
  403         hdl = fmd_module_hdl("zfs-diagnosis");
  404         _zfs_diagnosis_init(hdl);
  405         if (!fmd_module_initialized(hdl))
  406                 zed_log_die("Failed to initialize zfs diagnosis");
  407 
  408         hdl = fmd_module_hdl("zfs-retire");
  409         _zfs_retire_init(hdl);
  410         if (!fmd_module_initialized(hdl))
  411                 zed_log_die("Failed to initialize zfs retire");
  412 
  413         list_create(&agent_events, sizeof (agent_event_t),
  414             offsetof(struct agent_event, ae_node));
  415 
  416         if (pthread_create(&g_agents_tid, NULL, zfs_agent_consumer_thread,
  417             NULL) != 0) {
  418                 list_destroy(&agent_events);
  419                 zed_log_die("Failed to initialize agents");
  420         }
  421         pthread_setname_np(g_agents_tid, "agents");
  422 }
  423 
  424 void
  425 zfs_agent_fini(void)
  426 {
  427         fmd_hdl_t *hdl;
  428         agent_event_t *event;
  429 
  430         agent_exiting = 1;
  431         (void) pthread_cond_signal(&agent_cond);
  432 
  433         /* wait for zfs_enum_pools thread to complete */
  434         (void) pthread_join(g_agents_tid, NULL);
  435 
  436         /* drain any pending events */
  437         while ((event = (list_head(&agent_events))) != NULL) {
  438                 list_remove(&agent_events, event);
  439                 nvlist_free(event->ae_nvl);
  440                 free(event);
  441         }
  442 
  443         list_destroy(&agent_events);
  444 
  445         if ((hdl = fmd_module_hdl("zfs-retire")) != NULL) {
  446                 _zfs_retire_fini(hdl);
  447                 fmd_hdl_unregister(hdl);
  448         }
  449         if ((hdl = fmd_module_hdl("zfs-diagnosis")) != NULL) {
  450                 _zfs_diagnosis_fini(hdl);
  451                 fmd_hdl_unregister(hdl);
  452         }
  453 
  454         zed_log_msg(LOG_INFO, "Add Agent: fini");
  455         zfs_slm_fini();
  456 
  457         g_zfs_hdl = NULL;
  458 }

Cache object: e5f8fb44471bc136b2421d2c84df3feb


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.