Print this page
NEX-5736 implement autoreplace matching based on FRU slot number
NEX-6200 hot spares are not reactivated after reinserting into enclosure
NEX-9403 need to update FRU for spare and l2cache devices
NEX-9404 remove lofi autoreplace support from syseventd
NEX-9409 hotsparing doesn't work for vdevs without FRU
NEX-9424 zfs`vdev_online() needs better notification about state changes
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Portions contributed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-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>
        
*** 20,29 ****
--- 20,30 ----
   */
  
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
+  * Copyright 2017 Nexenta Systems, Inc.
   */
  
  #include <dlfcn.h>
  #include <errno.h>
  #include <libintl.h>
*** 33,42 ****
--- 34,44 ----
  #include <unistd.h>
  
  #include <libzfs.h>
  
  #include <fm/libtopo.h>
+ #include <fm/topo_hc.h>
  #include <sys/fm/protocol.h>
  #include <sys/systeminfo.h>
  
  #include "libzfs_impl.h"
  
*** 75,84 ****
--- 77,88 ----
  static char *(*_topo_node_name)(tnode_t *);
  static int (*_topo_prop_get_string)(tnode_t *, const char *, const char *,
      char **, int *);
  static int (*_topo_node_fru)(tnode_t *, nvlist_t **, nvlist_t *, int *);
  static int (*_topo_fmri_nvl2str)(topo_hdl_t *, nvlist_t *, char **, int *);
+ static int (*_topo_fmri_str2nvl)(topo_hdl_t *, const char *, nvlist_t **,
+     int *);
  static int (*_topo_fmri_strcmp_noauth)(topo_hdl_t *, const char *,
      const char *);
  
  #define ZFS_FRU_HASH_SIZE       257
  
*** 184,193 ****
--- 188,266 ----
  
          return (TOPO_WALK_NEXT);
  }
  
  /*
+  * Given a disk FRU, check that FRU contains a slot number and remove FRU
+  * details that aren't needed when comparing FRUs by slot number.
+  */
+ static char *
+ diskfru_to_slot(libzfs_handle_t *hdl, const char *diskfru)
+ {
+         nvlist_t *nvl, **hc;
+         char *hc_name, *tmp = NULL;
+         int ret, i;
+         uint_t hc_cnt;
+ 
+         /* string -> nvlist */
+         if (_topo_fmri_str2nvl(hdl->libzfs_topo_hdl, diskfru, &nvl, &ret) != 0)
+                 return (NULL);
+ 
+         /* Need slot (bay) number in the FRU */
+         if (nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hc,
+             &hc_cnt) != 0)
+                 goto out;
+ 
+         for (i = 0; i < hc_cnt; i++) {
+                 if (nvlist_lookup_string(hc[i], FM_FMRI_HC_NAME,
+                     &hc_name) == 0 && strcmp(hc_name, BAY) == 0)
+                         break;
+         }
+         if (i == hc_cnt)
+                 goto out;
+ 
+         /* Drop the unwanted components */
+         (void) nvlist_remove_all(nvl, FM_FMRI_HC_SERIAL_ID);
+         (void) nvlist_remove_all(nvl, FM_FMRI_HC_PART);
+         (void) nvlist_remove_all(nvl, FM_FMRI_HC_REVISION);
+ 
+         /* nvlist -> string */
+         if (_topo_fmri_nvl2str(hdl->libzfs_topo_hdl, nvl, &tmp, &ret) != 0)
+                 tmp = NULL;
+ out:
+         nvlist_free(nvl);
+         return (tmp);
+ }
+ 
+ /*
+  * Check if given FRUs match by slot number to skip comparing disk specific
+  * fields of the FRU.
+  */
+ /* ARGSUSED */
+ int
+ libzfs_fru_cmp_slot(libzfs_handle_t *hdl, const char *a, const char *b,
+     size_t len)
+ {
+         char *slota, *slotb;
+         int ret = -1;
+ 
+         if (a == NULL || b == NULL)
+                 return (-1);
+ 
+         slota = diskfru_to_slot(hdl, a);
+         slotb = diskfru_to_slot(hdl, b);
+ 
+         if (slota != NULL && slotb != NULL)
+                 ret = strcmp(slota, slotb);
+ 
+         _topo_hdl_strfree(hdl->libzfs_topo_hdl, slota);
+         _topo_hdl_strfree(hdl->libzfs_topo_hdl, slotb);
+ 
+         return (ret);
+ }
+ 
+ /*
   * Called during initialization to setup the dynamic libtopo connection.
   */
  #pragma init(libzfs_init_fru)
  static void
  libzfs_init_fru(void)
*** 229,248 ****
              dlsym(_topo_dlhandle, "topo_prop_get_string");
          _topo_node_fru = (int (*)())
              dlsym(_topo_dlhandle, "topo_node_fru");
          _topo_fmri_nvl2str = (int (*)())
              dlsym(_topo_dlhandle, "topo_fmri_nvl2str");
          _topo_fmri_strcmp_noauth = (int (*)())
              dlsym(_topo_dlhandle, "topo_fmri_strcmp_noauth");
  
          if (_topo_open == NULL || _topo_close == NULL ||
              _topo_snap_hold == NULL || _topo_snap_release == NULL ||
              _topo_walk_init == NULL || _topo_walk_step == NULL ||
              _topo_walk_fini == NULL || _topo_hdl_strfree == NULL ||
              _topo_node_name == NULL || _topo_prop_get_string == NULL ||
              _topo_node_fru == NULL || _topo_fmri_nvl2str == NULL ||
!             _topo_fmri_strcmp_noauth == NULL) {
                  (void) dlclose(_topo_dlhandle);
                  _topo_dlhandle = NULL;
          }
  }
  
--- 302,323 ----
              dlsym(_topo_dlhandle, "topo_prop_get_string");
          _topo_node_fru = (int (*)())
              dlsym(_topo_dlhandle, "topo_node_fru");
          _topo_fmri_nvl2str = (int (*)())
              dlsym(_topo_dlhandle, "topo_fmri_nvl2str");
+         _topo_fmri_str2nvl = (int (*)())
+             dlsym(_topo_dlhandle, "topo_fmri_str2nvl");
          _topo_fmri_strcmp_noauth = (int (*)())
              dlsym(_topo_dlhandle, "topo_fmri_strcmp_noauth");
  
          if (_topo_open == NULL || _topo_close == NULL ||
              _topo_snap_hold == NULL || _topo_snap_release == NULL ||
              _topo_walk_init == NULL || _topo_walk_step == NULL ||
              _topo_walk_fini == NULL || _topo_hdl_strfree == NULL ||
              _topo_node_name == NULL || _topo_prop_get_string == NULL ||
              _topo_node_fru == NULL || _topo_fmri_nvl2str == NULL ||
!             _topo_fmri_str2nvl == NULL || _topo_fmri_strcmp_noauth == NULL) {
                  (void) dlclose(_topo_dlhandle);
                  _topo_dlhandle = NULL;
          }
  }
  
*** 290,300 ****
           * for disks to add to the hash.
           */
          twp = _topo_walk_init(thp, FM_FMRI_SCHEME_HC,
              libzfs_fru_gather, hdl, &err);
          if (twp != NULL) {
!                 (void) _topo_walk_step(twp, TOPO_WALK_CHILD);
                  _topo_walk_fini(twp);
          }
  }
  
  /*
--- 365,378 ----
           * for disks to add to the hash.
           */
          twp = _topo_walk_init(thp, FM_FMRI_SCHEME_HC,
              libzfs_fru_gather, hdl, &err);
          if (twp != NULL) {
!                 int status;
! 
!                 status = _topo_walk_step(twp, TOPO_WALK_CHILD);
!                 assert(status != TOPO_WALK_NEXT);
                  _topo_walk_fini(twp);
          }
  }
  
  /*
*** 418,427 ****
--- 496,533 ----
                  return (B_FALSE);
  
          return (B_TRUE);
  }
  
+ /*
+  * Check if both FRUs belong to the same enclosure.
+  */
+ boolean_t
+ libzfs_fru_cmp_enclosure(const char *fru_a, const char *fru_b)
+ {
+         int a, b;
+         char *encl_a, *encl_b;
+         const char *encl_str = "/ses-enclosure=";
+         size_t encl_str_len = strlen(encl_str);
+ 
+         encl_a = strstr(fru_a, encl_str);
+         encl_b = strstr(fru_b, encl_str);
+         /* If both FRUs don't contain enclosure field, consider it a match */
+         if (encl_a == NULL && encl_b == NULL)
+                 return (B_TRUE);
+         /* If one FRU has the enclosure field, but the other one doesn't */
+         if (encl_a == NULL || encl_b == NULL)
+                 return (B_FALSE);
+ 
+         encl_a += encl_str_len;
+         encl_b += encl_str_len;
+         if (sscanf(encl_a, "%d", &a) != 1 || sscanf(encl_b, "%d", &b) != 1)
+                 return (B_FALSE);
+ 
+         return (a == b);
+ }
+ 
  /*
   * Clear memory associated with the FRU hash.
   */
  void
  libzfs_fru_clear(libzfs_handle_t *hdl, boolean_t final)