osm_sa_link_record.c revision 329564
1145516Sdarrenr/*
2145516Sdarrenr * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3145516Sdarrenr * Copyright (c) 2002-2007 Mellanox Technologies LTD. All rights reserved.
4255332Scy * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5145516Sdarrenr * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
6145516Sdarrenr *
7145516Sdarrenr * This software is available to you under a choice of one of two
8145516Sdarrenr * licenses.  You may choose to be licensed under the terms of the GNU
9145516Sdarrenr * General Public License (GPL) Version 2, available from the file
10145516Sdarrenr * COPYING in the main directory of this source tree, or the
11145516Sdarrenr * OpenIB.org BSD license below:
12145516Sdarrenr *
13145516Sdarrenr *     Redistribution and use in source and binary forms, with or
14145516Sdarrenr *     without modification, are permitted provided that the following
15145516Sdarrenr *     conditions are met:
16145516Sdarrenr *
17145516Sdarrenr *      - Redistributions of source code must retain the above
18145516Sdarrenr *        copyright notice, this list of conditions and the following
19145516Sdarrenr *        disclaimer.
20145516Sdarrenr *
21145516Sdarrenr *      - Redistributions in binary form must reproduce the above
22145516Sdarrenr *        copyright notice, this list of conditions and the following
23145516Sdarrenr *        disclaimer in the documentation and/or other materials
24145516Sdarrenr *        provided with the distribution.
25145516Sdarrenr *
26145516Sdarrenr * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27145516Sdarrenr * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28145516Sdarrenr * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29145516Sdarrenr * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30145516Sdarrenr * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31145516Sdarrenr * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32145516Sdarrenr * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33145516Sdarrenr * SOFTWARE.
34145516Sdarrenr *
35255332Scy */
36255332Scy
37255332Scy/*
38255332Scy * Abstract:
39145516Sdarrenr *    Implementation of osm_lr_rcv_t.
40145516Sdarrenr * This object represents the LinkRecord Receiver object.
41145516Sdarrenr * This object is part of the opensm family of objects.
42145516Sdarrenr */
43145516Sdarrenr
44145516Sdarrenr#if HAVE_CONFIG_H
45145516Sdarrenr#  include <config.h>
46145516Sdarrenr#endif				/* HAVE_CONFIG_H */
47145516Sdarrenr
48145516Sdarrenr#include <string.h>
49145516Sdarrenr#include <iba/ib_types.h>
50145516Sdarrenr#include <complib/cl_qmap.h>
51145516Sdarrenr#include <complib/cl_debug.h>
52145516Sdarrenr#include <opensm/osm_file_ids.h>
53145516Sdarrenr#define FILE_ID OSM_FILE_SA_LINK_RECORD_C
54145516Sdarrenr#include <vendor/osm_vendor_api.h>
55145516Sdarrenr#include <opensm/osm_node.h>
56145516Sdarrenr#include <opensm/osm_switch.h>
57145516Sdarrenr#include <opensm/osm_helper.h>
58145516Sdarrenr#include <opensm/osm_pkey.h>
59145516Sdarrenr#include <opensm/osm_sa.h>
60145516Sdarrenr
61145516Sdarrenr#define SA_LR_RESP_SIZE SA_ITEM_RESP_SIZE(link_rec)
62145516Sdarrenr
63145516Sdarrenrstatic void lr_rcv_build_physp_link(IN osm_sa_t * sa, IN ib_net16_t from_lid,
64145516Sdarrenr				    IN ib_net16_t to_lid, IN uint8_t from_port,
65145516Sdarrenr				    IN uint8_t to_port, IN cl_qlist_t * p_list)
66145516Sdarrenr{
67145516Sdarrenr	osm_sa_item_t *p_lr_item;
68145516Sdarrenr
69145516Sdarrenr	p_lr_item = malloc(SA_LR_RESP_SIZE);
70145516Sdarrenr	if (p_lr_item == NULL) {
71145516Sdarrenr		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1801: "
72145516Sdarrenr			"Unable to acquire link record\n"
73145516Sdarrenr			"\t\t\t\tFrom port %u\n" "\t\t\t\tTo port   %u\n"
74145516Sdarrenr			"\t\t\t\tFrom lid  %u\n" "\t\t\t\tTo lid    %u\n",
75145516Sdarrenr			from_port, to_port,
76145516Sdarrenr			cl_ntoh16(from_lid), cl_ntoh16(to_lid));
77145516Sdarrenr		return;
78145516Sdarrenr	}
79145516Sdarrenr	memset(p_lr_item, 0, SA_LR_RESP_SIZE);
80145516Sdarrenr
81145516Sdarrenr	p_lr_item->resp.link_rec.from_port_num = from_port;
82145516Sdarrenr	p_lr_item->resp.link_rec.to_port_num = to_port;
83145516Sdarrenr	p_lr_item->resp.link_rec.to_lid = to_lid;
84145516Sdarrenr	p_lr_item->resp.link_rec.from_lid = from_lid;
85145516Sdarrenr
86145516Sdarrenr	cl_qlist_insert_tail(p_list, &p_lr_item->list_item);
87145516Sdarrenr}
88145516Sdarrenr
89145516Sdarrenrstatic ib_net16_t get_base_lid(IN const osm_physp_t * p_physp)
90145516Sdarrenr{
91145516Sdarrenr	if (p_physp->p_node->node_info.node_type == IB_NODE_TYPE_SWITCH)
92145516Sdarrenr		p_physp = osm_node_get_physp_ptr(p_physp->p_node, 0);
93145516Sdarrenr	return osm_physp_get_base_lid(p_physp);
94145516Sdarrenr}
95145516Sdarrenr
96145516Sdarrenrstatic void lr_rcv_get_physp_link(IN osm_sa_t * sa,
97145516Sdarrenr				  IN const ib_link_record_t * p_lr,
98145516Sdarrenr				  IN const osm_physp_t * p_src_physp,
99145516Sdarrenr				  IN const osm_physp_t * p_dest_physp,
100145516Sdarrenr				  IN const ib_net64_t comp_mask,
101255332Scy				  IN cl_qlist_t * p_list,
102145516Sdarrenr				  IN const osm_physp_t * p_req_physp)
103145516Sdarrenr{
104145516Sdarrenr	uint8_t src_port_num;
105145516Sdarrenr	uint8_t dest_port_num;
106145516Sdarrenr	ib_net16_t from_base_lid;
107255332Scy	ib_net16_t to_base_lid;
108255332Scy	ib_net16_t lmc_mask;
109255332Scy
110255332Scy	OSM_LOG_ENTER(sa->p_log);
111255332Scy
112145516Sdarrenr	/*
113255332Scy	   If only one end of the link is specified, determine
114145516Sdarrenr	   the other side.
115255332Scy	 */
116255332Scy	if (p_src_physp) {
117255332Scy		if (p_dest_physp) {
118255332Scy			/*
119255332Scy			   Ensure the two physp's are actually connected.
120255332Scy			   If not, bail out.
121255332Scy			 */
122255332Scy			if (osm_physp_get_remote(p_src_physp) != p_dest_physp)
123255332Scy				goto Exit;
124255332Scy		} else {
125255332Scy			p_dest_physp = osm_physp_get_remote(p_src_physp);
126255332Scy			if (p_dest_physp == NULL)
127255332Scy				goto Exit;
128255332Scy		}
129255332Scy	} else {
130255332Scy		if (p_dest_physp) {
131255332Scy			p_src_physp = osm_physp_get_remote(p_dest_physp);
132255332Scy			if (p_src_physp == NULL)
133255332Scy				goto Exit;
134255332Scy		} else
135255332Scy			goto Exit;	/* no physp's, so nothing to do */
136255332Scy	}
137255332Scy
138255332Scy	/* Check that the p_src_physp, p_dest_physp and p_req_physp
139145516Sdarrenr	   all share a pkey (doesn't have to be the same p_key). */
140255332Scy	if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_dest_physp,
141255332Scy				  sa->p_subn->opt.allow_both_pkeys)) {
142255332Scy		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
143255332Scy			"Source and Dest PhysPorts do not share PKey\n");
144255332Scy		goto Exit;
145255332Scy	}
146145516Sdarrenr	if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_req_physp,
147145516Sdarrenr				  sa->p_subn->opt.allow_both_pkeys)) {
148255332Scy		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
149255332Scy			"Source and Requester PhysPorts do not share PKey\n");
150255332Scy		goto Exit;
151145516Sdarrenr	}
152145516Sdarrenr	if (!osm_physp_share_pkey(sa->p_log, p_req_physp, p_dest_physp,
153145516Sdarrenr				  sa->p_subn->opt.allow_both_pkeys)) {
154255332Scy		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
155255332Scy			"Requester and Dest PhysPorts do not share PKey\n");
156255332Scy		goto Exit;
157255332Scy	}
158255332Scy
159255332Scy	src_port_num = osm_physp_get_port_num(p_src_physp);
160255332Scy	dest_port_num = osm_physp_get_port_num(p_dest_physp);
161255332Scy
162255332Scy	if (comp_mask & IB_LR_COMPMASK_FROM_PORT)
163255332Scy		if (src_port_num != p_lr->from_port_num)
164255332Scy			goto Exit;
165255332Scy
166255332Scy	if (comp_mask & IB_LR_COMPMASK_TO_PORT)
167255332Scy		if (dest_port_num != p_lr->to_port_num)
168255332Scy			goto Exit;
169255332Scy
170255332Scy	from_base_lid = get_base_lid(p_src_physp);
171255332Scy	to_base_lid = get_base_lid(p_dest_physp);
172255332Scy
173255332Scy	lmc_mask = ~((1 << sa->p_subn->opt.lmc) - 1);
174255332Scy	lmc_mask = cl_hton16(lmc_mask);
175255332Scy
176255332Scy	if (comp_mask & IB_LR_COMPMASK_FROM_LID)
177255332Scy		if (from_base_lid != (p_lr->from_lid & lmc_mask))
178145516Sdarrenr			goto Exit;
179255332Scy
180145516Sdarrenr	if (comp_mask & IB_LR_COMPMASK_TO_LID)
181145516Sdarrenr		if (to_base_lid != (p_lr->to_lid & lmc_mask))
182145516Sdarrenr			goto Exit;
183145516Sdarrenr
184145516Sdarrenr	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Acquiring link record\n"
185145516Sdarrenr		"\t\t\t\tsrc port 0x%" PRIx64 " (port %u)"
186255332Scy		", dest port 0x%" PRIx64 " (port %u)\n",
187255332Scy		cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), src_port_num,
188255332Scy		cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)),
189255332Scy		dest_port_num);
190145516Sdarrenr
191255332Scy	lr_rcv_build_physp_link(sa, from_base_lid, to_base_lid, src_port_num,
192255332Scy				dest_port_num, p_list);
193255332Scy
194255332ScyExit:
195255332Scy	OSM_LOG_EXIT(sa->p_log);
196255332Scy}
197255332Scy
198255332Scystatic void lr_rcv_get_port_links(IN osm_sa_t * sa,
199255332Scy				  IN const ib_link_record_t * p_lr,
200255332Scy				  IN const osm_port_t * p_src_port,
201255332Scy				  IN const osm_port_t * p_dest_port,
202255332Scy				  IN const ib_net64_t comp_mask,
203255332Scy				  IN cl_qlist_t * p_list,
204255332Scy				  IN const osm_physp_t * p_req_physp)
205255332Scy{
206255332Scy	const osm_physp_t *p_src_physp;
207255332Scy	const osm_physp_t *p_dest_physp;
208255332Scy	const cl_qmap_t *p_node_tbl;
209255332Scy	osm_node_t *p_node;
210255332Scy	uint8_t port_num;
211255332Scy	uint8_t num_ports;
212255332Scy	uint8_t dest_num_ports;
213255332Scy	uint8_t dest_port_num;
214255332Scy
215255332Scy	OSM_LOG_ENTER(sa->p_log);
216255332Scy
217255332Scy	if (p_src_port) {
218255332Scy		if (p_dest_port) {
219255332Scy			/*
220255332Scy			   Build an LR for every link connected between both ports.
221255332Scy			   The inner function will discard physp combinations
222255332Scy			   that do not actually connect.  Don't bother screening
223255332Scy			   for that here.
224255332Scy			 */
225255332Scy			num_ports = osm_node_get_num_physp(p_src_port->p_node);
226255332Scy			dest_num_ports =
227255332Scy			    osm_node_get_num_physp(p_dest_port->p_node);
228255332Scy			for (port_num = 1; port_num < num_ports; port_num++) {
229255332Scy				p_src_physp =
230255332Scy				    osm_node_get_physp_ptr(p_src_port->p_node,
231145516Sdarrenr							   port_num);
232255332Scy				for (dest_port_num = 1;
233145516Sdarrenr				     dest_port_num < dest_num_ports;
234255332Scy				     dest_port_num++) {
235255332Scy					p_dest_physp =
236255332Scy					    osm_node_get_physp_ptr(p_dest_port->
237255332Scy								   p_node,
238145516Sdarrenr								   dest_port_num);
239255332Scy					/* both physical ports should be with data */
240145516Sdarrenr					if (p_src_physp && p_dest_physp)
241145516Sdarrenr						lr_rcv_get_physp_link
242145516Sdarrenr						    (sa, p_lr, p_src_physp,
243145516Sdarrenr						     p_dest_physp, comp_mask,
244145516Sdarrenr						     p_list, p_req_physp);
245255332Scy				}
246255332Scy			}
247255332Scy		} else {
248255332Scy			/*
249255332Scy			   Build an LR for every link connected from the source port.
250255332Scy			 */
251255332Scy			if (comp_mask & IB_LR_COMPMASK_FROM_PORT) {
252255332Scy				port_num = p_lr->from_port_num;
253255332Scy				/* If the port number is out of the range of the p_src_port, then
254255332Scy				   this couldn't be a relevant record. */
255255332Scy				if (port_num <
256255332Scy				    p_src_port->p_node->physp_tbl_size) {
257255332Scy					p_src_physp =
258255332Scy					    osm_node_get_physp_ptr(p_src_port->
259255332Scy								   p_node,
260255332Scy								   port_num);
261255332Scy					if (p_src_physp)
262255332Scy						lr_rcv_get_physp_link
263255332Scy						    (sa, p_lr, p_src_physp,
264255332Scy						     NULL, comp_mask, p_list,
265255332Scy						     p_req_physp);
266255332Scy				}
267255332Scy			} else {
268255332Scy				num_ports =
269255332Scy				    osm_node_get_num_physp(p_src_port->p_node);
270255332Scy				for (port_num = 1; port_num < num_ports;
271255332Scy				     port_num++) {
272255332Scy					p_src_physp =
273255332Scy					    osm_node_get_physp_ptr(p_src_port->
274255332Scy								   p_node,
275255332Scy								   port_num);
276255332Scy					if (p_src_physp)
277255332Scy						lr_rcv_get_physp_link
278255332Scy						    (sa, p_lr, p_src_physp,
279255332Scy						     NULL, comp_mask, p_list,
280255332Scy						     p_req_physp);
281255332Scy				}
282255332Scy			}
283255332Scy		}
284255332Scy	} else {
285255332Scy		if (p_dest_port) {
286255332Scy			/*
287255332Scy			   Build an LR for every link connected to the dest port.
288255332Scy			 */
289255332Scy			if (comp_mask & IB_LR_COMPMASK_TO_PORT) {
290255332Scy				port_num = p_lr->to_port_num;
291255332Scy				/* If the port number is out of the range of the p_dest_port, then
292255332Scy				   this couldn't be a relevant record. */
293255332Scy				if (port_num <
294255332Scy				    p_dest_port->p_node->physp_tbl_size) {
295255332Scy					p_dest_physp =
296255332Scy					    osm_node_get_physp_ptr(p_dest_port->
297255332Scy								   p_node,
298255332Scy								   port_num);
299255332Scy					if (p_dest_physp)
300255332Scy						lr_rcv_get_physp_link
301255332Scy						    (sa, p_lr, NULL,
302255332Scy						     p_dest_physp, comp_mask,
303255332Scy						     p_list, p_req_physp);
304255332Scy				}
305255332Scy			} else {
306255332Scy				num_ports =
307255332Scy				    osm_node_get_num_physp(p_dest_port->p_node);
308255332Scy				for (port_num = 1; port_num < num_ports;
309255332Scy				     port_num++) {
310255332Scy					p_dest_physp =
311145516Sdarrenr					    osm_node_get_physp_ptr(p_dest_port->
312145516Sdarrenr								   p_node,
313255332Scy								   port_num);
314145516Sdarrenr					if (p_dest_physp)
315145516Sdarrenr						lr_rcv_get_physp_link
316145516Sdarrenr						    (sa, p_lr, NULL,
317145516Sdarrenr						     p_dest_physp, comp_mask,
318145516Sdarrenr						     p_list, p_req_physp);
319145516Sdarrenr				}
320145516Sdarrenr			}
321255332Scy		} else {
322255332Scy			/*
323255332Scy			   Process the world (recurse once back into this function).
324255332Scy			 */
325145516Sdarrenr			p_node_tbl = &sa->p_subn->node_guid_tbl;
326145516Sdarrenr			p_node = (osm_node_t *) cl_qmap_head(p_node_tbl);
327145516Sdarrenr
328145516Sdarrenr			while (p_node != (osm_node_t *) cl_qmap_end(p_node_tbl)) {
329145516Sdarrenr				num_ports = osm_node_get_num_physp(p_node);
330145516Sdarrenr				for (port_num = 1; port_num < num_ports;
331145516Sdarrenr				     port_num++) {
332145516Sdarrenr					p_src_physp =
333145516Sdarrenr					    osm_node_get_physp_ptr(p_node,
334145516Sdarrenr								   port_num);
335145516Sdarrenr					if (p_src_physp)
336145516Sdarrenr						lr_rcv_get_physp_link
337145516Sdarrenr						    (sa, p_lr, p_src_physp,
338145516Sdarrenr						     NULL, comp_mask, p_list,
339255332Scy						     p_req_physp);
340145516Sdarrenr				}
341145516Sdarrenr				p_node = (osm_node_t *) cl_qmap_next(&p_node->
342145516Sdarrenr								     map_item);
343145516Sdarrenr			}
344145516Sdarrenr		}
345145516Sdarrenr	}
346145516Sdarrenr
347255332Scy	OSM_LOG_EXIT(sa->p_log);
348255332Scy}
349255332Scy
350255332Scy/**********************************************************************
351145516Sdarrenr Returns the SA status to return to the client.
352145516Sdarrenr **********************************************************************/
353145516Sdarrenrstatic ib_net16_t lr_rcv_get_end_points(IN osm_sa_t * sa,
354145516Sdarrenr					IN const osm_madw_t * p_madw,
355145516Sdarrenr					OUT const osm_port_t ** pp_src_port,
356145516Sdarrenr					OUT const osm_port_t ** pp_dest_port)
357145516Sdarrenr{
358145516Sdarrenr	const ib_link_record_t *p_lr;
359145516Sdarrenr	const ib_sa_mad_t *p_sa_mad;
360145516Sdarrenr	ib_net64_t comp_mask;
361145516Sdarrenr	ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS;
362145516Sdarrenr
363145516Sdarrenr	OSM_LOG_ENTER(sa->p_log);
364145516Sdarrenr
365145516Sdarrenr	/*
366145516Sdarrenr	   Determine what fields are valid and then get a pointer
367145516Sdarrenr	   to the source and destination port objects, if possible.
368145516Sdarrenr	 */
369255332Scy	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
370145516Sdarrenr	p_lr = (ib_link_record_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
371145516Sdarrenr
372145516Sdarrenr	comp_mask = p_sa_mad->comp_mask;
373145516Sdarrenr	*pp_src_port = NULL;
374145516Sdarrenr	*pp_dest_port = NULL;
375145516Sdarrenr
376145516Sdarrenr	if (comp_mask & IB_LR_COMPMASK_FROM_LID) {
377255332Scy		*pp_src_port = osm_get_port_by_lid(sa->p_subn, p_lr->from_lid);
378255332Scy		if (!*pp_src_port) {
379255332Scy			/*
380255332Scy			   This 'error' is the client's fault (bad lid) so
381145516Sdarrenr			   don't enter it as an error in our own log.
382255332Scy			   Return an error response to the client.
383255332Scy			 */
384145516Sdarrenr			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
385145516Sdarrenr				"No source port with LID %u\n",
386145516Sdarrenr				cl_ntoh16(p_lr->from_lid));
387145516Sdarrenr
388145516Sdarrenr			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
389145516Sdarrenr			goto Exit;
390153876Sguido		}
391153876Sguido	}
392153876Sguido
393153876Sguido	if (comp_mask & IB_LR_COMPMASK_TO_LID) {
394145516Sdarrenr		*pp_dest_port = osm_get_port_by_lid(sa->p_subn, p_lr->to_lid);
395145516Sdarrenr		if (!*pp_dest_port) {
396145516Sdarrenr			/*
397145516Sdarrenr			   This 'error' is the client's fault (bad lid) so
398145516Sdarrenr			   don't enter it as an error in our own log.
399145516Sdarrenr			   Return an error response to the client.
400145516Sdarrenr			 */
401145516Sdarrenr			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
402145516Sdarrenr				"No dest port with LID %u\n",
403145516Sdarrenr				cl_ntoh16(p_lr->to_lid));
404145516Sdarrenr
405145516Sdarrenr			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
406145516Sdarrenr			goto Exit;
407153876Sguido		}
408153876Sguido	}
409153876Sguido
410153876SguidoExit:
411145516Sdarrenr	OSM_LOG_EXIT(sa->p_log);
412145516Sdarrenr	return sa_status;
413145516Sdarrenr}
414145516Sdarrenr
415145516Sdarrenrvoid osm_lr_rcv_process(IN void *context, IN void *data)
416145516Sdarrenr{
417145516Sdarrenr	osm_sa_t *sa = context;
418145516Sdarrenr	osm_madw_t *p_madw = data;
419145516Sdarrenr	const ib_link_record_t *p_lr;
420145516Sdarrenr	const ib_sa_mad_t *p_sa_mad;
421145516Sdarrenr	const osm_port_t *p_src_port;
422255332Scy	const osm_port_t *p_dest_port;
423255332Scy	cl_qlist_t lr_list;
424255332Scy	ib_net16_t status;
425145516Sdarrenr	osm_physp_t *p_req_physp;
426145516Sdarrenr
427145516Sdarrenr	OSM_LOG_ENTER(sa->p_log);
428145516Sdarrenr
429255332Scy	CL_ASSERT(p_madw);
430145516Sdarrenr
431145516Sdarrenr	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
432145516Sdarrenr	p_lr = ib_sa_mad_get_payload_ptr(p_sa_mad);
433145516Sdarrenr
434145516Sdarrenr	CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_LINK_RECORD);
435145516Sdarrenr
436255332Scy	/* we only support SubnAdmGet and SubnAdmGetTable methods */
437255332Scy	if (p_sa_mad->method != IB_MAD_METHOD_GET &&
438255332Scy	    p_sa_mad->method != IB_MAD_METHOD_GETTABLE) {
439255332Scy		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1804: "
440145516Sdarrenr			"Unsupported Method (%s) for LinkRecord request\n",
441255332Scy			ib_get_sa_method_str(p_sa_mad->method));
442145516Sdarrenr		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
443145516Sdarrenr		goto Exit;
444255332Scy	}
445145516Sdarrenr
446255332Scy	cl_plock_acquire(sa->p_lock);
447145516Sdarrenr
448255332Scy	/* update the requester physical port */
449145516Sdarrenr	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
450145516Sdarrenr						osm_madw_get_mad_addr_ptr
451145516Sdarrenr						(p_madw));
452255332Scy	if (p_req_physp == NULL) {
453145516Sdarrenr		cl_plock_release(sa->p_lock);
454145516Sdarrenr		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1805: "
455145516Sdarrenr			"Cannot find requester physical port\n");
456145516Sdarrenr		goto Exit;
457145516Sdarrenr	}
458145516Sdarrenr
459145516Sdarrenr	if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
460145516Sdarrenr		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
461172776Sdarrenr			"Requester port GUID 0x%" PRIx64 "\n",
462145516Sdarrenr			cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
463145516Sdarrenr		osm_dump_link_record_v2(sa->p_log, p_lr, FILE_ID, OSM_LOG_DEBUG);
464255332Scy	}
465145516Sdarrenr
466145516Sdarrenr	cl_qlist_init(&lr_list);
467145516Sdarrenr
468145516Sdarrenr	/*
469145516Sdarrenr	   Most SA functions (including this one) are read-only on the
470145516Sdarrenr	   subnet object, so we grab the lock non-exclusively.
471145516Sdarrenr	 */
472145516Sdarrenr	status = lr_rcv_get_end_points(sa, p_madw, &p_src_port, &p_dest_port);
473145516Sdarrenr
474145516Sdarrenr	if (status == IB_SA_MAD_STATUS_SUCCESS)
475255332Scy		lr_rcv_get_port_links(sa, p_lr, p_src_port, p_dest_port,
476145516Sdarrenr				      p_sa_mad->comp_mask, &lr_list,
477145516Sdarrenr				      p_req_physp);
478145516Sdarrenr
479145516Sdarrenr	cl_plock_release(sa->p_lock);
480145516Sdarrenr
481145516Sdarrenr	osm_sa_respond(sa, p_madw, sizeof(ib_link_record_t), &lr_list);
482255332Scy
483255332ScyExit:
484145516Sdarrenr	OSM_LOG_EXIT(sa->p_log);
485255332Scy}
486145516Sdarrenr