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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Create a topology node for a PRI node of type 'bay'. Call the disk 28 * enumerator to enumerate any disks that may be attached. 29 */ 30 31 #include <sys/types.h> 32 #include <strings.h> 33 #include <sys/fm/protocol.h> 34 #include <fm/topo_mod.h> 35 #include <fm/topo_hc.h> 36 #include <libdevinfo.h> 37 #include <sys/pci.h> 38 #include <sys/mdesc.h> 39 #include "pi_impl.h" 40 41 #define _ENUM_NAME "enum_bay" 42 #define HBA_DRV_NAME "mpt_sas" 43 #define DEVICES "/devices" 44 45 #define PI_BAY_AP DDI_NT_SCSI_ATTACHMENT_POINT 46 #define PI_MAX_LUN 255 47 48 static boolean_t MPxIO_ENABLED = B_FALSE; 49 50 static const topo_pgroup_info_t io_pgroup = { 51 TOPO_PGROUP_IO, 52 TOPO_STABILITY_PRIVATE, 53 TOPO_STABILITY_PRIVATE, 54 1 55 }; 56 57 static const topo_pgroup_info_t binding_pgroup = { 58 TOPO_PGROUP_BINDING, 59 TOPO_STABILITY_PRIVATE, 60 TOPO_STABILITY_PRIVATE, 61 1 62 }; 63 64 65 /* 66 * Return the MPxIO occupant path bay property. 67 * 68 * The string must be freed with topo_mod_strfree(). 69 */ 70 static char * 71 pi_bay_ocpath(topo_mod_t *mod, di_node_t dnode) 72 { 73 74 int lun; 75 boolean_t got_w; 76 char buf[MAXPATHLEN]; 77 char *tgt_port = NULL; 78 79 /* 'target-port' property */ 80 tgt_port = pi_get_target_port(mod, dnode); 81 if (tgt_port == NULL) { 82 topo_mod_dprintf(mod, "pi_bay_ocpath: failed to get " 83 "'target-port' property\n"); 84 return (NULL); 85 } 86 87 /* 'lun' property */ 88 lun = pi_get_lun(mod, dnode); 89 if (lun < 0 || lun > PI_MAX_LUN) { 90 topo_mod_dprintf(mod, "pi_bay_ocpath: failed to get 'lun' " 91 "property\n"); 92 topo_mod_strfree(mod, tgt_port); 93 return (NULL); 94 } 95 96 /* 'target-port' leading 'w' is not consistent */ 97 got_w = tgt_port[0] == 'w' ? B_TRUE : B_FALSE; 98 99 /* 100 * Build occupatnt path: 101 * 'devfs_path' + "/disk@w" + 'target-port' + "," + 'lun' 102 */ 103 (void) snprintf(buf, MAXPATHLEN, "%s%s%s,%x", di_devfs_path(dnode), 104 (got_w ? "/disk@" : "/disk@w"), tgt_port, lun); 105 106 topo_mod_strfree(mod, tgt_port); 107 return (topo_mod_strdup(mod, buf)); 108 } 109 110 111 /* 112 * Create bay "io" pgroup, create and add "ap_path" property. 113 * Create bay "binding" pgroup, create and add "oc_path" property. 114 */ 115 static int 116 pi_bay_pgroups(topo_mod_t *mod, tnode_t *t_node, di_node_t cnode, 117 di_minor_t cminor) 118 { 119 int rv; 120 int err; 121 char *ap_path; 122 char *oc_path; 123 124 /* Create "io" pgroup and attachment point. */ 125 rv = topo_pgroup_create(t_node, &io_pgroup, &err); 126 if (rv != 0) { 127 topo_mod_dprintf(mod, "pi_bay_pgroups: failed to create " 128 "\"io\" pgroup: %s\n", topo_mod_seterrno(mod, err)); 129 return (err); 130 } 131 132 /* 133 * Create the ap_path property: 134 */ 135 ap_path = topo_mod_alloc(mod, MAXPATHLEN); 136 if (ap_path == NULL) { 137 topo_mod_dprintf(mod, "pi_bay_pgroups: EMOD_NOMEM for " 138 "ap_path\n"); 139 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 140 } 141 142 /* attachment point path: "/devices" + minor node path */ 143 (void) snprintf(ap_path, MAXPATHLEN, "%s%s", DEVICES, 144 di_devfs_minor_path(cminor)); 145 topo_mod_dprintf(mod, "pi_bay_pgroups: ap_path (%s)\n", ap_path); 146 147 /* add ap_path prop to io pgroup */ 148 rv = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_AP_PATH, 149 TOPO_PROP_IMMUTABLE, ap_path, &err); 150 if (rv != 0) { 151 topo_mod_dprintf(mod, "pi_bay_pgroups: failed to set " 152 "ap-path: %s\n", topo_strerror(err)); 153 topo_mod_free(mod, ap_path, MAXPATHLEN); 154 (void) topo_mod_seterrno(mod, err); 155 return (err); 156 } 157 topo_mod_free(mod, ap_path, MAXPATHLEN); 158 159 /* Create "binding" pgroup */ 160 rv = topo_pgroup_create(t_node, &binding_pgroup, &err); 161 if (rv != 0) { 162 topo_mod_dprintf(mod, "pi_bay_pgroups: failed to " 163 "create \"binding\" pgroup: %s\n", topo_strerror(err)); 164 (void) topo_mod_seterrno(mod, err); 165 return (err); 166 } 167 168 /* 169 * Create the oc_path property: 170 */ 171 if (MPxIO_ENABLED) { 172 oc_path = pi_bay_ocpath(mod, cnode); 173 } else { 174 oc_path = di_devfs_path(cnode); 175 } 176 if (oc_path == NULL) { 177 topo_mod_dprintf(mod, "pi_bay_pgroups: no occupant path\n"); 178 return (-1); 179 } 180 topo_mod_dprintf(mod, "pi_bay_proups: oc_path (%s)\n", oc_path); 181 182 /* add oc_path to binding pgroup */ 183 rv = topo_prop_set_string(t_node, TOPO_PGROUP_BINDING, 184 TOPO_BINDING_OCCUPANT, TOPO_PROP_IMMUTABLE, oc_path, &err); 185 if (rv != 0) { 186 topo_mod_dprintf(mod, "pi_bay_pgroups: failed to set " 187 "oc_path: %s\n", topo_strerror(err)); 188 (void) topo_mod_seterrno(mod, err); 189 rv = err; 190 } 191 192 if (MPxIO_ENABLED) { 193 topo_mod_strfree(mod, oc_path); 194 } else { 195 di_devfs_path_free(oc_path); 196 } 197 return (rv); 198 } 199 200 201 /* 202 * Find the child devinfo node of the HBA that matches the PHY, capture the 203 * minor attachment point node. 204 */ 205 static void 206 pi_bay_find_nodes(topo_mod_t *mod, di_node_t *nodep, di_node_t *sibp, 207 di_minor_t *minorp, int phy) 208 { 209 di_node_t sib = DI_NODE_NIL; 210 di_node_t gsib = DI_NODE_NIL; 211 di_minor_t minor = DI_MINOR_NIL; 212 213 /* 214 * When MPxIO is enabled the child node of the HBA (iport) contains 215 * the pathinfo property we're looking for; when MPxIO is disabled 216 * the grand-child of the HBA (disk) contains the devinfo property 217 * we're looking for. 218 */ 219 sib = di_child_node(*nodep); 220 while (sib != DI_NODE_NIL) { 221 /* match the PHY */ 222 if (phy == pi_get_phynum(mod, sib)) { 223 while ((minor = di_minor_next(sib, minor)) != 224 DI_MINOR_NIL) { 225 /* scsi attachment point */ 226 if (strncmp(di_minor_nodetype(minor), 227 PI_BAY_AP, 228 strlen(di_minor_nodetype(minor))) == 0) { 229 goto out; 230 } 231 } 232 } else { 233 /* look in grandchildren */ 234 gsib = di_child_node(sib); 235 while (gsib != DI_NODE_NIL) { 236 /* match the PHY */ 237 if (phy == pi_get_phynum(mod, gsib)) { 238 while ((minor = di_minor_next(sib, 239 minor)) != DI_MINOR_NIL) { 240 /* scsi attachment point */ 241 if (strncmp( 242 di_minor_nodetype(minor), 243 PI_BAY_AP, 244 strlen(di_minor_nodetype( 245 minor))) == 0) { 246 sib = gsib; 247 goto out; 248 } 249 } 250 } 251 gsib = di_sibling_node(gsib); 252 } 253 } 254 sib = di_sibling_node(sib); 255 } 256 out: 257 if (sib == DI_NODE_NIL) { 258 *sibp = DI_NODE_NIL; 259 } else { 260 bcopy(&sib, sibp, sizeof (di_node_t)); 261 } 262 263 if (minor == DI_MINOR_NIL) { 264 *minorp = DI_MINOR_NIL; 265 } else { 266 bcopy(&minor, minorp, sizeof (di_minor_t)); 267 } 268 } 269 270 271 /* 272 * Decoreate "bay" node with required properties for disk enumerator. 273 */ 274 static int 275 pi_bay_update_node(topo_mod_t *mod, tnode_t *t_node, uint8_t phy, 276 char *pri_path) 277 { 278 int rv; 279 char *hba_path; 280 char *mpxio_prop; 281 di_node_t devtree; 282 di_node_t dnode, sib; 283 di_minor_t minor = DI_MINOR_NIL; 284 285 /* 286 * The hba path and bay PHY come from the PRI; find the 287 * driver node that coresponds to the PHY and it's minor 288 * node name and create the occupant path/attachmeent_point 289 * path 290 */ 291 devtree = di_init("/", DINFOFORCE | DINFOSUBTREE | DINFOMINOR | 292 DINFOPROP | DINFOPATH); 293 294 for (dnode = di_drv_first_node(HBA_DRV_NAME, devtree); 295 dnode != DI_NODE_NIL; 296 dnode = di_drv_next_node(dnode)) { 297 /* find the dnode path that matches the pri path */ 298 hba_path = pi_get_dipath(mod, dnode); 299 if (strcmp(pri_path, hba_path) == 0) { 300 /* found our dnode */ 301 topo_mod_strfree(mod, hba_path); 302 break; 303 } 304 topo_mod_strfree(mod, hba_path); 305 } 306 if (dnode == DI_NODE_NIL) { 307 topo_mod_dprintf(mod, "pi_bay_update_node: failed to find " 308 "devinfo path.\n"); 309 return (-1); 310 } 311 312 /* 313 * The "mpxio-disable" variable determines if MPxIO (multipathing) 314 * is disabled (or enabled). 315 */ 316 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dnode, "mpxio-disable", 317 &mpxio_prop) < 0) { 318 /* no way to determine if MPxIO is enabled */ 319 topo_mod_dprintf(mod, 320 "pi_bay_update_node: no \"mpxio-disable\" property\n"); 321 return (-1); 322 } 323 324 /* set MPxIO_ENABLED inverse to "mpxio-disable" */ 325 topo_mod_dprintf(mod, "\"mpxio-disable\" = (%s)\n", mpxio_prop); 326 MPxIO_ENABLED = strncmp("no", mpxio_prop, strlen(mpxio_prop)) == 0 ? 327 B_TRUE : B_FALSE; 328 topo_mod_dprintf(mod, "MPxIO_ENABLED: %s\n", MPxIO_ENABLED ? "TRUE" : 329 "FALSE"); 330 331 /* 332 * Find the child node matching the PRI phy_number and determine the 333 * minor attachment point. 334 */ 335 pi_bay_find_nodes(mod, &dnode, &sib, &minor, phy); 336 if (sib == DI_NODE_NIL || minor == DI_MINOR_NIL) { 337 topo_mod_dprintf(mod, "pi_bay_update_node: no disk on " 338 "PHY %d.\n", phy); 339 return (-1); 340 } 341 342 /* add pgroups */ 343 rv = pi_bay_pgroups(mod, t_node, sib, minor); 344 if (rv != 0) { 345 topo_mod_dprintf(mod, "pi_bay_update_node: failed to add " 346 "pgroups.\n", _ENUM_NAME); 347 return (rv); 348 } 349 return (0); 350 } 351 352 /* ARGSUSED */ 353 int 354 pi_enum_bay(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, 355 topo_instance_t inst, tnode_t *t_parent, const char *hc_name, 356 tnode_t **t_node) 357 { 358 int i, rv; 359 int min = 0, max = 0; 360 int num_arcs; 361 int nphy; 362 size_t arcsize; 363 uint8_t *phy = NULL; 364 char *hba_pri_path; 365 mde_cookie_t *arcp; 366 367 /* count how many PHYs the bay node has */ 368 nphy = pi_get_priphy(mod, mdp, mde_node, phy); 369 if (nphy <= 0) { 370 topo_mod_dprintf(mod, "%s: node_0x%llx has no PHY\n", 371 _ENUM_NAME, (uint64_t)mde_node); 372 return (-1); 373 } 374 375 phy = topo_mod_alloc(mod, (nphy * sizeof (uint8_t))); 376 if (phy == NULL) { 377 topo_mod_dprintf(mod, "%s: node_0x%llx ENOMEM\n", 378 _ENUM_NAME, (uint64_t)mde_node); 379 return (-1); 380 } 381 382 /* get the PHY(s) for this bay node */ 383 rv = pi_get_priphy(mod, mdp, mde_node, phy); 384 if (rv != nphy) { 385 topo_mod_dprintf(mod, "%s: node_0x%llx failed to get PHY\n", 386 _ENUM_NAME, (uint64_t)mde_node); 387 return (-1); 388 } 389 topo_mod_dprintf(mod, "%s: node_0x%llx PHY: %d\n", _ENUM_NAME, 390 mde_node, *phy); 391 392 /* determine how many parent (HBA) nodes */ 393 num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_BACK, NULL, 0); 394 if (num_arcs == 0) { 395 topo_mod_dprintf(mod, "%s: node_0x%llx has no \"back\" arcs\n", 396 _ENUM_NAME, (uint64_t)mde_node); 397 return (-1); /* return partial here? */ 398 } 399 topo_mod_dprintf(mod, "%s: node_0x%llx has %d \"back\" arcs\n", 400 _ENUM_NAME, mde_node, num_arcs); 401 402 /* get the "back" nodes */ 403 arcsize = sizeof (mde_cookie_t) * num_arcs; 404 arcp = topo_mod_zalloc(mod, arcsize); 405 if (arcp == NULL) { 406 topo_mod_dprintf(mod, "%s: no memory\n", _ENUM_NAME); 407 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 408 return (-1); 409 } 410 num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_BACK, arcp, arcsize); 411 412 /* make sure there are as many HBA nodes as PHYs */ 413 if (num_arcs != nphy) { 414 topo_mod_dprintf(mod, "%s: %d PHYs for %d back arcs.\n", 415 _ENUM_NAME, nphy, num_arcs); 416 return (-1); 417 } 418 419 /* create topo bay node for each HBA attached to this bay */ 420 for (i = 0; i < num_arcs; i++) { 421 /* skip if topo-hc-skip = 1 */ 422 if (pi_skip_node(mod, mdp, arcp[i])) { 423 topo_mod_dprintf(mod, "%s: skipping node_0x%llx\n", 424 (uint64_t)arcp[i]); 425 continue; 426 } 427 428 /* 429 * Create a generic "bay" node; decorate below. 430 * 431 * If we have more than one HBA the bay inst here will be 432 * the same for both. This is okay since the paths will 433 * be different for each HBA. 434 */ 435 rv = pi_enum_generic_impl(mod, mdp, mde_node, inst, t_parent, 436 t_parent, hc_name, _ENUM_NAME, t_node, 0); 437 if (rv != 0 || *t_node == NULL) { 438 topo_mod_dprintf(mod, 439 "%s: node_0x%llx failed to create topo node: %s\n", 440 _ENUM_NAME, (uint64_t)mde_node, 441 topo_strerror(topo_mod_errno(mod))); 442 return (rv); 443 } 444 445 /* must be an ses expander if no path property - skip */ 446 rv = md_get_prop_str(mdp, arcp[i], MD_STR_PATH, &hba_pri_path); 447 if (rv != 0 || hba_pri_path == NULL || 448 strlen(hba_pri_path) == 0) { 449 topo_mod_dprintf(mod, "%s: node_0x%llx: no path " 450 "property\n", _ENUM_NAME, (uint64_t)arcp[i]); 451 continue; 452 } 453 454 /* Decorate the bay tnode */ 455 rv = pi_bay_update_node(mod, *t_node, phy[i], hba_pri_path); 456 if (rv != 0) { 457 topo_mod_dprintf(mod, "%s: failed to update " 458 "node_0x%llx.\n", _ENUM_NAME, (uint64_t)mde_node); 459 continue; 460 } 461 462 463 /* 464 * Call the disk enum passing in decorated bay tnode. 465 */ 466 if (topo_mod_load(mod, DISK, TOPO_VERSION) == NULL) { 467 topo_mod_dprintf(mod, 468 "%s: Failed to load %s module: %s\n", 469 _ENUM_NAME, DISK, 470 topo_strerror(topo_mod_errno(mod))); 471 return (topo_mod_errno(mod)); 472 } 473 474 rv = topo_node_range_create(mod, *t_node, DISK, min, max); 475 if (rv != 0) { 476 topo_mod_dprintf(mod, 477 "%s: failed to create range: %s\n", _ENUM_NAME, 478 topo_strerror(topo_mod_errno(mod))); 479 return (topo_mod_errno(mod)); 480 } 481 482 rv = topo_mod_enumerate(mod, *t_node, DISK, DISK, min, max, 483 NULL); 484 if (rv != 0) { 485 topo_mod_dprintf(mod, 486 "%s: %s enumeration failed: %s\n", _ENUM_NAME, 487 DISK, topo_strerror(topo_mod_errno(mod))); 488 return (topo_mod_errno(mod)); 489 } 490 } 491 492 /* clean up */ 493 topo_mod_free(mod, arcp, arcsize); 494 topo_mod_free(mod, phy, (nphy * sizeof (uint8_t))); 495 return (0); 496 } 497