xref: /illumos-gate/usr/src/uts/common/io/ib/ibtl/ibtl_ibnex.c (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/systm.h>
30 #include <sys/ib/ibtl/impl/ibtl.h>
31 #include <sys/ib/ibtl/impl/ibtl_ibnex.h>
32 
33 /*
34  * ibtl_ibnex.c
35  *    These routines tie the Device Manager into IBTL.
36  *
37  *    ibt_reprobe_dev which can be called by IBTF clients.
38  *    This results in calls to IBnexus callback.
39  */
40 
41 /*
42  * Globals.
43  */
44 static char		ibtl_ibnex[] = "ibtl_ibnex";
45 ibtl_ibnex_callback_t	ibtl_ibnex_callback_routine = NULL;
46 
47 /*
48  * Function:
49  *	ibtl_ibnex_get_hca_info
50  * Input:
51  *	hca_guid	- The HCA's node GUID.
52  *	flag		- Tells what to do
53  *			IBTL_IBNEX_LIST_CLNTS_FLAG - Build a NVLIST containing
54  *						client's names, their AP_IDs and
55  *						alternate_HCA information.
56  *						(-x list_clients option)
57  *			IBTL_IBNEX_UNCFG_CLNTS_FLAG - Build a NVLIST containing
58  *						clients' devpaths and their
59  *						AP_IDs. (-x unconfig_clients)
60  *	callback	- Callback function to get ap_id from ib(7d)
61  * Output:
62  *	buffer		- The information is returned in this buffer
63  *      bufsiz		- The size of the information buffer
64  * Returns:
65  *	IBT_SUCCESS/IBT_HCA_INVALID/IBT_INVALID_PARAM
66  * Description:
67  *      For a given HCA node GUID it figures out the registered clients
68  *	(ie. ones who called ibt_open_hca(9f) on this GUID) and creates
69  *	a NVL packed buffer (of client names/ap_ids or devpaths) and returns
70  *	it. If flag is not specified, then an error is returned.
71  */
72 ibt_status_t
73 ibtl_ibnex_get_hca_info(ib_guid_t hca_guid, int flag, char **buffer,
74     size_t *bufsiz, void (*callback)(dev_info_t *, char **))
75 {
76 	char			*node_name;
77 	char			*ret_apid;
78 	nvlist_t		*nvl;
79 	ibtl_hca_t		*ibt_hca;
80 	ibtl_clnt_t		*clntp;
81 	dev_info_t		*child;
82 	dev_info_t		*parent;
83 	ibtl_hca_devinfo_t	*hca_devp;
84 
85 	IBTF_DPRINTF_L3(ibtl_ibnex, "ibtl_ibnex_get_hca_info: "
86 	    "GUID  0x%llX, flag = 0x%x", hca_guid, flag);
87 
88 	*buffer = NULL;
89 	*bufsiz = 0;
90 
91 	/* verify that valid "flag" is passed */
92 	if (flag != IBTL_IBNEX_LIST_CLNTS_FLAG &&
93 	    flag != IBTL_IBNEX_UNCFG_CLNTS_FLAG) {
94 		return (IBT_INVALID_PARAM);
95 	}
96 
97 	mutex_enter(&ibtl_clnt_list_mutex);
98 
99 	if ((hca_devp = ibtl_get_hcadevinfo(hca_guid)) == NULL) {
100 		mutex_exit(&ibtl_clnt_list_mutex);
101 
102 		/*
103 		 * If we are here, then the requested HCA device is not
104 		 * present. Return the status as Invalid HCA GUID.
105 		 */
106 		IBTF_DPRINTF_L2(ibtl_ibnex, "ibtl_ibnex_get_hca_info: "
107 		    "HCA Not Found, Invalid HCA GUID 0x%llX", hca_guid);
108 		return (IBT_HCA_INVALID);
109 	}
110 
111 	/* Walk the client list */
112 	ibt_hca = hca_devp->hd_clnt_list;
113 	(void) nvlist_alloc(&nvl, 0, KM_SLEEP);
114 
115 	/* Allocate memory for ret_apid, instead of using stack */
116 	ret_apid = kmem_alloc(IBTL_IBNEX_APID_LEN, KM_SLEEP);
117 
118 	while (ibt_hca != NULL) {
119 		clntp = ibt_hca->ha_clnt_devp;
120 		child = clntp->clnt_dip;
121 		IBTF_DPRINTF_L4(ibtl_ibnex, "ibtl_ibnex_get_hca_info: "
122 		    "Client = %s", clntp->clnt_modinfop->mi_clnt_name);
123 
124 		if (flag == IBTL_IBNEX_LIST_CLNTS_FLAG) {
125 			(void) nvlist_add_string(nvl, "Client",
126 			    clntp->clnt_modinfop->mi_clnt_name);
127 
128 			/*
129 			 * Always check, first, if this client exists
130 			 * under this HCA anymore? If not, continue.
131 			 */
132 			if (clntp->clnt_hca_list == NULL) {
133 				(void) nvlist_add_string(nvl, "Alt_HCA", "no");
134 				(void) nvlist_add_string(nvl, "ApID", "-");
135 				ibt_hca = ibt_hca->ha_clnt_link;
136 				continue;
137 			}
138 
139 			/* Check if this client has more than one HCAs */
140 			if (clntp->clnt_hca_list->ha_hca_link == NULL)
141 				(void) nvlist_add_string(nvl, "Alt_HCA", "no");
142 			else
143 				(void) nvlist_add_string(nvl, "Alt_HCA", "yes");
144 
145 			if (child == NULL) {
146 				(void) nvlist_add_string(nvl, "ApID", "-");
147 				ibt_hca = ibt_hca->ha_clnt_link;
148 				continue;
149 			}
150 
151 			/*
152 			 * All IB clients (IOC, VPPA, Port, Pseudo etc.)
153 			 * need to be looked at. The parent of IOC nodes
154 			 * is "ib" nexus and node-name is "ioc". "VPPA/Port"s
155 			 * should have HCA as parent and node-name is "ibport".
156 			 * HCA validity is checked by looking at parent's "dip"
157 			 * and the dip saved in the ibtl_hca_devinfo_t.
158 			 * NOTE: We only want to list this HCA's IB clients.
159 			 * All others clients are ignored.
160 			 */
161 			parent = ddi_get_parent(child);
162 			if (parent == NULL || /* No parent? */
163 			    ddi_get_parent_data(child) == NULL) {
164 				(void) nvlist_add_string(nvl, "ApID", "-");
165 				ibt_hca = ibt_hca->ha_clnt_link;
166 				continue;
167 			}
168 
169 			node_name = ddi_node_name(child);
170 			if ((strcmp(ddi_node_name(parent), "ib") == 0) ||
171 			    ((hca_devp->hd_hca_dip == parent) &&
172 			    (strncmp(node_name, IBNEX_IBPORT_CNAME, 6) == 0))) {
173 				ASSERT(callback != NULL);
174 				/*
175 				 * Callback is invoked to figure out the
176 				 * ap_id string.
177 				 */
178 				callback(child, &ret_apid);
179 				(void) nvlist_add_string(nvl, "ApID", ret_apid);
180 			} else {
181 				(void) nvlist_add_string(nvl, "ApID", "-");
182 			}
183 
184 		} else if (flag == IBTL_IBNEX_UNCFG_CLNTS_FLAG) {
185 			char		path[MAXPATHLEN];
186 
187 			if (child == NULL) {
188 				IBTF_DPRINTF_L4(ibtl_ibnex,
189 				    "ibtl_ibnex_get_hca_info: No dip exists");
190 				ibt_hca = ibt_hca->ha_clnt_link;
191 				continue;
192 			}
193 
194 			/*
195 			 * if the child has a alternate HCA then skip it
196 			 */
197 			if (clntp->clnt_hca_list->ha_hca_link) {
198 				IBTF_DPRINTF_L4(ibtl_ibnex,
199 				    "ibtl_ibnex_get_hca_info: Alt HCA exists");
200 				ibt_hca = ibt_hca->ha_clnt_link;
201 				continue;
202 			}
203 
204 			/*
205 			 * See earlier comments on how to check if a client
206 			 * is IOC, VPPA, Port or a Pseudo node.
207 			 */
208 			parent = ddi_get_parent(child);
209 			if (parent == NULL || /* No parent? */
210 			    ddi_get_parent_data(child) == NULL) {
211 				IBTF_DPRINTF_L4(ibtl_ibnex,
212 				    "ibtl_ibnex_get_hca_info: no parent");
213 				ibt_hca = ibt_hca->ha_clnt_link;
214 				continue;
215 			}
216 
217 			node_name = ddi_node_name(child);
218 			if ((strcmp(ddi_node_name(parent), "ib") == 0) ||
219 			    ((hca_devp->hd_hca_dip == parent) &&
220 			    (strncmp(node_name, IBNEX_IBPORT_CNAME, 6) == 0))) {
221 				ASSERT(callback != NULL);
222 				/*
223 				 * Callback is invoked to figure out the
224 				 * ap_id string.
225 				 */
226 				callback(child, &ret_apid);
227 				(void) nvlist_add_string(nvl, "ApID", ret_apid);
228 
229 				/*
230 				 * ddi_pathname() doesn't supply /devices,
231 				 * so we do
232 				 */
233 				(void) strcpy(path, "/devices");
234 				(void) ddi_pathname(child, path + strlen(path));
235 				IBTF_DPRINTF_L4(ibtl_ibnex,
236 				    "ibtl_ibnex_get_hca_info: "
237 				    "device path = %s", path);
238 
239 				if (nvlist_add_string(nvl, "devpath", path)) {
240 					IBTF_DPRINTF_L2(ibtl_ibnex,
241 					    "ibtl_ibnex_get_hca_info: "
242 					    "failed to fill in path %s", path);
243 					mutex_exit(&ibtl_clnt_list_mutex);
244 					nvlist_free(nvl);
245 					kmem_free(ret_apid,
246 					    IBTL_IBNEX_APID_LEN);
247 					return (ibt_get_module_failure(
248 					    IBT_FAILURE_IBTL, 0));
249 				}
250 			} /* end of if */
251 		} /* end of while */
252 
253 		ibt_hca = ibt_hca->ha_clnt_link;
254 	} /* End of while */
255 	mutex_exit(&ibtl_clnt_list_mutex);
256 
257 	kmem_free(ret_apid, IBTL_IBNEX_APID_LEN);
258 
259 	/* Pack all data into "buffer" */
260 	if (nvlist_pack(nvl, buffer, bufsiz, NV_ENCODE_NATIVE, KM_SLEEP)) {
261 		IBTF_DPRINTF_L2(ibtl_ibnex, "ibtl_ibnex_get_hca_info: "
262 		    "nvlist_pack failed");
263 		nvlist_free(nvl);
264 		return (ibt_get_module_failure(IBT_FAILURE_IBTL, 0));
265 	}
266 
267 	IBTF_DPRINTF_L4(ibtl_ibnex,
268 		"ibtl_ibnex_get_hca_info: size = %x", *bufsiz);
269 	nvlist_free(nvl);
270 	return (IBT_SUCCESS);
271 }
272 
273 
274 /*
275  * Function:
276  *      ibtl_ibnex_register_callback()
277  * Input:
278  *	ibnex_cb	- IB nexus driver callback routine
279  * Output:
280  *      none
281  * Returns:
282  *      none
283  * Description:
284  *     	Register a callback routine for IB nexus driver
285  */
286 void
287 ibtl_ibnex_register_callback(ibtl_ibnex_callback_t ibnex_cb)
288 {
289 	IBTF_DPRINTF_L5(ibtl_ibnex, "ibtl_ibnex_register_callback:");
290 
291 	mutex_enter(&ibtl_clnt_list_mutex);
292 	ibtl_ibnex_callback_routine = ibnex_cb;
293 	mutex_exit(&ibtl_clnt_list_mutex);
294 }
295 
296 /*
297  * Function:
298  *      ibtl_ibnex_unregister_callback()
299  * Input:
300  *      none
301  * Output:
302  *      none
303  * Returns:
304  *      none
305  * Description:
306  *     	Un-register a callback routine for IB nexus driver
307  */
308 void
309 ibtl_ibnex_unregister_callback()
310 {
311 	IBTF_DPRINTF_L5(ibtl_ibnex, "ibtl_ibnex_unregister_callback: ibnex cb");
312 
313 	mutex_enter(&ibtl_clnt_list_mutex);
314 	ibtl_ibnex_callback_routine = NULL;
315 	mutex_exit(&ibtl_clnt_list_mutex);
316 }
317 
318 
319 /*
320  * Function:
321  *	ibtl_ibnex_hcadip2guid
322  * Input:
323  *	dev_info_t	- The "dip" of this HCA
324  * Output:
325  *	hca_guid	- The HCA's node GUID.
326  * Returns:
327  *	"HCA GUID" on SUCCESS, NULL on FAILURE
328  * Description:
329  *      For a given HCA node GUID it figures out the HCA GUID
330  *	and returns it. If not found, NULL is returned.
331  */
332 ib_guid_t
333 ibtl_ibnex_hcadip2guid(dev_info_t *hca_dip)
334 {
335 	ib_guid_t		hca_guid = 0LL;
336 	ibtl_hca_devinfo_t	*hca_devp;
337 
338 	mutex_enter(&ibtl_clnt_list_mutex);
339 	hca_devp = ibtl_hca_list;
340 
341 	while (hca_devp) {
342 		if (hca_devp->hd_hca_dip == hca_dip) {
343 			hca_guid = hca_devp->hd_hca_attr->hca_node_guid;
344 			break;
345 		}
346 		hca_devp = hca_devp->hd_hca_dev_link;
347 	}
348 	mutex_exit(&ibtl_clnt_list_mutex);
349 	IBTF_DPRINTF_L4(ibtl_ibnex, "ibtl_ibnex_hcadip_guid: hca_guid 0x%llX",
350 	    hca_guid);
351 	return (hca_guid);
352 }
353 
354 
355 /*
356  * Function:
357  *	ibtl_ibnex_hcaguid2dip
358  * Input:
359  *	hca_guid	- The HCA's node GUID.
360  * Output:
361  *	dev_info_t	- The "dip" of this HCA
362  * Returns:
363  *	"dip" on SUCCESS, NULL on FAILURE
364  * Description:
365  *      For a given HCA node GUID it figures out the "dip"
366  *	and returns it. If not found, NULL is returned.
367  */
368 dev_info_t *
369 ibtl_ibnex_hcaguid2dip(ib_guid_t hca_guid)
370 {
371 	dev_info_t		*dip = NULL;
372 	ibtl_hca_devinfo_t	*hca_devp;
373 
374 	IBTF_DPRINTF_L4(ibtl_ibnex, "ibtl_ibnex_hcaguid2dip:");
375 
376 	mutex_enter(&ibtl_clnt_list_mutex);
377 	hca_devp = ibtl_hca_list;
378 
379 	while (hca_devp) {
380 		if (hca_devp->hd_hca_attr->hca_node_guid == hca_guid) {
381 			dip = hca_devp->hd_hca_dip;
382 			break;
383 		}
384 		hca_devp = hca_devp->hd_hca_dev_link;
385 	}
386 	mutex_exit(&ibtl_clnt_list_mutex);
387 	return (dip);
388 }
389 
390 
391 /*
392  * Function:
393  *	ibtl_ibnex_get_hca_verbose_data
394  * Input:
395  *	hca_guid	- The HCA's node GUID.
396  * Output:
397  *	buffer		- The information is returned in this buffer
398  *      bufsiz		- The size of the information buffer
399  * Returns:
400  *	IBT_SUCCESS/IBT_HCA_INVALID
401  * Description:
402  *      For a given HCA node GUID it figures out the verbose listing display.
403  */
404 ibt_status_t
405 ibtl_ibnex_get_hca_verbose_data(ib_guid_t hca_guid, char **buffer,
406     size_t *bufsiz)
407 {
408 	char			path[IBTL_IBNEX_STR_LEN];
409 	char			tmp[MAXPATHLEN];
410 	uint_t			ii;
411 	ibt_hca_portinfo_t	*pinfop;
412 	ibtl_hca_devinfo_t	*hca_devp;
413 
414 	IBTF_DPRINTF_L3(ibtl_ibnex, "ibtl_ibnex_get_hca_verbose_data: "
415 	    "HCA GUID 0x%llX", hca_guid);
416 
417 	*buffer = NULL;
418 	*bufsiz = 0;
419 
420 	mutex_enter(&ibtl_clnt_list_mutex);
421 	if ((hca_devp = ibtl_get_hcadevinfo(hca_guid)) == NULL) {
422 		mutex_exit(&ibtl_clnt_list_mutex);
423 
424 		/*
425 		 * If we are here, then the requested HCA device is not
426 		 * present. Return the status as Invalid HCA GUID.
427 		 */
428 		IBTF_DPRINTF_L2(ibtl_ibnex, "ibtl_ibnex_get_hca_verbose_data: "
429 		    "HCA Not Found, Invalid HCA GUID");
430 		return (IBT_HCA_INVALID);
431 	}
432 
433 	(void) snprintf(tmp, MAXPATHLEN, "VID: 0x%x, PID: 0x%x, #ports: 0x%x",
434 	    hca_devp->hd_hca_attr->hca_vendor_id,
435 	    hca_devp->hd_hca_attr->hca_device_id,
436 	    hca_devp->hd_hca_attr->hca_nports);
437 
438 	pinfop = hca_devp->hd_portinfop;
439 	for (ii = 0; ii < hca_devp->hd_hca_attr->hca_nports; ii++) {
440 		(void) snprintf(path, IBTL_IBNEX_STR_LEN,
441 		    ", port%d GUID: 0x%llX", ii + 1,
442 		    (longlong_t)pinfop[ii].p_sgid_tbl->gid_guid);
443 		(void) strcat(tmp, path);
444 	}
445 	mutex_exit(&ibtl_clnt_list_mutex);
446 
447 	*bufsiz =  strlen(tmp);
448 	*buffer = kmem_alloc(*bufsiz, KM_SLEEP);
449 	(void) strncpy(*buffer, tmp, *bufsiz);
450 
451 	IBTF_DPRINTF_L4(ibtl_ibnex, "ibtl_ibnex_get_hca_verbose_data: "
452 	    "data = %s, size = 0x%x", *buffer, *bufsiz);
453 	return (IBT_SUCCESS);
454 }
455 
456 /*
457  * Function:
458  *      ibt_reprobe_dev()
459  * Input:
460  *      dev_info_t	*dip
461  * Output:
462  *      none
463  * Returns:
464  *      Return value from IBnexus callback handler
465  *		IBT_ILLEGAL_OP if IBnexus callback is not installed.
466  * Description:
467  *     	This function passes the IBTF client's "reprobe device
468  *		properties" request to IBnexus. See ibt_reprobe_dev(9f)
469  *		for details.
470  */
471 ibt_status_t
472 ibt_reprobe_dev(dev_info_t *dip)
473 {
474 	ibt_status_t		rv;
475 	ibtl_ibnex_cb_args_t	cb_args;
476 
477 	if (dip == NULL)
478 		return (IBT_NOT_SUPPORTED);
479 
480 	/*
481 	 * Restricting the reprobe request to the children of
482 	 * ibnexus. Note the IB_CONF_UPDATE_EVENT DDI event can
483 	 * be subscribed by any device on the IBnexus device tree.
484 	 */
485 	if (strcmp(ddi_node_name(ddi_get_parent(dip)), "ib") != 0)
486 		return (IBT_NOT_SUPPORTED);
487 
488 	/* Reprobe for IOC nodes only */
489 	if (strncmp(ddi_node_name(dip), IBNEX_IBPORT_CNAME, 6) == 0)
490 		return (IBT_NOT_SUPPORTED);
491 
492 	cb_args.cb_flag = IBTL_IBNEX_REPROBE_DEV_REQ;
493 	cb_args.cb_dip = dip;
494 	mutex_enter(&ibtl_clnt_list_mutex);
495 	if (ibtl_ibnex_callback_routine) {
496 		rv = (*ibtl_ibnex_callback_routine)(&cb_args);
497 		mutex_exit(&ibtl_clnt_list_mutex);
498 		return (rv);
499 	}
500 	mutex_exit(&ibtl_clnt_list_mutex);
501 
502 	/* Should -not- come here */
503 	IBTF_DPRINTF_L2("ibtl", "ibt_reprobe_dev: ibnex not registered!!");
504 	return (IBT_ILLEGAL_OP);
505 }
506 
507 
508 /*
509  * Function:
510  *	ibtl_ibnex_valid_hca_parent
511  * Input:
512  *	pdip		- The parent dip from client's child dev_info_t
513  * Output:
514  *	NONE
515  * Returns:
516  *	IBT_SUCCESS/IBT_NO_HCAS_AVAILABLE
517  * Description:
518  *	For a given pdip, of Port/VPPA devices, match it against all the
519  *	registered HCAs's dip.  If match found return IBT_SUCCESS,
520  *	else IBT_NO_HCAS_AVAILABLE.
521  *	For IOC/Pseudo devices check if the given pdip is that of
522  *	the ib(7d) nexus. If yes return IBT_SUCCESS,
523  *	else IBT_NO_HCAS_AVAILABLE.
524  */
525 ibt_status_t
526 ibtl_ibnex_valid_hca_parent(dev_info_t *pdip)
527 {
528 	ibtl_hca_devinfo_t	*hca_devp;
529 
530 	IBTF_DPRINTF_L4(ibtl_ibnex, "ibtl_ibnex_valid_hca_parent: pdip %p",
531 	    pdip);
532 
533 	/* For Pseudo devices and IOCs */
534 	if (strncmp(ddi_node_name(pdip), "ib", 2) == 0)
535 		return (IBT_SUCCESS);
536 	else {
537 		/* For Port devices and VPPAs */
538 		mutex_enter(&ibtl_clnt_list_mutex);
539 		hca_devp = ibtl_hca_list;
540 		while (hca_devp) {
541 			if (hca_devp->hd_hca_dip == pdip) {
542 				mutex_exit(&ibtl_clnt_list_mutex);
543 				return (IBT_SUCCESS);
544 			}
545 			hca_devp = hca_devp->hd_hca_dev_link;
546 		}
547 		mutex_exit(&ibtl_clnt_list_mutex);
548 		return (IBT_NO_HCAS_AVAILABLE);
549 	}
550 }
551