1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2006 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff * Copyright (c) 2008 Xsigo Systems Inc.  All rights reserved.
6219820Sjeff *
7219820Sjeff * This software is available to you under a choice of one of two
8219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
9219820Sjeff * General Public License (GPL) Version 2, available from the file
10219820Sjeff * COPYING in the main directory of this source tree, or the
11219820Sjeff * OpenIB.org BSD license below:
12219820Sjeff *
13219820Sjeff *     Redistribution and use in source and binary forms, with or
14219820Sjeff *     without modification, are permitted provided that the following
15219820Sjeff *     conditions are met:
16219820Sjeff *
17219820Sjeff *      - Redistributions of source code must retain the above
18219820Sjeff *        copyright notice, this list of conditions and the following
19219820Sjeff *        disclaimer.
20219820Sjeff *
21219820Sjeff *      - Redistributions in binary form must reproduce the above
22219820Sjeff *        copyright notice, this list of conditions and the following
23219820Sjeff *        disclaimer in the documentation and/or other materials
24219820Sjeff *        provided with the distribution.
25219820Sjeff *
26219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33219820Sjeff * SOFTWARE.
34219820Sjeff *
35219820Sjeff */
36219820Sjeff
37219820Sjeff/*
38219820Sjeff * Abstract:
39219820Sjeff *    Implementation of osm_pr_rcv_t.
40219820Sjeff * This object represents the PathRecord Receiver object.
41219820Sjeff * This object is part of the opensm family of objects.
42219820Sjeff */
43219820Sjeff
44219820Sjeff#if HAVE_CONFIG_H
45219820Sjeff#  include <config.h>
46219820Sjeff#endif				/* HAVE_CONFIG_H */
47219820Sjeff
48219820Sjeff#include <string.h>
49219820Sjeff#include <arpa/inet.h>
50219820Sjeff#include <iba/ib_types.h>
51219820Sjeff#include <complib/cl_qmap.h>
52219820Sjeff#include <complib/cl_passivelock.h>
53219820Sjeff#include <complib/cl_debug.h>
54219820Sjeff#include <complib/cl_qlist.h>
55219820Sjeff#include <vendor/osm_vendor_api.h>
56219820Sjeff#include <opensm/osm_base.h>
57219820Sjeff#include <opensm/osm_port.h>
58219820Sjeff#include <opensm/osm_node.h>
59219820Sjeff#include <opensm/osm_switch.h>
60219820Sjeff#include <opensm/osm_helper.h>
61219820Sjeff#include <opensm/osm_pkey.h>
62219820Sjeff#include <opensm/osm_multicast.h>
63219820Sjeff#include <opensm/osm_partition.h>
64219820Sjeff#include <opensm/osm_opensm.h>
65219820Sjeff#include <opensm/osm_qos_policy.h>
66219820Sjeff#include <opensm/osm_sa.h>
67219820Sjeff#include <opensm/osm_router.h>
68219820Sjeff#include <opensm/osm_prefix_route.h>
69219820Sjeff
70219820Sjeff#include <sys/socket.h>
71219820Sjeff
72219820Sjeffextern uint8_t osm_get_lash_sl(osm_opensm_t * p_osm,
73219820Sjeff			       const osm_port_t * p_src_port,
74219820Sjeff			       const osm_port_t * p_dst_port);
75219820Sjeff
76219820Sjefftypedef struct osm_pr_item {
77219820Sjeff	cl_list_item_t list_item;
78219820Sjeff	ib_path_rec_t path_rec;
79219820Sjeff} osm_pr_item_t;
80219820Sjeff
81219820Sjefftypedef struct osm_path_parms {
82219820Sjeff	ib_net16_t pkey;
83219820Sjeff	uint8_t mtu;
84219820Sjeff	uint8_t rate;
85219820Sjeff	uint8_t sl;
86219820Sjeff	uint8_t pkt_life;
87219820Sjeff	boolean_t reversible;
88219820Sjeff} osm_path_parms_t;
89219820Sjeff
90219820Sjeffstatic const ib_gid_t zero_gid = { {0x00, 0x00, 0x00, 0x00,
91219820Sjeff				    0x00, 0x00, 0x00, 0x00,
92219820Sjeff				    0x00, 0x00, 0x00, 0x00,
93219820Sjeff				    0x00, 0x00, 0x00, 0x00},
94219820Sjeff};
95219820Sjeff
96219820Sjeff/**********************************************************************
97219820Sjeff **********************************************************************/
98219820Sjeffstatic inline boolean_t
99219820Sjeff__osm_sa_path_rec_is_tavor_port(IN const osm_port_t * const p_port)
100219820Sjeff{
101219820Sjeff	osm_node_t const *p_node;
102219820Sjeff	ib_net32_t vend_id;
103219820Sjeff
104219820Sjeff	p_node = p_port->p_node;
105219820Sjeff	vend_id = ib_node_info_get_vendor_id(&p_node->node_info);
106219820Sjeff
107219820Sjeff	return ((p_node->node_info.device_id == CL_HTON16(23108)) &&
108219820Sjeff		((vend_id == CL_HTON32(OSM_VENDOR_ID_MELLANOX)) ||
109219820Sjeff		 (vend_id == CL_HTON32(OSM_VENDOR_ID_TOPSPIN)) ||
110219820Sjeff		 (vend_id == CL_HTON32(OSM_VENDOR_ID_SILVERSTORM)) ||
111219820Sjeff		 (vend_id == CL_HTON32(OSM_VENDOR_ID_VOLTAIRE))));
112219820Sjeff}
113219820Sjeff
114219820Sjeff/**********************************************************************
115219820Sjeff **********************************************************************/
116219820Sjeffstatic boolean_t
117219820Sjeff__osm_sa_path_rec_apply_tavor_mtu_limit(IN const ib_path_rec_t * const p_pr,
118219820Sjeff					IN const osm_port_t * const p_src_port,
119219820Sjeff					IN const osm_port_t * const p_dest_port,
120219820Sjeff					IN const ib_net64_t comp_mask)
121219820Sjeff{
122219820Sjeff	uint8_t required_mtu;
123219820Sjeff
124219820Sjeff	/* only if at least one of the ports is a Tavor device */
125219820Sjeff	if (!__osm_sa_path_rec_is_tavor_port(p_src_port) &&
126219820Sjeff	    !__osm_sa_path_rec_is_tavor_port(p_dest_port))
127219820Sjeff		return (FALSE);
128219820Sjeff
129219820Sjeff	/*
130219820Sjeff	   we can apply the patch if either:
131219820Sjeff	   1. No MTU required
132219820Sjeff	   2. Required MTU <
133219820Sjeff	   3. Required MTU = 1K or 512 or 256
134219820Sjeff	   4. Required MTU > 256 or 512
135219820Sjeff	 */
136219820Sjeff	required_mtu = ib_path_rec_mtu(p_pr);
137219820Sjeff	if ((comp_mask & IB_PR_COMPMASK_MTUSELEC) &&
138219820Sjeff	    (comp_mask & IB_PR_COMPMASK_MTU)) {
139219820Sjeff		switch (ib_path_rec_mtu_sel(p_pr)) {
140219820Sjeff		case 0:	/* must be greater than */
141219820Sjeff		case 2:	/* exact match */
142219820Sjeff			if (IB_MTU_LEN_1024 < required_mtu)
143219820Sjeff				return (FALSE);
144219820Sjeff			break;
145219820Sjeff
146219820Sjeff		case 1:	/* must be less than */
147219820Sjeff			/* can't be disqualified by this one */
148219820Sjeff			break;
149219820Sjeff
150219820Sjeff		case 3:	/* largest available */
151219820Sjeff			/* the ULP intentionally requested */
152219820Sjeff			/* the largest MTU possible */
153219820Sjeff			return (FALSE);
154219820Sjeff			break;
155219820Sjeff
156219820Sjeff		default:
157219820Sjeff			/* if we're here, there's a bug in ib_path_rec_mtu_sel() */
158219820Sjeff			CL_ASSERT(FALSE);
159219820Sjeff			break;
160219820Sjeff		}
161219820Sjeff	}
162219820Sjeff
163219820Sjeff	return (TRUE);
164219820Sjeff}
165219820Sjeff
166219820Sjeff/**********************************************************************
167219820Sjeff **********************************************************************/
168219820Sjeffstatic ib_api_status_t
169219820Sjeff__osm_pr_rcv_get_path_parms(IN osm_sa_t * sa,
170219820Sjeff			    IN const ib_path_rec_t * const p_pr,
171219820Sjeff			    IN const osm_port_t * const p_src_port,
172219820Sjeff			    IN const osm_port_t * const p_dest_port,
173219820Sjeff			    IN const uint16_t dest_lid_ho,
174219820Sjeff			    IN const ib_net64_t comp_mask,
175219820Sjeff			    OUT osm_path_parms_t * const p_parms)
176219820Sjeff{
177219820Sjeff	const osm_node_t *p_node;
178219820Sjeff	const osm_physp_t *p_physp;
179219820Sjeff	const osm_physp_t *p_src_physp;
180219820Sjeff	const osm_physp_t *p_dest_physp;
181219820Sjeff	const osm_prtn_t *p_prtn = NULL;
182219820Sjeff	osm_opensm_t *p_osm;
183219820Sjeff	const ib_port_info_t *p_pi;
184219820Sjeff	ib_api_status_t status = IB_SUCCESS;
185219820Sjeff	ib_net16_t pkey;
186219820Sjeff	uint8_t mtu;
187219820Sjeff	uint8_t rate;
188219820Sjeff	uint8_t pkt_life;
189219820Sjeff	uint8_t required_mtu;
190219820Sjeff	uint8_t required_rate;
191219820Sjeff	uint8_t required_pkt_life;
192219820Sjeff	uint8_t sl;
193219820Sjeff	uint8_t in_port_num;
194219820Sjeff	ib_net16_t dest_lid;
195219820Sjeff	uint8_t i;
196219820Sjeff	ib_slvl_table_t *p_slvl_tbl = NULL;
197219820Sjeff	osm_qos_level_t *p_qos_level = NULL;
198219820Sjeff	uint16_t valid_sl_mask = 0xffff;
199219820Sjeff	int is_lash;
200219820Sjeff
201219820Sjeff	OSM_LOG_ENTER(sa->p_log);
202219820Sjeff
203219820Sjeff	dest_lid = cl_hton16(dest_lid_ho);
204219820Sjeff
205219820Sjeff	p_dest_physp = p_dest_port->p_physp;
206219820Sjeff	p_physp = p_src_port->p_physp;
207219820Sjeff	p_src_physp = p_physp;
208219820Sjeff	p_pi = &p_physp->port_info;
209219820Sjeff	p_osm = sa->p_subn->p_osm;
210219820Sjeff
211219820Sjeff	mtu = ib_port_info_get_mtu_cap(p_pi);
212219820Sjeff	rate = ib_port_info_compute_rate(p_pi);
213219820Sjeff
214219820Sjeff	/*
215219820Sjeff	   Mellanox Tavor device performance is better using 1K MTU.
216219820Sjeff	   If required MTU and MTU selector are such that 1K is OK
217219820Sjeff	   and at least one end of the path is Tavor we override the
218219820Sjeff	   port MTU with 1K.
219219820Sjeff	 */
220219820Sjeff	if (sa->p_subn->opt.enable_quirks &&
221219820Sjeff	    __osm_sa_path_rec_apply_tavor_mtu_limit(p_pr, p_src_port,
222219820Sjeff						    p_dest_port, comp_mask))
223219820Sjeff		if (mtu > IB_MTU_LEN_1024) {
224219820Sjeff			mtu = IB_MTU_LEN_1024;
225219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
226219820Sjeff				"Optimized Path MTU to 1K for Mellanox Tavor device\n");
227219820Sjeff		}
228219820Sjeff
229219820Sjeff	/*
230219820Sjeff	   Walk the subnet object from source to destination,
231219820Sjeff	   tracking the most restrictive rate and mtu values along the way...
232219820Sjeff
233219820Sjeff	   If source port node is a switch, then p_physp should
234219820Sjeff	   point to the port that routes the destination lid
235219820Sjeff	 */
236219820Sjeff
237219820Sjeff	p_node = osm_physp_get_node_ptr(p_physp);
238219820Sjeff
239219820Sjeff	if (p_node->sw) {
240219820Sjeff		/*
241219820Sjeff		 * Source node is a switch.
242219820Sjeff		 * Make sure that p_physp points to the out port of the
243219820Sjeff		 * switch that routes to the destination lid (dest_lid_ho)
244219820Sjeff		 */
245219820Sjeff		p_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid);
246219820Sjeff		if (p_physp == 0) {
247219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F02: "
248219820Sjeff				"Cannot find routing to LID %u from switch for GUID 0x%016"
249219820Sjeff				PRIx64 "\n", dest_lid_ho,
250219820Sjeff				cl_ntoh64(osm_node_get_node_guid(p_node)));
251219820Sjeff			status = IB_NOT_FOUND;
252219820Sjeff			goto Exit;
253219820Sjeff		}
254219820Sjeff	}
255219820Sjeff
256219820Sjeff	if (sa->p_subn->opt.qos) {
257219820Sjeff
258219820Sjeff		/*
259219820Sjeff		 * Whether this node is switch or CA, the IN port for
260219820Sjeff		 * the sl2vl table is 0, because this is a source node.
261219820Sjeff		 */
262219820Sjeff		p_slvl_tbl = osm_physp_get_slvl_tbl(p_physp, 0);
263219820Sjeff
264219820Sjeff		/* update valid SLs that still exist on this route */
265219820Sjeff		for (i = 0; i < IB_MAX_NUM_VLS; i++) {
266219820Sjeff			if (valid_sl_mask & (1 << i) &&
267219820Sjeff			    ib_slvl_table_get(p_slvl_tbl, i) == IB_DROP_VL)
268219820Sjeff				valid_sl_mask &= ~(1 << i);
269219820Sjeff		}
270219820Sjeff		if (!valid_sl_mask) {
271219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
272219820Sjeff				"All the SLs lead to VL15 on this path\n");
273219820Sjeff			status = IB_NOT_FOUND;
274219820Sjeff			goto Exit;
275219820Sjeff		}
276219820Sjeff	}
277219820Sjeff
278219820Sjeff	/*
279219820Sjeff	 * Same as above
280219820Sjeff	 */
281219820Sjeff	p_node = osm_physp_get_node_ptr(p_dest_physp);
282219820Sjeff
283219820Sjeff	if (p_node->sw) {
284219820Sjeff		/*
285219820Sjeff		 * if destination is switch, we want p_dest_physp to point to port 0
286219820Sjeff		 */
287219820Sjeff		p_dest_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid);
288219820Sjeff
289219820Sjeff		if (p_dest_physp == 0) {
290219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F03: "
291219820Sjeff				"Cannot find routing to LID %u from switch for GUID 0x%016"
292219820Sjeff				PRIx64 "\n", dest_lid_ho,
293219820Sjeff				cl_ntoh64(osm_node_get_node_guid(p_node)));
294219820Sjeff			status = IB_NOT_FOUND;
295219820Sjeff			goto Exit;
296219820Sjeff		}
297219820Sjeff
298219820Sjeff	}
299219820Sjeff
300219820Sjeff	/*
301219820Sjeff	 * Now go through the path step by step
302219820Sjeff	 */
303219820Sjeff
304219820Sjeff	while (p_physp != p_dest_physp) {
305219820Sjeff
306219820Sjeff		p_node = osm_physp_get_node_ptr(p_physp);
307219820Sjeff		p_physp = osm_physp_get_remote(p_physp);
308219820Sjeff
309219820Sjeff		if (p_physp == 0) {
310219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F05: "
311219820Sjeff				"Cannot find remote phys port when routing to LID %u from node GUID 0x%016"
312219820Sjeff				PRIx64 "\n", dest_lid_ho,
313219820Sjeff				cl_ntoh64(osm_node_get_node_guid(p_node)));
314219820Sjeff			status = IB_ERROR;
315219820Sjeff			goto Exit;
316219820Sjeff		}
317219820Sjeff
318219820Sjeff		in_port_num = osm_physp_get_port_num(p_physp);
319219820Sjeff
320219820Sjeff		/*
321219820Sjeff		   This is point to point case (no switch in between)
322219820Sjeff		 */
323219820Sjeff		if (p_physp == p_dest_physp)
324219820Sjeff			break;
325219820Sjeff
326219820Sjeff		p_node = osm_physp_get_node_ptr(p_physp);
327219820Sjeff
328219820Sjeff		if (!p_node->sw) {
329219820Sjeff			/*
330219820Sjeff			   There is some sort of problem in the subnet object!
331219820Sjeff			   If this isn't a switch, we should have reached
332219820Sjeff			   the destination by now!
333219820Sjeff			 */
334219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F06: "
335219820Sjeff				"Internal error, bad path\n");
336219820Sjeff			status = IB_ERROR;
337219820Sjeff			goto Exit;
338219820Sjeff		}
339219820Sjeff
340219820Sjeff		/*
341219820Sjeff		   Check parameters for the ingress port in this switch.
342219820Sjeff		 */
343219820Sjeff		p_pi = &p_physp->port_info;
344219820Sjeff
345219820Sjeff		if (mtu > ib_port_info_get_mtu_cap(p_pi))
346219820Sjeff			mtu = ib_port_info_get_mtu_cap(p_pi);
347219820Sjeff
348219820Sjeff		if (rate > ib_port_info_compute_rate(p_pi))
349219820Sjeff			rate = ib_port_info_compute_rate(p_pi);
350219820Sjeff
351219820Sjeff		/*
352219820Sjeff		   Continue with the egress port on this switch.
353219820Sjeff		 */
354219820Sjeff		p_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid);
355219820Sjeff		if (p_physp == 0) {
356219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F07: "
357219820Sjeff				"Dead end on path to LID %u from switch for GUID 0x%016"
358219820Sjeff				PRIx64 "\n", dest_lid_ho,
359219820Sjeff				cl_ntoh64(osm_node_get_node_guid(p_node)));
360219820Sjeff			status = IB_ERROR;
361219820Sjeff			goto Exit;
362219820Sjeff		}
363219820Sjeff
364219820Sjeff		p_pi = &p_physp->port_info;
365219820Sjeff
366219820Sjeff		if (mtu > ib_port_info_get_mtu_cap(p_pi))
367219820Sjeff			mtu = ib_port_info_get_mtu_cap(p_pi);
368219820Sjeff
369219820Sjeff		if (rate > ib_port_info_compute_rate(p_pi))
370219820Sjeff			rate = ib_port_info_compute_rate(p_pi);
371219820Sjeff
372219820Sjeff		if (sa->p_subn->opt.qos) {
373219820Sjeff			/*
374219820Sjeff			 * Check SL2VL table of the switch and update valid SLs
375219820Sjeff			 */
376219820Sjeff			p_slvl_tbl = osm_physp_get_slvl_tbl(p_physp, in_port_num);
377219820Sjeff			for (i = 0; i < IB_MAX_NUM_VLS; i++) {
378219820Sjeff				if (valid_sl_mask & (1 << i) &&
379219820Sjeff				    ib_slvl_table_get(p_slvl_tbl, i) == IB_DROP_VL)
380219820Sjeff					valid_sl_mask &= ~(1 << i);
381219820Sjeff			}
382219820Sjeff			if (!valid_sl_mask) {
383219820Sjeff				OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "All the SLs "
384219820Sjeff					"lead to VL15 on this path\n");
385219820Sjeff				status = IB_NOT_FOUND;
386219820Sjeff				goto Exit;
387219820Sjeff			}
388219820Sjeff		}
389219820Sjeff	}
390219820Sjeff
391219820Sjeff	/*
392219820Sjeff	   p_physp now points to the destination
393219820Sjeff	 */
394219820Sjeff	p_pi = &p_physp->port_info;
395219820Sjeff
396219820Sjeff	if (mtu > ib_port_info_get_mtu_cap(p_pi))
397219820Sjeff		mtu = ib_port_info_get_mtu_cap(p_pi);
398219820Sjeff
399219820Sjeff	if (rate > ib_port_info_compute_rate(p_pi))
400219820Sjeff		rate = ib_port_info_compute_rate(p_pi);
401219820Sjeff
402219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
403219820Sjeff		"Path min MTU = %u, min rate = %u\n", mtu, rate);
404219820Sjeff
405219820Sjeff	/*
406219820Sjeff	 * Get QoS Level object according to the path request
407219820Sjeff	 * and adjust path parameters according to QoS settings
408219820Sjeff	 */
409219820Sjeff	if (sa->p_subn->opt.qos &&
410219820Sjeff	    sa->p_subn->p_qos_policy &&
411219820Sjeff	    (p_qos_level =
412219820Sjeff	     osm_qos_policy_get_qos_level_by_pr(sa->p_subn->p_qos_policy,
413219820Sjeff						p_pr, p_src_physp, p_dest_physp,
414219820Sjeff						comp_mask))) {
415219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
416219820Sjeff			"PathRecord request matches QoS Level '%s' (%s)\n",
417219820Sjeff			p_qos_level->name, p_qos_level->use ?
418219820Sjeff			p_qos_level->use : "no description");
419219820Sjeff
420219820Sjeff		if (p_qos_level->mtu_limit_set
421219820Sjeff		    && (mtu > p_qos_level->mtu_limit))
422219820Sjeff			mtu = p_qos_level->mtu_limit;
423219820Sjeff
424219820Sjeff		if (p_qos_level->rate_limit_set
425219820Sjeff		    && (rate > p_qos_level->rate_limit))
426219820Sjeff			rate = p_qos_level->rate_limit;
427219820Sjeff
428219820Sjeff		if (p_qos_level->sl_set) {
429219820Sjeff			sl = p_qos_level->sl;
430219820Sjeff			if (!(valid_sl_mask & (1 << sl))) {
431219820Sjeff				status = IB_NOT_FOUND;
432219820Sjeff				goto Exit;
433219820Sjeff			}
434219820Sjeff		}
435219820Sjeff	}
436219820Sjeff
437219820Sjeff	/*
438219820Sjeff	 * Set packet lifetime.
439219820Sjeff	 * According to spec definition IBA 1.2 Table 205
440219820Sjeff	 * PacketLifeTime description, for loopback paths,
441219820Sjeff	 * packetLifeTime shall be zero.
442219820Sjeff	 */
443219820Sjeff	if (p_src_port == p_dest_port)
444219820Sjeff		pkt_life = 0;
445219820Sjeff	else if (p_qos_level && p_qos_level->pkt_life_set)
446219820Sjeff		pkt_life = p_qos_level->pkt_life;
447219820Sjeff	else
448219820Sjeff		pkt_life = sa->p_subn->opt.subnet_timeout;
449219820Sjeff
450219820Sjeff	/*
451219820Sjeff	   Determine if these values meet the user criteria
452219820Sjeff	   and adjust appropriately
453219820Sjeff	 */
454219820Sjeff
455219820Sjeff	/* we silently ignore cases where only the MTU selector is defined */
456219820Sjeff	if ((comp_mask & IB_PR_COMPMASK_MTUSELEC) &&
457219820Sjeff	    (comp_mask & IB_PR_COMPMASK_MTU)) {
458219820Sjeff		required_mtu = ib_path_rec_mtu(p_pr);
459219820Sjeff		switch (ib_path_rec_mtu_sel(p_pr)) {
460219820Sjeff		case 0:	/* must be greater than */
461219820Sjeff			if (mtu <= required_mtu)
462219820Sjeff				status = IB_NOT_FOUND;
463219820Sjeff			break;
464219820Sjeff
465219820Sjeff		case 1:	/* must be less than */
466219820Sjeff			if (mtu >= required_mtu) {
467219820Sjeff				/* adjust to use the highest mtu
468219820Sjeff				   lower then the required one */
469219820Sjeff				if (required_mtu > 1)
470219820Sjeff					mtu = required_mtu - 1;
471219820Sjeff				else
472219820Sjeff					status = IB_NOT_FOUND;
473219820Sjeff			}
474219820Sjeff			break;
475219820Sjeff
476219820Sjeff		case 2:	/* exact match */
477219820Sjeff			if (mtu < required_mtu)
478219820Sjeff				status = IB_NOT_FOUND;
479219820Sjeff			else
480219820Sjeff				mtu = required_mtu;
481219820Sjeff			break;
482219820Sjeff
483219820Sjeff		case 3:	/* largest available */
484219820Sjeff			/* can't be disqualified by this one */
485219820Sjeff			break;
486219820Sjeff
487219820Sjeff		default:
488219820Sjeff			/* if we're here, there's a bug in ib_path_rec_mtu_sel() */
489219820Sjeff			CL_ASSERT(FALSE);
490219820Sjeff			status = IB_ERROR;
491219820Sjeff			break;
492219820Sjeff		}
493219820Sjeff	}
494219820Sjeff	if (status != IB_SUCCESS)
495219820Sjeff		goto Exit;
496219820Sjeff
497219820Sjeff	/* we silently ignore cases where only the Rate selector is defined */
498219820Sjeff	if ((comp_mask & IB_PR_COMPMASK_RATESELEC) &&
499219820Sjeff	    (comp_mask & IB_PR_COMPMASK_RATE)) {
500219820Sjeff		required_rate = ib_path_rec_rate(p_pr);
501219820Sjeff		switch (ib_path_rec_rate_sel(p_pr)) {
502219820Sjeff		case 0:	/* must be greater than */
503219820Sjeff			if (rate <= required_rate)
504219820Sjeff				status = IB_NOT_FOUND;
505219820Sjeff			break;
506219820Sjeff
507219820Sjeff		case 1:	/* must be less than */
508219820Sjeff			if (rate >= required_rate) {
509219820Sjeff				/* adjust the rate to use the highest rate
510219820Sjeff				   lower then the required one */
511219820Sjeff				if (required_rate > 2)
512219820Sjeff					rate = required_rate - 1;
513219820Sjeff				else
514219820Sjeff					status = IB_NOT_FOUND;
515219820Sjeff			}
516219820Sjeff			break;
517219820Sjeff
518219820Sjeff		case 2:	/* exact match */
519219820Sjeff			if (rate < required_rate)
520219820Sjeff				status = IB_NOT_FOUND;
521219820Sjeff			else
522219820Sjeff				rate = required_rate;
523219820Sjeff			break;
524219820Sjeff
525219820Sjeff		case 3:	/* largest available */
526219820Sjeff			/* can't be disqualified by this one */
527219820Sjeff			break;
528219820Sjeff
529219820Sjeff		default:
530219820Sjeff			/* if we're here, there's a bug in ib_path_rec_mtu_sel() */
531219820Sjeff			CL_ASSERT(FALSE);
532219820Sjeff			status = IB_ERROR;
533219820Sjeff			break;
534219820Sjeff		}
535219820Sjeff	}
536219820Sjeff	if (status != IB_SUCCESS)
537219820Sjeff		goto Exit;
538219820Sjeff
539219820Sjeff	/* we silently ignore cases where only the PktLife selector is defined */
540219820Sjeff	if ((comp_mask & IB_PR_COMPMASK_PKTLIFETIMESELEC) &&
541219820Sjeff	    (comp_mask & IB_PR_COMPMASK_PKTLIFETIME)) {
542219820Sjeff		required_pkt_life = ib_path_rec_pkt_life(p_pr);
543219820Sjeff		switch (ib_path_rec_pkt_life_sel(p_pr)) {
544219820Sjeff		case 0:	/* must be greater than */
545219820Sjeff			if (pkt_life <= required_pkt_life)
546219820Sjeff				status = IB_NOT_FOUND;
547219820Sjeff			break;
548219820Sjeff
549219820Sjeff		case 1:	/* must be less than */
550219820Sjeff			if (pkt_life >= required_pkt_life) {
551219820Sjeff				/* adjust the lifetime to use the highest possible
552219820Sjeff				   lower then the required one */
553219820Sjeff				if (required_pkt_life > 1)
554219820Sjeff					pkt_life = required_pkt_life - 1;
555219820Sjeff				else
556219820Sjeff					status = IB_NOT_FOUND;
557219820Sjeff			}
558219820Sjeff			break;
559219820Sjeff
560219820Sjeff		case 2:	/* exact match */
561219820Sjeff			if (pkt_life < required_pkt_life)
562219820Sjeff				status = IB_NOT_FOUND;
563219820Sjeff			else
564219820Sjeff				pkt_life = required_pkt_life;
565219820Sjeff			break;
566219820Sjeff
567219820Sjeff		case 3:	/* smallest available */
568219820Sjeff			/* can't be disqualified by this one */
569219820Sjeff			break;
570219820Sjeff
571219820Sjeff		default:
572219820Sjeff			/* if we're here, there's a bug in ib_path_rec_pkt_life_sel() */
573219820Sjeff			CL_ASSERT(FALSE);
574219820Sjeff			status = IB_ERROR;
575219820Sjeff			break;
576219820Sjeff		}
577219820Sjeff	}
578219820Sjeff
579219820Sjeff	if (status != IB_SUCCESS)
580219820Sjeff		goto Exit;
581219820Sjeff
582219820Sjeff	/*
583219820Sjeff	 * set Pkey for this path record request
584219820Sjeff	 */
585219820Sjeff
586219820Sjeff	if ((comp_mask & IB_PR_COMPMASK_RAWTRAFFIC) &&
587219820Sjeff	    (cl_ntoh32(p_pr->hop_flow_raw) & (1 << 31)))
588219820Sjeff		pkey = osm_physp_find_common_pkey(p_src_physp, p_dest_physp);
589219820Sjeff
590219820Sjeff	else if (comp_mask & IB_PR_COMPMASK_PKEY) {
591219820Sjeff		/*
592219820Sjeff		 * PR request has a specific pkey:
593219820Sjeff		 * Check that source and destination share this pkey.
594219820Sjeff		 * If QoS level has pkeys, check that this pkey exists
595219820Sjeff		 * in the QoS level pkeys.
596219820Sjeff		 * PR returned pkey is the requested pkey.
597219820Sjeff		 */
598219820Sjeff		pkey = p_pr->pkey;
599219820Sjeff		if (!osm_physp_share_this_pkey(p_src_physp, p_dest_physp, pkey)) {
600219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1A: "
601219820Sjeff				"Ports 0x%016" PRIx64 " 0x%016" PRIx64
602219820Sjeff				" do not share specified PKey 0x%04x\n",
603219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
604219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)),
605219820Sjeff				cl_ntoh16(pkey));
606219820Sjeff			status = IB_NOT_FOUND;
607219820Sjeff			goto Exit;
608219820Sjeff		}
609219820Sjeff		if (p_qos_level && p_qos_level->pkey_range_len &&
610219820Sjeff		    !osm_qos_level_has_pkey(p_qos_level, pkey)) {
611219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1D: "
612219820Sjeff				"Ports do not share PKeys defined by QoS level\n");
613219820Sjeff			status = IB_NOT_FOUND;
614219820Sjeff			goto Exit;
615219820Sjeff		}
616219820Sjeff
617219820Sjeff	} else if (p_qos_level && p_qos_level->pkey_range_len) {
618219820Sjeff		/*
619219820Sjeff		 * PR request doesn't have a specific pkey, but QoS level
620219820Sjeff		 * has pkeys - get shared pkey from QoS level pkeys
621219820Sjeff		 */
622219820Sjeff		pkey = osm_qos_level_get_shared_pkey(p_qos_level,
623219820Sjeff						     p_src_physp, p_dest_physp);
624219820Sjeff		if (!pkey) {
625219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1E: "
626219820Sjeff				"Ports 0x%016" PRIx64 " 0x%016" PRIx64
627219820Sjeff				" do not share PKeys defined by QoS level\n",
628219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
629219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)));
630219820Sjeff			status = IB_NOT_FOUND;
631219820Sjeff			goto Exit;
632219820Sjeff		}
633219820Sjeff	} else {
634219820Sjeff		/*
635219820Sjeff		 * Neither PR request nor QoS level have pkey.
636219820Sjeff		 * Just get any shared pkey.
637219820Sjeff		 */
638219820Sjeff		pkey = osm_physp_find_common_pkey(p_src_physp, p_dest_physp);
639219820Sjeff		if (!pkey) {
640219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1B: "
641219820Sjeff				"Ports 0x%016" PRIx64 " 0x%016" PRIx64
642219820Sjeff				" do not have any shared PKeys\n",
643219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
644219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)));
645219820Sjeff			status = IB_NOT_FOUND;
646219820Sjeff			goto Exit;
647219820Sjeff		}
648219820Sjeff	}
649219820Sjeff
650219820Sjeff	if (pkey) {
651219820Sjeff		p_prtn =
652219820Sjeff		    (osm_prtn_t *) cl_qmap_get(&sa->p_subn->prtn_pkey_tbl,
653219820Sjeff					       pkey & cl_hton16((uint16_t) ~
654219820Sjeff								0x8000));
655219820Sjeff		if (p_prtn ==
656219820Sjeff		    (osm_prtn_t *) cl_qmap_end(&sa->p_subn->prtn_pkey_tbl))
657219820Sjeff			p_prtn = NULL;
658219820Sjeff	}
659219820Sjeff
660219820Sjeff	/*
661219820Sjeff	 * Set PathRecord SL.
662219820Sjeff	 */
663219820Sjeff
664219820Sjeff	is_lash = (p_osm->routing_engine_used == OSM_ROUTING_ENGINE_TYPE_LASH);
665219820Sjeff
666219820Sjeff	if (comp_mask & IB_PR_COMPMASK_SL) {
667219820Sjeff		/*
668219820Sjeff		 * Specific SL was requested
669219820Sjeff		 */
670219820Sjeff		sl = ib_path_rec_sl(p_pr);
671219820Sjeff
672219820Sjeff		if (p_qos_level && p_qos_level->sl_set
673219820Sjeff		    && (p_qos_level->sl != sl)) {
674219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1F: "
675219820Sjeff				"QoS constaraints: required PathRecord SL (%u) "
676219820Sjeff				"doesn't match QoS policy SL (%u)\n", sl,
677219820Sjeff				p_qos_level->sl);
678219820Sjeff			status = IB_NOT_FOUND;
679219820Sjeff			goto Exit;
680219820Sjeff		}
681219820Sjeff
682219820Sjeff		if (is_lash
683219820Sjeff		    && osm_get_lash_sl(p_osm, p_src_port, p_dest_port) != sl) {
684219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F23: "
685219820Sjeff				"Required PathRecord SL (%u) doesn't "
686219820Sjeff				"match LASH SL\n", sl);
687219820Sjeff			status = IB_NOT_FOUND;
688219820Sjeff			goto Exit;
689219820Sjeff		}
690219820Sjeff
691219820Sjeff	} else if (is_lash) {
692219820Sjeff		/*
693219820Sjeff		 * No specific SL in PathRecord request.
694219820Sjeff		 * If it's LASH routing - use its SL.
695219820Sjeff		 * slid and dest_lid are stored in network in lash.
696219820Sjeff		 */
697219820Sjeff		sl = osm_get_lash_sl(p_osm, p_src_port, p_dest_port);
698219820Sjeff	} else if (p_qos_level && p_qos_level->sl_set) {
699219820Sjeff		/*
700219820Sjeff		 * No specific SL was requested, and we're not in
701219820Sjeff		 * LASH routing, but there is an SL in QoS level.
702219820Sjeff		 */
703219820Sjeff		sl = p_qos_level->sl;
704219820Sjeff
705219820Sjeff		if (pkey && p_prtn && p_prtn->sl != p_qos_level->sl)
706219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
707219820Sjeff				"QoS level SL (%u) overrides partition SL (%u)\n",
708219820Sjeff				p_qos_level->sl, p_prtn->sl);
709219820Sjeff
710219820Sjeff	} else if (pkey) {
711219820Sjeff		/*
712219820Sjeff		 * No specific SL in request or in QoS level - use partition SL
713219820Sjeff		 */
714219820Sjeff		if (!p_prtn) {
715219820Sjeff			sl = OSM_DEFAULT_SL;
716219820Sjeff			/* this may be possible when pkey tables are created somehow in
717219820Sjeff			   previous runs or things are going wrong here */
718219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1C: "
719219820Sjeff				"No partition found for PKey 0x%04x - using default SL %d\n",
720219820Sjeff				cl_ntoh16(pkey), sl);
721219820Sjeff		} else
722219820Sjeff			sl = p_prtn->sl;
723219820Sjeff	} else if (sa->p_subn->opt.qos) {
724219820Sjeff		if (valid_sl_mask & (1 << OSM_DEFAULT_SL))
725219820Sjeff			sl = OSM_DEFAULT_SL;
726219820Sjeff		else {
727219820Sjeff			for (i = 0; i < IB_MAX_NUM_VLS; i++)
728219820Sjeff				if (valid_sl_mask & (1 << i))
729219820Sjeff					break;
730219820Sjeff			sl = i;
731219820Sjeff		}
732219820Sjeff	} else
733219820Sjeff		sl = OSM_DEFAULT_SL;
734219820Sjeff
735219820Sjeff	if (sa->p_subn->opt.qos && !(valid_sl_mask & (1 << sl))) {
736219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F24: "
737219820Sjeff			"Selected SL (%u) leads to VL15\n", sl);
738219820Sjeff		status = IB_NOT_FOUND;
739219820Sjeff		goto Exit;
740219820Sjeff	}
741219820Sjeff
742219820Sjeff	/* reset pkey when raw traffic */
743219820Sjeff	if (comp_mask & IB_PR_COMPMASK_RAWTRAFFIC &&
744219820Sjeff	    cl_ntoh32(p_pr->hop_flow_raw) & (1 << 31))
745219820Sjeff		pkey = 0;
746219820Sjeff
747219820Sjeff	p_parms->mtu = mtu;
748219820Sjeff	p_parms->rate = rate;
749219820Sjeff	p_parms->pkt_life = pkt_life;
750219820Sjeff	p_parms->pkey = pkey;
751219820Sjeff	p_parms->sl = sl;
752219820Sjeff
753219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Path params: mtu = %u, rate = %u,"
754219820Sjeff		" packet lifetime = %u, pkey = 0x%04X, sl = %u\n",
755219820Sjeff		mtu, rate, pkt_life, cl_ntoh16(pkey), sl);
756219820SjeffExit:
757219820Sjeff	OSM_LOG_EXIT(sa->p_log);
758219820Sjeff	return (status);
759219820Sjeff}
760219820Sjeff
761219820Sjeff/**********************************************************************
762219820Sjeff **********************************************************************/
763219820Sjeffstatic void
764219820Sjeff__osm_pr_rcv_build_pr(IN osm_sa_t * sa,
765219820Sjeff		      IN const osm_port_t * const p_src_port,
766219820Sjeff		      IN const osm_port_t * const p_dest_port,
767219820Sjeff		      IN const ib_gid_t * const p_dgid,
768219820Sjeff		      IN const uint16_t src_lid_ho,
769219820Sjeff		      IN const uint16_t dest_lid_ho,
770219820Sjeff		      IN const uint8_t preference,
771219820Sjeff		      IN const osm_path_parms_t * const p_parms,
772219820Sjeff		      OUT ib_path_rec_t * const p_pr)
773219820Sjeff{
774219820Sjeff	const osm_physp_t *p_src_physp;
775219820Sjeff	const osm_physp_t *p_dest_physp;
776219820Sjeff	boolean_t is_nonzero_gid = 0;
777219820Sjeff
778219820Sjeff	OSM_LOG_ENTER(sa->p_log);
779219820Sjeff
780219820Sjeff	p_src_physp = p_src_port->p_physp;
781219820Sjeff
782219820Sjeff	if (p_dgid) {
783219820Sjeff		if (memcmp(p_dgid, &zero_gid, sizeof(*p_dgid)))
784219820Sjeff			is_nonzero_gid = 1;
785219820Sjeff	}
786219820Sjeff
787219820Sjeff	if (is_nonzero_gid)
788219820Sjeff		p_pr->dgid = *p_dgid;
789219820Sjeff	else {
790219820Sjeff		p_dest_physp = p_dest_port->p_physp;
791219820Sjeff
792219820Sjeff		p_pr->dgid.unicast.prefix =
793219820Sjeff		    osm_physp_get_subnet_prefix(p_dest_physp);
794219820Sjeff		p_pr->dgid.unicast.interface_id =
795219820Sjeff		    osm_physp_get_port_guid(p_dest_physp);
796219820Sjeff	}
797219820Sjeff
798219820Sjeff	p_pr->sgid.unicast.prefix = osm_physp_get_subnet_prefix(p_src_physp);
799219820Sjeff	p_pr->sgid.unicast.interface_id = osm_physp_get_port_guid(p_src_physp);
800219820Sjeff
801219820Sjeff	p_pr->dlid = cl_hton16(dest_lid_ho);
802219820Sjeff	p_pr->slid = cl_hton16(src_lid_ho);
803219820Sjeff
804219820Sjeff	p_pr->hop_flow_raw &= cl_hton32(1 << 31);
805219820Sjeff
806219820Sjeff	/* Only set HopLimit if going through a router */
807219820Sjeff	if (is_nonzero_gid)
808219820Sjeff		p_pr->hop_flow_raw |= cl_hton32(IB_HOPLIMIT_MAX);
809219820Sjeff
810219820Sjeff	p_pr->pkey = p_parms->pkey;
811219820Sjeff	ib_path_rec_set_sl(p_pr, p_parms->sl);
812219820Sjeff	ib_path_rec_set_qos_class(p_pr, 0);
813219820Sjeff	p_pr->mtu = (uint8_t) (p_parms->mtu | 0x80);
814219820Sjeff	p_pr->rate = (uint8_t) (p_parms->rate | 0x80);
815219820Sjeff
816219820Sjeff	/* According to 1.2 spec definition Table 205 PacketLifeTime description,
817219820Sjeff	   for loopback paths, packetLifeTime shall be zero. */
818219820Sjeff	if (p_src_port == p_dest_port)
819219820Sjeff		p_pr->pkt_life = 0x80;	/* loopback */
820219820Sjeff	else
821219820Sjeff		p_pr->pkt_life = (uint8_t) (p_parms->pkt_life | 0x80);
822219820Sjeff
823219820Sjeff	p_pr->preference = preference;
824219820Sjeff
825219820Sjeff	/* always return num_path = 0 so this is only the reversible component */
826219820Sjeff	if (p_parms->reversible)
827219820Sjeff		p_pr->num_path = 0x80;
828219820Sjeff
829219820Sjeff	OSM_LOG_EXIT(sa->p_log);
830219820Sjeff}
831219820Sjeff
832219820Sjeff/**********************************************************************
833219820Sjeff **********************************************************************/
834219820Sjeffstatic osm_pr_item_t *
835219820Sjeff__osm_pr_rcv_get_lid_pair_path(IN osm_sa_t * sa,
836219820Sjeff			       IN const ib_path_rec_t * const p_pr,
837219820Sjeff			       IN const osm_port_t * const p_src_port,
838219820Sjeff			       IN const osm_port_t * const p_dest_port,
839219820Sjeff			       IN const ib_gid_t * const p_dgid,
840219820Sjeff			       IN const uint16_t src_lid_ho,
841219820Sjeff			       IN const uint16_t dest_lid_ho,
842219820Sjeff			       IN const ib_net64_t comp_mask,
843219820Sjeff			       IN const uint8_t preference)
844219820Sjeff{
845219820Sjeff	osm_path_parms_t path_parms;
846219820Sjeff	osm_path_parms_t rev_path_parms;
847219820Sjeff	osm_pr_item_t *p_pr_item;
848219820Sjeff	ib_api_status_t status, rev_path_status;
849219820Sjeff
850219820Sjeff	OSM_LOG_ENTER(sa->p_log);
851219820Sjeff
852219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Src LID %u, Dest LID %u\n",
853219820Sjeff		src_lid_ho, dest_lid_ho);
854219820Sjeff
855219820Sjeff	p_pr_item = malloc(sizeof(*p_pr_item));
856219820Sjeff	if (p_pr_item == NULL) {
857219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F01: "
858219820Sjeff			"Unable to allocate path record\n");
859219820Sjeff		goto Exit;
860219820Sjeff	}
861219820Sjeff	memset(p_pr_item, 0, sizeof(*p_pr_item));
862219820Sjeff
863219820Sjeff	status = __osm_pr_rcv_get_path_parms(sa, p_pr, p_src_port,
864219820Sjeff					     p_dest_port, dest_lid_ho,
865219820Sjeff					     comp_mask, &path_parms);
866219820Sjeff
867219820Sjeff	if (status != IB_SUCCESS) {
868219820Sjeff		free(p_pr_item);
869219820Sjeff		p_pr_item = NULL;
870219820Sjeff		goto Exit;
871219820Sjeff	}
872219820Sjeff
873219820Sjeff	/* now try the reversible path */
874219820Sjeff	rev_path_status = __osm_pr_rcv_get_path_parms(sa, p_pr, p_dest_port,
875219820Sjeff						      p_src_port, src_lid_ho,
876219820Sjeff						      comp_mask,
877219820Sjeff						      &rev_path_parms);
878219820Sjeff	path_parms.reversible = (rev_path_status == IB_SUCCESS);
879219820Sjeff
880219820Sjeff	/* did we get a Reversible Path compmask ? */
881219820Sjeff	/*
882219820Sjeff	   NOTE that if the reversible component = 0, it is a don't care
883219820Sjeff	   rather then requiring non-reversible paths ...
884219820Sjeff	   see Vol1 Ver1.2 p900 l16
885219820Sjeff	 */
886219820Sjeff	if (comp_mask & IB_PR_COMPMASK_REVERSIBLE) {
887219820Sjeff		if ((!path_parms.reversible && (p_pr->num_path & 0x80))) {
888219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
889219820Sjeff				"Requested reversible path but failed to get one\n");
890219820Sjeff
891219820Sjeff			free(p_pr_item);
892219820Sjeff			p_pr_item = NULL;
893219820Sjeff			goto Exit;
894219820Sjeff		}
895219820Sjeff	}
896219820Sjeff
897219820Sjeff	__osm_pr_rcv_build_pr(sa, p_src_port, p_dest_port, p_dgid,
898219820Sjeff			      src_lid_ho, dest_lid_ho, preference, &path_parms,
899219820Sjeff			      &p_pr_item->path_rec);
900219820Sjeff
901219820SjeffExit:
902219820Sjeff	OSM_LOG_EXIT(sa->p_log);
903219820Sjeff	return (p_pr_item);
904219820Sjeff}
905219820Sjeff
906219820Sjeff/**********************************************************************
907219820Sjeff **********************************************************************/
908219820Sjeffstatic void
909219820Sjeff__osm_pr_rcv_get_port_pair_paths(IN osm_sa_t * sa,
910219820Sjeff				 IN const osm_madw_t * const p_madw,
911219820Sjeff				 IN const osm_port_t * const p_req_port,
912219820Sjeff				 IN const osm_port_t * const p_src_port,
913219820Sjeff				 IN const osm_port_t * const p_dest_port,
914219820Sjeff				 IN const ib_gid_t * const p_dgid,
915219820Sjeff				 IN const ib_net64_t comp_mask,
916219820Sjeff				 IN cl_qlist_t * const p_list)
917219820Sjeff{
918219820Sjeff	const ib_path_rec_t *p_pr;
919219820Sjeff	const ib_sa_mad_t *p_sa_mad;
920219820Sjeff	osm_pr_item_t *p_pr_item;
921219820Sjeff	uint16_t src_lid_min_ho;
922219820Sjeff	uint16_t src_lid_max_ho;
923219820Sjeff	uint16_t dest_lid_min_ho;
924219820Sjeff	uint16_t dest_lid_max_ho;
925219820Sjeff	uint16_t src_lid_ho;
926219820Sjeff	uint16_t dest_lid_ho;
927219820Sjeff	uint32_t path_num;
928219820Sjeff	uint8_t preference;
929219820Sjeff	uintn_t iterations;
930219820Sjeff	uintn_t src_offset;
931219820Sjeff	uintn_t dest_offset;
932219820Sjeff
933219820Sjeff	OSM_LOG_ENTER(sa->p_log);
934219820Sjeff
935219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
936219820Sjeff		"Src port 0x%016" PRIx64 ", Dst port 0x%016" PRIx64 "\n",
937219820Sjeff		cl_ntoh64(osm_port_get_guid(p_src_port)),
938219820Sjeff		cl_ntoh64(osm_port_get_guid(p_dest_port)));
939219820Sjeff
940219820Sjeff	/* Check that the req_port, src_port and dest_port all share a
941219820Sjeff	   pkey. The check is done on the default physical port of the ports. */
942219820Sjeff	if (osm_port_share_pkey(sa->p_log, p_req_port, p_src_port) == FALSE
943219820Sjeff	    || osm_port_share_pkey(sa->p_log, p_req_port,
944219820Sjeff				   p_dest_port) == FALSE
945219820Sjeff	    || osm_port_share_pkey(sa->p_log, p_src_port,
946219820Sjeff				   p_dest_port) == FALSE)
947219820Sjeff		/* One of the pairs doesn't share a pkey so the path is disqualified. */
948219820Sjeff		goto Exit;
949219820Sjeff
950219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
951219820Sjeff	p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
952219820Sjeff
953219820Sjeff	/*
954219820Sjeff	   We shouldn't be here if the paths are disqualified in some way...
955219820Sjeff	   Thus, we assume every possible connection is valid.
956219820Sjeff
957219820Sjeff	   We desire to return high-quality paths first.
958219820Sjeff	   In OpenSM, higher quality means least overlap with other paths.
959219820Sjeff	   This is acheived in practice by returning paths with
960219820Sjeff	   different LID value on each end, which means these
961219820Sjeff	   paths are more redundant that paths with the same LID repeated
962219820Sjeff	   on one side.  For example, in OpenSM the paths between two
963219820Sjeff	   endpoints with LMC = 1 might be as follows:
964219820Sjeff
965219820Sjeff	   Port A, LID 1 <-> Port B, LID 3
966219820Sjeff	   Port A, LID 1 <-> Port B, LID 4
967219820Sjeff	   Port A, LID 2 <-> Port B, LID 3
968219820Sjeff	   Port A, LID 2 <-> Port B, LID 4
969219820Sjeff
970219820Sjeff	   The OpenSM unicast routing algorithms attempt to disperse each path
971219820Sjeff	   to as varied a physical path as is reasonable.  1<->3 and 1<->4 have
972219820Sjeff	   more physical overlap (hence less redundancy) than 1<->3 and 2<->4.
973219820Sjeff
974219820Sjeff	   OpenSM ranks paths in three preference groups:
975219820Sjeff
976219820Sjeff	   Preference Value    Description
977219820Sjeff	   ----------------    -------------------------------------------
978219820Sjeff	   0             Redundant in both directions with other
979219820Sjeff	   pref value = 0 paths
980219820Sjeff
981219820Sjeff	   1             Redundant in one direction with other
982219820Sjeff	   pref value = 0 and pref value = 1 paths
983219820Sjeff
984219820Sjeff	   2             Not redundant in either direction with
985219820Sjeff	   other paths
986219820Sjeff
987219820Sjeff	   3-FF          Unused
988219820Sjeff
989219820Sjeff	   SA clients don't need to know these details, only that the lower
990219820Sjeff	   preference paths are preferred, as stated in the spec.  The paths
991219820Sjeff	   may not actually be physically redundant depending on the topology
992219820Sjeff	   of the subnet, but the point of LMC > 0 is to offer redundancy,
993219820Sjeff	   so it is assumed that the subnet is physically appropriate for the
994219820Sjeff	   specified LMC value.  A more advanced implementation would inspect for
995219820Sjeff	   physical redundancy, but I'm not going to bother with that now.
996219820Sjeff	 */
997219820Sjeff
998219820Sjeff	/*
999219820Sjeff	   Refine our search if the client specified end-point LIDs
1000219820Sjeff	 */
1001219820Sjeff	if (comp_mask & IB_PR_COMPMASK_DLID) {
1002219820Sjeff		dest_lid_min_ho = cl_ntoh16(p_pr->dlid);
1003219820Sjeff		dest_lid_max_ho = cl_ntoh16(p_pr->dlid);
1004219820Sjeff	} else
1005219820Sjeff		osm_port_get_lid_range_ho(p_dest_port, &dest_lid_min_ho,
1006219820Sjeff					  &dest_lid_max_ho);
1007219820Sjeff
1008219820Sjeff	if (comp_mask & IB_PR_COMPMASK_SLID) {
1009219820Sjeff		src_lid_min_ho = cl_ntoh16(p_pr->slid);
1010219820Sjeff		src_lid_max_ho = cl_ntoh16(p_pr->slid);
1011219820Sjeff	} else
1012219820Sjeff		osm_port_get_lid_range_ho(p_src_port, &src_lid_min_ho,
1013219820Sjeff					  &src_lid_max_ho);
1014219820Sjeff
1015219820Sjeff	if (src_lid_min_ho == 0) {
1016219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F20:"
1017219820Sjeff			"Obtained source LID of 0. No such LID possible\n");
1018219820Sjeff		goto Exit;
1019219820Sjeff	}
1020219820Sjeff
1021219820Sjeff	if (dest_lid_min_ho == 0) {
1022219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F21:"
1023219820Sjeff			"Obtained destination LID of 0. No such LID possible\n");
1024219820Sjeff		goto Exit;
1025219820Sjeff	}
1026219820Sjeff
1027219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1028219820Sjeff		"Src LIDs [%u-%u], Dest LIDs [%u-%u]\n",
1029219820Sjeff		src_lid_min_ho, src_lid_max_ho,
1030219820Sjeff		dest_lid_min_ho, dest_lid_max_ho);
1031219820Sjeff
1032219820Sjeff	src_lid_ho = src_lid_min_ho;
1033219820Sjeff	dest_lid_ho = dest_lid_min_ho;
1034219820Sjeff
1035219820Sjeff	/*
1036219820Sjeff	   Preferred paths come first in OpenSM
1037219820Sjeff	 */
1038219820Sjeff	preference = 0;
1039219820Sjeff	path_num = 0;
1040219820Sjeff
1041219820Sjeff	/* If SubnAdmGet, assume NumbPaths 1 (1.2 erratum) */
1042219820Sjeff	if (p_sa_mad->method != IB_MAD_METHOD_GET)
1043219820Sjeff		if (comp_mask & IB_PR_COMPMASK_NUMBPATH)
1044219820Sjeff			iterations = ib_path_rec_num_path(p_pr);
1045219820Sjeff		else
1046219820Sjeff			iterations = (uintn_t) (-1);
1047219820Sjeff	else
1048219820Sjeff		iterations = 1;
1049219820Sjeff
1050219820Sjeff	while (path_num < iterations) {
1051219820Sjeff		/*
1052219820Sjeff		   These paths are "fully redundant"
1053219820Sjeff		 */
1054219820Sjeff
1055219820Sjeff		p_pr_item = __osm_pr_rcv_get_lid_pair_path(sa, p_pr,
1056219820Sjeff							   p_src_port,
1057219820Sjeff							   p_dest_port, p_dgid,
1058219820Sjeff							   src_lid_ho,
1059219820Sjeff							   dest_lid_ho,
1060219820Sjeff							   comp_mask,
1061219820Sjeff							   preference);
1062219820Sjeff
1063219820Sjeff		if (p_pr_item) {
1064219820Sjeff			cl_qlist_insert_tail(p_list, &p_pr_item->list_item);
1065219820Sjeff			++path_num;
1066219820Sjeff		}
1067219820Sjeff
1068219820Sjeff		if (++src_lid_ho > src_lid_max_ho)
1069219820Sjeff			break;
1070219820Sjeff
1071219820Sjeff		if (++dest_lid_ho > dest_lid_max_ho)
1072219820Sjeff			break;
1073219820Sjeff	}
1074219820Sjeff
1075219820Sjeff	/*
1076219820Sjeff	   Check if we've accumulated all the paths that the user cares to see
1077219820Sjeff	 */
1078219820Sjeff	if (path_num == iterations)
1079219820Sjeff		goto Exit;
1080219820Sjeff
1081219820Sjeff	/*
1082219820Sjeff	   Don't bother reporting preference 1 paths for now.
1083219820Sjeff	   It's more trouble than it's worth and can only occur
1084219820Sjeff	   if ports have different LMC values, which isn't supported
1085219820Sjeff	   by OpenSM right now anyway.
1086219820Sjeff	 */
1087219820Sjeff	preference = 2;
1088219820Sjeff	src_lid_ho = src_lid_min_ho;
1089219820Sjeff	dest_lid_ho = dest_lid_min_ho;
1090219820Sjeff	src_offset = 0;
1091219820Sjeff	dest_offset = 0;
1092219820Sjeff
1093219820Sjeff	/*
1094219820Sjeff	   Iterate over the remaining paths
1095219820Sjeff	 */
1096219820Sjeff	while (path_num < iterations) {
1097219820Sjeff		dest_offset++;
1098219820Sjeff		dest_lid_ho++;
1099219820Sjeff
1100219820Sjeff		if (dest_lid_ho > dest_lid_max_ho) {
1101219820Sjeff			src_offset++;
1102219820Sjeff			src_lid_ho++;
1103219820Sjeff
1104219820Sjeff			if (src_lid_ho > src_lid_max_ho)
1105219820Sjeff				break;	/* done */
1106219820Sjeff
1107219820Sjeff			dest_offset = 0;
1108219820Sjeff			dest_lid_ho = dest_lid_min_ho;
1109219820Sjeff		}
1110219820Sjeff
1111219820Sjeff		/*
1112219820Sjeff		   These paths are "fully non-redundant" with paths already
1113219820Sjeff		   identified above and consequently not of much value.
1114219820Sjeff
1115219820Sjeff		   Don't return paths we already identified above, as indicated
1116219820Sjeff		   by the offset values being equal.
1117219820Sjeff		 */
1118219820Sjeff		if (src_offset == dest_offset)
1119219820Sjeff			continue;	/* already reported */
1120219820Sjeff
1121219820Sjeff		p_pr_item = __osm_pr_rcv_get_lid_pair_path(sa, p_pr,
1122219820Sjeff							   p_src_port,
1123219820Sjeff							   p_dest_port, p_dgid,
1124219820Sjeff							   src_lid_ho,
1125219820Sjeff							   dest_lid_ho,
1126219820Sjeff							   comp_mask,
1127219820Sjeff							   preference);
1128219820Sjeff
1129219820Sjeff		if (p_pr_item) {
1130219820Sjeff			cl_qlist_insert_tail(p_list, &p_pr_item->list_item);
1131219820Sjeff			++path_num;
1132219820Sjeff		}
1133219820Sjeff	}
1134219820Sjeff
1135219820SjeffExit:
1136219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1137219820Sjeff}
1138219820Sjeff
1139219820Sjeff/**********************************************************************
1140219820Sjeff **********************************************************************/
1141219820Sjeffstatic ib_net16_t
1142219820Sjeff__osm_pr_rcv_get_end_points(IN osm_sa_t * sa,
1143219820Sjeff			    IN const osm_madw_t * const p_madw,
1144219820Sjeff			    OUT const osm_port_t ** const pp_src_port,
1145219820Sjeff			    OUT const osm_port_t ** const pp_dest_port,
1146219820Sjeff			    OUT ib_gid_t * const p_dgid)
1147219820Sjeff{
1148219820Sjeff	const ib_path_rec_t *p_pr;
1149219820Sjeff	const ib_sa_mad_t *p_sa_mad;
1150219820Sjeff	ib_net64_t comp_mask;
1151219820Sjeff	ib_net64_t dest_guid;
1152219820Sjeff	ib_api_status_t status;
1153219820Sjeff	ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS;
1154219820Sjeff	osm_router_t *p_rtr;
1155219820Sjeff	osm_port_t *p_rtr_port;
1156219820Sjeff
1157219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1158219820Sjeff
1159219820Sjeff	/*
1160219820Sjeff	   Determine what fields are valid and then get a pointer
1161219820Sjeff	   to the source and destination port objects, if possible.
1162219820Sjeff	 */
1163219820Sjeff
1164219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1165219820Sjeff	p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1166219820Sjeff
1167219820Sjeff	comp_mask = p_sa_mad->comp_mask;
1168219820Sjeff
1169219820Sjeff	/*
1170219820Sjeff	   Check a few easy disqualifying cases up front before getting
1171219820Sjeff	   into the endpoints.
1172219820Sjeff	 */
1173219820Sjeff
1174219820Sjeff	if (comp_mask & IB_PR_COMPMASK_SGID) {
1175219820Sjeff		if (!ib_gid_is_link_local(&p_pr->sgid)) {
1176219820Sjeff			if (ib_gid_get_subnet_prefix(&p_pr->sgid) !=
1177219820Sjeff			    sa->p_subn->opt.subnet_prefix) {
1178219820Sjeff				/*
1179219820Sjeff				   This 'error' is the client's fault (bad gid)
1180219820Sjeff				   so don't enter it as an error in our own log.
1181219820Sjeff				   Return an error response to the client.
1182219820Sjeff				 */
1183219820Sjeff				OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1184219820Sjeff					"Non local SGID subnet prefix 0x%016"
1185219820Sjeff					PRIx64 "\n",
1186219820Sjeff					cl_ntoh64(p_pr->sgid.unicast.prefix));
1187219820Sjeff
1188219820Sjeff				sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1189219820Sjeff				goto Exit;
1190219820Sjeff			}
1191219820Sjeff		}
1192219820Sjeff
1193219820Sjeff		*pp_src_port = osm_get_port_by_guid(sa->p_subn,
1194219820Sjeff						    p_pr->sgid.unicast.
1195219820Sjeff						    interface_id);
1196219820Sjeff		if (!*pp_src_port) {
1197219820Sjeff			/*
1198219820Sjeff			   This 'error' is the client's fault (bad gid) so
1199219820Sjeff			   don't enter it as an error in our own log.
1200219820Sjeff			   Return an error response to the client.
1201219820Sjeff			 */
1202219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1203219820Sjeff				"No source port with GUID 0x%016" PRIx64 "\n",
1204219820Sjeff				cl_ntoh64(p_pr->sgid.unicast.interface_id));
1205219820Sjeff
1206219820Sjeff			sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1207219820Sjeff			goto Exit;
1208219820Sjeff		}
1209219820Sjeff	} else {
1210219820Sjeff		*pp_src_port = 0;
1211219820Sjeff		if (comp_mask & IB_PR_COMPMASK_SLID) {
1212219820Sjeff			status = cl_ptr_vector_at(&sa->p_subn->port_lid_tbl,
1213219820Sjeff						  cl_ntoh16(p_pr->slid),
1214219820Sjeff						  (void **)pp_src_port);
1215219820Sjeff
1216219820Sjeff			if ((status != CL_SUCCESS) || (*pp_src_port == NULL)) {
1217219820Sjeff				/*
1218219820Sjeff				   This 'error' is the client's fault (bad lid) so
1219219820Sjeff				   don't enter it as an error in our own log.
1220219820Sjeff				   Return an error response to the client.
1221219820Sjeff				 */
1222219820Sjeff				OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1223219820Sjeff					"No source port with LID %u\n",
1224219820Sjeff					cl_ntoh16(p_pr->slid));
1225219820Sjeff
1226219820Sjeff				sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
1227219820Sjeff				goto Exit;
1228219820Sjeff			}
1229219820Sjeff		}
1230219820Sjeff	}
1231219820Sjeff
1232219820Sjeff	if (p_dgid)
1233219820Sjeff		memset(p_dgid, 0, sizeof(*p_dgid));
1234219820Sjeff
1235219820Sjeff	if (comp_mask & IB_PR_COMPMASK_DGID) {
1236219820Sjeff		dest_guid = p_pr->dgid.unicast.interface_id;
1237219820Sjeff		if (!ib_gid_is_link_local(&p_pr->dgid)) {
1238219820Sjeff			if (!ib_gid_is_multicast(&p_pr->dgid) &&
1239219820Sjeff			    ib_gid_get_subnet_prefix(&p_pr->dgid) !=
1240219820Sjeff			    sa->p_subn->opt.subnet_prefix) {
1241219820Sjeff				OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1242219820Sjeff					"Non local DGID subnet prefix 0x%016"
1243219820Sjeff					PRIx64 "\n",
1244219820Sjeff					cl_ntoh64(p_pr->dgid.unicast.prefix));
1245219820Sjeff
1246219820Sjeff				/* Find the router port that is configured to
1247219820Sjeff				   handle this prefix, if any */
1248219820Sjeff				osm_prefix_route_t *route = NULL;
1249219820Sjeff				osm_prefix_route_t *r = (osm_prefix_route_t *)
1250219820Sjeff					cl_qlist_head(&sa->p_subn->prefix_routes_list);
1251219820Sjeff
1252219820Sjeff				while (r != (osm_prefix_route_t *)
1253219820Sjeff				       cl_qlist_end(&sa->p_subn->prefix_routes_list))
1254219820Sjeff				{
1255219820Sjeff					if (r->prefix == p_pr->dgid.unicast.prefix ||
1256219820Sjeff					    r->prefix == 0)
1257219820Sjeff					{
1258219820Sjeff						route = r;
1259219820Sjeff						break;
1260219820Sjeff					}
1261219820Sjeff					r = (osm_prefix_route_t *) cl_qlist_next(&r->list_item);
1262219820Sjeff				}
1263219820Sjeff
1264219820Sjeff				if (!route) {
1265219820Sjeff					/*
1266219820Sjeff					  This 'error' is the client's fault (bad gid) so
1267219820Sjeff					  don't enter it as an error in our own log.
1268219820Sjeff					  Return an error response to the client.
1269219820Sjeff					*/
1270219820Sjeff					sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1271219820Sjeff					goto Exit;
1272219820Sjeff				} else if (route->guid == 0) {
1273219820Sjeff					/* first router */
1274219820Sjeff					p_rtr = (osm_router_t *)
1275219820Sjeff						cl_qmap_head(&sa->
1276219820Sjeff							     p_subn->
1277219820Sjeff							     rtr_guid_tbl);
1278219820Sjeff				} else {
1279219820Sjeff					p_rtr = (osm_router_t *)
1280219820Sjeff						cl_qmap_get(&sa->
1281219820Sjeff							    p_subn->
1282219820Sjeff							    rtr_guid_tbl,
1283219820Sjeff							    route->guid);
1284219820Sjeff				}
1285219820Sjeff
1286219820Sjeff				if (p_rtr ==
1287219820Sjeff				    (osm_router_t *) cl_qmap_end(&sa->
1288219820Sjeff								 p_subn->
1289219820Sjeff								 rtr_guid_tbl))
1290219820Sjeff				{
1291219820Sjeff					OSM_LOG(sa->p_log, OSM_LOG_ERROR,
1292219820Sjeff						"ERR 1F22: "
1293219820Sjeff						"Off subnet DGID but router not found\n");
1294219820Sjeff					sa_status =
1295219820Sjeff					    IB_SA_MAD_STATUS_INVALID_GID;
1296219820Sjeff					goto Exit;
1297219820Sjeff				}
1298219820Sjeff
1299219820Sjeff				p_rtr_port = osm_router_get_port_ptr(p_rtr);
1300219820Sjeff				dest_guid = osm_port_get_guid(p_rtr_port);
1301219820Sjeff				if (p_dgid)
1302219820Sjeff					*p_dgid = p_pr->dgid;
1303219820Sjeff			}
1304219820Sjeff		}
1305219820Sjeff
1306219820Sjeff		*pp_dest_port = osm_get_port_by_guid(sa->p_subn, dest_guid);
1307219820Sjeff		if (!*pp_dest_port) {
1308219820Sjeff			/*
1309219820Sjeff			   This 'error' is the client's fault (bad gid) so
1310219820Sjeff			   don't enter it as an error in our own log.
1311219820Sjeff			   Return an error response to the client.
1312219820Sjeff			 */
1313219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1314219820Sjeff				"No dest port with GUID 0x%016" PRIx64 "\n",
1315219820Sjeff				cl_ntoh64(dest_guid));
1316219820Sjeff
1317219820Sjeff			sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1318219820Sjeff			goto Exit;
1319219820Sjeff		}
1320219820Sjeff	} else {
1321219820Sjeff		*pp_dest_port = 0;
1322219820Sjeff		if (comp_mask & IB_PR_COMPMASK_DLID) {
1323219820Sjeff			status = cl_ptr_vector_at(&sa->p_subn->port_lid_tbl,
1324219820Sjeff						  cl_ntoh16(p_pr->dlid),
1325219820Sjeff						  (void **)pp_dest_port);
1326219820Sjeff
1327219820Sjeff			if ((status != CL_SUCCESS) || (*pp_dest_port == NULL)) {
1328219820Sjeff				/*
1329219820Sjeff				   This 'error' is the client's fault (bad lid)
1330219820Sjeff				   so don't enter it as an error in our own log.
1331219820Sjeff				   Return an error response to the client.
1332219820Sjeff				 */
1333219820Sjeff				OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1334219820Sjeff					"No dest port with LID %u\n",
1335219820Sjeff					cl_ntoh16(p_pr->dlid));
1336219820Sjeff
1337219820Sjeff				sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
1338219820Sjeff				goto Exit;
1339219820Sjeff			}
1340219820Sjeff		}
1341219820Sjeff	}
1342219820Sjeff
1343219820SjeffExit:
1344219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1345219820Sjeff	return (sa_status);
1346219820Sjeff}
1347219820Sjeff
1348219820Sjeff/**********************************************************************
1349219820Sjeff **********************************************************************/
1350219820Sjeffstatic void
1351219820Sjeff__osm_pr_rcv_process_world(IN osm_sa_t * sa,
1352219820Sjeff			   IN const osm_madw_t * const p_madw,
1353219820Sjeff			   IN const osm_port_t * const requester_port,
1354219820Sjeff			   IN const ib_gid_t * const p_dgid,
1355219820Sjeff			   IN const ib_net64_t comp_mask,
1356219820Sjeff			   IN cl_qlist_t * const p_list)
1357219820Sjeff{
1358219820Sjeff	const cl_qmap_t *p_tbl;
1359219820Sjeff	const osm_port_t *p_dest_port;
1360219820Sjeff	const osm_port_t *p_src_port;
1361219820Sjeff
1362219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1363219820Sjeff
1364219820Sjeff	/*
1365219820Sjeff	   Iterate the entire port space over itself.
1366219820Sjeff	   A path record from a port to itself is legit, so no
1367219820Sjeff	   need for a special case there.
1368219820Sjeff
1369219820Sjeff	   We compute both A -> B and B -> A, since we don't have
1370219820Sjeff	   any check to determine the reversability of the paths.
1371219820Sjeff	 */
1372219820Sjeff	p_tbl = &sa->p_subn->port_guid_tbl;
1373219820Sjeff
1374219820Sjeff	p_dest_port = (osm_port_t *) cl_qmap_head(p_tbl);
1375219820Sjeff	while (p_dest_port != (osm_port_t *) cl_qmap_end(p_tbl)) {
1376219820Sjeff		p_src_port = (osm_port_t *) cl_qmap_head(p_tbl);
1377219820Sjeff		while (p_src_port != (osm_port_t *) cl_qmap_end(p_tbl)) {
1378219820Sjeff			__osm_pr_rcv_get_port_pair_paths(sa, p_madw,
1379219820Sjeff							 requester_port,
1380219820Sjeff							 p_src_port,
1381219820Sjeff							 p_dest_port, p_dgid,
1382219820Sjeff							 comp_mask, p_list);
1383219820Sjeff
1384219820Sjeff			p_src_port =
1385219820Sjeff			    (osm_port_t *) cl_qmap_next(&p_src_port->map_item);
1386219820Sjeff		}
1387219820Sjeff
1388219820Sjeff		p_dest_port =
1389219820Sjeff		    (osm_port_t *) cl_qmap_next(&p_dest_port->map_item);
1390219820Sjeff	}
1391219820Sjeff
1392219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1393219820Sjeff}
1394219820Sjeff
1395219820Sjeff/**********************************************************************
1396219820Sjeff **********************************************************************/
1397219820Sjeffstatic void
1398219820Sjeff__osm_pr_rcv_process_half(IN osm_sa_t * sa,
1399219820Sjeff			  IN const osm_madw_t * const p_madw,
1400219820Sjeff			  IN const osm_port_t * const requester_port,
1401219820Sjeff			  IN const osm_port_t * const p_src_port,
1402219820Sjeff			  IN const osm_port_t * const p_dest_port,
1403219820Sjeff			  IN const ib_gid_t * const p_dgid,
1404219820Sjeff			  IN const ib_net64_t comp_mask,
1405219820Sjeff			  IN cl_qlist_t * const p_list)
1406219820Sjeff{
1407219820Sjeff	const cl_qmap_t *p_tbl;
1408219820Sjeff	const osm_port_t *p_port;
1409219820Sjeff
1410219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1411219820Sjeff
1412219820Sjeff	/*
1413219820Sjeff	   Iterate over every port, looking for matches...
1414219820Sjeff	   A path record from a port to itself is legit, so no
1415219820Sjeff	   need to special case that one.
1416219820Sjeff	 */
1417219820Sjeff	p_tbl = &sa->p_subn->port_guid_tbl;
1418219820Sjeff
1419219820Sjeff	if (p_src_port) {
1420219820Sjeff		/*
1421219820Sjeff		   The src port if fixed, so iterate over destination ports.
1422219820Sjeff		 */
1423219820Sjeff		p_port = (osm_port_t *) cl_qmap_head(p_tbl);
1424219820Sjeff		while (p_port != (osm_port_t *) cl_qmap_end(p_tbl)) {
1425219820Sjeff			__osm_pr_rcv_get_port_pair_paths(sa, p_madw,
1426219820Sjeff							 requester_port,
1427219820Sjeff							 p_src_port, p_port,
1428219820Sjeff							 p_dgid, comp_mask,
1429219820Sjeff							 p_list);
1430219820Sjeff			p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item);
1431219820Sjeff		}
1432219820Sjeff	} else {
1433219820Sjeff		/*
1434219820Sjeff		   The dest port if fixed, so iterate over source ports.
1435219820Sjeff		 */
1436219820Sjeff		p_port = (osm_port_t *) cl_qmap_head(p_tbl);
1437219820Sjeff		while (p_port != (osm_port_t *) cl_qmap_end(p_tbl)) {
1438219820Sjeff			__osm_pr_rcv_get_port_pair_paths(sa, p_madw,
1439219820Sjeff							 requester_port, p_port,
1440219820Sjeff							 p_dest_port, p_dgid,
1441219820Sjeff							 comp_mask, p_list);
1442219820Sjeff			p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item);
1443219820Sjeff		}
1444219820Sjeff	}
1445219820Sjeff
1446219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1447219820Sjeff}
1448219820Sjeff
1449219820Sjeff/**********************************************************************
1450219820Sjeff **********************************************************************/
1451219820Sjeffstatic void
1452219820Sjeff__osm_pr_rcv_process_pair(IN osm_sa_t * sa,
1453219820Sjeff			  IN const osm_madw_t * const p_madw,
1454219820Sjeff			  IN const osm_port_t * const requester_port,
1455219820Sjeff			  IN const osm_port_t * const p_src_port,
1456219820Sjeff			  IN const osm_port_t * const p_dest_port,
1457219820Sjeff			  IN const ib_gid_t * const p_dgid,
1458219820Sjeff			  IN const ib_net64_t comp_mask,
1459219820Sjeff			  IN cl_qlist_t * const p_list)
1460219820Sjeff{
1461219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1462219820Sjeff
1463219820Sjeff	__osm_pr_rcv_get_port_pair_paths(sa, p_madw, requester_port,
1464219820Sjeff					 p_src_port, p_dest_port, p_dgid,
1465219820Sjeff					 comp_mask, p_list);
1466219820Sjeff
1467219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1468219820Sjeff}
1469219820Sjeff
1470219820Sjeff/**********************************************************************
1471219820Sjeff **********************************************************************/
1472219820Sjeffstatic osm_mgrp_t *pr_get_mgrp(IN osm_sa_t * sa,
1473219820Sjeff			       IN const osm_madw_t * const p_madw)
1474219820Sjeff{
1475219820Sjeff	ib_path_rec_t *p_pr;
1476219820Sjeff	const ib_sa_mad_t *p_sa_mad;
1477219820Sjeff	ib_net64_t comp_mask;
1478219820Sjeff	osm_mgrp_t *mgrp = NULL;
1479219820Sjeff
1480219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1481219820Sjeff	p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1482219820Sjeff
1483219820Sjeff	comp_mask = p_sa_mad->comp_mask;
1484219820Sjeff
1485219820Sjeff	if ((comp_mask & IB_PR_COMPMASK_DGID) &&
1486219820Sjeff	    !(mgrp = osm_get_mgrp_by_mgid(sa, &p_pr->dgid))) {
1487219820Sjeff		char gid_str[INET6_ADDRSTRLEN];
1488219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F09: "
1489219820Sjeff			"No MC group found for PathRecord destination GID %s\n",
1490219820Sjeff			inet_ntop(AF_INET6, p_pr->dgid.raw, gid_str,
1491219820Sjeff				  sizeof gid_str));
1492219820Sjeff		goto Exit;
1493219820Sjeff	}
1494219820Sjeff
1495219820Sjeff	if (comp_mask & IB_PR_COMPMASK_DLID) {
1496219820Sjeff		if (mgrp) {
1497219820Sjeff			/* check that the MLID in the MC group is */
1498219820Sjeff			/* the same as the DLID in the PathRecord */
1499219820Sjeff			if (mgrp->mlid != p_pr->dlid) {
1500219820Sjeff				/* Note: perhaps this might be better indicated as an invalid request */
1501219820Sjeff				OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F10: "
1502219820Sjeff					"MC group MLID 0x%x does not match "
1503219820Sjeff					"PathRecord destination LID 0x%x\n",
1504219820Sjeff					mgrp->mlid, p_pr->dlid);
1505219820Sjeff				mgrp = NULL;
1506219820Sjeff				goto Exit;
1507219820Sjeff			}
1508219820Sjeff		} else if (!(mgrp = osm_get_mgrp_by_mlid(sa->p_subn, p_pr->dlid)))
1509219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F11: "
1510219820Sjeff				"No MC group found for PathRecord "
1511219820Sjeff				"destination LID 0x%x\n", p_pr->dlid);
1512219820Sjeff	}
1513219820Sjeff
1514219820SjeffExit:
1515219820Sjeff	return mgrp;
1516219820Sjeff}
1517219820Sjeff
1518219820Sjeff/**********************************************************************
1519219820Sjeff **********************************************************************/
1520219820Sjeffstatic ib_api_status_t
1521219820Sjeff__osm_pr_match_mgrp_attributes(IN osm_sa_t * sa,
1522219820Sjeff			       IN const osm_madw_t * const p_madw,
1523219820Sjeff			       IN const osm_mgrp_t * const p_mgrp)
1524219820Sjeff{
1525219820Sjeff	const ib_path_rec_t *p_pr;
1526219820Sjeff	const ib_sa_mad_t *p_sa_mad;
1527219820Sjeff	ib_net64_t comp_mask;
1528219820Sjeff	ib_api_status_t status = IB_ERROR;
1529219820Sjeff	uint32_t flow_label;
1530219820Sjeff	uint8_t sl;
1531219820Sjeff	uint8_t hop_limit;
1532219820Sjeff
1533219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1534219820Sjeff
1535219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1536219820Sjeff	p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1537219820Sjeff
1538219820Sjeff	comp_mask = p_sa_mad->comp_mask;
1539219820Sjeff
1540219820Sjeff	/* If SGID and/or SLID specified, should validate as member of MC group */
1541219820Sjeff	/* Also, MTU, rate, packet lifetime, and raw traffic requested are not currently checked */
1542219820Sjeff	if (comp_mask & IB_PR_COMPMASK_PKEY) {
1543219820Sjeff		if (p_pr->pkey != p_mgrp->mcmember_rec.pkey)
1544219820Sjeff			goto Exit;
1545219820Sjeff	}
1546219820Sjeff
1547219820Sjeff	ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop,
1548219820Sjeff				  &sl, &flow_label, &hop_limit);
1549219820Sjeff
1550219820Sjeff	if (comp_mask & IB_PR_COMPMASK_SL) {
1551219820Sjeff		if (ib_path_rec_sl(p_pr) != sl)
1552219820Sjeff			goto Exit;
1553219820Sjeff	}
1554219820Sjeff
1555219820Sjeff	/* If SubnAdmGet, assume NumbPaths of 1 (1.2 erratum) */
1556219820Sjeff	if ((comp_mask & IB_PR_COMPMASK_NUMBPATH) &&
1557219820Sjeff	    (p_sa_mad->method != IB_MAD_METHOD_GET)) {
1558219820Sjeff		if (ib_path_rec_num_path(p_pr) == 0)
1559219820Sjeff			goto Exit;
1560219820Sjeff	}
1561219820Sjeff
1562219820Sjeff	if (comp_mask & IB_PR_COMPMASK_FLOWLABEL) {
1563219820Sjeff		if (ib_path_rec_flow_lbl(p_pr) != flow_label)
1564219820Sjeff			goto Exit;
1565219820Sjeff	}
1566219820Sjeff
1567219820Sjeff	if (comp_mask & IB_PR_COMPMASK_HOPLIMIT) {
1568219820Sjeff		if (ib_path_rec_hop_limit(p_pr) != hop_limit)
1569219820Sjeff			goto Exit;
1570219820Sjeff	}
1571219820Sjeff
1572219820Sjeff	if (comp_mask & IB_PR_COMPMASK_TCLASS) {
1573219820Sjeff		if (p_pr->tclass != p_mgrp->mcmember_rec.tclass)
1574219820Sjeff			goto Exit;
1575219820Sjeff	}
1576219820Sjeff
1577219820Sjeff	status = IB_SUCCESS;
1578219820Sjeff
1579219820SjeffExit:
1580219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1581219820Sjeff	return (status);
1582219820Sjeff}
1583219820Sjeff
1584219820Sjeff/**********************************************************************
1585219820Sjeff **********************************************************************/
1586219820Sjeffstatic int
1587219820Sjeff__osm_pr_rcv_check_mcast_dest(IN osm_sa_t * sa,
1588219820Sjeff			      IN const osm_madw_t * const p_madw)
1589219820Sjeff{
1590219820Sjeff	const ib_path_rec_t *p_pr;
1591219820Sjeff	const ib_sa_mad_t *p_sa_mad;
1592219820Sjeff	ib_net64_t comp_mask;
1593219820Sjeff	int is_multicast = 0;
1594219820Sjeff
1595219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1596219820Sjeff
1597219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1598219820Sjeff	p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1599219820Sjeff
1600219820Sjeff	comp_mask = p_sa_mad->comp_mask;
1601219820Sjeff
1602219820Sjeff	if (comp_mask & IB_PR_COMPMASK_DGID) {
1603219820Sjeff		is_multicast = ib_gid_is_multicast(&p_pr->dgid);
1604219820Sjeff		if (!is_multicast)
1605219820Sjeff			goto Exit;
1606219820Sjeff	}
1607219820Sjeff
1608219820Sjeff	if (comp_mask & IB_PR_COMPMASK_DLID) {
1609219820Sjeff		if (cl_ntoh16(p_pr->dlid) >= IB_LID_MCAST_START_HO &&
1610219820Sjeff		    cl_ntoh16(p_pr->dlid) <= IB_LID_MCAST_END_HO)
1611219820Sjeff			is_multicast = 1;
1612219820Sjeff		else if (is_multicast) {
1613219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F12: "
1614219820Sjeff				"PathRecord request indicates MGID but not MLID\n");
1615219820Sjeff			is_multicast = -1;
1616219820Sjeff		}
1617219820Sjeff	}
1618219820Sjeff
1619219820SjeffExit:
1620219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1621219820Sjeff	return (is_multicast);
1622219820Sjeff}
1623219820Sjeff
1624219820Sjeff/**********************************************************************
1625219820Sjeff **********************************************************************/
1626219820Sjeffvoid osm_pr_rcv_process(IN void *context, IN void *data)
1627219820Sjeff{
1628219820Sjeff	osm_sa_t *sa = context;
1629219820Sjeff	osm_madw_t *p_madw = data;
1630219820Sjeff	const ib_path_rec_t *p_pr;
1631219820Sjeff	const ib_sa_mad_t *p_sa_mad;
1632219820Sjeff	const osm_port_t *p_src_port;
1633219820Sjeff	const osm_port_t *p_dest_port;
1634219820Sjeff	cl_qlist_t pr_list;
1635219820Sjeff	ib_gid_t dgid;
1636219820Sjeff	ib_net16_t sa_status;
1637219820Sjeff	osm_port_t *requester_port;
1638219820Sjeff	int ret;
1639219820Sjeff
1640219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1641219820Sjeff
1642219820Sjeff	CL_ASSERT(p_madw);
1643219820Sjeff
1644219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1645219820Sjeff	p_pr = (ib_path_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1646219820Sjeff
1647219820Sjeff	CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_PATH_RECORD);
1648219820Sjeff
1649219820Sjeff	/* we only support SubnAdmGet and SubnAdmGetTable methods */
1650219820Sjeff	if (p_sa_mad->method != IB_MAD_METHOD_GET &&
1651219820Sjeff	    p_sa_mad->method != IB_MAD_METHOD_GETTABLE) {
1652219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F17: "
1653219820Sjeff			"Unsupported Method (%s)\n",
1654219820Sjeff			ib_get_sa_method_str(p_sa_mad->method));
1655219820Sjeff		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
1656219820Sjeff		goto Exit;
1657219820Sjeff	}
1658219820Sjeff
1659219820Sjeff	/* update the requester physical port. */
1660219820Sjeff	requester_port = osm_get_port_by_mad_addr(sa->p_log, sa->p_subn,
1661219820Sjeff						  osm_madw_get_mad_addr_ptr
1662219820Sjeff						  (p_madw));
1663219820Sjeff	if (requester_port == NULL) {
1664219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F16: "
1665219820Sjeff			"Cannot find requester physical port\n");
1666219820Sjeff		goto Exit;
1667219820Sjeff	}
1668219820Sjeff
1669219820Sjeff	if (osm_log_is_active(sa->p_log, OSM_LOG_DEBUG))
1670219820Sjeff		osm_dump_path_record(sa->p_log, p_pr, OSM_LOG_DEBUG);
1671219820Sjeff
1672219820Sjeff	cl_qlist_init(&pr_list);
1673219820Sjeff
1674219820Sjeff	/*
1675219820Sjeff	   Most SA functions (including this one) are read-only on the
1676219820Sjeff	   subnet object, so we grab the lock non-exclusively.
1677219820Sjeff	 */
1678219820Sjeff	cl_plock_acquire(sa->p_lock);
1679219820Sjeff
1680219820Sjeff	/* Handle multicast destinations separately */
1681219820Sjeff	if ((ret = __osm_pr_rcv_check_mcast_dest(sa, p_madw)) < 0) {
1682219820Sjeff		/* Multicast DGID with unicast DLID */
1683219820Sjeff		cl_plock_release(sa->p_lock);
1684219820Sjeff		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_INVALID_FIELD);
1685219820Sjeff		goto Exit;
1686219820Sjeff	}
1687219820Sjeff
1688219820Sjeff	if (ret > 0)
1689219820Sjeff		goto McastDest;
1690219820Sjeff
1691219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Unicast destination requested\n");
1692219820Sjeff
1693219820Sjeff	sa_status = __osm_pr_rcv_get_end_points(sa, p_madw,
1694219820Sjeff						&p_src_port, &p_dest_port,
1695219820Sjeff						&dgid);
1696219820Sjeff
1697219820Sjeff	if (sa_status == IB_SA_MAD_STATUS_SUCCESS) {
1698219820Sjeff		/*
1699219820Sjeff		   What happens next depends on the type of endpoint information
1700219820Sjeff		   that was specified....
1701219820Sjeff		 */
1702219820Sjeff		if (p_src_port) {
1703219820Sjeff			if (p_dest_port)
1704219820Sjeff				__osm_pr_rcv_process_pair(sa, p_madw,
1705219820Sjeff							  requester_port,
1706219820Sjeff							  p_src_port,
1707219820Sjeff							  p_dest_port, &dgid,
1708219820Sjeff							  p_sa_mad->comp_mask,
1709219820Sjeff							  &pr_list);
1710219820Sjeff			else
1711219820Sjeff				__osm_pr_rcv_process_half(sa, p_madw,
1712219820Sjeff							  requester_port,
1713219820Sjeff							  p_src_port, NULL,
1714219820Sjeff							  &dgid,
1715219820Sjeff							  p_sa_mad->comp_mask,
1716219820Sjeff							  &pr_list);
1717219820Sjeff		} else {
1718219820Sjeff			if (p_dest_port)
1719219820Sjeff				__osm_pr_rcv_process_half(sa, p_madw,
1720219820Sjeff							  requester_port, NULL,
1721219820Sjeff							  p_dest_port, &dgid,
1722219820Sjeff							  p_sa_mad->comp_mask,
1723219820Sjeff							  &pr_list);
1724219820Sjeff			else
1725219820Sjeff				/*
1726219820Sjeff				   Katie, bar the door!
1727219820Sjeff				 */
1728219820Sjeff				__osm_pr_rcv_process_world(sa, p_madw,
1729219820Sjeff							   requester_port,
1730219820Sjeff							   &dgid,
1731219820Sjeff							   p_sa_mad->comp_mask,
1732219820Sjeff							   &pr_list);
1733219820Sjeff		}
1734219820Sjeff	}
1735219820Sjeff	goto Unlock;
1736219820Sjeff
1737219820SjeffMcastDest:
1738219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Multicast destination requested\n");
1739219820Sjeff	{
1740219820Sjeff		osm_mgrp_t *p_mgrp = NULL;
1741219820Sjeff		ib_api_status_t status;
1742219820Sjeff		osm_pr_item_t *p_pr_item;
1743219820Sjeff		uint32_t flow_label;
1744219820Sjeff		uint8_t sl;
1745219820Sjeff		uint8_t hop_limit;
1746219820Sjeff
1747219820Sjeff		/* First, get the MC info */
1748219820Sjeff		p_mgrp = pr_get_mgrp(sa, p_madw);
1749219820Sjeff
1750219820Sjeff		if (!p_mgrp)
1751219820Sjeff			goto Unlock;
1752219820Sjeff
1753219820Sjeff		/* Make sure the rest of the PathRecord matches the MC group attributes */
1754219820Sjeff		status = __osm_pr_match_mgrp_attributes(sa, p_madw, p_mgrp);
1755219820Sjeff		if (status != IB_SUCCESS) {
1756219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F19: "
1757219820Sjeff				"MC group attributes don't match PathRecord request\n");
1758219820Sjeff			goto Unlock;
1759219820Sjeff		}
1760219820Sjeff
1761219820Sjeff		p_pr_item = malloc(sizeof(*p_pr_item));
1762219820Sjeff		if (p_pr_item == NULL) {
1763219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F18: "
1764219820Sjeff				"Unable to allocate path record for MC group\n");
1765219820Sjeff			goto Unlock;
1766219820Sjeff		}
1767219820Sjeff		memset(p_pr_item, 0, sizeof(*p_pr_item));
1768219820Sjeff
1769219820Sjeff		/* Copy PathRecord request into response */
1770219820Sjeff		p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1771219820Sjeff		p_pr = (ib_path_rec_t *)
1772219820Sjeff		    ib_sa_mad_get_payload_ptr(p_sa_mad);
1773219820Sjeff		p_pr_item->path_rec = *p_pr;
1774219820Sjeff
1775219820Sjeff		/* Now, use the MC info to cruft up the PathRecord response */
1776219820Sjeff		p_pr_item->path_rec.dgid = p_mgrp->mcmember_rec.mgid;
1777219820Sjeff		p_pr_item->path_rec.dlid = p_mgrp->mcmember_rec.mlid;
1778219820Sjeff		p_pr_item->path_rec.tclass = p_mgrp->mcmember_rec.tclass;
1779219820Sjeff		p_pr_item->path_rec.num_path = 1;
1780219820Sjeff		p_pr_item->path_rec.pkey = p_mgrp->mcmember_rec.pkey;
1781219820Sjeff
1782219820Sjeff		/* MTU, rate, and packet lifetime should be exactly */
1783219820Sjeff		p_pr_item->path_rec.mtu = (2 << 6) | p_mgrp->mcmember_rec.mtu;
1784219820Sjeff		p_pr_item->path_rec.rate = (2 << 6) | p_mgrp->mcmember_rec.rate;
1785219820Sjeff		p_pr_item->path_rec.pkt_life =
1786219820Sjeff		    (2 << 6) | p_mgrp->mcmember_rec.pkt_life;
1787219820Sjeff
1788219820Sjeff		/* SL, Hop Limit, and Flow Label */
1789219820Sjeff		ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop,
1790219820Sjeff					  &sl, &flow_label, &hop_limit);
1791219820Sjeff		ib_path_rec_set_sl(&p_pr_item->path_rec, sl);
1792219820Sjeff		ib_path_rec_set_qos_class(&p_pr_item->path_rec, 0);
1793219820Sjeff
1794219820Sjeff		/* HopLimit is not yet set in non link local MC groups */
1795219820Sjeff		/* If it were, this would not be needed */
1796219820Sjeff		if (ib_mgid_get_scope(&p_mgrp->mcmember_rec.mgid) != IB_MC_SCOPE_LINK_LOCAL)
1797219820Sjeff			hop_limit = IB_HOPLIMIT_MAX;
1798219820Sjeff
1799219820Sjeff		p_pr_item->path_rec.hop_flow_raw =
1800219820Sjeff			cl_hton32(hop_limit) | (flow_label << 8);
1801219820Sjeff
1802219820Sjeff		cl_qlist_insert_tail(&pr_list, &p_pr_item->list_item);
1803219820Sjeff	}
1804219820Sjeff
1805219820SjeffUnlock:
1806219820Sjeff	cl_plock_release(sa->p_lock);
1807219820Sjeff
1808219820Sjeff	/* Now, (finally) respond to the PathRecord request */
1809219820Sjeff	osm_sa_respond(sa, p_madw, sizeof(ib_path_rec_t), &pr_list);
1810219820Sjeff
1811219820SjeffExit:
1812219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1813219820Sjeff}
1814