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 *
pi_bay_ocpath(topo_mod_t * mod,di_node_t dnode)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
pi_bay_pgroups(topo_mod_t * mod,tnode_t * t_node,di_node_t cnode,di_minor_t cminor)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
pi_bay_find_nodes(topo_mod_t * mod,di_node_t * nodep,di_node_t * sibp,di_minor_t * minorp,int phy)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
pi_bay_update_node(topo_mod_t * mod,tnode_t * t_node,uint8_t phy,char * pri_path)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
pi_enum_bay(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,topo_instance_t inst,tnode_t * t_parent,const char * hc_name,tnode_t ** t_node)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