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/*
37 * Abstract:
38 *    Implementation of osm_lr_rcv_t.
39 * This object represents the LinkRecord Receiver object.
40 * This object is part of the opensm family of objects.
41 */
42
43#if HAVE_CONFIG_H
44#  include <config.h>
45#endif				/* HAVE_CONFIG_H */
46
47#include <string.h>
48#include <iba/ib_types.h>
49#include <complib/cl_qmap.h>
50#include <complib/cl_debug.h>
51#include <vendor/osm_vendor_api.h>
52#include <opensm/osm_node.h>
53#include <opensm/osm_switch.h>
54#include <opensm/osm_helper.h>
55#include <opensm/osm_pkey.h>
56#include <opensm/osm_sa.h>
57
58typedef struct osm_lr_item {
59	cl_list_item_t list_item;
60	ib_link_record_t link_rec;
61} osm_lr_item_t;
62
63/**********************************************************************
64 **********************************************************************/
65static void
66__osm_lr_rcv_build_physp_link(IN osm_sa_t * sa,
67			      IN const ib_net16_t from_lid,
68			      IN const ib_net16_t to_lid,
69			      IN const uint8_t from_port,
70			      IN const uint8_t to_port, IN cl_qlist_t * p_list)
71{
72	osm_lr_item_t *p_lr_item;
73
74	p_lr_item = malloc(sizeof(*p_lr_item));
75	if (p_lr_item == NULL) {
76		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1801: "
77			"Unable to acquire link record\n"
78			"\t\t\t\tFrom port %u\n"
79			"\t\t\t\tTo port   %u\n"
80			"\t\t\t\tFrom lid  %u\n"
81			"\t\t\t\tTo lid    %u\n",
82			from_port, to_port,
83			cl_ntoh16(from_lid), cl_ntoh16(to_lid));
84		return;
85	}
86	memset(p_lr_item, 0, sizeof(*p_lr_item));
87
88	p_lr_item->link_rec.from_port_num = from_port;
89	p_lr_item->link_rec.to_port_num = to_port;
90	p_lr_item->link_rec.to_lid = to_lid;
91	p_lr_item->link_rec.from_lid = from_lid;
92
93	cl_qlist_insert_tail(p_list, &p_lr_item->list_item);
94}
95
96/**********************************************************************
97 **********************************************************************/
98static void
99__get_base_lid(IN const osm_physp_t * p_physp, OUT ib_net16_t * p_base_lid)
100{
101	if (p_physp->p_node->node_info.node_type == IB_NODE_TYPE_SWITCH)
102		*p_base_lid = osm_physp_get_base_lid
103			      (osm_node_get_physp_ptr(p_physp->p_node, 0));
104	else
105		*p_base_lid = osm_physp_get_base_lid(p_physp);
106}
107
108/**********************************************************************
109 **********************************************************************/
110static void
111__osm_lr_rcv_get_physp_link(IN osm_sa_t * sa,
112			    IN const ib_link_record_t * const p_lr,
113			    IN const osm_physp_t * p_src_physp,
114			    IN const osm_physp_t * p_dest_physp,
115			    IN const ib_net64_t comp_mask,
116			    IN cl_qlist_t * const p_list,
117			    IN const osm_physp_t * p_req_physp)
118{
119	uint8_t src_port_num;
120	uint8_t dest_port_num;
121	ib_net16_t from_base_lid;
122	ib_net16_t to_base_lid;
123	ib_net16_t lmc_mask;
124
125	OSM_LOG_ENTER(sa->p_log);
126
127	/*
128	   If only one end of the link is specified, determine
129	   the other side.
130	 */
131	if (p_src_physp) {
132		if (p_dest_physp) {
133			/*
134			   Ensure the two physp's are actually connected.
135			   If not, bail out.
136			 */
137			if (osm_physp_get_remote(p_src_physp) != p_dest_physp)
138				goto Exit;
139		} else {
140			p_dest_physp = osm_physp_get_remote(p_src_physp);
141			if (p_dest_physp == NULL)
142				goto Exit;
143		}
144	} else {
145		if (p_dest_physp) {
146			p_src_physp = osm_physp_get_remote(p_dest_physp);
147			if (p_src_physp == NULL)
148				goto Exit;
149		} else
150			goto Exit;	/* no physp's, so nothing to do */
151	}
152
153	/* Check that the p_src_physp, p_dest_physp and p_req_physp
154	   all share a pkey (doesn't have to be the same p_key). */
155	if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_dest_physp)) {
156		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
157			"Source and Dest PhysPorts do not share PKey\n");
158		goto Exit;
159	}
160	if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_req_physp)) {
161		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
162			"Source and Requester PhysPorts do not share PKey\n");
163		goto Exit;
164	}
165	if (!osm_physp_share_pkey(sa->p_log, p_req_physp, p_dest_physp)) {
166		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
167			"Requester and Dest PhysPorts do not share PKey\n");
168		goto Exit;
169	}
170
171	src_port_num = osm_physp_get_port_num(p_src_physp);
172	dest_port_num = osm_physp_get_port_num(p_dest_physp);
173
174	if (comp_mask & IB_LR_COMPMASK_FROM_PORT)
175		if (src_port_num != p_lr->from_port_num)
176			goto Exit;
177
178	if (comp_mask & IB_LR_COMPMASK_TO_PORT)
179		if (dest_port_num != p_lr->to_port_num)
180			goto Exit;
181
182	__get_base_lid(p_src_physp, &from_base_lid);
183	__get_base_lid(p_dest_physp, &to_base_lid);
184
185	lmc_mask = ~((1 << sa->p_subn->opt.lmc) - 1);
186	lmc_mask = cl_hton16(lmc_mask);
187
188	if (comp_mask & IB_LR_COMPMASK_FROM_LID)
189		if (from_base_lid != (p_lr->from_lid & lmc_mask))
190			goto Exit;
191
192	if (comp_mask & IB_LR_COMPMASK_TO_LID)
193		if (to_base_lid != (p_lr->to_lid & lmc_mask))
194			goto Exit;
195
196	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Acquiring link record\n"
197		"\t\t\t\tsrc port 0x%" PRIx64 " (port %u)"
198		", dest port 0x%" PRIx64 " (port %u)\n",
199		cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), src_port_num,
200		cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)),
201		dest_port_num);
202
203
204	__osm_lr_rcv_build_physp_link(sa, from_base_lid, to_base_lid,
205				      src_port_num, dest_port_num, p_list);
206
207Exit:
208	OSM_LOG_EXIT(sa->p_log);
209}
210
211/**********************************************************************
212 **********************************************************************/
213static void
214__osm_lr_rcv_get_port_links(IN osm_sa_t * sa,
215			    IN const ib_link_record_t * const p_lr,
216			    IN const osm_port_t * p_src_port,
217			    IN const osm_port_t * p_dest_port,
218			    IN const ib_net64_t comp_mask,
219			    IN cl_qlist_t * const p_list,
220			    IN const osm_physp_t * p_req_physp)
221{
222	const osm_physp_t *p_src_physp;
223	const osm_physp_t *p_dest_physp;
224	const cl_qmap_t *p_node_tbl;
225	osm_node_t * p_node;
226	uint8_t port_num;
227	uint8_t num_ports;
228	uint8_t dest_num_ports;
229	uint8_t dest_port_num;
230
231	OSM_LOG_ENTER(sa->p_log);
232
233	if (p_src_port) {
234		if (p_dest_port) {
235			/*
236			   Build an LR for every link connected between both ports.
237			   The inner function will discard physp combinations
238			   that do not actually connect.  Don't bother screening
239			   for that here.
240			 */
241			num_ports = osm_node_get_num_physp(p_src_port->p_node);
242			dest_num_ports =
243			    osm_node_get_num_physp(p_dest_port->p_node);
244			for (port_num = 1; port_num < num_ports; port_num++) {
245				p_src_physp =
246				    osm_node_get_physp_ptr(p_src_port->p_node,
247							   port_num);
248				for (dest_port_num = 1;
249				     dest_port_num < dest_num_ports;
250				     dest_port_num++) {
251					p_dest_physp =
252					    osm_node_get_physp_ptr(p_dest_port->
253								   p_node,
254								   dest_port_num);
255					/* both physical ports should be with data */
256					if (p_src_physp && p_dest_physp)
257						__osm_lr_rcv_get_physp_link
258						    (sa, p_lr, p_src_physp,
259						     p_dest_physp, comp_mask,
260						     p_list, p_req_physp);
261				}
262			}
263		} else {
264			/*
265			   Build an LR for every link connected from the source port.
266			 */
267			if (comp_mask & IB_LR_COMPMASK_FROM_PORT) {
268				port_num = p_lr->from_port_num;
269				/* If the port number is out of the range of the p_src_port, then
270				   this couldn't be a relevant record. */
271				if (port_num <
272				    p_src_port->p_node->physp_tbl_size) {
273					p_src_physp =
274					    osm_node_get_physp_ptr(p_src_port->
275								   p_node,
276								   port_num);
277					if (p_src_physp)
278						__osm_lr_rcv_get_physp_link
279						    (sa, p_lr, p_src_physp,
280						     NULL, comp_mask, p_list,
281						     p_req_physp);
282				}
283			} else {
284				num_ports =
285				    osm_node_get_num_physp(p_src_port->p_node);
286				for (port_num = 1; port_num < num_ports;
287				     port_num++) {
288					p_src_physp =
289					    osm_node_get_physp_ptr(p_src_port->
290								   p_node,
291								   port_num);
292					if (p_src_physp)
293						__osm_lr_rcv_get_physp_link
294						    (sa, p_lr, p_src_physp,
295						     NULL, comp_mask, p_list,
296						     p_req_physp);
297				}
298			}
299		}
300	} else {
301		if (p_dest_port) {
302			/*
303			   Build an LR for every link connected to the dest port.
304			 */
305			if (comp_mask & IB_LR_COMPMASK_TO_PORT) {
306				port_num = p_lr->to_port_num;
307				/* If the port number is out of the range of the p_dest_port, then
308				   this couldn't be a relevant record. */
309				if (port_num <
310				    p_dest_port->p_node->physp_tbl_size) {
311					p_dest_physp =
312					    osm_node_get_physp_ptr(p_dest_port->
313								   p_node,
314								   port_num);
315					if (p_dest_physp)
316						__osm_lr_rcv_get_physp_link
317						    (sa, p_lr, NULL,
318						     p_dest_physp, comp_mask,
319						     p_list, p_req_physp);
320				}
321			} else {
322				num_ports =
323				    osm_node_get_num_physp(p_dest_port->p_node);
324				for (port_num = 1; port_num < num_ports;
325				     port_num++) {
326					p_dest_physp =
327					    osm_node_get_physp_ptr(p_dest_port->
328								   p_node,
329								   port_num);
330					if (p_dest_physp)
331						__osm_lr_rcv_get_physp_link
332						    (sa, p_lr, NULL,
333						     p_dest_physp, comp_mask,
334						     p_list, p_req_physp);
335				}
336			}
337		} else {
338			/*
339			   Process the world (recurse once back into this function).
340			 */
341			p_node_tbl = &sa->p_subn->node_guid_tbl;
342			p_node = (osm_node_t *)cl_qmap_head(p_node_tbl);
343
344			while (p_node != (osm_node_t *)cl_qmap_end(p_node_tbl)) {
345				num_ports = osm_node_get_num_physp(p_node);
346				for (port_num = 1; port_num < num_ports;
347				     port_num++) {
348					p_src_physp =
349					    osm_node_get_physp_ptr(p_node,
350								   port_num);
351					if (p_src_physp)
352						__osm_lr_rcv_get_physp_link
353						    (sa, p_lr, p_src_physp,
354						     NULL, comp_mask, p_list,
355						     p_req_physp);
356				}
357				p_node = (osm_node_t *) cl_qmap_next(&p_node->
358								     map_item);
359			}
360		}
361	}
362
363	OSM_LOG_EXIT(sa->p_log);
364}
365
366/**********************************************************************
367 Returns the SA status to return to the client.
368 **********************************************************************/
369static ib_net16_t
370__osm_lr_rcv_get_end_points(IN osm_sa_t * sa,
371			    IN const osm_madw_t * const p_madw,
372			    OUT const osm_port_t ** const pp_src_port,
373			    OUT const osm_port_t ** const pp_dest_port)
374{
375	const ib_link_record_t *p_lr;
376	const ib_sa_mad_t *p_sa_mad;
377	ib_net64_t comp_mask;
378	ib_api_status_t status;
379	ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS;
380
381	OSM_LOG_ENTER(sa->p_log);
382
383	/*
384	   Determine what fields are valid and then get a pointer
385	   to the source and destination port objects, if possible.
386	 */
387	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
388	p_lr = (ib_link_record_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
389
390	comp_mask = p_sa_mad->comp_mask;
391	*pp_src_port = NULL;
392	*pp_dest_port = NULL;
393
394	if (p_sa_mad->comp_mask & IB_LR_COMPMASK_FROM_LID) {
395		status = osm_get_port_by_base_lid(sa->p_subn,
396						  p_lr->from_lid, pp_src_port);
397
398		if ((status != IB_SUCCESS) || (*pp_src_port == NULL)) {
399			/*
400			   This 'error' is the client's fault (bad lid) so
401			   don't enter it as an error in our own log.
402			   Return an error response to the client.
403			 */
404			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
405				"No source port with LID %u\n",
406				cl_ntoh16(p_lr->from_lid));
407
408			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
409			goto Exit;
410		}
411	}
412
413	if (p_sa_mad->comp_mask & IB_LR_COMPMASK_TO_LID) {
414		status = osm_get_port_by_base_lid(sa->p_subn,
415						  p_lr->to_lid, pp_dest_port);
416
417		if ((status != IB_SUCCESS) || (*pp_dest_port == NULL)) {
418			/*
419			   This 'error' is the client's fault (bad lid) so
420			   don't enter it as an error in our own log.
421			   Return an error response to the client.
422			 */
423			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
424				"No dest port with LID %u\n",
425				cl_ntoh16(p_lr->to_lid));
426
427			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
428			goto Exit;
429		}
430	}
431
432Exit:
433	OSM_LOG_EXIT(sa->p_log);
434	return (sa_status);
435}
436
437/**********************************************************************
438 **********************************************************************/
439void osm_lr_rcv_process(IN void *context, IN void *data)
440{
441	osm_sa_t *sa = context;
442	osm_madw_t *p_madw = data;
443	const ib_link_record_t *p_lr;
444	const ib_sa_mad_t *p_sa_mad;
445	const osm_port_t *p_src_port;
446	const osm_port_t *p_dest_port;
447	cl_qlist_t lr_list;
448	ib_net16_t sa_status;
449	osm_physp_t *p_req_physp;
450
451	OSM_LOG_ENTER(sa->p_log);
452
453	CL_ASSERT(p_madw);
454
455	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
456	p_lr = (ib_link_record_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
457
458	CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_LINK_RECORD);
459
460	/* we only support SubnAdmGet and SubnAdmGetTable methods */
461	if (p_sa_mad->method != IB_MAD_METHOD_GET &&
462	    p_sa_mad->method != IB_MAD_METHOD_GETTABLE) {
463		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1804: "
464			"Unsupported Method (%s)\n",
465			ib_get_sa_method_str(p_sa_mad->method));
466		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
467		goto Exit;
468	}
469
470	/* update the requester physical port. */
471	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
472						osm_madw_get_mad_addr_ptr
473						(p_madw));
474	if (p_req_physp == NULL) {
475		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1805: "
476			"Cannot find requester physical port\n");
477		goto Exit;
478	}
479
480	if (osm_log_is_active(sa->p_log, OSM_LOG_DEBUG))
481		osm_dump_link_record(sa->p_log, p_lr, OSM_LOG_DEBUG);
482
483	cl_qlist_init(&lr_list);
484
485	/*
486	   Most SA functions (including this one) are read-only on the
487	   subnet object, so we grab the lock non-exclusively.
488	 */
489	cl_plock_acquire(sa->p_lock);
490
491	sa_status = __osm_lr_rcv_get_end_points(sa, p_madw,
492						&p_src_port, &p_dest_port);
493
494	if (sa_status == IB_SA_MAD_STATUS_SUCCESS)
495		__osm_lr_rcv_get_port_links(sa, p_lr, p_src_port,
496					    p_dest_port, p_sa_mad->comp_mask,
497					    &lr_list, p_req_physp);
498
499	cl_plock_release(sa->p_lock);
500
501	osm_sa_respond(sa, p_madw, sizeof(ib_link_record_t), &lr_list);
502
503Exit:
504	OSM_LOG_EXIT(sa->p_log);
505}
506