osm_sa_link_record.c revision 326169
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
63static 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
89static 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
96static 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
194Exit:
195	OSM_LOG_EXIT(sa->p_log);
196}
197
198static 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 **********************************************************************/
353static 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
410Exit:
411	OSM_LOG_EXIT(sa->p_log);
412	return sa_status;
413}
414
415void 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
483Exit:
484	OSM_LOG_EXIT(sa->p_log);
485}
486