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
48static boolean_t MPxIO_ENABLED = B_FALSE;
49
50static const topo_pgroup_info_t io_pgroup = {
51	TOPO_PGROUP_IO,
52	TOPO_STABILITY_PRIVATE,
53	TOPO_STABILITY_PRIVATE,
54	1
55};
56
57static 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 */
70static char *
71pi_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 */
115static int
116pi_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 */
205static void
206pi_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	}
256out:
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 */
274static int
275pi_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 */
353int
354pi_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