1/*
2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2011 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 * Copyright (c) 2008 Xsigo Systems Inc. All rights reserved.
6 * Copyright (c) 2009 HNR Consulting. All rights reserved.
7 * Copyright (c) 2010 Sun Microsystems, Inc. All rights reserved.
8 * Copyright (c) 2009-2011 ZIH, TU Dresden, Federal Republic of Germany. All rights reserved.
9 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
10 *
11 * This software is available to you under a choice of one of two
12 * licenses.  You may choose to be licensed under the terms of the GNU
13 * General Public License (GPL) Version 2, available from the file
14 * COPYING in the main directory of this source tree, or the
15 * OpenIB.org BSD license below:
16 *
17 *     Redistribution and use in source and binary forms, with or
18 *     without modification, are permitted provided that the following
19 *     conditions are met:
20 *
21 *      - Redistributions of source code must retain the above
22 *        copyright notice, this list of conditions and the following
23 *        disclaimer.
24 *
25 *      - Redistributions in binary form must reproduce the above
26 *        copyright notice, this list of conditions and the following
27 *        disclaimer in the documentation and/or other materials
28 *        provided with the distribution.
29 *
30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
34 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
35 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
36 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 * SOFTWARE.
38 *
39 */
40
41/*
42 * Abstract:
43 *    Implementation of osm_pr_rcv_t.
44 * This object represents the PathRecord Receiver object.
45 * This object is part of the opensm family of objects.
46 */
47
48#if HAVE_CONFIG_H
49#  include <config.h>
50#endif				/* HAVE_CONFIG_H */
51
52#include <string.h>
53#include <arpa/inet.h>
54#include <sys/socket.h>
55#include <iba/ib_types.h>
56#include <complib/cl_qmap.h>
57#include <complib/cl_passivelock.h>
58#include <complib/cl_debug.h>
59#include <complib/cl_qlist.h>
60#include <opensm/osm_file_ids.h>
61#define FILE_ID OSM_FILE_SA_PATH_RECORD_C
62#include <vendor/osm_vendor_api.h>
63#include <opensm/osm_base.h>
64#include <opensm/osm_port.h>
65#include <opensm/osm_node.h>
66#include <opensm/osm_switch.h>
67#include <opensm/osm_helper.h>
68#include <opensm/osm_pkey.h>
69#include <opensm/osm_multicast.h>
70#include <opensm/osm_partition.h>
71#include <opensm/osm_opensm.h>
72#include <opensm/osm_qos_policy.h>
73#include <opensm/osm_sa.h>
74#include <opensm/osm_router.h>
75#include <opensm/osm_prefix_route.h>
76#include <opensm/osm_ucast_lash.h>
77
78#define SA_PR_RESP_SIZE SA_ITEM_RESP_SIZE(path_rec)
79
80#define MAX_HOPS 64
81
82static inline boolean_t sa_path_rec_is_tavor_port(IN const osm_port_t * p_port)
83{
84	osm_node_t const *p_node;
85	ib_net32_t vend_id;
86
87	p_node = p_port->p_node;
88	vend_id = ib_node_info_get_vendor_id(&p_node->node_info);
89
90	return ((p_node->node_info.device_id == CL_HTON16(23108)) &&
91		((vend_id == CL_HTON32(OSM_VENDOR_ID_MELLANOX)) ||
92		 (vend_id == CL_HTON32(OSM_VENDOR_ID_TOPSPIN)) ||
93		 (vend_id == CL_HTON32(OSM_VENDOR_ID_SILVERSTORM)) ||
94		 (vend_id == CL_HTON32(OSM_VENDOR_ID_VOLTAIRE))));
95}
96
97static boolean_t
98sa_path_rec_apply_tavor_mtu_limit(IN const ib_path_rec_t * p_pr,
99				  IN const osm_port_t * p_src_port,
100				  IN const osm_port_t * p_dest_port,
101				  IN const ib_net64_t comp_mask)
102{
103	uint8_t required_mtu;
104
105	/* only if at least one of the ports is a Tavor device */
106	if (!sa_path_rec_is_tavor_port(p_src_port) &&
107	    !sa_path_rec_is_tavor_port(p_dest_port))
108		return FALSE;
109
110	/*
111	   we can apply the patch if either:
112	   1. No MTU required
113	   2. Required MTU <
114	   3. Required MTU = 1K or 512 or 256
115	   4. Required MTU > 256 or 512
116	 */
117	required_mtu = ib_path_rec_mtu(p_pr);
118	if ((comp_mask & IB_PR_COMPMASK_MTUSELEC) &&
119	    (comp_mask & IB_PR_COMPMASK_MTU)) {
120		switch (ib_path_rec_mtu_sel(p_pr)) {
121		case 0:	/* must be greater than */
122		case 2:	/* exact match */
123			if (IB_MTU_LEN_1024 < required_mtu)
124				return FALSE;
125			break;
126
127		case 1:	/* must be less than */
128			/* can't be disqualified by this one */
129			break;
130
131		case 3:	/* largest available */
132			/* the ULP intentionally requested */
133			/* the largest MTU possible */
134			return FALSE;
135
136		default:
137			/* if we're here, there's a bug in ib_path_rec_mtu_sel() */
138			CL_ASSERT(FALSE);
139			break;
140		}
141	}
142
143	return TRUE;
144}
145
146static ib_api_status_t pr_rcv_get_path_parms(IN osm_sa_t * sa,
147					     IN const ib_path_rec_t * p_pr,
148					     IN const osm_alias_guid_t * p_src_alias_guid,
149					     IN const uint16_t src_lid_ho,
150					     IN const osm_alias_guid_t * p_dest_alias_guid,
151					     IN const uint16_t dest_lid_ho,
152					     IN const ib_net64_t comp_mask,
153					     OUT osm_path_parms_t * p_parms)
154{
155	const osm_node_t *p_node;
156	const osm_physp_t *p_physp, *p_physp0;
157	const osm_physp_t *p_src_physp;
158	const osm_physp_t *p_dest_physp;
159	const osm_prtn_t *p_prtn = NULL;
160	osm_opensm_t *p_osm;
161	struct osm_routing_engine *p_re;
162	const ib_port_info_t *p_pi, *p_pi0;
163	ib_api_status_t status = IB_SUCCESS;
164	ib_net16_t pkey;
165	uint8_t mtu;
166	uint8_t rate, p0_extended_rate, dest_rate;
167	uint8_t pkt_life;
168	uint8_t required_mtu;
169	uint8_t required_rate;
170	uint8_t required_pkt_life;
171	uint8_t sl;
172	uint8_t in_port_num;
173	ib_net16_t dest_lid;
174	uint8_t i;
175	ib_slvl_table_t *p_slvl_tbl = NULL;
176	osm_qos_level_t *p_qos_level = NULL;
177	uint16_t valid_sl_mask = 0xffff;
178	int hops = 0;
179	int extended, p0_extended;
180
181	OSM_LOG_ENTER(sa->p_log);
182
183	dest_lid = cl_hton16(dest_lid_ho);
184
185	p_dest_physp = p_dest_alias_guid->p_base_port->p_physp;
186	p_physp = p_src_alias_guid->p_base_port->p_physp;
187	p_src_physp = p_physp;
188	p_pi = &p_physp->port_info;
189	p_osm = sa->p_subn->p_osm;
190	p_re = p_osm->routing_engine_used;
191
192	mtu = ib_port_info_get_mtu_cap(p_pi);
193	extended = p_pi->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
194	rate = ib_port_info_compute_rate(p_pi, extended);
195
196	/*
197	   Mellanox Tavor device performance is better using 1K MTU.
198	   If required MTU and MTU selector are such that 1K is OK
199	   and at least one end of the path is Tavor we override the
200	   port MTU with 1K.
201	 */
202	if (sa->p_subn->opt.enable_quirks &&
203	    sa_path_rec_apply_tavor_mtu_limit(p_pr,
204					      p_src_alias_guid->p_base_port,
205					      p_dest_alias_guid->p_base_port,
206					      comp_mask))
207		if (mtu > IB_MTU_LEN_1024) {
208			mtu = IB_MTU_LEN_1024;
209			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
210				"Optimized Path MTU to 1K for Mellanox Tavor device\n");
211		}
212
213	/*
214	   Walk the subnet object from source to destination,
215	   tracking the most restrictive rate and mtu values along the way...
216
217	   If source port node is a switch, then p_physp should
218	   point to the port that routes the destination lid
219	 */
220
221	p_node = osm_physp_get_node_ptr(p_physp);
222
223	if (p_node->sw) {
224		/*
225		 * Source node is a switch.
226		 * Make sure that p_physp points to the out port of the
227		 * switch that routes to the destination lid (dest_lid_ho)
228		 */
229		p_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid);
230		if (p_physp == 0) {
231			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F02: "
232				"Cannot find routing from LID %u to LID %u on "
233				"switch %s (GUID: 0x%016" PRIx64 ")\n",
234				src_lid_ho, dest_lid_ho, p_node->print_desc,
235				cl_ntoh64(osm_node_get_node_guid(p_node)));
236			status = IB_NOT_FOUND;
237			goto Exit;
238		}
239	}
240
241	if (sa->p_subn->opt.qos) {
242		/*
243		 * Whether this node is switch or CA, the IN port for
244		 * the sl2vl table is 0, because this is a source node.
245		 */
246		p_slvl_tbl = osm_physp_get_slvl_tbl(p_physp, 0);
247
248		/* update valid SLs that still exist on this route */
249		for (i = 0; i < IB_MAX_NUM_VLS; i++) {
250			if (valid_sl_mask & (1 << i) &&
251			    ib_slvl_table_get(p_slvl_tbl, i) == IB_DROP_VL)
252				valid_sl_mask &= ~(1 << i);
253		}
254		if (!valid_sl_mask) {
255			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
256				"All the SLs lead to VL15 on this path\n");
257			status = IB_NOT_FOUND;
258			goto Exit;
259		}
260	}
261
262	/*
263	 * Same as above
264	 */
265	p_node = osm_physp_get_node_ptr(p_dest_physp);
266
267	if (p_node->sw) {
268		/*
269		 * if destination is switch, we want p_dest_physp to point to port 0
270		 */
271		p_dest_physp =
272		    osm_switch_get_route_by_lid(p_node->sw, dest_lid);
273
274		if (p_dest_physp == 0) {
275			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F03: "
276				"Can't find routing from LID %u to LID %u on "
277				"switch %s (GUID: 0x%016" PRIx64 ")\n",
278				src_lid_ho, dest_lid_ho, p_node->print_desc,
279				cl_ntoh64(osm_node_get_node_guid(p_node)));
280			status = IB_NOT_FOUND;
281			goto Exit;
282		}
283
284	}
285
286	/*
287	 * Now go through the path step by step
288	 */
289
290	while (p_physp != p_dest_physp) {
291
292		int tmp_pnum = p_physp->port_num;
293		p_node = osm_physp_get_node_ptr(p_physp);
294		p_physp = osm_physp_get_remote(p_physp);
295
296		if (p_physp == 0) {
297			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F05: "
298				"Can't find remote phys port of %s (GUID: "
299				"0x%016"PRIx64") port %d "
300				"while routing from LID %u to LID %u\n",
301				p_node->print_desc,
302				cl_ntoh64(osm_node_get_node_guid(p_node)),
303				tmp_pnum, src_lid_ho, dest_lid_ho);
304			status = IB_ERROR;
305			goto Exit;
306		}
307
308		in_port_num = osm_physp_get_port_num(p_physp);
309
310		/*
311		   This is point to point case (no switch in between)
312		 */
313		if (p_physp == p_dest_physp)
314			break;
315
316		p_node = osm_physp_get_node_ptr(p_physp);
317
318		if (!p_node->sw) {
319			/*
320			   There is some sort of problem in the subnet object!
321			   If this isn't a switch, we should have reached
322			   the destination by now!
323			 */
324			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F06: "
325				"Internal error, bad path while routing "
326				"%s (GUID: 0x%016"PRIx64") port %d to "
327				"%s (GUID: 0x%016"PRIx64") port %d; "
328				"ended at %s port %d\n",
329				p_src_alias_guid->p_base_port->p_node->print_desc,
330				cl_ntoh64(p_src_alias_guid->p_base_port->p_node->node_info.node_guid),
331				p_src_alias_guid->p_base_port->p_physp->port_num,
332				p_dest_alias_guid->p_base_port->p_node->print_desc,
333				cl_ntoh64(p_dest_alias_guid->p_base_port->p_node->node_info.node_guid),
334				p_dest_alias_guid->p_base_port->p_physp->port_num,
335				p_node->print_desc,
336				p_physp->port_num);
337			status = IB_ERROR;
338			goto Exit;
339		}
340
341		/*
342		   Check parameters for the ingress port in this switch.
343		 */
344		p_pi = &p_physp->port_info;
345
346		if (mtu > ib_port_info_get_mtu_cap(p_pi))
347			mtu = ib_port_info_get_mtu_cap(p_pi);
348
349		p_physp0 = osm_node_get_physp_ptr((osm_node_t *)p_node, 0);
350		p_pi0 = &p_physp0->port_info;
351		p0_extended = p_pi0->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
352		p0_extended_rate = ib_port_info_compute_rate(p_pi, p0_extended);
353		if (ib_path_compare_rates(rate, p0_extended_rate) > 0)
354			rate = p0_extended_rate;
355
356		/*
357		   Continue with the egress port on this switch.
358		 */
359		p_physp = osm_switch_get_route_by_lid(p_node->sw, dest_lid);
360		if (p_physp == 0) {
361			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F07: "
362				"Dead end path on switch "
363				"%s (GUID: 0x%016"PRIx64") to LID %u\n",
364				p_node->print_desc,
365				cl_ntoh64(osm_node_get_node_guid(p_node)),
366				dest_lid_ho);
367			status = IB_ERROR;
368			goto Exit;
369		}
370
371		p_pi = &p_physp->port_info;
372
373		if (mtu > ib_port_info_get_mtu_cap(p_pi))
374			mtu = ib_port_info_get_mtu_cap(p_pi);
375
376		p_physp0 = osm_node_get_physp_ptr((osm_node_t *)p_node, 0);
377		p_pi0 = &p_physp0->port_info;
378		p0_extended = p_pi0->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
379		p0_extended_rate = ib_port_info_compute_rate(p_pi, p0_extended);
380		if (ib_path_compare_rates(rate, p0_extended_rate) > 0)
381			rate = p0_extended_rate;
382
383		if (sa->p_subn->opt.qos) {
384			/*
385			 * Check SL2VL table of the switch and update valid SLs
386			 */
387			p_slvl_tbl =
388			    osm_physp_get_slvl_tbl(p_physp, in_port_num);
389			for (i = 0; i < IB_MAX_NUM_VLS; i++) {
390				if (valid_sl_mask & (1 << i) &&
391				    ib_slvl_table_get(p_slvl_tbl,
392						      i) == IB_DROP_VL)
393					valid_sl_mask &= ~(1 << i);
394			}
395			if (!valid_sl_mask) {
396				OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "All the SLs "
397					"lead to VL15 on this path\n");
398				status = IB_NOT_FOUND;
399				goto Exit;
400			}
401		}
402
403		/* update number of hops traversed */
404		hops++;
405		if (hops > MAX_HOPS) {
406			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F25: "
407				"Path from GUID 0x%016" PRIx64 " (%s port %d) "
408				"to lid %u GUID 0x%016" PRIx64 " (%s port %d) "
409				"needs more than %d hops, max %d hops allowed\n",
410				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
411				p_src_physp->p_node->print_desc,
412				p_src_physp->port_num,
413				dest_lid_ho,
414				cl_ntoh64(osm_physp_get_port_guid
415					  (p_dest_physp)),
416				p_dest_physp->p_node->print_desc,
417				p_dest_physp->port_num,
418				hops,
419				MAX_HOPS);
420			status = IB_NOT_FOUND;
421			goto Exit;
422		}
423	}
424
425	/*
426	   p_physp now points to the destination
427	 */
428	p_pi = &p_physp->port_info;
429
430	if (mtu > ib_port_info_get_mtu_cap(p_pi))
431		mtu = ib_port_info_get_mtu_cap(p_pi);
432
433	extended = p_pi->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
434	dest_rate = ib_port_info_compute_rate(p_pi, extended);
435	if (ib_path_compare_rates(rate, dest_rate) > 0)
436		rate = dest_rate;
437
438	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
439		"Path min MTU = %u, min rate = %u\n", mtu, rate);
440
441	/*
442	 * Get QoS Level object according to the path request
443	 * and adjust path parameters according to QoS settings
444	 */
445	if (sa->p_subn->opt.qos &&
446	    sa->p_subn->p_qos_policy &&
447	    (p_qos_level =
448	     osm_qos_policy_get_qos_level_by_pr(sa->p_subn->p_qos_policy,
449						p_pr, p_src_physp, p_dest_physp,
450						comp_mask))) {
451		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
452			"PathRecord request matches QoS Level '%s' (%s)\n",
453			p_qos_level->name, p_qos_level->use ?
454			p_qos_level->use : "no description");
455
456		if (p_qos_level->mtu_limit_set
457		    && (mtu > p_qos_level->mtu_limit))
458			mtu = p_qos_level->mtu_limit;
459
460		if (p_qos_level->rate_limit_set
461		    && (ib_path_compare_rates(rate, p_qos_level->rate_limit) > 0))
462			rate = p_qos_level->rate_limit;
463
464		if (p_qos_level->sl_set) {
465			sl = p_qos_level->sl;
466			if (!(valid_sl_mask & (1 << sl))) {
467				status = IB_NOT_FOUND;
468				goto Exit;
469			}
470		}
471	}
472
473	/*
474	 * Set packet lifetime.
475	 * According to spec definition IBA 1.2 Table 205
476	 * PacketLifeTime description, for loopback paths,
477	 * packetLifeTime shall be zero.
478	 */
479	if (p_src_alias_guid->p_base_port == p_dest_alias_guid->p_base_port)
480		pkt_life = 0;
481	else if (p_qos_level && p_qos_level->pkt_life_set)
482		pkt_life = p_qos_level->pkt_life;
483	else
484		pkt_life = sa->p_subn->opt.subnet_timeout;
485
486	/*
487	   Determine if these values meet the user criteria
488	   and adjust appropriately
489	 */
490
491	/* we silently ignore cases where only the MTU selector is defined */
492	if ((comp_mask & IB_PR_COMPMASK_MTUSELEC) &&
493	    (comp_mask & IB_PR_COMPMASK_MTU)) {
494		required_mtu = ib_path_rec_mtu(p_pr);
495		switch (ib_path_rec_mtu_sel(p_pr)) {
496		case 0:	/* must be greater than */
497			if (mtu <= required_mtu)
498				status = IB_NOT_FOUND;
499			break;
500
501		case 1:	/* must be less than */
502			if (mtu >= required_mtu) {
503				/* adjust to use the highest mtu
504				   lower than the required one */
505				if (required_mtu > 1)
506					mtu = required_mtu - 1;
507				else
508					status = IB_NOT_FOUND;
509			}
510			break;
511
512		case 2:	/* exact match */
513			if (mtu < required_mtu)
514				status = IB_NOT_FOUND;
515			else
516				mtu = required_mtu;
517			break;
518
519		case 3:	/* largest available */
520			/* can't be disqualified by this one */
521			break;
522
523		default:
524			/* if we're here, there's a bug in ib_path_rec_mtu_sel() */
525			CL_ASSERT(FALSE);
526			status = IB_ERROR;
527			break;
528		}
529	}
530	if (status != IB_SUCCESS)
531		goto Exit;
532
533	/* we silently ignore cases where only the Rate selector is defined */
534	if ((comp_mask & IB_PR_COMPMASK_RATESELEC) &&
535	    (comp_mask & IB_PR_COMPMASK_RATE)) {
536		required_rate = ib_path_rec_rate(p_pr);
537		switch (ib_path_rec_rate_sel(p_pr)) {
538		case 0:	/* must be greater than */
539			if (ib_path_compare_rates(rate, required_rate) <= 0)
540				status = IB_NOT_FOUND;
541			break;
542
543		case 1:	/* must be less than */
544			if (ib_path_compare_rates(rate, required_rate) >= 0) {
545				/* adjust the rate to use the highest rate
546				   lower than the required one */
547				rate = ib_path_rate_get_prev(required_rate);
548				if (!rate)
549					status = IB_NOT_FOUND;
550			}
551			break;
552
553		case 2:	/* exact match */
554			if (ib_path_compare_rates(rate, required_rate))
555				status = IB_NOT_FOUND;
556			else
557				rate = required_rate;
558			break;
559
560		case 3:	/* largest available */
561			/* can't be disqualified by this one */
562			break;
563
564		default:
565			/* if we're here, there's a bug in ib_path_rec_mtu_sel() */
566			CL_ASSERT(FALSE);
567			status = IB_ERROR;
568			break;
569		}
570	}
571	if (status != IB_SUCCESS)
572		goto Exit;
573
574	/* we silently ignore cases where only the PktLife selector is defined */
575	if ((comp_mask & IB_PR_COMPMASK_PKTLIFETIMESELEC) &&
576	    (comp_mask & IB_PR_COMPMASK_PKTLIFETIME)) {
577		required_pkt_life = ib_path_rec_pkt_life(p_pr);
578		switch (ib_path_rec_pkt_life_sel(p_pr)) {
579		case 0:	/* must be greater than */
580			if (pkt_life <= required_pkt_life)
581				status = IB_NOT_FOUND;
582			break;
583
584		case 1:	/* must be less than */
585			if (pkt_life >= required_pkt_life) {
586				/* adjust the lifetime to use the highest possible
587				   lower than the required one */
588				if (required_pkt_life > 1)
589					pkt_life = required_pkt_life - 1;
590				else
591					status = IB_NOT_FOUND;
592			}
593			break;
594
595		case 2:	/* exact match */
596			if (pkt_life < required_pkt_life)
597				status = IB_NOT_FOUND;
598			else
599				pkt_life = required_pkt_life;
600			break;
601
602		case 3:	/* smallest available */
603			/* can't be disqualified by this one */
604			break;
605
606		default:
607			/* if we're here, there's a bug in ib_path_rec_pkt_life_sel() */
608			CL_ASSERT(FALSE);
609			status = IB_ERROR;
610			break;
611		}
612	}
613
614	if (status != IB_SUCCESS)
615		goto Exit;
616
617	/*
618	 * set Pkey for this path record request
619	 */
620
621	if ((comp_mask & IB_PR_COMPMASK_RAWTRAFFIC) &&
622	    (cl_ntoh32(p_pr->hop_flow_raw) & (1 << 31)))
623		pkey = osm_physp_find_common_pkey(p_src_physp, p_dest_physp,
624						  sa->p_subn->opt.allow_both_pkeys);
625
626	else if (comp_mask & IB_PR_COMPMASK_PKEY) {
627		/*
628		 * PR request has a specific pkey:
629		 * Check that source and destination share this pkey.
630		 * If QoS level has pkeys, check that this pkey exists
631		 * in the QoS level pkeys.
632		 * PR returned pkey is the requested pkey.
633		 */
634		pkey = p_pr->pkey;
635		if (!osm_physp_share_this_pkey(p_src_physp, p_dest_physp, pkey,
636					       sa->p_subn->opt.allow_both_pkeys)) {
637			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1A: "
638				"Ports 0x%016" PRIx64 " (%s port %d) and "
639				"0x%016" PRIx64 " (%s port %d) "
640				"do not share specified PKey 0x%04x\n",
641				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
642				p_src_physp->p_node->print_desc,
643				p_src_physp->port_num,
644				cl_ntoh64(osm_physp_get_port_guid
645					  (p_dest_physp)),
646				p_dest_physp->p_node->print_desc,
647				p_dest_physp->port_num,
648				cl_ntoh16(pkey));
649			status = IB_NOT_FOUND;
650			goto Exit;
651		}
652		if (p_qos_level && p_qos_level->pkey_range_len &&
653		    !osm_qos_level_has_pkey(p_qos_level, pkey)) {
654			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1D: "
655				"QoS level \"%s\" doesn't define specified PKey 0x%04x "
656				"for ports 0x%016" PRIx64 " (%s port %d) and "
657				"0x%016"PRIx64" (%s port %d)\n",
658				p_qos_level->name,
659				cl_ntoh16(pkey),
660				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
661				p_src_physp->p_node->print_desc,
662				p_src_alias_guid->p_base_port->p_physp->port_num,
663				cl_ntoh64(osm_physp_get_port_guid
664					  (p_dest_physp)),
665				p_dest_physp->p_node->print_desc,
666				p_dest_alias_guid->p_base_port->p_physp->port_num);
667			status = IB_NOT_FOUND;
668			goto Exit;
669		}
670
671	} else if (p_qos_level && p_qos_level->pkey_range_len) {
672		/*
673		 * PR request doesn't have a specific pkey, but QoS level
674		 * has pkeys - get shared pkey from QoS level pkeys
675		 */
676		pkey = osm_qos_level_get_shared_pkey(p_qos_level,
677						     p_src_physp, p_dest_physp,
678						     sa->p_subn->opt.allow_both_pkeys);
679		if (!pkey) {
680			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1E: "
681				"Ports 0x%016" PRIx64 " (%s) and "
682				"0x%016" PRIx64 " (%s) do not share "
683				"PKeys defined by QoS level \"%s\"\n",
684				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
685				p_src_physp->p_node->print_desc,
686				cl_ntoh64(osm_physp_get_port_guid
687					  (p_dest_physp)),
688				p_dest_physp->p_node->print_desc,
689				p_qos_level->name);
690			status = IB_NOT_FOUND;
691			goto Exit;
692		}
693	} else {
694		/*
695		 * Neither PR request nor QoS level have pkey.
696		 * Just get any shared pkey.
697		 */
698		pkey = osm_physp_find_common_pkey(p_src_physp, p_dest_physp,
699						  sa->p_subn->opt.allow_both_pkeys);
700		if (!pkey) {
701			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1B: "
702				"Ports src 0x%016"PRIx64" (%s port %d) and "
703				"dst 0x%016"PRIx64" (%s port %d) do not have "
704				"any shared PKeys\n",
705				cl_ntoh64(osm_physp_get_port_guid(p_src_physp)),
706				p_src_physp->p_node->print_desc,
707				p_src_physp->port_num,
708				cl_ntoh64(osm_physp_get_port_guid
709					  (p_dest_physp)),
710				p_dest_physp->p_node->print_desc,
711				p_dest_physp->port_num);
712			status = IB_NOT_FOUND;
713			goto Exit;
714		}
715	}
716
717	if (pkey) {
718		p_prtn =
719		    (osm_prtn_t *) cl_qmap_get(&sa->p_subn->prtn_pkey_tbl,
720					       pkey & cl_hton16((uint16_t) ~
721								0x8000));
722		if (p_prtn ==
723		    (osm_prtn_t *) cl_qmap_end(&sa->p_subn->prtn_pkey_tbl))
724			p_prtn = NULL;
725	}
726
727	/*
728	 * Set PathRecord SL
729	 */
730
731	if (comp_mask & IB_PR_COMPMASK_SL) {
732		/*
733		 * Specific SL was requested
734		 */
735		sl = ib_path_rec_sl(p_pr);
736
737		if (p_qos_level && p_qos_level->sl_set
738		    && (p_qos_level->sl != sl)) {
739			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1F: "
740				"QoS constraints: required PathRecord SL (%u) "
741				"doesn't match QoS policy \"%s\" SL (%u) "
742				"[%s port %d <-> %s port %d]\n", sl,
743				p_qos_level->name,
744				p_qos_level->sl,
745				p_src_alias_guid->p_base_port->p_node->print_desc,
746				p_src_alias_guid->p_base_port->p_physp->port_num,
747				p_dest_alias_guid->p_base_port->p_node->print_desc,
748				p_dest_alias_guid->p_base_port->p_physp->port_num);
749			status = IB_NOT_FOUND;
750			goto Exit;
751		}
752
753	} else if (p_qos_level && p_qos_level->sl_set) {
754		/*
755		 * No specific SL was requested, but there is an SL in
756		 * QoS level.
757		 */
758		sl = p_qos_level->sl;
759
760		if (pkey && p_prtn && p_prtn->sl != p_qos_level->sl)
761			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
762				"QoS level SL (%u) overrides partition SL (%u)\n",
763				p_qos_level->sl, p_prtn->sl);
764
765	} else if (pkey) {
766		/*
767		 * No specific SL in request or in QoS level - use partition SL
768		 */
769		if (!p_prtn) {
770			sl = OSM_DEFAULT_SL;
771			/* this may be possible when pkey tables are created somehow in
772			   previous runs or things are going wrong here */
773			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F1C: "
774				"No partition found for PKey 0x%04x - "
775				"using default SL %d "
776				"[%s port %d <-> %s port %d]\n",
777				cl_ntoh16(pkey), sl,
778				p_src_alias_guid->p_base_port->p_node->print_desc,
779				p_src_alias_guid->p_base_port->p_physp->port_num,
780				p_dest_alias_guid->p_base_port->p_node->print_desc,
781				p_dest_alias_guid->p_base_port->p_physp->port_num);
782		} else
783			sl = p_prtn->sl;
784	} else if (sa->p_subn->opt.qos) {
785		if (valid_sl_mask & (1 << OSM_DEFAULT_SL))
786			sl = OSM_DEFAULT_SL;
787		else {
788			for (i = 0; i < IB_MAX_NUM_VLS; i++)
789				if (valid_sl_mask & (1 << i))
790					break;
791			sl = i;
792		}
793	} else
794		sl = OSM_DEFAULT_SL;
795
796	if (sa->p_subn->opt.qos && !(valid_sl_mask & (1 << sl))) {
797		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F24: "
798			"Selected SL (%u) leads to VL15 "
799			"[%s port %d <-> %s port %d]\n",
800			sl,
801			p_src_alias_guid->p_base_port->p_node->print_desc,
802			p_src_alias_guid->p_base_port->p_physp->port_num,
803			p_dest_alias_guid->p_base_port->p_node->print_desc,
804			p_dest_alias_guid->p_base_port->p_physp->port_num);
805		status = IB_NOT_FOUND;
806		goto Exit;
807	}
808
809	/*
810	 * If the routing engine wants to have a say in path SL selection,
811	 * send the currently computed SL value as a hint and let the routing
812	 * engine override it.
813	 */
814	if (p_re && p_re->path_sl) {
815		uint8_t pr_sl;
816		pr_sl = sl;
817
818		sl = p_re->path_sl(p_re->context, sl,
819				   cl_hton16(src_lid_ho), cl_hton16(dest_lid_ho));
820
821		if ((comp_mask & IB_PR_COMPMASK_SL) && (sl != pr_sl)) {
822			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F2A: "
823				"Requested SL (%u) doesn't match SL calculated"
824				"by routing engine (%u) "
825				"[%s port %d <-> %s port %d]\n",
826				pr_sl,
827				sl,
828				p_src_alias_guid->p_base_port->p_node->print_desc,
829				p_src_alias_guid->p_base_port->p_physp->port_num,
830				p_dest_alias_guid->p_base_port->p_node->print_desc,
831				p_dest_alias_guid->p_base_port->p_physp->port_num);
832			status = IB_NOT_FOUND;
833			goto Exit;
834		}
835	}
836	/* reset pkey when raw traffic */
837	if (comp_mask & IB_PR_COMPMASK_RAWTRAFFIC &&
838	    cl_ntoh32(p_pr->hop_flow_raw) & (1 << 31))
839		pkey = 0;
840
841	p_parms->mtu = mtu;
842	p_parms->rate = rate;
843	p_parms->pkt_life = pkt_life;
844	p_parms->pkey = pkey;
845	p_parms->sl = sl;
846
847	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Path params: mtu = %u, rate = %u,"
848		" packet lifetime = %u, pkey = 0x%04X, sl = %u\n",
849		mtu, rate, pkt_life, cl_ntoh16(pkey), sl);
850Exit:
851	OSM_LOG_EXIT(sa->p_log);
852	return status;
853}
854
855ib_api_status_t osm_get_path_params(IN osm_sa_t * sa,
856				    IN const osm_port_t * p_src_port,
857				    IN const uint16_t slid_ho,
858				    IN const osm_port_t * p_dest_port,
859				    IN const uint16_t dlid_ho,
860				    OUT osm_path_parms_t * p_parms)
861{
862	osm_alias_guid_t *p_src_alias_guid, *p_dest_alias_guid;
863	ib_path_rec_t pr;
864
865	if (!p_src_port || !slid_ho || !p_dest_port || !dlid_ho)
866		return IB_INVALID_PARAMETER;
867
868	memset(&pr, 0, sizeof(ib_path_rec_t));
869
870	p_src_alias_guid = osm_get_alias_guid_by_guid(sa->p_subn,
871						      osm_port_get_guid(p_src_port));
872	p_dest_alias_guid = osm_get_alias_guid_by_guid(sa->p_subn,
873						       osm_port_get_guid(p_dest_port));
874	return pr_rcv_get_path_parms(sa, &pr,
875				     p_src_alias_guid, slid_ho,
876				     p_dest_alias_guid, dlid_ho, 0, p_parms);
877}
878
879static void pr_rcv_build_pr(IN osm_sa_t * sa,
880			    IN const osm_alias_guid_t * p_src_alias_guid,
881			    IN const osm_alias_guid_t * p_dest_alias_guid,
882			    IN const ib_gid_t * p_sgid,
883			    IN const ib_gid_t * p_dgid,
884			    IN const uint16_t src_lid_ho,
885			    IN const uint16_t dest_lid_ho,
886			    IN const uint8_t preference,
887			    IN const osm_path_parms_t * p_parms,
888			    OUT ib_path_rec_t * p_pr)
889{
890	const osm_physp_t *p_src_physp, *p_dest_physp;
891
892	OSM_LOG_ENTER(sa->p_log);
893
894	if (p_dgid)
895		p_pr->dgid = *p_dgid;
896	else {
897		p_dest_physp = p_dest_alias_guid->p_base_port->p_physp;
898
899		p_pr->dgid.unicast.prefix =
900		    osm_physp_get_subnet_prefix(p_dest_physp);
901		p_pr->dgid.unicast.interface_id = p_dest_alias_guid->alias_guid;
902	}
903	if (p_sgid)
904		p_pr->sgid = *p_sgid;
905	else {
906		p_src_physp = p_src_alias_guid->p_base_port->p_physp;
907
908		p_pr->sgid.unicast.prefix = osm_physp_get_subnet_prefix(p_src_physp);
909		p_pr->sgid.unicast.interface_id = p_src_alias_guid->alias_guid;
910	}
911
912	p_pr->dlid = cl_hton16(dest_lid_ho);
913	p_pr->slid = cl_hton16(src_lid_ho);
914
915	p_pr->hop_flow_raw &= cl_hton32(1 << 31);
916
917	/* Only set HopLimit if going through a router */
918	if (p_dgid)
919		p_pr->hop_flow_raw |= cl_hton32(IB_HOPLIMIT_MAX);
920
921	p_pr->pkey = p_parms->pkey;
922	ib_path_rec_set_sl(p_pr, p_parms->sl);
923	ib_path_rec_set_qos_class(p_pr, 0);
924	p_pr->mtu = (uint8_t) (p_parms->mtu | 0x80);
925	p_pr->rate = (uint8_t) (p_parms->rate | 0x80);
926
927	/* According to 1.2 spec definition Table 205 PacketLifeTime description,
928	   for loopback paths, packetLifeTime shall be zero. */
929	if (p_src_alias_guid->p_base_port == p_dest_alias_guid->p_base_port)
930		p_pr->pkt_life = 0x80;	/* loopback */
931	else
932		p_pr->pkt_life = (uint8_t) (p_parms->pkt_life | 0x80);
933
934	p_pr->preference = preference;
935
936	/* always return num_path = 0 so this is only the reversible component */
937	if (p_parms->reversible)
938		p_pr->num_path = 0x80;
939
940	OSM_LOG_EXIT(sa->p_log);
941}
942
943static osm_sa_item_t *pr_rcv_get_lid_pair_path(IN osm_sa_t * sa,
944					       IN const ib_path_rec_t * p_pr,
945					       IN const osm_alias_guid_t * p_src_alias_guid,
946					       IN const osm_alias_guid_t * p_dest_alias_guid,
947					       IN const ib_gid_t * p_sgid,
948					       IN const ib_gid_t * p_dgid,
949					       IN const uint16_t src_lid_ho,
950					       IN const uint16_t dest_lid_ho,
951					       IN const ib_net64_t comp_mask,
952					       IN const uint8_t preference)
953{
954	osm_path_parms_t path_parms;
955	osm_path_parms_t rev_path_parms;
956	osm_sa_item_t *p_pr_item;
957	ib_api_status_t status, rev_path_status;
958
959	OSM_LOG_ENTER(sa->p_log);
960
961	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Src LID %u, Dest LID %u\n",
962		src_lid_ho, dest_lid_ho);
963
964	p_pr_item = malloc(SA_PR_RESP_SIZE);
965	if (p_pr_item == NULL) {
966		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F01: "
967			"Unable to allocate path record\n");
968		goto Exit;
969	}
970	memset(p_pr_item, 0, SA_PR_RESP_SIZE);
971
972	status = pr_rcv_get_path_parms(sa, p_pr, p_src_alias_guid, src_lid_ho,
973				       p_dest_alias_guid, dest_lid_ho,
974				       comp_mask, &path_parms);
975
976	if (status != IB_SUCCESS) {
977		free(p_pr_item);
978		p_pr_item = NULL;
979		goto Exit;
980	}
981
982	/* now try the reversible path */
983	rev_path_status = pr_rcv_get_path_parms(sa, p_pr, p_dest_alias_guid,
984						dest_lid_ho, p_src_alias_guid,
985						src_lid_ho, comp_mask,
986						&rev_path_parms);
987
988	path_parms.reversible = (rev_path_status == IB_SUCCESS);
989
990	/* did we get a Reversible Path compmask ? */
991	/*
992	   NOTE that if the reversible component = 0, it is a don't care
993	   rather than requiring non-reversible paths ...
994	   see Vol1 Ver1.2 p900 l16
995	 */
996	if ((comp_mask & IB_PR_COMPMASK_REVERSIBLE) &&
997	    !path_parms.reversible && (p_pr->num_path & 0x80)) {
998		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
999			"Requested reversible path but failed to get one\n");
1000		free(p_pr_item);
1001		p_pr_item = NULL;
1002		goto Exit;
1003	}
1004
1005	pr_rcv_build_pr(sa, p_src_alias_guid, p_dest_alias_guid, p_sgid, p_dgid,
1006			src_lid_ho, dest_lid_ho, preference, &path_parms,
1007			&p_pr_item->resp.path_rec);
1008
1009Exit:
1010	OSM_LOG_EXIT(sa->p_log);
1011	return p_pr_item;
1012}
1013
1014static void pr_rcv_get_port_pair_paths(IN osm_sa_t * sa,
1015				       IN const ib_sa_mad_t *sa_mad,
1016				       IN const osm_port_t * p_req_port,
1017				       IN const osm_alias_guid_t * p_src_alias_guid,
1018				       IN const osm_alias_guid_t * p_dest_alias_guid,
1019				       IN const ib_gid_t * p_sgid,
1020				       IN const ib_gid_t * p_dgid,
1021				       IN cl_qlist_t * p_list)
1022{
1023	const ib_path_rec_t *p_pr = ib_sa_mad_get_payload_ptr(sa_mad);
1024	ib_net64_t comp_mask = sa_mad->comp_mask;
1025	osm_sa_item_t *p_pr_item;
1026	uint16_t src_lid_min_ho;
1027	uint16_t src_lid_max_ho;
1028	uint16_t dest_lid_min_ho;
1029	uint16_t dest_lid_max_ho;
1030	uint16_t src_lid_ho;
1031	uint16_t dest_lid_ho;
1032	uint32_t path_num;
1033	uint8_t preference;
1034	unsigned iterations, src_offset, dest_offset;
1035
1036	OSM_LOG_ENTER(sa->p_log);
1037
1038	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1039		"Src port 0x%016" PRIx64 ", Dst port 0x%016" PRIx64 "\n",
1040		cl_ntoh64(p_src_alias_guid->alias_guid),
1041		cl_ntoh64(p_dest_alias_guid->alias_guid));
1042
1043	/* Check that the req_port, src_port and dest_port all share a
1044	   pkey. The check is done on the default physical port of the ports. */
1045	if (osm_port_share_pkey(sa->p_log, p_req_port,
1046				p_src_alias_guid->p_base_port,
1047				sa->p_subn->opt.allow_both_pkeys) == FALSE
1048	    || osm_port_share_pkey(sa->p_log, p_req_port,
1049				   p_dest_alias_guid->p_base_port,
1050				   sa->p_subn->opt.allow_both_pkeys) == FALSE
1051	    || osm_port_share_pkey(sa->p_log, p_src_alias_guid->p_base_port,
1052				   p_dest_alias_guid->p_base_port,
1053				   sa->p_subn->opt.allow_both_pkeys) == FALSE)
1054		/* One of the pairs doesn't share a pkey so the path is disqualified. */
1055		goto Exit;
1056
1057	/*
1058	   We shouldn't be here if the paths are disqualified in some way...
1059	   Thus, we assume every possible connection is valid.
1060
1061	   We desire to return high-quality paths first.
1062	   In OpenSM, higher quality means least overlap with other paths.
1063	   This is acheived in practice by returning paths with
1064	   different LID value on each end, which means these
1065	   paths are more redundant that paths with the same LID repeated
1066	   on one side.  For example, in OpenSM the paths between two
1067	   endpoints with LMC = 1 might be as follows:
1068
1069	   Port A, LID 1 <-> Port B, LID 3
1070	   Port A, LID 1 <-> Port B, LID 4
1071	   Port A, LID 2 <-> Port B, LID 3
1072	   Port A, LID 2 <-> Port B, LID 4
1073
1074	   The OpenSM unicast routing algorithms attempt to disperse each path
1075	   to as varied a physical path as is reasonable.  1<->3 and 1<->4 have
1076	   more physical overlap (hence less redundancy) than 1<->3 and 2<->4.
1077
1078	   OpenSM ranks paths in three preference groups:
1079
1080	   Preference Value    Description
1081	   ----------------    -------------------------------------------
1082	   0             Redundant in both directions with other
1083	   pref value = 0 paths
1084
1085	   1             Redundant in one direction with other
1086	   pref value = 0 and pref value = 1 paths
1087
1088	   2             Not redundant in either direction with
1089	   other paths
1090
1091	   3-FF          Unused
1092
1093	   SA clients don't need to know these details, only that the lower
1094	   preference paths are preferred, as stated in the spec.  The paths
1095	   may not actually be physically redundant depending on the topology
1096	   of the subnet, but the point of LMC > 0 is to offer redundancy,
1097	   so it is assumed that the subnet is physically appropriate for the
1098	   specified LMC value.  A more advanced implementation would inspect for
1099	   physical redundancy, but I'm not going to bother with that now.
1100	 */
1101
1102	/*
1103	   Refine our search if the client specified end-point LIDs
1104	 */
1105	if (comp_mask & IB_PR_COMPMASK_DLID)
1106		dest_lid_max_ho = dest_lid_min_ho = cl_ntoh16(p_pr->dlid);
1107	else
1108		osm_port_get_lid_range_ho(p_dest_alias_guid->p_base_port,
1109					  &dest_lid_min_ho, &dest_lid_max_ho);
1110
1111	if (comp_mask & IB_PR_COMPMASK_SLID)
1112		src_lid_max_ho = src_lid_min_ho = cl_ntoh16(p_pr->slid);
1113	else
1114		osm_port_get_lid_range_ho(p_src_alias_guid->p_base_port,
1115					  &src_lid_min_ho, &src_lid_max_ho);
1116
1117	if (src_lid_min_ho == 0) {
1118		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1119			"Obtained source LID of 0. No such LID possible "
1120			"(%s port %d)\n",
1121			p_src_alias_guid->p_base_port->p_node->print_desc,
1122			p_src_alias_guid->p_base_port->p_physp->port_num);
1123		goto Exit;
1124	}
1125
1126	if (dest_lid_min_ho == 0) {
1127		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1128			"Obtained destination LID of 0. No such LID possible "
1129			"(%s port %d)\n",
1130			p_dest_alias_guid->p_base_port->p_node->print_desc,
1131			p_dest_alias_guid->p_base_port->p_physp->port_num);
1132		goto Exit;
1133	}
1134
1135	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1136		"Src LIDs [%u-%u], Dest LIDs [%u-%u]\n",
1137		src_lid_min_ho, src_lid_max_ho,
1138		dest_lid_min_ho, dest_lid_max_ho);
1139
1140	src_lid_ho = src_lid_min_ho;
1141	dest_lid_ho = dest_lid_min_ho;
1142
1143	/*
1144	   Preferred paths come first in OpenSM
1145	 */
1146	preference = 0;
1147	path_num = 0;
1148
1149	/* If SubnAdmGet, assume NumbPaths 1 (1.2 erratum) */
1150	if (sa_mad->method == IB_MAD_METHOD_GET)
1151		iterations = 1;
1152	else if (comp_mask & IB_PR_COMPMASK_NUMBPATH)
1153		iterations = ib_path_rec_num_path(p_pr);
1154	else
1155		iterations = (unsigned) (-1);
1156
1157	while (path_num < iterations) {
1158		/*
1159		   These paths are "fully redundant"
1160		 */
1161
1162		p_pr_item = pr_rcv_get_lid_pair_path(sa, p_pr, p_src_alias_guid,
1163						     p_dest_alias_guid,
1164						     p_sgid, p_dgid,
1165						     src_lid_ho, dest_lid_ho,
1166						     comp_mask, preference);
1167
1168		if (p_pr_item) {
1169			cl_qlist_insert_tail(p_list, &p_pr_item->list_item);
1170			++path_num;
1171		}
1172
1173		if (++src_lid_ho > src_lid_max_ho)
1174			break;
1175
1176		if (++dest_lid_ho > dest_lid_max_ho)
1177			break;
1178	}
1179
1180	/*
1181	   Check if we've accumulated all the paths that the user cares to see
1182	 */
1183	if (path_num == iterations)
1184		goto Exit;
1185
1186	/*
1187	   Don't bother reporting preference 1 paths for now.
1188	   It's more trouble than it's worth and can only occur
1189	   if ports have different LMC values, which isn't supported
1190	   by OpenSM right now anyway.
1191	 */
1192	preference = 2;
1193	src_lid_ho = src_lid_min_ho;
1194	dest_lid_ho = dest_lid_min_ho;
1195	src_offset = 0;
1196	dest_offset = 0;
1197
1198	/*
1199	   Iterate over the remaining paths
1200	 */
1201	while (path_num < iterations) {
1202		dest_offset++;
1203		dest_lid_ho++;
1204
1205		if (dest_lid_ho > dest_lid_max_ho) {
1206			src_offset++;
1207			src_lid_ho++;
1208
1209			if (src_lid_ho > src_lid_max_ho)
1210				break;	/* done */
1211
1212			dest_offset = 0;
1213			dest_lid_ho = dest_lid_min_ho;
1214		}
1215
1216		/*
1217		   These paths are "fully non-redundant" with paths already
1218		   identified above and consequently not of much value.
1219
1220		   Don't return paths we already identified above, as indicated
1221		   by the offset values being equal.
1222		 */
1223		if (src_offset == dest_offset)
1224			continue;	/* already reported */
1225
1226		p_pr_item = pr_rcv_get_lid_pair_path(sa, p_pr, p_src_alias_guid,
1227						     p_dest_alias_guid, p_sgid,
1228						     p_dgid, src_lid_ho,
1229						     dest_lid_ho, comp_mask,
1230						     preference);
1231
1232		if (p_pr_item) {
1233			cl_qlist_insert_tail(p_list, &p_pr_item->list_item);
1234			++path_num;
1235		}
1236	}
1237
1238Exit:
1239	OSM_LOG_EXIT(sa->p_log);
1240}
1241
1242/* Find the router port that is configured to handle this prefix, if any */
1243static ib_net64_t find_router(const osm_sa_t *sa, ib_net64_t prefix)
1244{
1245	osm_prefix_route_t *route = NULL;
1246	osm_router_t *rtr;
1247	cl_qlist_t *l = &sa->p_subn->prefix_routes_list;
1248	cl_list_item_t *i;
1249
1250	OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, "Non local DGID subnet prefix "
1251		"0x%016" PRIx64 "\n", cl_ntoh64(prefix));
1252
1253	for (i = cl_qlist_head(l); i != cl_qlist_end(l); i = cl_qlist_next(i)) {
1254		osm_prefix_route_t *r = (osm_prefix_route_t *)i;
1255		if (!r->prefix || r->prefix == prefix) {
1256			route = r;
1257			break;
1258		}
1259	}
1260	if (!route)
1261		return 0;
1262
1263	if (route->guid == 0) /* first router */
1264		rtr = (osm_router_t *) cl_qmap_head(&sa->p_subn->rtr_guid_tbl);
1265	else
1266		rtr = (osm_router_t *) cl_qmap_get(&sa->p_subn->rtr_guid_tbl,
1267						   route->guid);
1268
1269	if (rtr == (osm_router_t *) cl_qmap_end(&sa->p_subn->rtr_guid_tbl))
1270		return 0;
1271
1272	return osm_port_get_guid(osm_router_get_port_ptr(rtr));
1273}
1274
1275ib_net16_t osm_pr_get_end_points(IN osm_sa_t * sa,
1276				 IN const ib_sa_mad_t *sa_mad,
1277				 OUT const osm_alias_guid_t ** pp_src_alias_guid,
1278				 OUT const osm_alias_guid_t ** pp_dest_alias_guid,
1279				 OUT const osm_port_t ** pp_src_port,
1280				 OUT const osm_port_t ** pp_dest_port,
1281				 OUT const ib_gid_t ** pp_sgid,
1282				 OUT const ib_gid_t ** pp_dgid)
1283{
1284	const ib_path_rec_t *p_pr = ib_sa_mad_get_payload_ptr(sa_mad);
1285	ib_net64_t comp_mask = sa_mad->comp_mask;
1286	ib_net64_t dest_guid;
1287	ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS;
1288
1289	OSM_LOG_ENTER(sa->p_log);
1290
1291	/*
1292	   Determine what fields are valid and then get a pointer
1293	   to the source and destination port objects, if possible.
1294	 */
1295
1296	/*
1297	   Check a few easy disqualifying cases up front before getting
1298	   into the endpoints.
1299	 */
1300
1301	*pp_src_alias_guid = NULL;
1302	*pp_src_port = NULL;
1303	if (comp_mask & IB_PR_COMPMASK_SGID) {
1304		if (!ib_gid_is_link_local(&p_pr->sgid)) {
1305			if (ib_gid_get_subnet_prefix(&p_pr->sgid) !=
1306			    sa->p_subn->opt.subnet_prefix) {
1307				/*
1308				   This 'error' is the client's fault (bad gid)
1309				   so don't enter it as an error in our own log.
1310				   Return an error response to the client.
1311				 */
1312				OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1313					"Non local SGID subnet prefix 0x%016"
1314					PRIx64 "\n",
1315					cl_ntoh64(p_pr->sgid.unicast.prefix));
1316				sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1317				goto Exit;
1318			}
1319		}
1320
1321		*pp_src_alias_guid = osm_get_alias_guid_by_guid(sa->p_subn,
1322								p_pr->sgid.unicast.interface_id);
1323		if (!*pp_src_alias_guid) {
1324			/*
1325			   This 'error' is the client's fault (bad gid) so
1326			   don't enter it as an error in our own log.
1327			   Return an error response to the client.
1328			 */
1329			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1330				"No source port with GUID 0x%016" PRIx64 "\n",
1331				cl_ntoh64(p_pr->sgid.unicast.interface_id));
1332			sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1333			goto Exit;
1334		}
1335		if (pp_sgid)
1336			*pp_sgid = &p_pr->sgid;
1337	}
1338
1339	if (comp_mask & IB_PR_COMPMASK_SLID) {
1340		*pp_src_port = osm_get_port_by_lid(sa->p_subn, p_pr->slid);
1341		if (!*pp_src_port) {
1342			/*
1343			   This 'error' is the client's fault (bad lid) so
1344			   don't enter it as an error in our own log.
1345			   Return an error response to the client.
1346			 */
1347			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, "No source port "
1348				"with LID %u\n", cl_ntoh16(p_pr->slid));
1349			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
1350			goto Exit;
1351		}
1352	}
1353
1354	*pp_dest_alias_guid = NULL;
1355	*pp_dest_port = NULL;
1356	if (comp_mask & IB_PR_COMPMASK_DGID) {
1357		if (!ib_gid_is_link_local(&p_pr->dgid) &&
1358		    !ib_gid_is_multicast(&p_pr->dgid) &&
1359		    ib_gid_get_subnet_prefix(&p_pr->dgid) !=
1360		    sa->p_subn->opt.subnet_prefix) {
1361			dest_guid = find_router(sa, p_pr->dgid.unicast.prefix);
1362			if (!dest_guid) {
1363				char gid_str[INET6_ADDRSTRLEN];
1364				OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1365					"Off subnet DGID %s, but router not "
1366					"found\n",
1367					inet_ntop(AF_INET6, p_pr->dgid.raw,
1368						  gid_str, sizeof(gid_str)));
1369				sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1370				goto Exit;
1371			}
1372			if (pp_dgid)
1373				*pp_dgid = &p_pr->dgid;
1374		} else
1375			dest_guid = p_pr->dgid.unicast.interface_id;
1376
1377		*pp_dest_alias_guid = osm_get_alias_guid_by_guid(sa->p_subn,
1378								 dest_guid);
1379		if (!*pp_dest_alias_guid) {
1380			/*
1381			   This 'error' is the client's fault (bad gid) so
1382			   don't enter it as an error in our own log.
1383			   Return an error response to the client.
1384			 */
1385			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1386				"No dest port with GUID 0x%016" PRIx64 "\n",
1387				cl_ntoh64(dest_guid));
1388			sa_status = IB_SA_MAD_STATUS_INVALID_GID;
1389			goto Exit;
1390		}
1391	}
1392
1393	if (comp_mask & IB_PR_COMPMASK_DLID) {
1394		*pp_dest_port = osm_get_port_by_lid(sa->p_subn, p_pr->dlid);
1395		if (!*pp_dest_port) {
1396			/*
1397			   This 'error' is the client's fault (bad lid)
1398			   so don't enter it as an error in our own log.
1399			   Return an error response to the client.
1400			 */
1401			OSM_LOG(sa->p_log, OSM_LOG_VERBOSE, "No dest port "
1402				"with LID %u\n", cl_ntoh16(p_pr->dlid));
1403			sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
1404			goto Exit;
1405		}
1406	}
1407
1408Exit:
1409	OSM_LOG_EXIT(sa->p_log);
1410	return sa_status;
1411}
1412
1413static void pr_rcv_process_world(IN osm_sa_t * sa, IN const ib_sa_mad_t * sa_mad,
1414				 IN const osm_port_t * requester_port,
1415				 IN const ib_gid_t * p_sgid,
1416				 IN const ib_gid_t * p_dgid,
1417				 IN cl_qlist_t * p_list)
1418{
1419	const cl_qmap_t *p_tbl;
1420	const osm_alias_guid_t *p_dest_alias_guid, *p_src_alias_guid;
1421
1422	OSM_LOG_ENTER(sa->p_log);
1423
1424	/*
1425	   Iterate the entire port space over itself.
1426	   A path record from a port to itself is legit, so no
1427	   need for a special case there.
1428
1429	   We compute both A -> B and B -> A, since we don't have
1430	   any check to determine the reversability of the paths.
1431	 */
1432	p_tbl = &sa->p_subn->alias_port_guid_tbl;
1433
1434	p_dest_alias_guid = (osm_alias_guid_t *) cl_qmap_head(p_tbl);
1435	while (p_dest_alias_guid != (osm_alias_guid_t *) cl_qmap_end(p_tbl)) {
1436		p_src_alias_guid = (osm_alias_guid_t *) cl_qmap_head(p_tbl);
1437		while (p_src_alias_guid != (osm_alias_guid_t *) cl_qmap_end(p_tbl)) {
1438			pr_rcv_get_port_pair_paths(sa, sa_mad, requester_port,
1439						   p_src_alias_guid,
1440						   p_dest_alias_guid,
1441						   p_sgid, p_dgid, p_list);
1442			if (sa_mad->method == IB_MAD_METHOD_GET &&
1443			    cl_qlist_count(p_list) > 0)
1444				goto Exit;
1445
1446			p_src_alias_guid =
1447			    (osm_alias_guid_t *) cl_qmap_next(&p_src_alias_guid->map_item);
1448		}
1449
1450		p_dest_alias_guid =
1451		    (osm_alias_guid_t *) cl_qmap_next(&p_dest_alias_guid->map_item);
1452	}
1453
1454Exit:
1455	OSM_LOG_EXIT(sa->p_log);
1456}
1457
1458void osm_pr_process_half(IN osm_sa_t * sa, IN const ib_sa_mad_t * sa_mad,
1459				IN const osm_port_t * requester_port,
1460				IN const osm_alias_guid_t * p_src_alias_guid,
1461				IN const osm_alias_guid_t * p_dest_alias_guid,
1462				IN const ib_gid_t * p_sgid,
1463				IN const ib_gid_t * p_dgid,
1464				IN cl_qlist_t * p_list)
1465{
1466	const cl_qmap_t *p_tbl;
1467	const osm_alias_guid_t *p_alias_guid;
1468
1469	OSM_LOG_ENTER(sa->p_log);
1470
1471	/*
1472	   Iterate over every port, looking for matches...
1473	   A path record from a port to itself is legit, so no
1474	   need to special case that one.
1475	 */
1476	p_tbl = &sa->p_subn->alias_port_guid_tbl;
1477
1478	if (p_src_alias_guid) {
1479		/*
1480		   The src port if fixed, so iterate over destination ports.
1481		 */
1482		p_alias_guid = (osm_alias_guid_t *) cl_qmap_head(p_tbl);
1483		while (p_alias_guid != (osm_alias_guid_t *) cl_qmap_end(p_tbl)) {
1484			pr_rcv_get_port_pair_paths(sa, sa_mad, requester_port,
1485						   p_src_alias_guid,
1486						   p_alias_guid,
1487						   p_sgid, p_dgid, p_list);
1488			if (sa_mad->method == IB_MAD_METHOD_GET &&
1489			    cl_qlist_count(p_list) > 0)
1490				break;
1491			p_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_alias_guid->map_item);
1492		}
1493	} else {
1494		/*
1495		   The dest port if fixed, so iterate over source ports.
1496		 */
1497		p_alias_guid = (osm_alias_guid_t *) cl_qmap_head(p_tbl);
1498		while (p_alias_guid != (osm_alias_guid_t *) cl_qmap_end(p_tbl)) {
1499			pr_rcv_get_port_pair_paths(sa, sa_mad, requester_port,
1500						   p_alias_guid,
1501						   p_dest_alias_guid, p_sgid,
1502						   p_dgid, p_list);
1503			if (sa_mad->method == IB_MAD_METHOD_GET &&
1504			    cl_qlist_count(p_list) > 0)
1505				break;
1506			p_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_alias_guid->map_item);
1507		}
1508	}
1509
1510	OSM_LOG_EXIT(sa->p_log);
1511}
1512
1513void osm_pr_process_pair(IN osm_sa_t * sa, IN const ib_sa_mad_t * sa_mad,
1514				IN const osm_port_t * requester_port,
1515				IN const osm_alias_guid_t * p_src_alias_guid,
1516				IN const osm_alias_guid_t * p_dest_alias_guid,
1517				IN const ib_gid_t * p_sgid,
1518				IN const ib_gid_t * p_dgid,
1519				IN cl_qlist_t * p_list)
1520{
1521	OSM_LOG_ENTER(sa->p_log);
1522
1523	pr_rcv_get_port_pair_paths(sa, sa_mad, requester_port, p_src_alias_guid,
1524				   p_dest_alias_guid, p_sgid, p_dgid, p_list);
1525
1526	OSM_LOG_EXIT(sa->p_log);
1527}
1528
1529static ib_api_status_t pr_match_mgrp_attributes(IN osm_sa_t * sa,
1530						IN const ib_sa_mad_t * sa_mad,
1531						IN const osm_mgrp_t * p_mgrp)
1532{
1533	const ib_path_rec_t *p_pr = ib_sa_mad_get_payload_ptr(sa_mad);
1534	ib_net64_t comp_mask = sa_mad->comp_mask;
1535	const osm_port_t *port;
1536	ib_api_status_t status = IB_ERROR;
1537	uint32_t flow_label;
1538	uint8_t sl, hop_limit;
1539
1540	OSM_LOG_ENTER(sa->p_log);
1541
1542	/* check that MLID of the MC group matches the PathRecord DLID */
1543	if ((comp_mask & IB_PR_COMPMASK_DLID) && p_mgrp->mlid != p_pr->dlid) {
1544		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1545			"DLID 0x%x is not MLID 0x%x for MC group\n",
1546			 cl_ntoh16(p_pr->dlid), cl_ntoh16(p_mgrp->mlid));
1547		goto Exit;
1548	}
1549
1550	/* If SGID and/or SLID specified, should validate as member of MC group */
1551	if (comp_mask & IB_PR_COMPMASK_SGID) {
1552		if (!osm_mgrp_get_mcm_alias_guid(p_mgrp,
1553						 p_pr->sgid.unicast.interface_id)) {
1554			char gid_str[INET6_ADDRSTRLEN];
1555			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1556				"SGID %s is not a member of MC group\n",
1557				inet_ntop(AF_INET6, p_pr->sgid.raw,
1558					  gid_str, sizeof gid_str));
1559			goto Exit;
1560		}
1561	}
1562
1563	if (comp_mask & IB_PR_COMPMASK_SLID) {
1564		port = osm_get_port_by_lid(sa->p_subn, p_pr->slid);
1565		if (!port || !osm_mgrp_get_mcm_port(p_mgrp, port->guid)) {
1566			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1567				"Either no port with SLID %u found or "
1568				"SLID not a member of MC group\n",
1569				cl_ntoh16(p_pr->slid));
1570			goto Exit;
1571		}
1572	}
1573
1574	/* Also, MTU, rate, packet lifetime, and raw traffic requested are not currently checked */
1575	if ((comp_mask & IB_PR_COMPMASK_PKEY) &&
1576	    p_pr->pkey != p_mgrp->mcmember_rec.pkey) {
1577		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1578			"Pkey 0x%x doesn't match MC group Pkey 0x%x\n",
1579			cl_ntoh16(p_pr->pkey),
1580			cl_ntoh16(p_mgrp->mcmember_rec.pkey));
1581		goto Exit;
1582	}
1583
1584	ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop,
1585				  &sl, &flow_label, &hop_limit);
1586
1587	if ((comp_mask & IB_PR_COMPMASK_SL) && ib_path_rec_sl(p_pr) != sl) {
1588		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1589			"SL %d doesn't match MC group SL %d\n",
1590			ib_path_rec_sl(p_pr), sl);
1591		goto Exit;
1592	}
1593
1594	/* If SubnAdmGet, assume NumbPaths of 1 (1.2 erratum) */
1595	if ((comp_mask & IB_PR_COMPMASK_NUMBPATH) &&
1596	    sa_mad->method != IB_MAD_METHOD_GET &&
1597	    ib_path_rec_num_path(p_pr) == 0) {
1598		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1599			"Number of paths requested is 0\n");
1600		goto Exit;
1601	}
1602
1603	if ((comp_mask & IB_PR_COMPMASK_FLOWLABEL) &&
1604	    ib_path_rec_flow_lbl(p_pr) != flow_label) {
1605		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1606			"Flow label 0x%x doesn't match MC group "
1607			" flow label 0x%x\n",
1608			ib_path_rec_flow_lbl(p_pr), flow_label);
1609		goto Exit;
1610	}
1611
1612	if ((comp_mask & IB_PR_COMPMASK_HOPLIMIT) &&
1613	    ib_path_rec_hop_limit(p_pr) != hop_limit) {
1614		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1615			"Hop limit %u doesn't match MC group hop limit %u\n",
1616			ib_path_rec_hop_limit(p_pr), hop_limit);
1617		goto Exit;
1618	}
1619
1620
1621	if ((comp_mask & IB_PR_COMPMASK_TCLASS) &&
1622	    p_pr->tclass != p_mgrp->mcmember_rec.tclass) {
1623		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1624			"TClass 0x%02x doesn't match MC group TClass 0x%02x\n",
1625			p_pr->tclass, p_mgrp->mcmember_rec.tclass);
1626		goto Exit;
1627	}
1628
1629	status = IB_SUCCESS;
1630
1631Exit:
1632	OSM_LOG_EXIT(sa->p_log);
1633	return status;
1634}
1635
1636static void pr_process_multicast(osm_sa_t * sa, const ib_sa_mad_t *sa_mad,
1637				 cl_qlist_t *list)
1638{
1639	ib_path_rec_t *pr = ib_sa_mad_get_payload_ptr(sa_mad);
1640	osm_mgrp_t *mgrp;
1641	ib_api_status_t status;
1642	osm_sa_item_t *pr_item;
1643	uint32_t flow_label;
1644	uint8_t sl, hop_limit;
1645
1646	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Multicast destination requested\n");
1647
1648	mgrp = osm_get_mgrp_by_mgid(sa->p_subn, &pr->dgid);
1649	if (!mgrp) {
1650		char gid_str[INET6_ADDRSTRLEN];
1651		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F09: "
1652			"No MC group found for PathRecord destination GID %s\n",
1653			inet_ntop(AF_INET6, pr->dgid.raw, gid_str,
1654				  sizeof gid_str));
1655		return;
1656	}
1657
1658	/* Make sure the rest of the PathRecord matches the MC group attributes */
1659	status = pr_match_mgrp_attributes(sa, sa_mad, mgrp);
1660	if (status != IB_SUCCESS) {
1661		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F19: "
1662			"MC group attributes don't match PathRecord request\n");
1663		return;
1664	}
1665
1666	pr_item = malloc(SA_PR_RESP_SIZE);
1667	if (pr_item == NULL) {
1668		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F18: "
1669			"Unable to allocate path record for MC group\n");
1670		return;
1671	}
1672	memset(pr_item, 0, sizeof(cl_list_item_t));
1673
1674	/* Copy PathRecord request into response */
1675	pr_item->resp.path_rec = *pr;
1676
1677	/* Now, use the MC info to cruft up the PathRecord response */
1678	pr_item->resp.path_rec.dgid = mgrp->mcmember_rec.mgid;
1679	pr_item->resp.path_rec.dlid = mgrp->mcmember_rec.mlid;
1680	pr_item->resp.path_rec.tclass = mgrp->mcmember_rec.tclass;
1681	pr_item->resp.path_rec.num_path = 1;
1682	pr_item->resp.path_rec.pkey = mgrp->mcmember_rec.pkey;
1683
1684	/* MTU, rate, and packet lifetime should be exactly */
1685	pr_item->resp.path_rec.mtu = (IB_PATH_SELECTOR_EXACTLY << 6) | mgrp->mcmember_rec.mtu;
1686	pr_item->resp.path_rec.rate = (IB_PATH_SELECTOR_EXACTLY << 6) | mgrp->mcmember_rec.rate;
1687	pr_item->resp.path_rec.pkt_life = (IB_PATH_SELECTOR_EXACTLY << 6) | mgrp->mcmember_rec.pkt_life;
1688
1689	/* SL, Hop Limit, and Flow Label */
1690	ib_member_get_sl_flow_hop(mgrp->mcmember_rec.sl_flow_hop,
1691				  &sl, &flow_label, &hop_limit);
1692	ib_path_rec_set_sl(&pr_item->resp.path_rec, sl);
1693	ib_path_rec_set_qos_class(&pr_item->resp.path_rec, 0);
1694
1695	/* HopLimit is not yet set in non link local MC groups */
1696	/* If it were, this would not be needed */
1697	if (ib_mgid_get_scope(&mgrp->mcmember_rec.mgid) !=
1698	    IB_MC_SCOPE_LINK_LOCAL)
1699		hop_limit = IB_HOPLIMIT_MAX;
1700
1701	pr_item->resp.path_rec.hop_flow_raw =
1702	    cl_hton32(hop_limit) | (flow_label << 8);
1703
1704	cl_qlist_insert_tail(list, &pr_item->list_item);
1705}
1706
1707void osm_pr_rcv_process(IN void *context, IN void *data)
1708{
1709	osm_sa_t *sa = context;
1710	osm_madw_t *p_madw = data;
1711	const ib_sa_mad_t *p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1712	ib_path_rec_t *p_pr = ib_sa_mad_get_payload_ptr(p_sa_mad);
1713	cl_qlist_t pr_list;
1714	const ib_gid_t *p_sgid = NULL, *p_dgid = NULL;
1715	const osm_alias_guid_t *p_src_alias_guid, *p_dest_alias_guid;
1716	const osm_port_t *p_src_port, *p_dest_port;
1717	osm_port_t *requester_port;
1718	uint8_t rate, mtu;
1719
1720	OSM_LOG_ENTER(sa->p_log);
1721
1722	CL_ASSERT(p_madw);
1723
1724	CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_PATH_RECORD);
1725
1726	/* we only support SubnAdmGet and SubnAdmGetTable methods */
1727	if (p_sa_mad->method != IB_MAD_METHOD_GET &&
1728	    p_sa_mad->method != IB_MAD_METHOD_GETTABLE) {
1729		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F17: "
1730			"Unsupported Method (%s) for PathRecord request\n",
1731			ib_get_sa_method_str(p_sa_mad->method));
1732		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
1733		goto Exit;
1734	}
1735
1736	/* Validate rate if supplied */
1737	if ((p_sa_mad->comp_mask & IB_PR_COMPMASK_RATESELEC) &&
1738	    (p_sa_mad->comp_mask & IB_PR_COMPMASK_RATE)) {
1739		rate = ib_path_rec_rate(p_pr);
1740		if (!ib_rate_is_valid(rate)) {
1741			osm_sa_send_error(sa, p_madw,
1742					  IB_SA_MAD_STATUS_REQ_INVALID);
1743			goto Exit;
1744		}
1745	}
1746	/* Validate MTU if supplied */
1747	if ((p_sa_mad->comp_mask & IB_PR_COMPMASK_MTUSELEC) &&
1748	    (p_sa_mad->comp_mask & IB_PR_COMPMASK_MTU)) {
1749		mtu = ib_path_rec_mtu(p_pr);
1750		if (!ib_mtu_is_valid(mtu)) {
1751			osm_sa_send_error(sa, p_madw,
1752					  IB_SA_MAD_STATUS_REQ_INVALID);
1753			goto Exit;
1754		}
1755	}
1756
1757	/* Make sure either none or both ServiceID parameters are supplied */
1758	if ((p_sa_mad->comp_mask & IB_PR_COMPMASK_SERVICEID) != 0 &&
1759	    (p_sa_mad->comp_mask & IB_PR_COMPMASK_SERVICEID) !=
1760	     IB_PR_COMPMASK_SERVICEID) {
1761		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_INSUF_COMPS);
1762		goto Exit;
1763	}
1764
1765	cl_qlist_init(&pr_list);
1766
1767	/*
1768	   Most SA functions (including this one) are read-only on the
1769	   subnet object, so we grab the lock non-exclusively.
1770	 */
1771	cl_plock_acquire(sa->p_lock);
1772
1773	/* update the requester physical port */
1774	requester_port = osm_get_port_by_mad_addr(sa->p_log, sa->p_subn,
1775						  osm_madw_get_mad_addr_ptr
1776						  (p_madw));
1777	if (requester_port == NULL) {
1778		cl_plock_release(sa->p_lock);
1779		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1F16: "
1780			"Cannot find requester physical port\n");
1781		goto Exit;
1782	}
1783
1784	if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
1785		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1786			"Requester port GUID 0x%" PRIx64 "\n",
1787			cl_ntoh64(osm_port_get_guid(requester_port)));
1788		osm_dump_path_record_v2(sa->p_log, p_pr, FILE_ID, OSM_LOG_DEBUG);
1789	}
1790
1791	/* Handle multicast destinations separately */
1792	if ((p_sa_mad->comp_mask & IB_PR_COMPMASK_DGID) &&
1793	    ib_gid_is_multicast(&p_pr->dgid)) {
1794		pr_process_multicast(sa, p_sa_mad, &pr_list);
1795		goto Unlock;
1796	}
1797
1798	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Unicast destination requested\n");
1799
1800	if (osm_pr_get_end_points(sa, p_sa_mad,
1801				  &p_src_alias_guid, &p_dest_alias_guid,
1802				  &p_src_port, &p_dest_port,
1803				  &p_sgid, &p_dgid) != IB_SA_MAD_STATUS_SUCCESS)
1804		goto Unlock;
1805
1806	if (p_src_alias_guid && p_src_port &&
1807	    p_src_alias_guid->p_base_port != p_src_port) {
1808		cl_plock_release(sa->p_lock);
1809		OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1810			"Requester port GUID 0x%" PRIx64 ": Port for SGUID "
1811			"0x%" PRIx64 " not same as port for SLID %u\n",
1812			cl_ntoh64(osm_port_get_guid(requester_port)),
1813			cl_ntoh64(p_pr->sgid.unicast.interface_id),
1814			cl_ntoh16(p_pr->slid));
1815		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1816		goto Exit;
1817	}
1818
1819	if (p_dest_alias_guid && p_dest_port &&
1820	    p_dest_alias_guid->p_base_port != p_dest_port) {
1821		cl_plock_release(sa->p_lock);
1822		OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1823			"Requester port GUID 0x%" PRIx64 ": Port for DGUID "
1824			"0x%" PRIx64 " not same as port for DLID %u\n",
1825			cl_ntoh64(osm_port_get_guid(requester_port)),
1826			cl_ntoh64(p_pr->dgid.unicast.interface_id),
1827			cl_ntoh16(p_pr->dlid));
1828		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1829		goto Exit;
1830	}
1831
1832	/*
1833	   What happens next depends on the type of endpoint information
1834	   that was specified....
1835	 */
1836	if (p_src_alias_guid) {
1837		if (p_dest_alias_guid)
1838			osm_pr_process_pair(sa, p_sa_mad, requester_port,
1839					    p_src_alias_guid, p_dest_alias_guid,
1840					    p_sgid, p_dgid, &pr_list);
1841		else if (!p_dest_port)
1842			osm_pr_process_half(sa, p_sa_mad, requester_port,
1843					    p_src_alias_guid, NULL, p_sgid,
1844					    p_dgid, &pr_list);
1845		else {
1846			/* Get all alias GUIDs for the dest port */
1847			p_dest_alias_guid = (osm_alias_guid_t *) cl_qmap_head(&sa->p_subn->alias_port_guid_tbl);
1848			while (p_dest_alias_guid !=
1849			       (osm_alias_guid_t *) cl_qmap_end(&sa->p_subn->alias_port_guid_tbl)) {
1850				if (osm_get_port_by_alias_guid(sa->p_subn, p_dest_alias_guid->alias_guid) ==
1851				    p_dest_port)
1852					osm_pr_process_pair(sa, p_sa_mad,
1853							    requester_port,
1854							    p_src_alias_guid,
1855							    p_dest_alias_guid,
1856							    p_sgid, p_dgid,
1857							    &pr_list);
1858				if (p_sa_mad->method == IB_MAD_METHOD_GET &&
1859				    cl_qlist_count(&pr_list) > 0)
1860					break;
1861
1862				p_dest_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_dest_alias_guid->map_item);
1863			}
1864		}
1865	} else {
1866		if (p_dest_alias_guid && !p_src_port)
1867			osm_pr_process_half(sa, p_sa_mad, requester_port,
1868					    NULL, p_dest_alias_guid, p_sgid,
1869					    p_dgid, &pr_list);
1870		else if (!p_src_port && !p_dest_port)
1871			/*
1872			   Katie, bar the door!
1873			 */
1874			pr_rcv_process_world(sa, p_sa_mad, requester_port,
1875					     p_sgid, p_dgid, &pr_list);
1876		else if (p_dest_alias_guid && p_src_port) {
1877			/* Get all alias GUIDs for the src port */
1878			p_src_alias_guid = (osm_alias_guid_t *) cl_qmap_head(&sa->p_subn->alias_port_guid_tbl);
1879			while (p_src_alias_guid !=
1880			       (osm_alias_guid_t *) cl_qmap_end(&sa->p_subn->alias_port_guid_tbl)) {
1881				if (osm_get_port_by_alias_guid(sa->p_subn,
1882							       p_src_alias_guid->alias_guid) ==
1883				    p_src_port)
1884					osm_pr_process_pair(sa, p_sa_mad,
1885							    requester_port,
1886							    p_src_alias_guid,
1887							    p_dest_alias_guid,
1888							    p_sgid, p_dgid,
1889							    &pr_list);
1890				if (p_sa_mad->method == IB_MAD_METHOD_GET &&
1891				    cl_qlist_count(&pr_list) > 0)
1892					break;
1893				p_src_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_src_alias_guid->map_item);
1894			}
1895		} else if (p_src_port && !p_dest_port) {
1896			/* Get all alias GUIDs for the src port */
1897			p_src_alias_guid = (osm_alias_guid_t *) cl_qmap_head(&sa->p_subn->alias_port_guid_tbl);
1898			while (p_src_alias_guid !=
1899			       (osm_alias_guid_t *) cl_qmap_end(&sa->p_subn->alias_port_guid_tbl)) {
1900				if (osm_get_port_by_alias_guid(sa->p_subn,
1901							       p_src_alias_guid->alias_guid) ==
1902				    p_src_port)
1903					osm_pr_process_half(sa, p_sa_mad,
1904							    requester_port,
1905							    p_src_alias_guid,
1906							    NULL, p_sgid,
1907							    p_dgid, &pr_list);
1908				p_src_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_src_alias_guid->map_item);
1909			}
1910		} else if (p_dest_port && !p_src_port) {
1911			/* Get all alias GUIDs for the dest port */
1912			p_dest_alias_guid = (osm_alias_guid_t *) cl_qmap_head(&sa->p_subn->alias_port_guid_tbl);
1913			while (p_dest_alias_guid !=
1914			       (osm_alias_guid_t *) cl_qmap_end(&sa->p_subn->alias_port_guid_tbl)) {
1915				if (osm_get_port_by_alias_guid(sa->p_subn,
1916							       p_dest_alias_guid->alias_guid) ==
1917				    p_dest_port)
1918					osm_pr_process_half(sa, p_sa_mad,
1919							    requester_port,
1920							    NULL,
1921							    p_dest_alias_guid,
1922							    p_sgid, p_dgid,
1923							    &pr_list);
1924				p_dest_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_dest_alias_guid->map_item);
1925			}
1926		} else {
1927			/* Get all alias GUIDs for the src port */
1928			p_src_alias_guid = (osm_alias_guid_t *) cl_qmap_head(&sa->p_subn->alias_port_guid_tbl);
1929			while (p_src_alias_guid !=
1930			       (osm_alias_guid_t *) cl_qmap_end(&sa->p_subn->alias_port_guid_tbl)) {
1931				if (osm_get_port_by_alias_guid(sa->p_subn,
1932							       p_src_alias_guid->alias_guid) ==
1933				    p_src_port) {
1934					/* Get all alias GUIDs for the dest port */
1935					p_dest_alias_guid = (osm_alias_guid_t *) cl_qmap_head(&sa->p_subn->alias_port_guid_tbl);
1936					while (p_dest_alias_guid !=
1937					       (osm_alias_guid_t *) cl_qmap_end(&sa->p_subn->alias_port_guid_tbl)) {
1938						if (osm_get_port_by_alias_guid(sa->p_subn,
1939									       p_dest_alias_guid->alias_guid) ==
1940						    p_dest_port)
1941						osm_pr_process_pair(sa,
1942								    p_sa_mad,
1943								    requester_port,
1944								    p_src_alias_guid,
1945								    p_dest_alias_guid,
1946								    p_sgid,
1947								    p_dgid,
1948								    &pr_list);
1949						if (p_sa_mad->method == IB_MAD_METHOD_GET &&
1950						    cl_qlist_count(&pr_list) > 0)
1951							break;
1952						p_dest_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_dest_alias_guid->map_item);
1953					}
1954				}
1955				if (p_sa_mad->method == IB_MAD_METHOD_GET &&
1956				    cl_qlist_count(&pr_list) > 0)
1957					break;
1958				p_src_alias_guid = (osm_alias_guid_t *) cl_qmap_next(&p_src_alias_guid->map_item);
1959			}
1960		}
1961	}
1962
1963Unlock:
1964	cl_plock_release(sa->p_lock);
1965
1966	/* Now, (finally) respond to the PathRecord request */
1967	osm_sa_respond(sa, p_madw, sizeof(ib_path_rec_t), &pr_list);
1968
1969Exit:
1970	OSM_LOG_EXIT(sa->p_log);
1971}
1972