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,10 +20,11 @@
  */
 
 /*
  * 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,10 +34,11 @@
 #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,10 +77,12 @@
 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,10 +188,79 @@
 
         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,20 +302,22 @@
             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_strcmp_noauth == NULL) {
+            _topo_fmri_str2nvl == NULL || _topo_fmri_strcmp_noauth == NULL) {
                 (void) dlclose(_topo_dlhandle);
                 _topo_dlhandle = NULL;
         }
 }
 

@@ -290,11 +365,14 @@
          * 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);
+                int status;
+
+                status = _topo_walk_step(twp, TOPO_WALK_CHILD);
+                assert(status != TOPO_WALK_NEXT);
                 _topo_walk_fini(twp);
         }
 }
 
 /*

@@ -418,10 +496,38 @@
                 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)