1*10817SEric.Schrock@Sun.COM /* 2*10817SEric.Schrock@Sun.COM * CDDL HEADER START 3*10817SEric.Schrock@Sun.COM * 4*10817SEric.Schrock@Sun.COM * The contents of this file are subject to the terms of the 5*10817SEric.Schrock@Sun.COM * Common Development and Distribution License (the "License"). 6*10817SEric.Schrock@Sun.COM * You may not use this file except in compliance with the License. 7*10817SEric.Schrock@Sun.COM * 8*10817SEric.Schrock@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*10817SEric.Schrock@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*10817SEric.Schrock@Sun.COM * See the License for the specific language governing permissions 11*10817SEric.Schrock@Sun.COM * and limitations under the License. 12*10817SEric.Schrock@Sun.COM * 13*10817SEric.Schrock@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*10817SEric.Schrock@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*10817SEric.Schrock@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*10817SEric.Schrock@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*10817SEric.Schrock@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*10817SEric.Schrock@Sun.COM * 19*10817SEric.Schrock@Sun.COM * CDDL HEADER END 20*10817SEric.Schrock@Sun.COM */ 21*10817SEric.Schrock@Sun.COM 22*10817SEric.Schrock@Sun.COM /* 23*10817SEric.Schrock@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*10817SEric.Schrock@Sun.COM * Use is subject to license terms. 25*10817SEric.Schrock@Sun.COM */ 26*10817SEric.Schrock@Sun.COM 27*10817SEric.Schrock@Sun.COM #include <dlfcn.h> 28*10817SEric.Schrock@Sun.COM #include <errno.h> 29*10817SEric.Schrock@Sun.COM #include <libintl.h> 30*10817SEric.Schrock@Sun.COM #include <link.h> 31*10817SEric.Schrock@Sun.COM #include <pthread.h> 32*10817SEric.Schrock@Sun.COM #include <strings.h> 33*10817SEric.Schrock@Sun.COM #include <unistd.h> 34*10817SEric.Schrock@Sun.COM 35*10817SEric.Schrock@Sun.COM #include <libzfs.h> 36*10817SEric.Schrock@Sun.COM 37*10817SEric.Schrock@Sun.COM #include <fm/libtopo.h> 38*10817SEric.Schrock@Sun.COM #include <sys/fm/protocol.h> 39*10817SEric.Schrock@Sun.COM #include <sys/systeminfo.h> 40*10817SEric.Schrock@Sun.COM 41*10817SEric.Schrock@Sun.COM #include "libzfs_impl.h" 42*10817SEric.Schrock@Sun.COM 43*10817SEric.Schrock@Sun.COM /* 44*10817SEric.Schrock@Sun.COM * This file is responsible for determining the relationship between I/O 45*10817SEric.Schrock@Sun.COM * devices paths and physical locations. In the world of MPxIO and external 46*10817SEric.Schrock@Sun.COM * enclosures, the device path is not synonymous with the physical location. 47*10817SEric.Schrock@Sun.COM * If you remove a drive and insert it into a different slot, it will end up 48*10817SEric.Schrock@Sun.COM * with the same path under MPxIO. If you recable storage enclosures, the 49*10817SEric.Schrock@Sun.COM * device paths may change. All of this makes it difficult to implement the 50*10817SEric.Schrock@Sun.COM * 'autoreplace' property, which is supposed to automatically manage disk 51*10817SEric.Schrock@Sun.COM * replacement based on physical slot. 52*10817SEric.Schrock@Sun.COM * 53*10817SEric.Schrock@Sun.COM * In order to work around these limitations, we have a per-vdev FRU property 54*10817SEric.Schrock@Sun.COM * that is the libtopo path (minus disk-specific authority information) to the 55*10817SEric.Schrock@Sun.COM * physical location of the device on the system. This is an optional 56*10817SEric.Schrock@Sun.COM * property, and is only needed when using the 'autoreplace' property or when 57*10817SEric.Schrock@Sun.COM * generating FMA faults against vdevs. 58*10817SEric.Schrock@Sun.COM */ 59*10817SEric.Schrock@Sun.COM 60*10817SEric.Schrock@Sun.COM /* 61*10817SEric.Schrock@Sun.COM * Because the FMA packages depend on ZFS, we have to dlopen() libtopo in case 62*10817SEric.Schrock@Sun.COM * it is not present. We only need this once per library instance, so it is 63*10817SEric.Schrock@Sun.COM * not part of the libzfs handle. 64*10817SEric.Schrock@Sun.COM */ 65*10817SEric.Schrock@Sun.COM static void *_topo_dlhandle; 66*10817SEric.Schrock@Sun.COM static topo_hdl_t *(*_topo_open)(int, const char *, int *); 67*10817SEric.Schrock@Sun.COM static void (*_topo_close)(topo_hdl_t *); 68*10817SEric.Schrock@Sun.COM static char *(*_topo_snap_hold)(topo_hdl_t *, const char *, int *); 69*10817SEric.Schrock@Sun.COM static void (*_topo_snap_release)(topo_hdl_t *); 70*10817SEric.Schrock@Sun.COM static topo_walk_t *(*_topo_walk_init)(topo_hdl_t *, const char *, 71*10817SEric.Schrock@Sun.COM topo_walk_cb_t, void *, int *); 72*10817SEric.Schrock@Sun.COM static int (*_topo_walk_step)(topo_walk_t *, int); 73*10817SEric.Schrock@Sun.COM static void (*_topo_walk_fini)(topo_walk_t *); 74*10817SEric.Schrock@Sun.COM static void (*_topo_hdl_strfree)(topo_hdl_t *, char *); 75*10817SEric.Schrock@Sun.COM static char *(*_topo_node_name)(tnode_t *); 76*10817SEric.Schrock@Sun.COM static int (*_topo_prop_get_string)(tnode_t *, const char *, const char *, 77*10817SEric.Schrock@Sun.COM char **, int *); 78*10817SEric.Schrock@Sun.COM static int (*_topo_node_fru)(tnode_t *, nvlist_t **, nvlist_t *, int *); 79*10817SEric.Schrock@Sun.COM static int (*_topo_fmri_nvl2str)(topo_hdl_t *, nvlist_t *, char **, int *); 80*10817SEric.Schrock@Sun.COM static int (*_topo_fmri_strcmp_noauth)(topo_hdl_t *, const char *, 81*10817SEric.Schrock@Sun.COM const char *); 82*10817SEric.Schrock@Sun.COM 83*10817SEric.Schrock@Sun.COM #define ZFS_FRU_HASH_SIZE 257 84*10817SEric.Schrock@Sun.COM 85*10817SEric.Schrock@Sun.COM static size_t 86*10817SEric.Schrock@Sun.COM fru_strhash(const char *key) 87*10817SEric.Schrock@Sun.COM { 88*10817SEric.Schrock@Sun.COM ulong_t g, h = 0; 89*10817SEric.Schrock@Sun.COM const char *p; 90*10817SEric.Schrock@Sun.COM 91*10817SEric.Schrock@Sun.COM for (p = key; *p != '\0'; p++) { 92*10817SEric.Schrock@Sun.COM h = (h << 4) + *p; 93*10817SEric.Schrock@Sun.COM 94*10817SEric.Schrock@Sun.COM if ((g = (h & 0xf0000000)) != 0) { 95*10817SEric.Schrock@Sun.COM h ^= (g >> 24); 96*10817SEric.Schrock@Sun.COM h ^= g; 97*10817SEric.Schrock@Sun.COM } 98*10817SEric.Schrock@Sun.COM } 99*10817SEric.Schrock@Sun.COM 100*10817SEric.Schrock@Sun.COM return (h % ZFS_FRU_HASH_SIZE); 101*10817SEric.Schrock@Sun.COM } 102*10817SEric.Schrock@Sun.COM 103*10817SEric.Schrock@Sun.COM static int 104*10817SEric.Schrock@Sun.COM libzfs_fru_gather(topo_hdl_t *thp, tnode_t *tn, void *arg) 105*10817SEric.Schrock@Sun.COM { 106*10817SEric.Schrock@Sun.COM libzfs_handle_t *hdl = arg; 107*10817SEric.Schrock@Sun.COM nvlist_t *fru; 108*10817SEric.Schrock@Sun.COM char *devpath, *frustr; 109*10817SEric.Schrock@Sun.COM int err; 110*10817SEric.Schrock@Sun.COM libzfs_fru_t *frup; 111*10817SEric.Schrock@Sun.COM size_t idx; 112*10817SEric.Schrock@Sun.COM 113*10817SEric.Schrock@Sun.COM /* 114*10817SEric.Schrock@Sun.COM * If this is the chassis node, and we don't yet have the system 115*10817SEric.Schrock@Sun.COM * chassis ID, then fill in this value now. 116*10817SEric.Schrock@Sun.COM */ 117*10817SEric.Schrock@Sun.COM if (hdl->libzfs_chassis_id[0] == '\0' && 118*10817SEric.Schrock@Sun.COM strcmp(_topo_node_name(tn), "chassis") == 0) { 119*10817SEric.Schrock@Sun.COM if (_topo_prop_get_string(tn, FM_FMRI_AUTHORITY, 120*10817SEric.Schrock@Sun.COM FM_FMRI_AUTH_CHASSIS, &devpath, &err) == 0) 121*10817SEric.Schrock@Sun.COM (void) strlcpy(hdl->libzfs_chassis_id, devpath, 122*10817SEric.Schrock@Sun.COM sizeof (hdl->libzfs_chassis_id)); 123*10817SEric.Schrock@Sun.COM } 124*10817SEric.Schrock@Sun.COM 125*10817SEric.Schrock@Sun.COM /* 126*10817SEric.Schrock@Sun.COM * Skip non-disk nodes. 127*10817SEric.Schrock@Sun.COM */ 128*10817SEric.Schrock@Sun.COM if (strcmp(_topo_node_name(tn), "disk") != 0) 129*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 130*10817SEric.Schrock@Sun.COM 131*10817SEric.Schrock@Sun.COM /* 132*10817SEric.Schrock@Sun.COM * Get the devfs path and FRU. 133*10817SEric.Schrock@Sun.COM */ 134*10817SEric.Schrock@Sun.COM if (_topo_prop_get_string(tn, "io", "devfs-path", &devpath, &err) != 0) 135*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 136*10817SEric.Schrock@Sun.COM 137*10817SEric.Schrock@Sun.COM if (libzfs_fru_lookup(hdl, devpath) != NULL) { 138*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, devpath); 139*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 140*10817SEric.Schrock@Sun.COM } 141*10817SEric.Schrock@Sun.COM 142*10817SEric.Schrock@Sun.COM if (_topo_node_fru(tn, &fru, NULL, &err) != 0) { 143*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, devpath); 144*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 145*10817SEric.Schrock@Sun.COM } 146*10817SEric.Schrock@Sun.COM 147*10817SEric.Schrock@Sun.COM /* 148*10817SEric.Schrock@Sun.COM * Convert the FRU into a string. 149*10817SEric.Schrock@Sun.COM */ 150*10817SEric.Schrock@Sun.COM if (_topo_fmri_nvl2str(thp, fru, &frustr, &err) != 0) { 151*10817SEric.Schrock@Sun.COM nvlist_free(fru); 152*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, devpath); 153*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 154*10817SEric.Schrock@Sun.COM } 155*10817SEric.Schrock@Sun.COM 156*10817SEric.Schrock@Sun.COM nvlist_free(fru); 157*10817SEric.Schrock@Sun.COM 158*10817SEric.Schrock@Sun.COM /* 159*10817SEric.Schrock@Sun.COM * Finally, we have a FRU string and device path. Add it to the hash. 160*10817SEric.Schrock@Sun.COM */ 161*10817SEric.Schrock@Sun.COM if ((frup = calloc(sizeof (libzfs_fru_t), 1)) == NULL) { 162*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, devpath); 163*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, frustr); 164*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 165*10817SEric.Schrock@Sun.COM } 166*10817SEric.Schrock@Sun.COM 167*10817SEric.Schrock@Sun.COM if ((frup->zf_device = strdup(devpath)) == NULL || 168*10817SEric.Schrock@Sun.COM (frup->zf_fru = strdup(frustr)) == NULL) { 169*10817SEric.Schrock@Sun.COM free(frup->zf_device); 170*10817SEric.Schrock@Sun.COM free(frup); 171*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, devpath); 172*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, frustr); 173*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 174*10817SEric.Schrock@Sun.COM } 175*10817SEric.Schrock@Sun.COM 176*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, devpath); 177*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, frustr); 178*10817SEric.Schrock@Sun.COM 179*10817SEric.Schrock@Sun.COM idx = fru_strhash(frup->zf_device); 180*10817SEric.Schrock@Sun.COM frup->zf_chain = hdl->libzfs_fru_hash[idx]; 181*10817SEric.Schrock@Sun.COM hdl->libzfs_fru_hash[idx] = frup; 182*10817SEric.Schrock@Sun.COM frup->zf_next = hdl->libzfs_fru_list; 183*10817SEric.Schrock@Sun.COM hdl->libzfs_fru_list = frup; 184*10817SEric.Schrock@Sun.COM 185*10817SEric.Schrock@Sun.COM return (TOPO_WALK_NEXT); 186*10817SEric.Schrock@Sun.COM } 187*10817SEric.Schrock@Sun.COM 188*10817SEric.Schrock@Sun.COM /* 189*10817SEric.Schrock@Sun.COM * Called during initialization to setup the dynamic libtopo connection. 190*10817SEric.Schrock@Sun.COM */ 191*10817SEric.Schrock@Sun.COM #pragma init(libzfs_init_fru) 192*10817SEric.Schrock@Sun.COM static void 193*10817SEric.Schrock@Sun.COM libzfs_init_fru(void) 194*10817SEric.Schrock@Sun.COM { 195*10817SEric.Schrock@Sun.COM char path[MAXPATHLEN]; 196*10817SEric.Schrock@Sun.COM char isa[257]; 197*10817SEric.Schrock@Sun.COM 198*10817SEric.Schrock@Sun.COM #if defined(_LP64) 199*10817SEric.Schrock@Sun.COM if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0) 200*10817SEric.Schrock@Sun.COM isa[0] = '\0'; 201*10817SEric.Schrock@Sun.COM #else 202*10817SEric.Schrock@Sun.COM isa[0] = '\0'; 203*10817SEric.Schrock@Sun.COM #endif 204*10817SEric.Schrock@Sun.COM (void) snprintf(path, sizeof (path), 205*10817SEric.Schrock@Sun.COM "/usr/lib/fm/%s/libtopo.so", isa); 206*10817SEric.Schrock@Sun.COM 207*10817SEric.Schrock@Sun.COM if ((_topo_dlhandle = dlopen(path, RTLD_LAZY)) == NULL) 208*10817SEric.Schrock@Sun.COM return; 209*10817SEric.Schrock@Sun.COM 210*10817SEric.Schrock@Sun.COM _topo_open = (topo_hdl_t *(*)()) 211*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_open"); 212*10817SEric.Schrock@Sun.COM _topo_close = (void (*)()) 213*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_close"); 214*10817SEric.Schrock@Sun.COM _topo_snap_hold = (char *(*)()) 215*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_snap_hold"); 216*10817SEric.Schrock@Sun.COM _topo_snap_release = (void (*)()) 217*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_snap_release"); 218*10817SEric.Schrock@Sun.COM _topo_walk_init = (topo_walk_t *(*)()) 219*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_walk_init"); 220*10817SEric.Schrock@Sun.COM _topo_walk_step = (int (*)()) 221*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_walk_step"); 222*10817SEric.Schrock@Sun.COM _topo_walk_fini = (void (*)()) 223*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_walk_fini"); 224*10817SEric.Schrock@Sun.COM _topo_hdl_strfree = (void (*)()) 225*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_hdl_strfree"); 226*10817SEric.Schrock@Sun.COM _topo_node_name = (char *(*)()) 227*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_node_name"); 228*10817SEric.Schrock@Sun.COM _topo_prop_get_string = (int (*)()) 229*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_prop_get_string"); 230*10817SEric.Schrock@Sun.COM _topo_node_fru = (int (*)()) 231*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_node_fru"); 232*10817SEric.Schrock@Sun.COM _topo_fmri_nvl2str = (int (*)()) 233*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_fmri_nvl2str"); 234*10817SEric.Schrock@Sun.COM _topo_fmri_strcmp_noauth = (int (*)()) 235*10817SEric.Schrock@Sun.COM dlsym(_topo_dlhandle, "topo_fmri_strcmp_noauth"); 236*10817SEric.Schrock@Sun.COM 237*10817SEric.Schrock@Sun.COM if (_topo_open == NULL || _topo_close == NULL || 238*10817SEric.Schrock@Sun.COM _topo_snap_hold == NULL || _topo_snap_release == NULL || 239*10817SEric.Schrock@Sun.COM _topo_walk_init == NULL || _topo_walk_step == NULL || 240*10817SEric.Schrock@Sun.COM _topo_walk_fini == NULL || _topo_hdl_strfree == NULL || 241*10817SEric.Schrock@Sun.COM _topo_node_name == NULL || _topo_prop_get_string == NULL || 242*10817SEric.Schrock@Sun.COM _topo_node_fru == NULL || _topo_fmri_nvl2str == NULL || 243*10817SEric.Schrock@Sun.COM _topo_fmri_strcmp_noauth == NULL) { 244*10817SEric.Schrock@Sun.COM (void) dlclose(_topo_dlhandle); 245*10817SEric.Schrock@Sun.COM _topo_dlhandle = NULL; 246*10817SEric.Schrock@Sun.COM } 247*10817SEric.Schrock@Sun.COM } 248*10817SEric.Schrock@Sun.COM 249*10817SEric.Schrock@Sun.COM /* 250*10817SEric.Schrock@Sun.COM * Refresh the mappings from device path -> FMRI. We do this by walking the 251*10817SEric.Schrock@Sun.COM * hc topology looking for disk nodes, and recording the io/devfs-path and FRU. 252*10817SEric.Schrock@Sun.COM * Note that we strip out the disk-specific authority information (serial, 253*10817SEric.Schrock@Sun.COM * part, revision, etc) so that we are left with only the identifying 254*10817SEric.Schrock@Sun.COM * characteristics of the slot (hc path and chassis-id). 255*10817SEric.Schrock@Sun.COM */ 256*10817SEric.Schrock@Sun.COM void 257*10817SEric.Schrock@Sun.COM libzfs_fru_refresh(libzfs_handle_t *hdl) 258*10817SEric.Schrock@Sun.COM { 259*10817SEric.Schrock@Sun.COM int err; 260*10817SEric.Schrock@Sun.COM char *uuid; 261*10817SEric.Schrock@Sun.COM topo_hdl_t *thp; 262*10817SEric.Schrock@Sun.COM topo_walk_t *twp; 263*10817SEric.Schrock@Sun.COM 264*10817SEric.Schrock@Sun.COM if (_topo_dlhandle == NULL) 265*10817SEric.Schrock@Sun.COM return; 266*10817SEric.Schrock@Sun.COM 267*10817SEric.Schrock@Sun.COM /* 268*10817SEric.Schrock@Sun.COM * Clear the FRU hash and initialize our basic structures. 269*10817SEric.Schrock@Sun.COM */ 270*10817SEric.Schrock@Sun.COM libzfs_fru_clear(hdl, B_FALSE); 271*10817SEric.Schrock@Sun.COM 272*10817SEric.Schrock@Sun.COM if ((hdl->libzfs_topo_hdl = _topo_open(TOPO_VERSION, 273*10817SEric.Schrock@Sun.COM NULL, &err)) == NULL) 274*10817SEric.Schrock@Sun.COM return; 275*10817SEric.Schrock@Sun.COM 276*10817SEric.Schrock@Sun.COM thp = hdl->libzfs_topo_hdl; 277*10817SEric.Schrock@Sun.COM 278*10817SEric.Schrock@Sun.COM if ((uuid = _topo_snap_hold(thp, NULL, &err)) == NULL) 279*10817SEric.Schrock@Sun.COM return; 280*10817SEric.Schrock@Sun.COM 281*10817SEric.Schrock@Sun.COM _topo_hdl_strfree(thp, uuid); 282*10817SEric.Schrock@Sun.COM 283*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL && 284*10817SEric.Schrock@Sun.COM (hdl->libzfs_fru_hash = 285*10817SEric.Schrock@Sun.COM calloc(ZFS_FRU_HASH_SIZE * sizeof (void *), 1)) == NULL) 286*10817SEric.Schrock@Sun.COM return; 287*10817SEric.Schrock@Sun.COM 288*10817SEric.Schrock@Sun.COM /* 289*10817SEric.Schrock@Sun.COM * We now have a topo snapshot, so iterate over the hc topology looking 290*10817SEric.Schrock@Sun.COM * for disks to add to the hash. 291*10817SEric.Schrock@Sun.COM */ 292*10817SEric.Schrock@Sun.COM twp = _topo_walk_init(thp, FM_FMRI_SCHEME_HC, 293*10817SEric.Schrock@Sun.COM libzfs_fru_gather, hdl, &err); 294*10817SEric.Schrock@Sun.COM if (twp != NULL) { 295*10817SEric.Schrock@Sun.COM (void) _topo_walk_step(twp, TOPO_WALK_CHILD); 296*10817SEric.Schrock@Sun.COM _topo_walk_fini(twp); 297*10817SEric.Schrock@Sun.COM } 298*10817SEric.Schrock@Sun.COM } 299*10817SEric.Schrock@Sun.COM 300*10817SEric.Schrock@Sun.COM /* 301*10817SEric.Schrock@Sun.COM * Given a devfs path, return the FRU for the device, if known. This will 302*10817SEric.Schrock@Sun.COM * automatically call libzfs_fru_refresh() if it hasn't already been called by 303*10817SEric.Schrock@Sun.COM * the consumer. The string returned is valid until the next call to 304*10817SEric.Schrock@Sun.COM * libzfs_fru_refresh(). 305*10817SEric.Schrock@Sun.COM */ 306*10817SEric.Schrock@Sun.COM const char * 307*10817SEric.Schrock@Sun.COM libzfs_fru_lookup(libzfs_handle_t *hdl, const char *devpath) 308*10817SEric.Schrock@Sun.COM { 309*10817SEric.Schrock@Sun.COM size_t idx = fru_strhash(devpath); 310*10817SEric.Schrock@Sun.COM libzfs_fru_t *frup; 311*10817SEric.Schrock@Sun.COM 312*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL) 313*10817SEric.Schrock@Sun.COM libzfs_fru_refresh(hdl); 314*10817SEric.Schrock@Sun.COM 315*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL) 316*10817SEric.Schrock@Sun.COM return (NULL); 317*10817SEric.Schrock@Sun.COM 318*10817SEric.Schrock@Sun.COM for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL; 319*10817SEric.Schrock@Sun.COM frup = frup->zf_chain) { 320*10817SEric.Schrock@Sun.COM if (strcmp(devpath, frup->zf_device) == 0) 321*10817SEric.Schrock@Sun.COM return (frup->zf_fru); 322*10817SEric.Schrock@Sun.COM } 323*10817SEric.Schrock@Sun.COM 324*10817SEric.Schrock@Sun.COM return (NULL); 325*10817SEric.Schrock@Sun.COM } 326*10817SEric.Schrock@Sun.COM 327*10817SEric.Schrock@Sun.COM /* 328*10817SEric.Schrock@Sun.COM * Given a fru path, return the device path. This will automatically call 329*10817SEric.Schrock@Sun.COM * libzfs_fru_refresh() if it hasn't already been called by the consumer. The 330*10817SEric.Schrock@Sun.COM * string returned is valid until the next call to libzfs_fru_refresh(). 331*10817SEric.Schrock@Sun.COM */ 332*10817SEric.Schrock@Sun.COM const char * 333*10817SEric.Schrock@Sun.COM libzfs_fru_devpath(libzfs_handle_t *hdl, const char *fru) 334*10817SEric.Schrock@Sun.COM { 335*10817SEric.Schrock@Sun.COM libzfs_fru_t *frup; 336*10817SEric.Schrock@Sun.COM size_t idx; 337*10817SEric.Schrock@Sun.COM 338*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL) 339*10817SEric.Schrock@Sun.COM libzfs_fru_refresh(hdl); 340*10817SEric.Schrock@Sun.COM 341*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL) 342*10817SEric.Schrock@Sun.COM return (NULL); 343*10817SEric.Schrock@Sun.COM 344*10817SEric.Schrock@Sun.COM for (idx = 0; idx < ZFS_FRU_HASH_SIZE; idx++) { 345*10817SEric.Schrock@Sun.COM for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL; 346*10817SEric.Schrock@Sun.COM frup = frup->zf_next) { 347*10817SEric.Schrock@Sun.COM if (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl, 348*10817SEric.Schrock@Sun.COM fru, frup->zf_fru)) 349*10817SEric.Schrock@Sun.COM return (frup->zf_device); 350*10817SEric.Schrock@Sun.COM } 351*10817SEric.Schrock@Sun.COM } 352*10817SEric.Schrock@Sun.COM 353*10817SEric.Schrock@Sun.COM return (NULL); 354*10817SEric.Schrock@Sun.COM } 355*10817SEric.Schrock@Sun.COM 356*10817SEric.Schrock@Sun.COM /* 357*10817SEric.Schrock@Sun.COM * Change the stored FRU for the given vdev. 358*10817SEric.Schrock@Sun.COM */ 359*10817SEric.Schrock@Sun.COM int 360*10817SEric.Schrock@Sun.COM zpool_fru_set(zpool_handle_t *zhp, uint64_t vdev_guid, const char *fru) 361*10817SEric.Schrock@Sun.COM { 362*10817SEric.Schrock@Sun.COM zfs_cmd_t zc = { 0 }; 363*10817SEric.Schrock@Sun.COM 364*10817SEric.Schrock@Sun.COM (void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); 365*10817SEric.Schrock@Sun.COM (void) strncpy(zc.zc_value, fru, sizeof (zc.zc_value)); 366*10817SEric.Schrock@Sun.COM zc.zc_guid = vdev_guid; 367*10817SEric.Schrock@Sun.COM 368*10817SEric.Schrock@Sun.COM if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SETFRU, &zc) != 0) 369*10817SEric.Schrock@Sun.COM return (zpool_standard_error_fmt(zhp->zpool_hdl, errno, 370*10817SEric.Schrock@Sun.COM dgettext(TEXT_DOMAIN, "cannot set FRU"))); 371*10817SEric.Schrock@Sun.COM 372*10817SEric.Schrock@Sun.COM return (0); 373*10817SEric.Schrock@Sun.COM } 374*10817SEric.Schrock@Sun.COM 375*10817SEric.Schrock@Sun.COM /* 376*10817SEric.Schrock@Sun.COM * Compare to two FRUs, ignoring any authority information. 377*10817SEric.Schrock@Sun.COM */ 378*10817SEric.Schrock@Sun.COM boolean_t 379*10817SEric.Schrock@Sun.COM libzfs_fru_compare(libzfs_handle_t *hdl, const char *a, const char *b) 380*10817SEric.Schrock@Sun.COM { 381*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL) 382*10817SEric.Schrock@Sun.COM libzfs_fru_refresh(hdl); 383*10817SEric.Schrock@Sun.COM 384*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL) 385*10817SEric.Schrock@Sun.COM return (strcmp(a, b) == 0); 386*10817SEric.Schrock@Sun.COM 387*10817SEric.Schrock@Sun.COM return (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl, a, b)); 388*10817SEric.Schrock@Sun.COM } 389*10817SEric.Schrock@Sun.COM 390*10817SEric.Schrock@Sun.COM /* 391*10817SEric.Schrock@Sun.COM * This special function checks to see whether the FRU indicates it's supposed 392*10817SEric.Schrock@Sun.COM * to be in the system chassis, but the chassis-id doesn't match. This can 393*10817SEric.Schrock@Sun.COM * happen in a clustered case, where both head nodes have the same logical 394*10817SEric.Schrock@Sun.COM * disk, but opening the device on the other head node is meaningless. 395*10817SEric.Schrock@Sun.COM */ 396*10817SEric.Schrock@Sun.COM boolean_t 397*10817SEric.Schrock@Sun.COM libzfs_fru_notself(libzfs_handle_t *hdl, const char *fru) 398*10817SEric.Schrock@Sun.COM { 399*10817SEric.Schrock@Sun.COM const char *chassisid; 400*10817SEric.Schrock@Sun.COM size_t len; 401*10817SEric.Schrock@Sun.COM 402*10817SEric.Schrock@Sun.COM if (hdl->libzfs_fru_hash == NULL) 403*10817SEric.Schrock@Sun.COM libzfs_fru_refresh(hdl); 404*10817SEric.Schrock@Sun.COM 405*10817SEric.Schrock@Sun.COM if (hdl->libzfs_chassis_id[0] == '\0') 406*10817SEric.Schrock@Sun.COM return (B_FALSE); 407*10817SEric.Schrock@Sun.COM 408*10817SEric.Schrock@Sun.COM if (strstr(fru, "/chassis=0/") == NULL) 409*10817SEric.Schrock@Sun.COM return (B_FALSE); 410*10817SEric.Schrock@Sun.COM 411*10817SEric.Schrock@Sun.COM if ((chassisid = strstr(fru, ":chassis-id=")) == NULL) 412*10817SEric.Schrock@Sun.COM return (B_FALSE); 413*10817SEric.Schrock@Sun.COM 414*10817SEric.Schrock@Sun.COM chassisid += 12; 415*10817SEric.Schrock@Sun.COM len = strlen(hdl->libzfs_chassis_id); 416*10817SEric.Schrock@Sun.COM if (strncmp(chassisid, hdl->libzfs_chassis_id, len) == 0 && 417*10817SEric.Schrock@Sun.COM (chassisid[len] == '/' || chassisid[len] == ':')) 418*10817SEric.Schrock@Sun.COM return (B_FALSE); 419*10817SEric.Schrock@Sun.COM 420*10817SEric.Schrock@Sun.COM return (B_TRUE); 421*10817SEric.Schrock@Sun.COM } 422*10817SEric.Schrock@Sun.COM 423*10817SEric.Schrock@Sun.COM /* 424*10817SEric.Schrock@Sun.COM * Clear memory associated with the FRU hash. 425*10817SEric.Schrock@Sun.COM */ 426*10817SEric.Schrock@Sun.COM void 427*10817SEric.Schrock@Sun.COM libzfs_fru_clear(libzfs_handle_t *hdl, boolean_t final) 428*10817SEric.Schrock@Sun.COM { 429*10817SEric.Schrock@Sun.COM libzfs_fru_t *frup; 430*10817SEric.Schrock@Sun.COM 431*10817SEric.Schrock@Sun.COM while ((frup = hdl->libzfs_fru_list) != NULL) { 432*10817SEric.Schrock@Sun.COM hdl->libzfs_fru_list = frup->zf_next; 433*10817SEric.Schrock@Sun.COM free(frup->zf_device); 434*10817SEric.Schrock@Sun.COM free(frup->zf_fru); 435*10817SEric.Schrock@Sun.COM free(frup); 436*10817SEric.Schrock@Sun.COM } 437*10817SEric.Schrock@Sun.COM 438*10817SEric.Schrock@Sun.COM hdl->libzfs_fru_list = NULL; 439*10817SEric.Schrock@Sun.COM 440*10817SEric.Schrock@Sun.COM if (hdl->libzfs_topo_hdl != NULL) { 441*10817SEric.Schrock@Sun.COM _topo_snap_release(hdl->libzfs_topo_hdl); 442*10817SEric.Schrock@Sun.COM _topo_close(hdl->libzfs_topo_hdl); 443*10817SEric.Schrock@Sun.COM hdl->libzfs_topo_hdl = NULL; 444*10817SEric.Schrock@Sun.COM } 445*10817SEric.Schrock@Sun.COM 446*10817SEric.Schrock@Sun.COM if (final) { 447*10817SEric.Schrock@Sun.COM free(hdl->libzfs_fru_hash); 448*10817SEric.Schrock@Sun.COM } else if (hdl->libzfs_fru_hash != NULL) { 449*10817SEric.Schrock@Sun.COM bzero(hdl->libzfs_fru_hash, 450*10817SEric.Schrock@Sun.COM ZFS_FRU_HASH_SIZE * sizeof (void *)); 451*10817SEric.Schrock@Sun.COM } 452*10817SEric.Schrock@Sun.COM } 453