1 /*
2  * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35 
36 #if HAVE_CONFIG_H
37 #  include <config.h>
38 #endif				/* HAVE_CONFIG_H */
39 
40 #include <stdlib.h>
41 #include <string.h>
42 #if defined(OSM_VENDOR_INTF_MTL) | defined(OSM_VENDOR_INTF_TS)
43 #undef IN
44 #undef OUT
45 #include <vapi_types.h>
46 #include <evapi.h>
47 #include <vendor/osm_vendor_api.h>
48 #include <opensm/osm_log.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 
52 /********************************************************************************
53  *
54  * Provide the functionality for selecting an HCA Port and Obtaining it's guid.
55  *
56  ********************************************************************************/
57 
58 typedef struct _osm_ca_info {
59 	ib_net64_t guid;
60 	size_t attr_size;
61 	ib_ca_attr_t *p_attr;
62 } osm_ca_info_t;
63 
64 /**********************************************************************
65  * Convert the given GID to GUID by copy of it's upper 8 bytes
66  **********************************************************************/
67 ib_api_status_t
68 __osm_vendor_gid_to_guid(IN u_int8_t * gid, OUT VAPI_gid_t * guid)
69 {
70 	memcpy(guid, gid + 8, 8);
71 	return (IB_SUCCESS);
72 }
73 
74 /**********************************************************************
75  * Returns a pointer to the port attribute of the specified port
76  * owned by this CA.
77  ************************************************************************/
78 static ib_port_attr_t *__osm_ca_info_get_port_attr_ptr(IN const osm_ca_info_t *
79 						       const p_ca_info,
80 						       IN const uint8_t index)
81 {
82 	return (&p_ca_info->p_attr->p_port_attr[index]);
83 }
84 
85 /********************************************************************************
86  * get the CA names available on the system
87  * NOTE: user of this function needs to deallocate p_hca_ids after usage.
88  ********************************************************************************/
89 static ib_api_status_t
90 __osm_vendor_get_ca_ids(IN osm_vendor_t * const p_vend,
91 			IN VAPI_hca_id_t ** const p_hca_ids,
92 			IN uint32_t * const p_num_guids)
93 {
94 	ib_api_status_t status;
95 	VAPI_ret_t vapi_res;
96 
97 	OSM_LOG_ENTER(p_vend->p_log);
98 
99 	CL_ASSERT(p_hca_ids);
100 	CL_ASSERT(p_num_guids);
101 
102 	/* first call is just to get the number */
103 	vapi_res = EVAPI_list_hcas(0, p_num_guids, NULL);
104 
105 	/* fail ? */
106 	if (vapi_res == VAPI_EINVAL_PARAM) {
107 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
108 			"__osm_vendor_get_ca_ids: ERR 3D08: : "
109 			"Bad parameter in calling: EVAPI_list_hcas. (%d)\n",
110 			vapi_res);
111 		status = IB_ERROR;
112 		goto Exit;
113 	}
114 
115 	/* NO HCA ? */
116 	if (*p_num_guids == 0) {
117 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
118 			"__osm_vendor_get_ca_ids: ERR 3D09: "
119 			"No available channel adapters.\n");
120 		status = IB_INSUFFICIENT_RESOURCES;
121 		goto Exit;
122 	}
123 
124 	/* allocate and really call - user of this function needs to deallocate it */
125 	*p_hca_ids =
126 	    (VAPI_hca_id_t *) malloc(*p_num_guids * sizeof(VAPI_hca_id_t));
127 
128 	/* now call it really */
129 	vapi_res = EVAPI_list_hcas(*p_num_guids, p_num_guids, *p_hca_ids);
130 
131 	/* too many ? */
132 	if (vapi_res == VAPI_EAGAIN) {
133 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
134 			"__osm_vendor_get_ca_ids: ERR 3D10: "
135 			"More CA GUIDs than allocated array (%d).\n",
136 			*p_num_guids);
137 		status = IB_ERROR;
138 		goto Exit;
139 	}
140 
141 	/* fail ? */
142 	if (vapi_res != VAPI_OK) {
143 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
144 			"__osm_vendor_get_ca_ids: ERR 3D11: : "
145 			"Bad parameter in calling: EVAPI_list_hcas.\n");
146 		status = IB_ERROR;
147 		goto Exit;
148 	}
149 
150 	if (osm_log_is_active(p_vend->p_log, OSM_LOG_DEBUG)) {
151 		osm_log(p_vend->p_log, OSM_LOG_DEBUG,
152 			"__osm_vendor_get_ca_ids: "
153 			"Detected %u local channel adapters.\n", *p_num_guids);
154 	}
155 
156 	status = IB_SUCCESS;
157 
158 Exit:
159 	OSM_LOG_EXIT(p_vend->p_log);
160 	return (status);
161 }
162 
163 /**********************************************************************
164  * Initialize an Info Struct for the Given HCA by its Id
165  **********************************************************************/
166 static ib_api_status_t
167 __osm_ca_info_init(IN osm_vendor_t * const p_vend,
168 		   IN VAPI_hca_id_t ca_id, OUT osm_ca_info_t * const p_ca_info)
169 {
170 	ib_api_status_t status = IB_ERROR;
171 	VAPI_ret_t vapi_res;
172 	VAPI_hca_hndl_t hca_hndl;
173 	VAPI_hca_vendor_t hca_vendor;
174 	VAPI_hca_cap_t hca_cap;
175 	VAPI_hca_port_t hca_port;
176 	uint8_t port_num;
177 	IB_gid_t *p_port_gid;
178 	uint16_t maxNumGids;
179 
180 	OSM_LOG_ENTER(p_vend->p_log);
181 
182 	/* get the HCA handle */
183 	vapi_res = EVAPI_get_hca_hndl(ca_id, &hca_hndl);
184 	if (vapi_res != VAPI_OK) {
185 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
186 			"__osm_ca_info_init: ERR 3D05: "
187 			"Fail to get HCA handle (%u).\n", vapi_res);
188 		goto Exit;
189 	}
190 
191 	if (osm_log_is_active(p_vend->p_log, OSM_LOG_DEBUG)) {
192 		osm_log(p_vend->p_log, OSM_LOG_DEBUG,
193 			"__osm_ca_info_init: " "Querying CA %s.\n", ca_id);
194 	}
195 
196 	/* query and get the HCA capability */
197 	vapi_res = VAPI_query_hca_cap(hca_hndl, &hca_vendor, &hca_cap);
198 	if (vapi_res != VAPI_OK) {
199 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
200 			"__osm_ca_info_init: ERR 3D06: "
201 			"Fail to get HCA Capabilities (%u).\n", vapi_res);
202 		goto Exit;
203 	}
204 
205 	/* get the guid of the HCA */
206 	memcpy(&(p_ca_info->guid), hca_cap.node_guid, 8 * sizeof(u_int8_t));
207 	p_ca_info->attr_size = 1;
208 	p_ca_info->p_attr = (ib_ca_attr_t *) malloc(sizeof(ib_ca_attr_t));
209 	memcpy(&(p_ca_info->p_attr->ca_guid), hca_cap.node_guid,
210 	       8 * sizeof(u_int8_t));
211 
212 	/* now obtain the attributes of the ports */
213 	p_ca_info->p_attr->num_ports = hca_cap.phys_port_num;
214 	p_ca_info->p_attr->p_port_attr =
215 	    (ib_port_attr_t *) malloc(hca_cap.phys_port_num *
216 				      sizeof(ib_port_attr_t));
217 
218 	for (port_num = 0; port_num < p_ca_info->p_attr->num_ports; port_num++) {
219 
220 		/* query the port attributes */
221 		vapi_res =
222 		    VAPI_query_hca_port_prop(hca_hndl, port_num + 1, &hca_port);
223 		if (vapi_res != VAPI_OK) {
224 			osm_log(p_vend->p_log, OSM_LOG_ERROR,
225 				"__osm_ca_info_init: ERR 3D07: "
226 				"Fail to get HCA Port Attributes (%d).\n",
227 				vapi_res);
228 			goto Exit;
229 		}
230 
231 		/* first call to know the size of the gid table */
232 		vapi_res =
233 		    VAPI_query_hca_gid_tbl(hca_hndl, port_num + 1, 0,
234 					   &maxNumGids, NULL);
235 		p_port_gid = (IB_gid_t *) malloc(maxNumGids * sizeof(IB_gid_t));
236 
237 		vapi_res =
238 		    VAPI_query_hca_gid_tbl(hca_hndl, port_num + 1, maxNumGids,
239 					   &maxNumGids, p_port_gid);
240 		if (vapi_res != VAPI_OK) {
241 			osm_log(p_vend->p_log, OSM_LOG_ERROR,
242 				"__osm_ca_info_init: ERR 3D12: "
243 				"Fail to get HCA Port GID (%d).\n", vapi_res);
244 			goto Exit;
245 		}
246 
247 		__osm_vendor_gid_to_guid(p_port_gid[0],
248 					 (IB_gid_t *) & p_ca_info->p_attr->
249 					 p_port_attr[port_num].port_guid);
250 		p_ca_info->p_attr->p_port_attr[port_num].lid = hca_port.lid;
251 		p_ca_info->p_attr->p_port_attr[port_num].link_state =
252 		    hca_port.state;
253 		p_ca_info->p_attr->p_port_attr[port_num].sm_lid =
254 		    hca_port.sm_lid;
255 
256 		free(p_port_gid);
257 	}
258 
259 	status = IB_SUCCESS;
260 Exit:
261 	OSM_LOG_EXIT(p_vend->p_log);
262 	return (status);
263 }
264 
265 void
266 osm_ca_info_destroy(IN osm_vendor_t * const p_vend,
267 		    IN osm_ca_info_t * const p_ca_info, IN uint8_t num_ca)
268 {
269 	osm_ca_info_t *p_ca;
270 	uint8_t i;
271 
272 	OSM_LOG_ENTER(p_vend->p_log);
273 
274 	for (i = 0; i < num_ca; i++) {
275 		p_ca = &p_ca_info[i];
276 
277 		if (NULL != p_ca->p_attr) {
278 			if (0 != p_ca->p_attr->num_ports) {
279 				free(p_ca->p_attr->p_port_attr);
280 			}
281 
282 			free(p_ca->p_attr);
283 		}
284 	}
285 
286 	free(p_ca_info);
287 
288 	OSM_LOG_EXIT(p_vend->p_log);
289 }
290 
291 /**********************************************************************
292  * Fill in the array of port_attr with all available ports on ALL the
293  * avilable CAs on this machine.
294  * ALSO -
295  * Update the vendor object list of ca_info structs
296  **********************************************************************/
297 ib_api_status_t
298 osm_vendor_get_all_port_attr(IN osm_vendor_t * const p_vend,
299 			     IN ib_port_attr_t * const p_attr_array,
300 			     IN uint32_t * const p_num_ports)
301 {
302 	ib_api_status_t status;
303 
304 	uint32_t ca;
305 	uint32_t ca_count = 0;
306 	uint32_t port_count = 0;
307 	uint8_t port_num;
308 	uint32_t total_ports = 0;
309 	VAPI_hca_id_t *p_ca_ids = NULL;
310 	osm_ca_info_t *p_ca_infos = NULL;
311 	uint32_t attr_array_sz = *p_num_ports;
312 
313 	OSM_LOG_ENTER(p_vend->p_log);
314 
315 	CL_ASSERT(p_vend);
316 
317 	/* determine the number of CA's */
318 	status = __osm_vendor_get_ca_ids(p_vend, &p_ca_ids, &ca_count);
319 	if (status != IB_SUCCESS) {
320 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
321 			"osm_vendor_get_all_port_attr: ERR 3D13: "
322 			"Fail to get CA Ids.\n");
323 		goto Exit;
324 	}
325 
326 	/* Allocate an array big enough to hold the ca info objects */
327 	p_ca_infos = malloc(ca_count * sizeof(osm_ca_info_t));
328 	if (p_ca_infos == NULL) {
329 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
330 			"osm_vendor_get_all_port_attr: ERR 3D14: "
331 			"Unable to allocate CA information array.\n");
332 		goto Exit;
333 	}
334 
335 	memset(p_ca_infos, 0, ca_count * sizeof(osm_ca_info_t));
336 
337 	/*
338 	 * For each CA, retrieve the CA info attributes
339 	 */
340 	for (ca = 0; ca < ca_count; ca++) {
341 		status =
342 		    __osm_ca_info_init(p_vend, p_ca_ids[ca], &p_ca_infos[ca]);
343 		if (status != IB_SUCCESS) {
344 			osm_log(p_vend->p_log, OSM_LOG_ERROR,
345 				"osm_vendor_get_all_port_attr: ERR 3D15: "
346 				"Unable to initialize CA Info object (%s).\n",
347 				ib_get_err_str(status));
348 			goto Exit;
349 		}
350 		total_ports += p_ca_infos[ca].p_attr->num_ports;
351 	}
352 
353 	*p_num_ports = total_ports;
354 	osm_log(p_vend->p_log, OSM_LOG_DEBUG,
355 		"osm_vendor_get_all_port_attr: total ports:%u \n", total_ports);
356 
357 	/*
358 	 * If the user supplied enough storage, return the port guids,
359 	 * otherwise, return the appropriate error.
360 	 */
361 	if (attr_array_sz >= total_ports) {
362 		for (ca = 0; ca < ca_count; ca++) {
363 			uint32_t num_ports;
364 
365 			num_ports = p_ca_infos[ca].p_attr->num_ports;
366 
367 			for (port_num = 0; port_num < num_ports; port_num++) {
368 				p_attr_array[port_count] =
369 				    *__osm_ca_info_get_port_attr_ptr(&p_ca_infos
370 								     [ca],
371 								     port_num);
372 				port_count++;
373 			}
374 		}
375 	} else {
376 		status = IB_INSUFFICIENT_MEMORY;
377 		goto Exit;
378 	}
379 
380 	status = IB_SUCCESS;
381 
382 Exit:
383 	if (p_ca_ids)
384 		free(p_ca_ids);
385 
386 	if (p_ca_infos) {
387 		osm_ca_info_destroy(p_vend, p_ca_infos, ca_count);
388 	}
389 
390 	OSM_LOG_EXIT(p_vend->p_log);
391 	return (status);
392 }
393 
394 /**********************************************************************
395  * Given the vendor obj and a guid
396  * return the ca id and port number that have that guid
397  **********************************************************************/
398 
399 ib_api_status_t
400 osm_vendor_get_guid_ca_and_port(IN osm_vendor_t * const p_vend,
401 				IN ib_net64_t const guid,
402 				OUT VAPI_hca_hndl_t * p_hca_hndl,
403 				OUT VAPI_hca_id_t * p_hca_id,
404 				OUT uint8_t * p_hca_idx,
405 				OUT uint32_t * p_port_num)
406 {
407 
408 	ib_api_status_t status;
409 	VAPI_hca_id_t *p_ca_ids = NULL;
410 	VAPI_ret_t vapi_res;
411 	VAPI_hca_hndl_t hca_hndl;
412 	VAPI_hca_vendor_t hca_vendor;
413 	VAPI_hca_cap_t hca_cap;
414 	IB_gid_t *p_port_gid = NULL;
415 	uint16_t maxNumGids;
416 	ib_net64_t port_guid;
417 	uint32_t ca, portIdx, ca_count;
418 
419 	OSM_LOG_ENTER(p_vend->p_log);
420 
421 	CL_ASSERT(p_vend);
422 
423 	/*
424 	 * 1) Determine the number of CA's
425 	 * 2) Allocate an array big enough to hold the ca info objects.
426 	 * 3) Call again to retrieve the guids.
427 	 */
428 	status = __osm_vendor_get_ca_ids(p_vend, &p_ca_ids, &ca_count);
429 	if (status != IB_SUCCESS) {
430 		osm_log(p_vend->p_log, OSM_LOG_ERROR,
431 			"osm_vendor_get_guid_ca_and_port: ERR 3D16: "
432 			"Fail to get CA Ids.\n");
433 		goto Exit;
434 	}
435 
436 	/*
437 	 * For each CA, retrieve the CA info attributes
438 	 */
439 	for (ca = 0; ca < ca_count; ca++) {
440 		/* get the HCA handle */
441 		vapi_res = EVAPI_get_hca_hndl(p_ca_ids[ca], &hca_hndl);
442 		if (vapi_res != VAPI_OK) {
443 			osm_log(p_vend->p_log, OSM_LOG_ERROR,
444 				"osm_vendor_get_guid_ca_and_port: ERR 3D17: "
445 				"Fail to get HCA handle (%u).\n", vapi_res);
446 			goto Exit;
447 		}
448 
449 		/* get the CA attributes - to know how many ports it has: */
450 		if (osm_log_is_active(p_vend->p_log, OSM_LOG_DEBUG)) {
451 			osm_log(p_vend->p_log, OSM_LOG_DEBUG,
452 				"osm_vendor_get_guid_ca_and_port: "
453 				"Querying CA %s.\n", p_ca_ids[ca]);
454 		}
455 
456 		/* query and get the HCA capability */
457 		vapi_res = VAPI_query_hca_cap(hca_hndl, &hca_vendor, &hca_cap);
458 		if (vapi_res != VAPI_OK) {
459 			osm_log(p_vend->p_log, OSM_LOG_ERROR,
460 				"osm_vendor_get_guid_ca_and_port: ERR 3D18: "
461 				"Fail to get HCA Capabilities (%u).\n",
462 				vapi_res);
463 			goto Exit;
464 		}
465 
466 		/* go over all ports - to obtail their guids */
467 		for (portIdx = 0; portIdx < hca_cap.phys_port_num; portIdx++) {
468 			vapi_res =
469 			    VAPI_query_hca_gid_tbl(hca_hndl, portIdx + 1, 0,
470 						   &maxNumGids, NULL);
471 			p_port_gid =
472 			    (IB_gid_t *) malloc(maxNumGids * sizeof(IB_gid_t));
473 
474 			/* get the port guid */
475 			vapi_res =
476 			    VAPI_query_hca_gid_tbl(hca_hndl, portIdx + 1,
477 						   maxNumGids, &maxNumGids,
478 						   p_port_gid);
479 			if (vapi_res != VAPI_OK) {
480 				osm_log(p_vend->p_log, OSM_LOG_ERROR,
481 					"osm_vendor_get_guid_ca_and_port: ERR 3D19: "
482 					"Fail to get HCA Port GID (%d).\n",
483 					vapi_res);
484 				goto Exit;
485 			}
486 
487 			/* convert to SF style */
488 			__osm_vendor_gid_to_guid(p_port_gid[0],
489 						 (VAPI_gid_t *) & port_guid);
490 
491 			/* finally did we find it ? */
492 			if (port_guid == guid) {
493 				*p_hca_hndl = hca_hndl;
494 				memcpy(p_hca_id, p_ca_ids[ca],
495 				       sizeof(VAPI_hca_id_t));
496 				*p_hca_idx = ca;
497 				*p_port_num = portIdx + 1;
498 				status = IB_SUCCESS;
499 				goto Exit;
500 			}
501 
502 			free(p_port_gid);
503 			p_port_gid = NULL;
504 		}		/*  ALL PORTS  */
505 	}			/*  all HCAs */
506 
507 	osm_log(p_vend->p_log, OSM_LOG_ERROR,
508 		"osm_vendor_get_guid_ca_and_port: ERR 3D20: "
509 		"Fail to find HCA and Port for Port Guid 0x%" PRIx64 "\n",
510 		cl_ntoh64(guid));
511 	status = IB_INVALID_GUID;
512 
513 Exit:
514 	if (p_ca_ids != NULL)
515 		free(p_ca_ids);
516 	if (p_port_gid != NULL)
517 		free(p_port_gid);
518 	OSM_LOG_EXIT(p_vend->p_log);
519 	return (status);
520 }
521 
522 #endif
523