1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2007 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
6  *
7  * This software is available to you under a choice of one of two
8  * licenses.  You may choose to be licensed under the terms of the GNU
9  * General Public License (GPL) Version 2, available from the file
10  * COPYING in the main directory of this source tree, or the
11  * OpenIB.org BSD license below:
12  *
13  *     Redistribution and use in source and binary forms, with or
14  *     without modification, are permitted provided that the following
15  *     conditions are met:
16  *
17  *      - Redistributions of source code must retain the above
18  *        copyright notice, this list of conditions and the following
19  *        disclaimer.
20  *
21  *      - Redistributions in binary form must reproduce the above
22  *        copyright notice, this list of conditions and the following
23  *        disclaimer in the documentation and/or other materials
24  *        provided with the distribution.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33  * SOFTWARE.
34  *
35  */
36 
37 /*
38  * Abstract:
39  *    Implementation of osm_lr_rcv_t.
40  * This object represents the LinkRecord Receiver object.
41  * This object is part of the opensm family of objects.
42  */
43 
44 #if HAVE_CONFIG_H
45 #  include <config.h>
46 #endif				/* HAVE_CONFIG_H */
47 
48 #include <string.h>
49 #include <iba/ib_types.h>
50 #include <complib/cl_qmap.h>
51 #include <complib/cl_debug.h>
52 #include <opensm/osm_file_ids.h>
53 #define FILE_ID OSM_FILE_SA_LINK_RECORD_C
54 #include <vendor/osm_vendor_api.h>
55 #include <opensm/osm_node.h>
56 #include <opensm/osm_switch.h>
57 #include <opensm/osm_helper.h>
58 #include <opensm/osm_pkey.h>
59 #include <opensm/osm_sa.h>
60 
61 #define SA_LR_RESP_SIZE SA_ITEM_RESP_SIZE(link_rec)
62 
63 static void lr_rcv_build_physp_link(IN osm_sa_t * sa, IN ib_net16_t from_lid,
64 				    IN ib_net16_t to_lid, IN uint8_t from_port,
65 				    IN uint8_t to_port, IN cl_qlist_t * p_list)
66 {
67 	osm_sa_item_t *p_lr_item;
68 
69 	p_lr_item = malloc(SA_LR_RESP_SIZE);
70 	if (p_lr_item == NULL) {
71 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1801: "
72 			"Unable to acquire link record\n"
73 			"\t\t\t\tFrom port %u\n" "\t\t\t\tTo port   %u\n"
74 			"\t\t\t\tFrom lid  %u\n" "\t\t\t\tTo lid    %u\n",
75 			from_port, to_port,
76 			cl_ntoh16(from_lid), cl_ntoh16(to_lid));
77 		return;
78 	}
79 	memset(p_lr_item, 0, SA_LR_RESP_SIZE);
80 
81 	p_lr_item->resp.link_rec.from_port_num = from_port;
82 	p_lr_item->resp.link_rec.to_port_num = to_port;
83 	p_lr_item->resp.link_rec.to_lid = to_lid;
84 	p_lr_item->resp.link_rec.from_lid = from_lid;
85 
86 	cl_qlist_insert_tail(p_list, &p_lr_item->list_item);
87 }
88 
89 static ib_net16_t get_base_lid(IN const osm_physp_t * p_physp)
90 {
91 	if (p_physp->p_node->node_info.node_type == IB_NODE_TYPE_SWITCH)
92 		p_physp = osm_node_get_physp_ptr(p_physp->p_node, 0);
93 	return osm_physp_get_base_lid(p_physp);
94 }
95 
96 static void lr_rcv_get_physp_link(IN osm_sa_t * sa,
97 				  IN const ib_link_record_t * p_lr,
98 				  IN const osm_physp_t * p_src_physp,
99 				  IN const osm_physp_t * p_dest_physp,
100 				  IN const ib_net64_t comp_mask,
101 				  IN cl_qlist_t * p_list,
102 				  IN const osm_physp_t * p_req_physp)
103 {
104 	uint8_t src_port_num;
105 	uint8_t dest_port_num;
106 	ib_net16_t from_base_lid;
107 	ib_net16_t to_base_lid;
108 	ib_net16_t lmc_mask;
109 
110 	OSM_LOG_ENTER(sa->p_log);
111 
112 	/*
113 	   If only one end of the link is specified, determine
114 	   the other side.
115 	 */
116 	if (p_src_physp) {
117 		if (p_dest_physp) {
118 			/*
119 			   Ensure the two physp's are actually connected.
120 			   If not, bail out.
121 			 */
122 			if (osm_physp_get_remote(p_src_physp) != p_dest_physp)
123 				goto Exit;
124 		} else {
125 			p_dest_physp = osm_physp_get_remote(p_src_physp);
126 			if (p_dest_physp == NULL)
127 				goto Exit;
128 		}
129 	} else {
130 		if (p_dest_physp) {
131 			p_src_physp = osm_physp_get_remote(p_dest_physp);
132 			if (p_src_physp == NULL)
133 				goto Exit;
134 		} else
135 			goto Exit;	/* no physp's, so nothing to do */
136 	}
137 
138 	/* Check that the p_src_physp, p_dest_physp and p_req_physp
139 	   all share a pkey (doesn't have to be the same p_key). */
140 	if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_dest_physp,
141 				  sa->p_subn->opt.allow_both_pkeys)) {
142 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
143 			"Source and Dest PhysPorts do not share PKey\n");
144 		goto Exit;
145 	}
146 	if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_req_physp,
147 				  sa->p_subn->opt.allow_both_pkeys)) {
148 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
149 			"Source and Requester PhysPorts do not share PKey\n");
150 		goto Exit;
151 	}
152 	if (!osm_physp_share_pkey(sa->p_log, p_req_physp, p_dest_physp,
153 				  sa->p_subn->opt.allow_both_pkeys)) {
154 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
155 			"Requester and Dest PhysPorts do not share PKey\n");
156 		goto Exit;
157 	}
158 
159 	src_port_num = osm_physp_get_port_num(p_src_physp);
160 	dest_port_num = osm_physp_get_port_num(p_dest_physp);
161 
162 	if (comp_mask & IB_LR_COMPMASK_FROM_PORT)
163 		if (src_port_num != p_lr->from_port_num)
164 			goto Exit;
165 
166 	if (comp_mask & IB_LR_COMPMASK_TO_PORT)
167 		if (dest_port_num != p_lr->to_port_num)
168 			goto Exit;
169 
170 	from_base_lid = get_base_lid(p_src_physp);
171 	to_base_lid = get_base_lid(p_dest_physp);
172 
173 	lmc_mask = ~((1 << sa->p_subn->opt.lmc) - 1);
174 	lmc_mask = cl_hton16(lmc_mask);
175 
176 	if (comp_mask & IB_LR_COMPMASK_FROM_LID)
177 		if (from_base_lid != (p_lr->from_lid & lmc_mask))
178 			goto Exit;
179 
180 	if (comp_mask & IB_LR_COMPMASK_TO_LID)
181 		if (to_base_lid != (p_lr->to_lid & lmc_mask))
182 			goto Exit;
183 
184 	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Acquiring link record\n"
185 		"\t\t\t\tsrc port 0x%" PRIx64 " (port %u)"
186 		", dest port 0x%" PRIx64 " (port %u)\n",
187 		cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), src_port_num,
188 		cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)),
189 		dest_port_num);
190 
191 	lr_rcv_build_physp_link(sa, from_base_lid, to_base_lid, src_port_num,
192 				dest_port_num, p_list);
193 
194 Exit:
195 	OSM_LOG_EXIT(sa->p_log);
196 }
197 
198 static void lr_rcv_get_port_links(IN osm_sa_t * sa,
199 				  IN const ib_link_record_t * p_lr,
200 				  IN const osm_port_t * p_src_port,
201 				  IN const osm_port_t * p_dest_port,
202 				  IN const ib_net64_t comp_mask,
203 				  IN cl_qlist_t * p_list,
204 				  IN const osm_physp_t * p_req_physp)
205 {
206 	const osm_physp_t *p_src_physp;
207 	const osm_physp_t *p_dest_physp;
208 	const cl_qmap_t *p_node_tbl;
209 	osm_node_t *p_node;
210 	uint8_t port_num;
211 	uint8_t num_ports;
212 	uint8_t dest_num_ports;
213 	uint8_t dest_port_num;
214 
215 	OSM_LOG_ENTER(sa->p_log);
216 
217 	if (p_src_port) {
218 		if (p_dest_port) {
219 			/*
220 			   Build an LR for every link connected between both ports.
221 			   The inner function will discard physp combinations
222 			   that do not actually connect.  Don't bother screening
223 			   for that here.
224 			 */
225 			num_ports = osm_node_get_num_physp(p_src_port->p_node);
226 			dest_num_ports =
227 			    osm_node_get_num_physp(p_dest_port->p_node);
228 			for (port_num = 1; port_num < num_ports; port_num++) {
229 				p_src_physp =
230 				    osm_node_get_physp_ptr(p_src_port->p_node,
231 							   port_num);
232 				for (dest_port_num = 1;
233 				     dest_port_num < dest_num_ports;
234 				     dest_port_num++) {
235 					p_dest_physp =
236 					    osm_node_get_physp_ptr(p_dest_port->
237 								   p_node,
238 								   dest_port_num);
239 					/* both physical ports should be with data */
240 					if (p_src_physp && p_dest_physp)
241 						lr_rcv_get_physp_link
242 						    (sa, p_lr, p_src_physp,
243 						     p_dest_physp, comp_mask,
244 						     p_list, p_req_physp);
245 				}
246 			}
247 		} else {
248 			/*
249 			   Build an LR for every link connected from the source port.
250 			 */
251 			if (comp_mask & IB_LR_COMPMASK_FROM_PORT) {
252 				port_num = p_lr->from_port_num;
253 				/* If the port number is out of the range of the p_src_port, then
254 				   this couldn't be a relevant record. */
255 				if (port_num <
256 				    p_src_port->p_node->physp_tbl_size) {
257 					p_src_physp =
258 					    osm_node_get_physp_ptr(p_src_port->
259 								   p_node,
260 								   port_num);
261 					if (p_src_physp)
262 						lr_rcv_get_physp_link
263 						    (sa, p_lr, p_src_physp,
264 						     NULL, comp_mask, p_list,
265 						     p_req_physp);
266 				}
267 			} else {
268 				num_ports =
269 				    osm_node_get_num_physp(p_src_port->p_node);
270 				for (port_num = 1; port_num < num_ports;
271 				     port_num++) {
272 					p_src_physp =
273 					    osm_node_get_physp_ptr(p_src_port->
274 								   p_node,
275 								   port_num);
276 					if (p_src_physp)
277 						lr_rcv_get_physp_link
278 						    (sa, p_lr, p_src_physp,
279 						     NULL, comp_mask, p_list,
280 						     p_req_physp);
281 				}
282 			}
283 		}
284 	} else {
285 		if (p_dest_port) {
286 			/*
287 			   Build an LR for every link connected to the dest port.
288 			 */
289 			if (comp_mask & IB_LR_COMPMASK_TO_PORT) {
290 				port_num = p_lr->to_port_num;
291 				/* If the port number is out of the range of the p_dest_port, then
292 				   this couldn't be a relevant record. */
293 				if (port_num <
294 				    p_dest_port->p_node->physp_tbl_size) {
295 					p_dest_physp =
296 					    osm_node_get_physp_ptr(p_dest_port->
297 								   p_node,
298 								   port_num);
299 					if (p_dest_physp)
300 						lr_rcv_get_physp_link
301 						    (sa, p_lr, NULL,
302 						     p_dest_physp, comp_mask,
303 						     p_list, p_req_physp);
304 				}
305 			} else {
306 				num_ports =
307 				    osm_node_get_num_physp(p_dest_port->p_node);
308 				for (port_num = 1; port_num < num_ports;
309 				     port_num++) {
310 					p_dest_physp =
311 					    osm_node_get_physp_ptr(p_dest_port->
312 								   p_node,
313 								   port_num);
314 					if (p_dest_physp)
315 						lr_rcv_get_physp_link
316 						    (sa, p_lr, NULL,
317 						     p_dest_physp, comp_mask,
318 						     p_list, p_req_physp);
319 				}
320 			}
321 		} else {
322 			/*
323 			   Process the world (recurse once back into this function).
324 			 */
325 			p_node_tbl = &sa->p_subn->node_guid_tbl;
326 			p_node = (osm_node_t *) cl_qmap_head(p_node_tbl);
327 
328 			while (p_node != (osm_node_t *) cl_qmap_end(p_node_tbl)) {
329 				num_ports = osm_node_get_num_physp(p_node);
330 				for (port_num = 1; port_num < num_ports;
331 				     port_num++) {
332 					p_src_physp =
333 					    osm_node_get_physp_ptr(p_node,
334 								   port_num);
335 					if (p_src_physp)
336 						lr_rcv_get_physp_link
337 						    (sa, p_lr, p_src_physp,
338 						     NULL, comp_mask, p_list,
339 						     p_req_physp);
340 				}
341 				p_node = (osm_node_t *) cl_qmap_next(&p_node->
342 								     map_item);
343 			}
344 		}
345 	}
346 
347 	OSM_LOG_EXIT(sa->p_log);
348 }
349 
350 /**********************************************************************
351  Returns the SA status to return to the client.
352  **********************************************************************/
353 static ib_net16_t lr_rcv_get_end_points(IN osm_sa_t * sa,
354 					IN const osm_madw_t * p_madw,
355 					OUT const osm_port_t ** pp_src_port,
356 					OUT const osm_port_t ** pp_dest_port)
357 {
358 	const ib_link_record_t *p_lr;
359 	const ib_sa_mad_t *p_sa_mad;
360 	ib_net64_t comp_mask;
361 	ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS;
362 
363 	OSM_LOG_ENTER(sa->p_log);
364 
365 	/*
366 	   Determine what fields are valid and then get a pointer
367 	   to the source and destination port objects, if possible.
368 	 */
369 	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
370 	p_lr = (ib_link_record_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
371 
372 	comp_mask = p_sa_mad->comp_mask;
373 	*pp_src_port = NULL;
374 	*pp_dest_port = NULL;
375 
376 	if (comp_mask & IB_LR_COMPMASK_FROM_LID) {
377 		*pp_src_port = osm_get_port_by_lid(sa->p_subn, p_lr->from_lid);
378 		if (!*pp_src_port) {
379 			/*
380 			   This 'error' is the client's fault (bad lid) so
381 			   don't enter it as an error in our own log.
382 			   Return an error response to the client.
383 			 */
384 			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
385 				"No source port with LID %u\n",
386 				cl_ntoh16(p_lr->from_lid));
387 
388 			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
389 			goto Exit;
390 		}
391 	}
392 
393 	if (comp_mask & IB_LR_COMPMASK_TO_LID) {
394 		*pp_dest_port = osm_get_port_by_lid(sa->p_subn, p_lr->to_lid);
395 		if (!*pp_dest_port) {
396 			/*
397 			   This 'error' is the client's fault (bad lid) so
398 			   don't enter it as an error in our own log.
399 			   Return an error response to the client.
400 			 */
401 			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
402 				"No dest port with LID %u\n",
403 				cl_ntoh16(p_lr->to_lid));
404 
405 			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
406 			goto Exit;
407 		}
408 	}
409 
410 Exit:
411 	OSM_LOG_EXIT(sa->p_log);
412 	return sa_status;
413 }
414 
415 void osm_lr_rcv_process(IN void *context, IN void *data)
416 {
417 	osm_sa_t *sa = context;
418 	osm_madw_t *p_madw = data;
419 	const ib_link_record_t *p_lr;
420 	const ib_sa_mad_t *p_sa_mad;
421 	const osm_port_t *p_src_port;
422 	const osm_port_t *p_dest_port;
423 	cl_qlist_t lr_list;
424 	ib_net16_t status;
425 	osm_physp_t *p_req_physp;
426 
427 	OSM_LOG_ENTER(sa->p_log);
428 
429 	CL_ASSERT(p_madw);
430 
431 	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
432 	p_lr = ib_sa_mad_get_payload_ptr(p_sa_mad);
433 
434 	CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_LINK_RECORD);
435 
436 	/* we only support SubnAdmGet and SubnAdmGetTable methods */
437 	if (p_sa_mad->method != IB_MAD_METHOD_GET &&
438 	    p_sa_mad->method != IB_MAD_METHOD_GETTABLE) {
439 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1804: "
440 			"Unsupported Method (%s) for LinkRecord request\n",
441 			ib_get_sa_method_str(p_sa_mad->method));
442 		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
443 		goto Exit;
444 	}
445 
446 	cl_plock_acquire(sa->p_lock);
447 
448 	/* update the requester physical port */
449 	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
450 						osm_madw_get_mad_addr_ptr
451 						(p_madw));
452 	if (p_req_physp == NULL) {
453 		cl_plock_release(sa->p_lock);
454 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1805: "
455 			"Cannot find requester physical port\n");
456 		goto Exit;
457 	}
458 
459 	if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
460 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
461 			"Requester port GUID 0x%" PRIx64 "\n",
462 			cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
463 		osm_dump_link_record_v2(sa->p_log, p_lr, FILE_ID, OSM_LOG_DEBUG);
464 	}
465 
466 	cl_qlist_init(&lr_list);
467 
468 	/*
469 	   Most SA functions (including this one) are read-only on the
470 	   subnet object, so we grab the lock non-exclusively.
471 	 */
472 	status = lr_rcv_get_end_points(sa, p_madw, &p_src_port, &p_dest_port);
473 
474 	if (status == IB_SA_MAD_STATUS_SUCCESS)
475 		lr_rcv_get_port_links(sa, p_lr, p_src_port, p_dest_port,
476 				      p_sa_mad->comp_mask, &lr_list,
477 				      p_req_physp);
478 
479 	cl_plock_release(sa->p_lock);
480 
481 	osm_sa_respond(sa, p_madw, sizeof(ib_link_record_t), &lr_list);
482 
483 Exit:
484 	OSM_LOG_EXIT(sa->p_log);
485 }
486