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