Print this page
NEX-10626 Hot spare doesn't replace failed SSD
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5736 implement autoreplace matching based on FRU slot number
NEX-6200 hot spares are not reactivated after reinserting into enclosure
NEX-9403 need to update FRU for spare and l2cache devices
NEX-9404 remove lofi autoreplace support from syseventd
NEX-9409 hotsparing doesn't work for vdevs without FRU
NEX-9424 zfs`vdev_online() needs better notification about state changes
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Portions contributed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-7397 Hotspare didn't kick in automatically when one of the drive in pool went "Faulty" (is_ssd fix)
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-7397 Hotspare didn't kick in automatically when one of the drive in pool went "Faulty"
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-5753 FMD core dumps
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-5774 fix for NEX-3166 has a tunable typo
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-3166 need to add FMA events for SSD lifespan
Reviewed by: Jeffry Molanus <jeffry.molanus@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-2846 Enable Automatic/Intelligent Hot Sparing capability (lint fix)
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
NEX-2846 Enable Automatic/Intelligent Hot Sparing capability
Reviewed by: Jeffry Molanus <jeffry.molanus@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-5163 backport illumos 6027 EOL zulu (XVR-4000)
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
6027 EOL zulu (XVR-4000)
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Peter Tribble <peter.tribble@gmail.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-5162 backport illumos 6507 i386 makecontext(3c) needs to 16-byte align the stack
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
6507 i386 makecontext(3c) needs to 16-byte align the stack
Reviewed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-5207 attempt to activate spare cores fmd
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-1438 bump slow-io threshold and default to disabled automated response
NEX-941 zfs doesn't replace "UNAVAIL" disk from spares in pool
OS-66 Retired devices may still get attached leading to ndi_devi_online errors
OS-65 New FMA agent is needed to consume diagnosed slow IO
Portions contributed by Marcel Telka.
zfsxx issue #11: support for spare device groups
re #12393 rb3935 Kerberos and smbd disagree about who is our AD server (fix elf runtime attributes check)
re #11612 rb3907 Failing vdev of a mirrored pool should not take zfs operations out of action for extended periods of time.

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c
          +++ new/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c
↓ open down ↓ 10 lines elided ↑ open up ↑
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
       21 +
  21   22  /*
  22   23   * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
       24 + * Copyright 2017 Nexenta Systems, Inc.
  23   25   */
  24   26  
  25   27  /*
  26   28   * The ZFS retire agent is responsible for managing hot spares across all pools.
  27   29   * When we see a device fault or a device removal, we try to open the associated
  28   30   * pool and look for any hot spares.  We iterate over any available hot spares
  29   31   * and attempt a 'zpool replace' for each one.
  30   32   *
  31   33   * For vdevs diagnosed as faulty, the agent is also responsible for proactively
  32   34   * marking the vdev FAULTY (for I/O errors) or DEGRADED (for checksum errors).
  33   35   */
  34   36  
  35   37  #include <fm/fmd_api.h>
  36   38  #include <sys/fs/zfs.h>
  37   39  #include <sys/fm/protocol.h>
  38   40  #include <sys/fm/fs/zfs.h>
  39   41  #include <libzfs.h>
  40   42  #include <fm/libtopo.h>
  41   43  #include <string.h>
       44 +#include <sys/int_fmtio.h>
       45 +#include <devid.h>
  42   46  
  43   47  typedef struct zfs_retire_repaired {
  44   48          struct zfs_retire_repaired      *zrr_next;
  45   49          uint64_t                        zrr_pool;
  46   50          uint64_t                        zrr_vdev;
  47   51  } zfs_retire_repaired_t;
  48   52  
  49   53  typedef struct zfs_retire_data {
  50   54          libzfs_handle_t                 *zrd_hdl;
  51   55          zfs_retire_repaired_t           *zrd_repaired;
↓ open down ↓ 7 lines elided ↑ open up ↑
  59   63          while ((zrp = zdp->zrd_repaired) != NULL) {
  60   64                  zdp->zrd_repaired = zrp->zrr_next;
  61   65                  fmd_hdl_free(hdl, zrp, sizeof (zfs_retire_repaired_t));
  62   66          }
  63   67  }
  64   68  
  65   69  /*
  66   70   * Find a pool with a matching GUID.
  67   71   */
  68   72  typedef struct find_cbdata {
       73 +        fmd_hdl_t       *cb_hdl;
  69   74          uint64_t        cb_guid;
  70   75          const char      *cb_fru;
       76 +        ddi_devid_t     cb_devid;
  71   77          zpool_handle_t  *cb_zhp;
  72   78          nvlist_t        *cb_vdev;
  73   79  } find_cbdata_t;
  74   80  
  75   81  static int
  76   82  find_pool(zpool_handle_t *zhp, void *data)
  77   83  {
  78   84          find_cbdata_t *cbp = data;
  79   85  
  80   86          if (cbp->cb_guid ==
↓ open down ↓ 3 lines elided ↑ open up ↑
  84   90          }
  85   91  
  86   92          zpool_close(zhp);
  87   93          return (0);
  88   94  }
  89   95  
  90   96  /*
  91   97   * Find a vdev within a tree with a matching GUID.
  92   98   */
  93   99  static nvlist_t *
  94      -find_vdev(libzfs_handle_t *zhdl, nvlist_t *nv, const char *search_fru,
  95      -    uint64_t search_guid)
      100 +find_vdev(fmd_hdl_t *hdl, libzfs_handle_t *zhdl, nvlist_t *nv,
      101 +    const char *search_fru, ddi_devid_t search_devid, uint64_t search_guid)
  96  102  {
  97  103          uint64_t guid;
  98  104          nvlist_t **child;
  99  105          uint_t c, children;
 100  106          nvlist_t *ret;
 101      -        char *fru;
      107 +        char *fru, *devidstr, *path;
      108 +        ddi_devid_t devid;
 102  109  
 103      -        if (search_fru != NULL) {
 104      -                if (nvlist_lookup_string(nv, ZPOOL_CONFIG_FRU, &fru) == 0 &&
 105      -                    libzfs_fru_compare(zhdl, fru, search_fru))
      110 +        if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0)
      111 +                fmd_hdl_debug(hdl, "find_vdev: vdev path: %s", path);
      112 +
      113 +        if (search_fru != NULL &&
      114 +            nvlist_lookup_string(nv, ZPOOL_CONFIG_FRU, &fru) == 0) {
      115 +                fmd_hdl_debug(hdl, "find_vdev: found fru: %s", fru);
      116 +                if (libzfs_fru_compare(zhdl, fru, search_fru))
 106  117                          return (nv);
 107      -        } else {
 108      -                if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 &&
 109      -                    guid == search_guid)
 110      -                        return (nv);
 111  118          }
 112  119  
      120 +        if (search_devid != NULL &&
      121 +            nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &devidstr) == 0) {
      122 +                fmd_hdl_debug(hdl, "find_vdev: found devid: %s", devidstr);
      123 +
      124 +                if (devid_str_decode(devidstr, &devid, NULL) == 0) {
      125 +                        if (devid_compare(search_devid, devid) == 0) {
      126 +                                devid_free(devid);
      127 +                                return (nv);
      128 +                        }
      129 +
      130 +                        devid_free(devid);
      131 +                }
      132 +        }
      133 +
      134 +        if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 &&
      135 +            guid == search_guid)
      136 +                return (nv);
      137 +
 113  138          if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
 114  139              &child, &children) != 0)
 115  140                  return (NULL);
 116  141  
 117  142          for (c = 0; c < children; c++) {
 118      -                if ((ret = find_vdev(zhdl, child[c], search_fru,
 119      -                    search_guid)) != NULL)
      143 +                if ((ret = find_vdev(hdl, zhdl, child[c], search_fru,
      144 +                    search_devid, search_guid)) != NULL)
 120  145                          return (ret);
 121  146          }
 122  147  
 123  148          if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
 124  149              &child, &children) != 0)
 125  150                  return (NULL);
 126  151  
 127  152          for (c = 0; c < children; c++) {
 128      -                if ((ret = find_vdev(zhdl, child[c], search_fru,
 129      -                    search_guid)) != NULL)
      153 +                if ((ret = find_vdev(hdl, zhdl, child[c], search_fru,
      154 +                    search_devid, search_guid)) != NULL)
 130  155                          return (ret);
 131  156          }
 132  157  
 133  158          return (NULL);
 134  159  }
 135  160  
 136  161  /*
 137  162   * Given a (pool, vdev) GUID pair, find the matching pool and vdev.
 138  163   */
 139  164  static zpool_handle_t *
 140      -find_by_guid(libzfs_handle_t *zhdl, uint64_t pool_guid, uint64_t vdev_guid,
 141      -    nvlist_t **vdevp)
      165 +find_by_guid(fmd_hdl_t *hdl, libzfs_handle_t *zhdl, uint64_t pool_guid,
      166 +    uint64_t vdev_guid, nvlist_t **vdevp)
 142  167  {
 143  168          find_cbdata_t cb;
 144  169          zpool_handle_t *zhp;
 145  170          nvlist_t *config, *nvroot;
 146  171  
 147  172          /*
 148  173           * Find the corresponding pool and make sure the vdev still exists.
 149  174           */
 150  175          cb.cb_guid = pool_guid;
 151  176          if (zpool_iter(zhdl, find_pool, &cb) != 1)
↓ open down ↓ 1 lines elided ↑ open up ↑
 153  178  
 154  179          zhp = cb.cb_zhp;
 155  180          config = zpool_get_config(zhp, NULL);
 156  181          if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
 157  182              &nvroot) != 0) {
 158  183                  zpool_close(zhp);
 159  184                  return (NULL);
 160  185          }
 161  186  
 162  187          if (vdev_guid != 0) {
 163      -                if ((*vdevp = find_vdev(zhdl, nvroot, NULL,
      188 +                if ((*vdevp = find_vdev(hdl, zhdl, nvroot, NULL, NULL,
 164  189                      vdev_guid)) == NULL) {
 165  190                          zpool_close(zhp);
 166  191                          return (NULL);
 167  192                  }
 168  193          }
 169  194  
 170  195          return (zhp);
 171  196  }
 172  197  
 173  198  static int
 174  199  search_pool(zpool_handle_t *zhp, void *data)
 175  200  {
 176  201          find_cbdata_t *cbp = data;
 177  202          nvlist_t *config;
 178  203          nvlist_t *nvroot;
 179  204  
 180  205          config = zpool_get_config(zhp, NULL);
 181  206          if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
 182  207              &nvroot) != 0) {
 183  208                  zpool_close(zhp);
      209 +                fmd_hdl_debug(cbp->cb_hdl, "search_pool: "
      210 +                    "unable to get vdev tree");
 184  211                  return (0);
 185  212          }
 186  213  
 187      -        if ((cbp->cb_vdev = find_vdev(zpool_get_handle(zhp), nvroot,
 188      -            cbp->cb_fru, 0)) != NULL) {
      214 +        if ((cbp->cb_vdev = find_vdev(cbp->cb_hdl, zpool_get_handle(zhp),
      215 +            nvroot, cbp->cb_fru, cbp->cb_devid, cbp->cb_guid)) != NULL) {
 189  216                  cbp->cb_zhp = zhp;
 190  217                  return (1);
 191  218          }
 192  219  
 193  220          zpool_close(zhp);
 194  221          return (0);
 195  222  }
 196  223  
 197  224  /*
 198      - * Given a FRU FMRI, find the matching pool and vdev.
      225 + * Given a FRU FMRI, devid, or guid: find the matching pool and vdev.
 199  226   */
 200  227  static zpool_handle_t *
 201      -find_by_fru(libzfs_handle_t *zhdl, const char *fru, nvlist_t **vdevp)
      228 +find_by_anything(fmd_hdl_t *hdl, libzfs_handle_t *zhdl, const char *fru,
      229 +    ddi_devid_t devid, uint64_t guid, nvlist_t **vdevp)
 202  230  {
 203  231          find_cbdata_t cb;
 204  232  
      233 +        (void) memset(&cb, 0, sizeof (cb));
      234 +        cb.cb_hdl = hdl;
 205  235          cb.cb_fru = fru;
      236 +        cb.cb_devid = devid;
      237 +        cb.cb_guid = guid;
 206  238          cb.cb_zhp = NULL;
      239 +
 207  240          if (zpool_iter(zhdl, search_pool, &cb) != 1)
 208  241                  return (NULL);
 209  242  
 210  243          *vdevp = cb.cb_vdev;
 211  244          return (cb.cb_zhp);
 212  245  }
 213  246  
 214  247  /*
 215      - * Given a vdev, attempt to replace it with every known spare until one
 216      - * succeeds.
      248 + * Create a solved FMD case and add the fault to it
 217  249   */
 218  250  static void
      251 +generate_fault(fmd_hdl_t *hdl, nvlist_t *vdev, char *faultname)
      252 +{
      253 +        char *devid, *fdevid, *physpath, *s;
      254 +        fmd_case_t *c;
      255 +        fmd_hdl_topo_node_info_t *node;
      256 +        nvlist_t *fault = NULL;
      257 +        uint64_t wd;
      258 +
      259 +        assert(hdl != NULL);
      260 +        assert(vdev != NULL);
      261 +        assert(faultname != NULL);
      262 +
      263 +        if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PHYS_PATH,
      264 +            &physpath) != 0 ||
      265 +            nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK, &wd) != 0)
      266 +                return;
      267 +
      268 +        if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_DEVID,
      269 +            &devid) == 0) {
      270 +                fdevid = strdup(devid);
      271 +        } else {
      272 +                fdevid = devid_str_from_path(physpath);
      273 +        }
      274 +        if (fdevid == NULL) {
      275 +                fmd_hdl_debug(hdl, "%s: failed to get devid", __func__);
      276 +                return;
      277 +        }
      278 +
      279 +        if (wd && (s = strrchr(fdevid, '/')) != NULL)
      280 +                *s = '\0';
      281 +
      282 +        c = fmd_case_open(hdl, NULL);
      283 +        if ((node = fmd_hdl_topo_node_get_by_devid(hdl, fdevid)) == NULL) {
      284 +                fault = fmd_nvl_create_fault(hdl, faultname, 100, NULL, vdev,
      285 +                    NULL);
      286 +        } else {
      287 +                fault = fmd_nvl_create_fault(hdl, faultname, 100,
      288 +                    node->resource, node->fru, node->resource);
      289 +                nvlist_free(node->fru);
      290 +                nvlist_free(node->resource);
      291 +                fmd_hdl_free(hdl, node,
      292 +                    sizeof (fmd_hdl_topo_node_info_t));
      293 +        }
      294 +        fmd_case_add_suspect(hdl, c, fault);
      295 +        fmd_case_setspecific(hdl, c, fdevid);
      296 +        fmd_case_solve(hdl, c);
      297 +
      298 +        devid_str_free(fdevid);
      299 +        fmd_hdl_debug(hdl, "%s: dispatched %s", __func__, faultname);
      300 +}
      301 +
      302 +/*
      303 + * Determine if the FRU fields for the spare and the failed device match.
      304 + */
      305 +static boolean_t
      306 +match_fru(fmd_hdl_t *hdl, char *ffru, nvlist_t *spare)
      307 +{
      308 +        char *sfru;
      309 +        boolean_t ret = B_FALSE;
      310 +
      311 +        if (nvlist_lookup_string(spare, ZPOOL_CONFIG_FRU, &sfru) != 0) {
      312 +                fmd_hdl_debug(hdl, "%s: spare FRU not set", __func__);
      313 +                return (B_FALSE);
      314 +        }
      315 +
      316 +        /* We match on enclosure only at the moment */
      317 +        ret = libzfs_fru_cmp_enclosure(ffru, sfru);
      318 +        if (!ret)
      319 +                fmd_hdl_debug(hdl, "%s: enclosure not matched", __func__);
      320 +
      321 +        return (ret);
      322 +}
      323 +
      324 +static boolean_t
      325 +do_replace(zpool_handle_t *zhp, const char *fpath, const char *spath,
      326 +    nvlist_t *spare)
      327 +{
      328 +        nvlist_t *nvroot;
      329 +        boolean_t ret = B_FALSE;
      330 +
      331 +        if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0)
      332 +                return (B_FALSE);
      333 +
      334 +        if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 ||
      335 +            nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
      336 +            &spare, 1) != 0)
      337 +                goto fail;
      338 +
      339 +        ret = (zpool_vdev_attach(zhp, fpath, spath, nvroot, B_TRUE) == 0);
      340 +
      341 +fail:
      342 +        nvlist_free(nvroot);
      343 +        return (ret);
      344 +}
      345 +
      346 +/*
      347 + * Attempt to replace failed device with spare.
      348 + *
      349 + * Spare selection is done in the following order:
      350 + * - If failed device has sparegroup property set, look for the spares that
      351 + *   belongs to the same sparegroup. If no suitable spare is found, skip
      352 + *   the spares that have sparegroup property set while doing other match types.
      353 + * - If failed device has FRU set, look for the spares in the same enclosure.
      354 + * - Finally, try using any available spare.
      355 + *
      356 + * Note that all match types do a media-type match first, so that we don't
      357 + * replace HDD with SSD and vice versa.
      358 + */
      359 +static void
 219  360  replace_with_spare(fmd_hdl_t *hdl, zpool_handle_t *zhp, nvlist_t *vdev)
 220  361  {
 221      -        nvlist_t *config, *nvroot, *replacement;
 222      -        nvlist_t **spares;
 223      -        uint_t s, nspares;
 224      -        char *dev_name;
      362 +        nvlist_t *config, *nvroot, **spares;
      363 +        uint_t i, nspares;
      364 +        boolean_t uu1, uu2, log;
      365 +        char *devpath;
      366 +        char fdevpath[PATH_MAX];        /* devpath of failed device */
      367 +        char *ffru = NULL;              /* FRU of failed device */
      368 +        char fsg[MAXNAMELEN];           /* sparegroup of failed device */
      369 +        boolean_t use_sg = B_FALSE;     /* do sparegroup matching */
      370 +        boolean_t done_sg = B_FALSE;    /* done sparegroup matching */
      371 +        boolean_t use_fru = B_FALSE;    /* do FRU matching */
      372 +        boolean_t done_fru = B_FALSE;   /* done FRU matching */
      373 +        boolean_t fssd = B_FALSE;       /* failed device is SSD */
      374 +        uint64_t wd;
 225  375  
 226      -        config = zpool_get_config(zhp, NULL);
 227      -        if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
 228      -            &nvroot) != 0)
      376 +        if ((config = zpool_get_config(zhp, NULL)) == NULL ||
      377 +            nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0)
 229  378                  return;
 230  379  
 231      -        /*
 232      -         * Find out if there are any hot spares available in the pool.
 233      -         */
 234      -        if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
 235      -            &spares, &nspares) != 0)
      380 +        /* Check if there are any hot spares available in the pool */
      381 +        if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares,
      382 +            &nspares) != 0) {
      383 +                fmd_hdl_debug(hdl, "%s: no spares found", __func__);
 236  384                  return;
      385 +        }
 237  386  
 238      -        replacement = fmd_nvl_alloc(hdl, FMD_SLEEP);
      387 +        if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &devpath) != 0 ||
      388 +            nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK, &wd) != 0 ||
      389 +            nvlist_lookup_boolean_value(vdev, ZPOOL_CONFIG_IS_SSD, &fssd) != 0)
      390 +                return;
      391 +        (void) strlcpy(fdevpath, devpath, sizeof (fdevpath));
      392 +        if (wd)
      393 +                fdevpath[strlen(fdevpath) - 2] = '\0';
 239  394  
 240      -        (void) nvlist_add_string(replacement, ZPOOL_CONFIG_TYPE,
 241      -            VDEV_TYPE_ROOT);
      395 +        /* Spares can't replace log devices */
      396 +        (void) zpool_find_vdev(zhp, fdevpath, &uu1, &uu2, &log, NULL);
      397 +        if (log)
      398 +                return;
 242  399  
 243      -        dev_name = zpool_vdev_name(NULL, zhp, vdev, B_FALSE);
      400 +        /* Check if we should do sparegroup matching */
      401 +        if (vdev_get_prop(zhp, fdevpath, VDEV_PROP_SPAREGROUP, fsg,
      402 +            sizeof (fsg)) == 0 && strcmp(fsg, "-") != 0)
      403 +                use_sg = B_TRUE;
 244  404  
 245      -        /*
 246      -         * Try to replace each spare, ending when we successfully
 247      -         * replace it.
 248      -         */
 249      -        for (s = 0; s < nspares; s++) {
 250      -                char *spare_name;
      405 +        use_fru = (fmd_prop_get_int32(hdl, "fru_compare") == FMD_B_TRUE);
      406 +        /* Disable FRU matching if failed device doesn't have FRU set */
      407 +        if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_FRU, &ffru) != 0)
      408 +                use_fru = B_FALSE;
 251  409  
 252      -                if (nvlist_lookup_string(spares[s], ZPOOL_CONFIG_PATH,
 253      -                    &spare_name) != 0)
      410 +again:
      411 +        /* Go through the spares list */
      412 +        for (i = 0; i < nspares; i++) {
      413 +                char sdevpath[PATH_MAX];        /* devpath of spare */
      414 +                char ssg[MAXNAMELEN];           /* sparegroup of spare */
      415 +                boolean_t sssd = B_FALSE;       /* spare is SSD */
      416 +                boolean_t ssg_set = B_FALSE;
      417 +
      418 +                if (nvlist_lookup_string(spares[i], ZPOOL_CONFIG_PATH,
      419 +                    &devpath) != 0 ||
      420 +                    nvlist_lookup_uint64(spares[i], ZPOOL_CONFIG_WHOLE_DISK,
      421 +                    &wd) != 0)
 254  422                          continue;
 255  423  
 256      -                (void) nvlist_add_nvlist_array(replacement,
 257      -                    ZPOOL_CONFIG_CHILDREN, &spares[s], 1);
      424 +                (void) strlcpy(sdevpath, devpath, sizeof (sdevpath));
      425 +                if (wd)
      426 +                        sdevpath[strlen(sdevpath) - 2] = '\0';
 258  427  
 259      -                if (zpool_vdev_attach(zhp, dev_name, spare_name,
 260      -                    replacement, B_TRUE) == 0)
 261      -                        break;
      428 +                /* Don't swap HDD for SSD and vice versa */
      429 +                if (nvlist_lookup_boolean_value(spares[i], ZPOOL_CONFIG_IS_SSD,
      430 +                    &sssd) != 0 || fssd != sssd) {
      431 +                        continue;
      432 +                }
      433 +
      434 +                /* Get the sparegroup property for the spare */
      435 +                if (vdev_get_prop(zhp, sdevpath, VDEV_PROP_SPAREGROUP, ssg,
      436 +                    sizeof (ssg)) == 0 && strcmp(ssg, "-") != 0)
      437 +                        ssg_set = B_TRUE;
      438 +
      439 +                if (use_sg) {
      440 +                        if (!ssg_set || strcmp(fsg, ssg) != 0)
      441 +                                continue;
      442 +                        /* Found spare in the the same group */
      443 +                        if (do_replace(zhp, fdevpath, sdevpath, spares[i]))
      444 +                                return;
      445 +                        continue;
      446 +                }
      447 +
      448 +                /*
      449 +                 * If we tried matching on sparegroup and have not found
      450 +                 * any suitable spare, skip all spares with sparegroup
      451 +                 * set.
      452 +                 */
      453 +                if (done_sg && ssg_set)
      454 +                        continue;
      455 +
      456 +                if (use_fru) {
      457 +                        if (!match_fru(hdl, ffru, spares[i]))
      458 +                                continue;
      459 +                        /* Found spare with matching FRU */
      460 +                        if (do_replace(zhp, fdevpath, sdevpath, spares[i]))
      461 +                                return;
      462 +                        continue;
      463 +                }
      464 +
      465 +                /*
      466 +                 * sparegroup and FRU matching was either not used or didn't
      467 +                 * find any suitable spares, use the first available one.
      468 +                 */
      469 +                if (do_replace(zhp, fdevpath, sdevpath, spares[i])) {
      470 +                        /* If we tried intellegent sparing, generate fault */
      471 +                        if (done_sg || done_fru) {
      472 +                                generate_fault(hdl, vdev,
      473 +                                    "fault.fs.zfs.vdev.dumb_spared");
      474 +                        }
      475 +                        return;
      476 +                }
 262  477          }
 263  478  
 264      -        free(dev_name);
 265      -        nvlist_free(replacement);
      479 +        if (use_sg) {
      480 +                done_sg = B_TRUE;
      481 +                use_sg = B_FALSE;
      482 +                goto again;
      483 +        } else if (use_fru) {
      484 +                done_fru = B_TRUE;
      485 +                use_fru = B_FALSE;
      486 +                goto again;
      487 +        }
      488 +
      489 +        generate_fault(hdl, vdev, "fault.fs.zfs.vdev.not_spared");
 266  490  }
 267  491  
 268  492  /*
 269  493   * Repair this vdev if we had diagnosed a 'fault.fs.zfs.device' and
 270  494   * ASRU is now usable.  ZFS has found the device to be present and
 271  495   * functioning.
 272  496   */
 273  497  /*ARGSUSED*/
 274  498  void
 275  499  zfs_vdev_repair(fmd_hdl_t *hdl, nvlist_t *nvl)
↓ open down ↓ 64 lines elided ↑ open up ↑
 340  564                  topo_hdl_strfree(thp, fmri);
 341  565          }
 342  566          nvlist_free(asru);
 343  567          zrp = fmd_hdl_alloc(hdl, sizeof (zfs_retire_repaired_t), FMD_SLEEP);
 344  568          zrp->zrr_next = zdp->zrd_repaired;
 345  569          zrp->zrr_pool = pool_guid;
 346  570          zrp->zrr_vdev = vdev_guid;
 347  571          zdp->zrd_repaired = zrp;
 348  572  }
 349  573  
      574 +static int
      575 +zfs_get_vdev_state(fmd_hdl_t *hdl, libzfs_handle_t *zhdl, zpool_handle_t *zhp,
      576 +    uint64_t vdev_guid, nvlist_t **vdev)
      577 +{
      578 +        nvlist_t *config, *nvroot;
      579 +        vdev_stat_t *vs;
      580 +        uint_t cnt;
      581 +        boolean_t missing;
      582 +
      583 +        if (zpool_refresh_stats(zhp, &missing) != 0 ||
      584 +            missing != B_FALSE) {
      585 +                fmd_hdl_debug(hdl, "zfs_get_vdev_state: can't refresh stats");
      586 +                return (VDEV_STATE_UNKNOWN);
      587 +        }
      588 +
      589 +        config = zpool_get_config(zhp, NULL);
      590 +        if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
      591 +            &nvroot) != 0) {
      592 +                fmd_hdl_debug(hdl, "zfs_get_vdev_state: can't get vdev tree");
      593 +                return (VDEV_STATE_UNKNOWN);
      594 +        }
      595 +
      596 +        *vdev = find_vdev(hdl, zhdl, nvroot, NULL, NULL, vdev_guid);
      597 +
      598 +        if (nvlist_lookup_uint64_array(*vdev, ZPOOL_CONFIG_VDEV_STATS,
      599 +            (uint64_t **)&vs, &cnt) != 0) {
      600 +                fmd_hdl_debug(hdl, "zfs_get_vdev_state: can't get vdev stats");
      601 +                return (VDEV_STATE_UNKNOWN);
      602 +        }
      603 +
      604 +        return (vs->vs_state);
      605 +}
      606 +
      607 +int
      608 +zfs_retire_device(fmd_hdl_t *hdl, char *path, boolean_t retire)
      609 +{
      610 +        di_retire_t drt = {0};
      611 +        int err;
      612 +
      613 +        drt.rt_abort = (void (*)(void *, const char *, ...))fmd_hdl_abort;
      614 +        drt.rt_debug = (void (*)(void *, const char *, ...))fmd_hdl_debug;
      615 +        drt.rt_hdl = hdl;
      616 +
      617 +        fmd_hdl_debug(hdl, "zfs_retire_device: "
      618 +            "attempting to %sretire %s", retire ? "" : "un", path);
      619 +
      620 +        err = retire ?
      621 +            di_retire_device(path, &drt, 0) :
      622 +            di_unretire_device(path, &drt);
      623 +
      624 +        if (err != 0)
      625 +                fmd_hdl_debug(hdl, "zfs_retire_device: ",
      626 +                    "di_%sretire_device failed: %d %s",
      627 +                    retire ? "" : "un", err, path);
      628 +
      629 +        return (err);
      630 +}
      631 +
 350  632  /*ARGSUSED*/
 351  633  static void
 352  634  zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
 353  635      const char *class)
 354  636  {
 355  637          uint64_t pool_guid, vdev_guid;
 356  638          zpool_handle_t *zhp;
 357      -        nvlist_t *resource, *fault, *fru;
      639 +        nvlist_t *resource, *fault, *fru, *asru;
 358  640          nvlist_t **faults;
 359  641          uint_t f, nfaults;
 360  642          zfs_retire_data_t *zdp = fmd_hdl_getspecific(hdl);
 361  643          libzfs_handle_t *zhdl = zdp->zrd_hdl;
 362  644          boolean_t fault_device, degrade_device;
 363  645          boolean_t is_repair;
 364      -        char *scheme, *fmri;
      646 +        char *scheme = NULL, *fmri = NULL, *devidstr = NULL, *path = NULL;
      647 +        ddi_devid_t devid;
 365  648          nvlist_t *vdev;
 366  649          char *uuid;
 367  650          int repair_done = 0;
 368  651          boolean_t retire;
 369  652          boolean_t is_disk;
      653 +        boolean_t retire_device = B_FALSE;
 370  654          vdev_aux_t aux;
 371      -        topo_hdl_t *thp;
      655 +        topo_hdl_t *thp = NULL;
 372  656          int err;
 373  657  
 374  658          /*
 375  659           * If this is a resource notifying us of device removal, then simply
 376  660           * check for an available spare and continue.
 377  661           */
 378  662          if (strcmp(class, "resource.fs.zfs.removed") == 0) {
 379  663                  if (nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_POOL_GUID,
 380  664                      &pool_guid) != 0 ||
 381  665                      nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID,
 382  666                      &vdev_guid) != 0)
 383  667                          return;
 384  668  
 385      -                if ((zhp = find_by_guid(zhdl, pool_guid, vdev_guid,
      669 +                if ((zhp = find_by_guid(hdl, zhdl, pool_guid, vdev_guid,
 386  670                      &vdev)) == NULL)
 387  671                          return;
 388  672  
 389  673                  if (fmd_prop_get_int32(hdl, "spare_on_remove"))
 390  674                          replace_with_spare(hdl, zhp, vdev);
 391  675                  zpool_close(zhp);
 392  676                  return;
 393  677          }
 394  678  
 395  679          if (strcmp(class, FM_LIST_RESOLVED_CLASS) == 0)
↓ open down ↓ 23 lines elided ↑ open up ↑
 419  703          for (f = 0; f < nfaults; f++) {
 420  704                  fault = faults[f];
 421  705  
 422  706                  fault_device = B_FALSE;
 423  707                  degrade_device = B_FALSE;
 424  708                  is_disk = B_FALSE;
 425  709  
 426  710                  if (nvlist_lookup_boolean_value(fault, FM_SUSPECT_RETIRE,
 427  711                      &retire) == 0 && retire == 0)
 428  712                          continue;
      713 +                if (fmd_nvl_class_match(hdl, fault, "fault.io.disk.slow-io") &&
      714 +                    fmd_prop_get_int32(hdl, "slow_io_skip_retire") ==
      715 +                    FMD_B_TRUE) {
      716 +                        fmd_hdl_debug(hdl, "ignoring slow io fault");
      717 +                        continue;
      718 +                }
 429  719  
 430  720                  if (fmd_nvl_class_match(hdl, fault,
 431  721                      "fault.io.disk.ssm-wearout") &&
 432  722                      fmd_prop_get_int32(hdl, "ssm_wearout_skip_retire") ==
 433  723                      FMD_B_TRUE) {
 434  724                          fmd_hdl_debug(hdl, "zfs-retire: ignoring SSM fault");
 435  725                          continue;
 436  726                  }
 437  727  
      728 +                if (fmd_nvl_class_match(hdl, fault,
      729 +                    "fault.io.disk.ssm-wearout") &&
      730 +                    fmd_prop_get_int32(hdl, "ssm_wearout_skip_retire") ==
      731 +                    FMD_B_TRUE) {
      732 +                        fmd_hdl_debug(hdl, "zfs-retire: ignoring SSM fault");
      733 +                        continue;
      734 +                }
      735 +
 438  736                  /*
 439  737                   * While we subscribe to fault.fs.zfs.*, we only take action
 440  738                   * for faults targeting a specific vdev (open failure or SERD
 441  739                   * failure).  We also subscribe to fault.io.* events, so that
 442  740                   * faulty disks will be faulted in the ZFS configuration.
 443  741                   */
 444  742                  if (fmd_nvl_class_match(hdl, fault, "fault.fs.zfs.vdev.io")) {
 445  743                          fault_device = B_TRUE;
 446  744                  } else if (fmd_nvl_class_match(hdl, fault,
 447  745                      "fault.fs.zfs.vdev.checksum")) {
 448  746                          degrade_device = B_TRUE;
 449  747                  } else if (fmd_nvl_class_match(hdl, fault,
      748 +                    "fault.fs.zfs.vdev.timeout")) {
      749 +                        fault_device = B_TRUE;
      750 +                } else if (fmd_nvl_class_match(hdl, fault,
 450  751                      "fault.fs.zfs.device")) {
 451  752                          fault_device = B_FALSE;
 452      -                } else if (fmd_nvl_class_match(hdl, fault, "fault.io.*")) {
      753 +                } else if (fmd_nvl_class_match(hdl, fault, "fault.io.disk.*") ||
      754 +                    fmd_nvl_class_match(hdl, fault, "fault.io.scsi.*")) {
 453  755                          is_disk = B_TRUE;
 454  756                          fault_device = B_TRUE;
 455  757                  } else {
 456  758                          continue;
 457  759                  }
 458  760  
 459  761                  if (is_disk) {
 460  762                          /*
 461      -                         * This is a disk fault.  Lookup the FRU, convert it to
 462      -                         * an FMRI string, and attempt to find a matching vdev.
      763 +                         * This is a disk fault.  Lookup the FRU and ASRU,
      764 +                         * convert them to FMRI and devid strings, and attempt
      765 +                         * to find a matching vdev. If no vdev is found, the
      766 +                         * device might still be retired/unretired.
 463  767                           */
 464  768                          if (nvlist_lookup_nvlist(fault, FM_FAULT_FRU,
 465  769                              &fru) != 0 ||
 466  770                              nvlist_lookup_string(fru, FM_FMRI_SCHEME,
 467      -                            &scheme) != 0)
 468      -                                continue;
      771 +                            &scheme) != 0) {
      772 +                                fmd_hdl_debug(hdl,
      773 +                                    "zfs_retire_recv: unable to get FRU");
      774 +                                goto nofru;
      775 +                        }
 469  776  
 470      -                        if (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
 471      -                                continue;
      777 +                        if (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) {
      778 +                                fmd_hdl_debug(hdl,
      779 +                                    "zfs_retire_recv: not hc scheme: %s",
      780 +                                    scheme);
      781 +                                goto nofru;
      782 +                        }
 472  783  
 473  784                          thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
 474  785                          if (topo_fmri_nvl2str(thp, fru, &fmri, &err) != 0) {
 475  786                                  fmd_hdl_topo_rele(hdl, thp);
 476      -                                continue;
      787 +                                fmd_hdl_debug(hdl,
      788 +                                    "zfs_retire_recv: unable to get FMRI");
      789 +                                goto nofru;
 477  790                          }
 478  791  
 479      -                        zhp = find_by_fru(zhdl, fmri, &vdev);
 480      -                        topo_hdl_strfree(thp, fmri);
 481      -                        fmd_hdl_topo_rele(hdl, thp);
      792 +                        fmd_hdl_debug(hdl, "zfs_retire_recv: got FMRI %s",
      793 +                            fmri);
 482  794  
 483      -                        if (zhp == NULL)
      795 +                nofru:
      796 +                        if (nvlist_lookup_nvlist(fault, FM_FAULT_ASRU,
      797 +                            &asru) != 0 ||
      798 +                            nvlist_lookup_string(asru, FM_FMRI_SCHEME,
      799 +                            &scheme) != 0) {
      800 +                                fmd_hdl_debug(hdl,
      801 +                                    "zfs_retire_recv: unable to get ASRU");
      802 +                                goto nodevid;
      803 +                        }
      804 +
      805 +                        if (strcmp(scheme, FM_FMRI_SCHEME_DEV) != 0) {
      806 +                                fmd_hdl_debug(hdl,
      807 +                                    "zfs_retire_recv: not dev scheme: %s",
      808 +                                    scheme);
      809 +                                goto nodevid;
      810 +                        }
      811 +
      812 +                        if (nvlist_lookup_string(asru, FM_FMRI_DEV_ID,
      813 +                            &devidstr) != 0) {
      814 +                                fmd_hdl_debug(hdl,
      815 +                                    "zfs_retire_recv: couldn't get devid");
      816 +                                goto nodevid;
      817 +                        }
      818 +
      819 +                        fmd_hdl_debug(hdl, "zfs_retire_recv: got devid %s",
      820 +                            devidstr);
      821 +
      822 +                        if (devid_str_decode(devidstr, &devid, NULL) != 0) {
      823 +                                fmd_hdl_debug(hdl,
      824 +                                    "zfs_retire_recv: devid_str_decode failed");
      825 +                                goto nodevid;
      826 +                        }
      827 +
      828 +                        if (nvlist_lookup_string(asru, FM_FMRI_DEV_PATH,
      829 +                            &path) != 0) {
      830 +                                fmd_hdl_debug(hdl,
      831 +                                    "zfs_retire_recv: couldn't get path, "
      832 +                                    "won't be able to retire device");
      833 +                                goto nodevid;
      834 +                        }
      835 +
      836 +                        fmd_hdl_debug(hdl, "zfs_retire_recv: got path %s",
      837 +                            path);
      838 +
      839 +                nodevid:
      840 +                        zhp = find_by_anything(hdl, zhdl, fmri, devid, 0,
      841 +                            &vdev);
      842 +                        if (fmri) {
      843 +                                topo_hdl_strfree(thp, fmri);
      844 +                                fmd_hdl_topo_rele(hdl, thp);
      845 +                        }
      846 +                        if (devid)
      847 +                                devid_free(devid);
      848 +
      849 +                        if (zhp == NULL) {
      850 +                                fmd_hdl_debug(hdl, "zfs_retire_recv: no zhp");
      851 +                                if (path != NULL)
      852 +                                        (void) zfs_retire_device(hdl, path,
      853 +                                            !is_repair);
 484  854                                  continue;
      855 +                        }
 485  856  
 486      -                        (void) nvlist_lookup_uint64(vdev,
 487      -                            ZPOOL_CONFIG_GUID, &vdev_guid);
      857 +                        (void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_GUID,
      858 +                            &vdev_guid);
      859 +
      860 +                        fmd_hdl_debug(hdl, "zfs_retire_recv: found vdev GUID: %"
      861 +                            PRIx64, vdev_guid);
      862 +
 488  863                          aux = VDEV_AUX_EXTERNAL;
 489  864                  } else {
 490  865                          /*
 491  866                           * This is a ZFS fault.  Lookup the resource, and
 492  867                           * attempt to find the matching vdev.
 493  868                           */
 494  869                          if (nvlist_lookup_nvlist(fault, FM_FAULT_RESOURCE,
 495  870                              &resource) != 0 ||
 496  871                              nvlist_lookup_string(resource, FM_FMRI_SCHEME,
 497  872                              &scheme) != 0)
↓ open down ↓ 7 lines elided ↑ open up ↑
 505  880                                  continue;
 506  881  
 507  882                          if (nvlist_lookup_uint64(resource, FM_FMRI_ZFS_VDEV,
 508  883                              &vdev_guid) != 0) {
 509  884                                  if (is_repair)
 510  885                                          vdev_guid = 0;
 511  886                                  else
 512  887                                          continue;
 513  888                          }
 514  889  
 515      -                        if ((zhp = find_by_guid(zhdl, pool_guid, vdev_guid,
      890 +                        if ((zhp = find_by_guid(hdl, zhdl, pool_guid, vdev_guid,
 516  891                              &vdev)) == NULL)
 517  892                                  continue;
 518  893  
 519      -                        aux = VDEV_AUX_ERR_EXCEEDED;
      894 +                        if (fmd_nvl_class_match(hdl, fault,
      895 +                            "fault.fs.zfs.vdev.open_failed"))
      896 +                                aux = VDEV_AUX_OPEN_FAILED;
      897 +                        else
      898 +                                aux = VDEV_AUX_ERR_EXCEEDED;
 520  899                  }
 521  900  
 522  901                  if (vdev_guid == 0) {
 523  902                          /*
 524  903                           * For pool-level repair events, clear the entire pool.
 525  904                           */
 526  905                          (void) zpool_clear(zhp, NULL, NULL);
 527  906                          zpool_close(zhp);
 528  907                          continue;
 529  908                  }
 530  909  
 531  910                  /*
 532  911                   * If this is a repair event, then mark the vdev as repaired and
 533  912                   * continue.
 534  913                   */
 535  914                  if (is_repair) {
      915 +                        if (is_disk && path != NULL &&
      916 +                            zfs_retire_device(hdl, path, B_FALSE) != 0)
      917 +                                continue;
      918 +
 536  919                          repair_done = 1;
 537  920                          (void) zpool_vdev_clear(zhp, vdev_guid);
 538  921                          zpool_close(zhp);
 539  922                          continue;
 540  923                  }
 541  924  
 542  925                  /*
 543  926                   * Actively fault the device if needed.
 544  927                   */
 545      -                if (fault_device)
      928 +                if (fault_device) {
 546  929                          (void) zpool_vdev_fault(zhp, vdev_guid, aux);
      930 +
      931 +                        if (zfs_get_vdev_state(hdl, zhdl, zhp, vdev_guid, &vdev)
      932 +                            == VDEV_STATE_FAULTED)
      933 +                                retire_device = B_TRUE;
      934 +                }
      935 +
 547  936                  if (degrade_device)
 548  937                          (void) zpool_vdev_degrade(zhp, vdev_guid, aux);
 549  938  
 550  939                  /*
 551  940                   * Attempt to substitute a hot spare.
 552  941                   */
 553  942                  replace_with_spare(hdl, zhp, vdev);
 554  943                  zpool_close(zhp);
      944 +
      945 +                if (is_disk && retire_device && path != NULL)
      946 +                        (void) zfs_retire_device(hdl, path, B_TRUE);
 555  947          }
 556  948  
 557  949          if (strcmp(class, FM_LIST_REPAIRED_CLASS) == 0 && repair_done &&
 558  950              nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) == 0)
 559  951                  fmd_case_uuresolved(hdl, uuid);
 560  952  }
 561  953  
 562  954  static const fmd_hdl_ops_t fmd_ops = {
 563  955          zfs_retire_recv,        /* fmdo_recv */
 564  956          NULL,                   /* fmdo_timeout */
 565  957          NULL,                   /* fmdo_close */
 566  958          NULL,                   /* fmdo_stats */
 567  959          NULL,                   /* fmdo_gc */
 568  960  };
 569  961  
 570  962  static const fmd_prop_t fmd_props[] = {
 571  963          { "spare_on_remove", FMD_TYPE_BOOL, "true" },
      964 +        { "slow_io_skip_retire", FMD_TYPE_BOOL, "true"},
 572  965          { "ssm_wearout_skip_retire", FMD_TYPE_BOOL, "true"},
      966 +        { "fru_compare", FMD_TYPE_BOOL, "true"},
 573  967          { NULL, 0, NULL }
 574  968  };
 575  969  
 576  970  static const fmd_hdl_info_t fmd_info = {
 577      -        "ZFS Retire Agent", "1.0", &fmd_ops, fmd_props
      971 +        "ZFS Retire Agent", "1.1", &fmd_ops, fmd_props
 578  972  };
 579  973  
 580  974  void
 581  975  _fmd_init(fmd_hdl_t *hdl)
 582  976  {
 583  977          zfs_retire_data_t *zdp;
 584  978          libzfs_handle_t *zhdl;
 585  979  
 586  980          if ((zhdl = libzfs_init()) == NULL)
 587  981                  return;
↓ open down ↓ 23 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX