[openib-general] [RFC] [PATCH] OpenSM: Add support for SA MultiPathRecord

Hal Rosenstock halr at voltaire.com
Fri May 5 10:52:15 PDT 2006


OpenSM: Add support for SA MultiPathRecord

Add the optional support for SA MultiPathRecord. This is an initial
implementation. Note that this capability is not enabled in the build
just yet.

Signed-off-by: Hal Rosenstock <halr at voltaire.com>

Index: osm/include/opensm/osm_sa_multipath_record.h
===================================================================
--- osm/include/opensm/osm_sa_multipath_record.h	(revision 0)
+++ osm/include/opensm/osm_sa_multipath_record.h	(revision 0)
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
+ * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * $Id$
+ */
+
+
+/*
+ * Abstract:
+ * 	Declaration of osm_mpr_rcv_t.
+ *	This object represents the MultiPathRecord Receiver object.
+ *	attribute from a node.
+ *	This object is part of the OpenSM family of objects.
+ *
+ * Environment:
+ * 	Linux User Mode
+ *
+ */
+
+#ifndef _OSM_MPR_RCV_H_
+#define _OSM_MPR_RCV_H_
+
+#include <complib/cl_passivelock.h>
+#include <complib/cl_qlist.h>
+#include <complib/cl_qlockpool.h>
+#include <opensm/osm_base.h>
+#include <opensm/osm_madw.h>
+#include <opensm/osm_sa_response.h>
+#include <opensm/osm_subnet.h>
+#include <opensm/osm_port.h>
+#include <opensm/osm_log.h>
+#include <opensm/osm_state_mgr.h>
+
+#ifdef __cplusplus
+#  define BEGIN_C_DECLS extern "C" {
+#  define END_C_DECLS   }
+#else /* !__cplusplus */
+#  define BEGIN_C_DECLS
+#  define END_C_DECLS
+#endif /* __cplusplus */
+
+BEGIN_C_DECLS
+
+/****h* OpenSM/MultiPath Record Receiver
+* NAME
+*	MultiPath Record Receiver
+*
+* DESCRIPTION
+*	The MultiPath Record Receiver object encapsulates the information
+*	needed to receive the PathRecord request from a node.
+*
+*	The MultiPath Record Receiver object is thread safe.
+*
+*	This object should be treated as opaque and should be
+*	manipulated only through the provided functions.
+*
+* AUTHOR
+*	Hal Rosenstock, Voltaire
+*
+*********/
+
+/****s* OpenSM: MultiPath Record Receiver/osm_mpr_rcv_t
+* NAME
+*	osm_mpr_rcv_t
+*
+* DESCRIPTION
+*	MultiPath Record Receiver structure.
+*
+*	This object should be treated as opaque and should
+*	be manipulated only through the provided functions.
+*
+* SYNOPSIS
+*/
+typedef struct _osm_mpr_rcv
+{
+	osm_subn_t	*p_subn;
+	osm_sa_resp_t	*p_resp;
+	osm_mad_pool_t	*p_mad_pool;
+	osm_log_t	*p_log;
+	cl_plock_t	*p_lock;
+	cl_qlock_pool_t	pr_pool;
+} osm_mpr_rcv_t;
+/*
+* FIELDS
+*	p_subn
+*		Pointer to the Subnet object for this subnet.
+*
+*	p_gen_req_ctrl
+*		Pointer to the generic request controller.
+*
+*	p_log
+*		Pointer to the log object.
+*
+*	p_lock
+*		Pointer to the serializing lock.
+*
+*	pr_pool
+*		Pool of multipath record objects used to generate query responses.
+*
+* SEE ALSO
+*	MultiPath Record Receiver object
+*********/
+
+/****f* OpenSM: MultiPath Record Receiver/osm_mpr_rcv_construct
+* NAME
+*	osm_mpr_rcv_construct
+*
+* DESCRIPTION
+*	This function constructs a MultiPath Record Receiver object.
+*
+* SYNOPSIS
+*/
+void
+osm_mpr_rcv_construct(
+	IN osm_mpr_rcv_t* const p_rcv );
+/*
+* PARAMETERS
+*	p_rcv
+*		[in] Pointer to a MultiPath Record Receiver object to construct.
+*
+* RETURN VALUE
+*	This function does not return a value.
+*
+* NOTES
+*	Allows calling osm_mpr_rcv_init, osm_mpr_rcv_destroy
+*
+*	Calling osm_mpr_rcv_construct is a prerequisite to calling any other
+*	method except osm_mpr_rcv_init.
+*
+* SEE ALSO
+*	MultiPath Record Receiver object, osm_mpr_rcv_init, osm_mpr_rcv_destroy
+*********/
+
+/****f* OpenSM: MultiPath Record Receiver/osm_mpr_rcv_destroy
+* NAME
+*	osm_mpr_rcv_destroy
+*
+* DESCRIPTION
+*	The osm_mpr_rcv_destroy function destroys the object, releasing
+*	all resources.
+*
+* SYNOPSIS
+*/
+void
+osm_mpr_rcv_destroy(
+	IN osm_mpr_rcv_t* const p_rcv );
+/*
+* PARAMETERS
+*	p_rcv
+*		[in] Pointer to the object to destroy.
+*
+* RETURN VALUE
+*	This function does not return a value.
+*
+* NOTES
+*	Performs any necessary cleanup of the specified
+*	MultiPath Record Receiver object.
+*	Further operations should not be attempted on the destroyed object.
+*	This function should only be called after a call to
+*	osm_mpr_rcv_construct or osm_mpr_rcv_init.
+*
+* SEE ALSO
+*	MultiPath Record Receiver object, osm_mpr_rcv_construct,
+*	osm_mpr_rcv_init
+*********/
+
+/****f* OpenSM: MultiPath Record Receiver/osm_mpr_rcv_init
+* NAME
+*	osm_mpr_rcv_init
+*
+* DESCRIPTION
+*	The osm_mpr_rcv_init function initializes a
+*	MultiPath Record Receiver object for use.
+*
+* SYNOPSIS
+*/
+ib_api_status_t
+osm_mpr_rcv_init(
+	IN osm_mpr_rcv_t* const p_rcv,
+	IN osm_sa_resp_t* const p_resp,
+	IN osm_mad_pool_t* const p_mad_pool,
+	IN osm_subn_t* const p_subn,
+	IN osm_log_t* const p_log,
+	IN cl_plock_t* const p_lock );
+/*
+* PARAMETERS
+*	p_rcv
+*		[in] Pointer to an osm_mpr_rcv_t object to initialize.
+*
+*	p_subn
+*		[in] Pointer to the Subnet object for this subnet.
+*
+*	p_log
+*		[in] Pointer to the log object.
+*
+*	p_lock
+*		[in] Pointer to the OpenSM serializing lock.
+*
+* RETURN VALUES
+*	IB_SUCCESS if the MultiPath Record Receiver object was initialized
+*	successfully.
+*
+* NOTES
+*	Allows calling other MultiPath Record Receiver methods.
+*
+* SEE ALSO
+*	MultiPath Record Receiver object, osm_mpr_rcv_construct,
+*	osm_mpr_rcv_destroy
+*********/
+
+/****f* OpenSM: MultiPath Record Receiver/osm_mpr_rcv_process
+* NAME
+*	osm_mpr_rcv_process
+*
+* DESCRIPTION
+*	Process the MultiPathRecord request.
+*
+* SYNOPSIS
+*/
+void
+osm_mpr_rcv_process(
+	IN osm_mpr_rcv_t*		const p_rcv,
+	IN osm_madw_t*		const p_madw );
+/*
+* PARAMETERS
+*	p_rcv
+*		[in] Pointer to an osm_mpr_rcv_t object.
+*
+*	p_madw
+*		[in] Pointer to the MAD Wrapper containing the MAD
+*		that contains the node's MultiPathRecord attribute.
+*
+* RETURN VALUES
+*	IB_SUCCESS if the MultiPathRecord processing was successful.
+*
+* NOTES
+*	This function processes a MultiPathRecord attribute.
+*
+* SEE ALSO
+*	MultiPath Record Receiver, Node Info Response Controller
+*********/
+
+END_C_DECLS
+
+#endif	/* _OSM_MPR_RCV_H_ */

Property changes on: osm/include/opensm/osm_sa_multipath_record.h
___________________________________________________________________
Name: svn:keywords
   + Id

Index: osm/include/opensm/osm_helper.h
===================================================================
--- osm/include/opensm/osm_helper.h	(revision 6920)
+++ osm/include/opensm/osm_helper.h	(working copy)
@@ -217,6 +217,12 @@ osm_dump_path_record(
 	IN const osm_log_level_t log_level );
 
 void
+osm_dump_multipath_record(
+	IN osm_log_t* const p_log,
+	IN const ib_multipath_rec_t* const p_mpr,
+	IN const osm_log_level_t log_level );
+
+void
 osm_dump_node_record(
 	IN osm_log_t* const p_log,
 	IN const ib_node_record_t* const p_nr,
Index: osm/include/opensm/osm_sa.h
===================================================================
--- osm/include/opensm/osm_sa.h	(revision 6920)
+++ osm/include/opensm/osm_sa.h	(working copy)
@@ -67,6 +67,7 @@
 #include <opensm/osm_sa_guidinfo_record_ctrl.h>
 #include <opensm/osm_sa_link_record_ctrl.h>
 #include <opensm/osm_sa_path_record_ctrl.h>
+#include <opensm/osm_sa_multipath_record_ctrl.h>
 #include <opensm/osm_sa_sminfo_record_ctrl.h>
 #include <opensm/osm_sa_mad_ctrl.h>
 #include <opensm/osm_sa_mcmember_record_ctrl.h>
@@ -167,7 +168,11 @@ typedef struct _osm_sa
 	osm_mcmr_rcv_ctrl_t			mcmr_rcv_ctlr;
 	osm_sr_rcv_t				sr_rcv;
 	osm_sr_rcv_ctrl_t			sr_rcv_ctrl;
-	
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+	osm_mpr_rcv_t				mpr_rcv;
+	osm_mpr_rcv_ctrl_t			mpr_rcv_ctrl;
+#endif
+
   /* InformInfo Receiver */
   osm_infr_rcv_t				infr_rcv;
   osm_infr_rcv_ctrl_t		infr_rcv_ctrl;
Index: osm/include/opensm/osm_msgdef.h
===================================================================
--- osm/include/opensm/osm_msgdef.h	(revision 6920)
+++ osm/include/opensm/osm_msgdef.h	(working copy)
@@ -192,6 +192,9 @@ enum
 	OSM_MSG_MAD_VL_ARB,
 	OSM_MSG_MAD_SLVL,
 	OSM_MSG_MAD_GUIDINFO_RECORD,
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+	OSM_MSG_MAD_MULTIPATH_RECORD,
+#endif
 	OSM_MSG_MAX
 };
 
Index: osm/include/opensm/osm_sa_multipath_record_ctrl.h
===================================================================
--- osm/include/opensm/osm_sa_multipath_record_ctrl.h	(revision 0)
+++ osm/include/opensm/osm_sa_multipath_record_ctrl.h	(revision 0)
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
+ * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * $Id$
+ */
+
+
+/*
+ * Abstract:
+ * 	Declaration of osm_mpr_rcv_ctrl_t.
+ *	This object represents a controller that receives the IBA
+ *	MultiPathRecord attribute from a node.
+ *	This object is part of the OpenSM family of objects.
+ *
+ * Environment:
+ * 	Linux User Mode
+ *
+ */
+
+
+#ifndef _OSM_MPRCTRL_H_
+#define _OSM_MPRCTRL_H_
+
+
+#include <complib/cl_dispatcher.h>
+#include <opensm/osm_base.h>
+#include <opensm/osm_madw.h>
+#include <opensm/osm_log.h>
+#include <opensm/osm_sa_multipath_record.h>
+
+#ifdef __cplusplus
+#  define BEGIN_C_DECLS extern "C" {
+#  define END_C_DECLS   }
+#else /* !__cplusplus */
+#  define BEGIN_C_DECLS
+#  define END_C_DECLS
+#endif /* __cplusplus */
+
+BEGIN_C_DECLS
+
+/****h* OpenSM/MultiPath Record Receive Controller
+* NAME
+*	MultiPath Record Receive Controller
+*
+* DESCRIPTION
+*	The MultiPath Record Receive Controller object encapsulates
+*	the information	needed to receive the MultiPathRecord attribute from a node.
+*
+*	The MultiPath record Receive Controller object is thread safe.
+*
+*	This object should be treated as opaque and should be
+*	manipulated only through the provided functions.
+*
+* AUTHOR
+*	Hal Rosenstock, Voltaire 
+*
+*********/
+/****s* OpenSM: MultiPath Record Receive Controller/osm_mpr_rcv_ctrl_t
+* NAME
+*	osm_mpr_rcv_ctrl_t
+*
+* DESCRIPTION
+*	MultiPath Record Receive Controller structure.
+*
+*	This object should be treated as opaque and should
+*	be manipulated only through the provided functions.
+*
+* SYNOPSIS
+*/
+typedef struct _osm_mpr_rcv_ctrl
+{
+	osm_mpr_rcv_t				*p_rcv;
+	osm_log_t					*p_log;
+	cl_dispatcher_t				*p_disp;
+	cl_disp_reg_handle_t		h_disp;
+
+} osm_mpr_rcv_ctrl_t;
+/*
+* FIELDS
+*	p_rcv
+*		Pointer to the MultiPath Record Receiver object.
+*
+*	p_log
+*		Pointer to the log object.
+*
+*	p_disp
+*		Pointer to the Dispatcher.
+*
+*	h_disp
+*		Handle returned from dispatcher registration.
+*
+* SEE ALSO
+*	MultiPath Record Receive Controller object
+*	MultiPath Record Receiver object
+*********/
+
+/****f* OpenSM: MultiPath Record Receive Controller/osm_pr_rcv_ctrl_construct
+* NAME
+*	osm_mpr_rcv_ctrl_construct
+*
+* DESCRIPTION
+*	This function constructs a MultiPath Record Receive Controller object.
+*
+* SYNOPSIS
+*/
+void osm_mpr_rcv_ctrl_construct(
+	IN osm_mpr_rcv_ctrl_t* const p_ctrl );
+/*
+* PARAMETERS
+*	p_ctrl
+*		[in] Pointer to a MultiPath Record Receive Controller
+*		object to construct.
+*
+* RETURN VALUE
+*	This function does not return a value.
+*
+* NOTES
+*	Allows calling osm_mpr_rcv_ctrl_init, osm_mpr_rcv_ctrl_destroy,
+*	and osm_mpr_rcv_ctrl_is_inited.
+*
+*	Calling osm_mpr_rcv_ctrl_construct is a prerequisite to calling any
+*	other method except osm_mpr_rcv_ctrl_init.
+*
+* SEE ALSO
+*	MultiPath Record Receive Controller object, osm_mpr_rcv_ctrl_init,
+*	osm_mpr_rcv_ctrl_destroy, osm_mpr_rcv_ctrl_is_inited
+*********/
+
+/****f* OpenSM: MultiPath Record Receive Controller/osm_mpr_rcv_ctrl_destroy
+* NAME
+*	osm_mpr_rcv_ctrl_destroy
+*
+* DESCRIPTION
+*	The osm_mpr_rcv_ctrl_destroy function destroys the object, releasing
+*	all resources.
+*
+* SYNOPSIS
+*/
+void osm_mpr_rcv_ctrl_destroy(
+	IN osm_mpr_rcv_ctrl_t* const p_ctrl );
+/*
+* PARAMETERS
+*	p_ctrl
+*		[in] Pointer to the object to destroy.
+*
+* RETURN VALUE
+*	This function does not return a value.
+*
+* NOTES
+*	Performs any necessary cleanup of the specified
+*	MultiPath Record Receive Controller object.
+*	Further operations should not be attempted on the destroyed object.
+*	This function should only be called after a call to
+*	osm_mpr_rcv_ctrl_construct or osm_mpr_rcv_ctrl_init.
+*
+* SEE ALSO
+*	MultiPath Record Receive Controller object, osm_mpr_rcv_ctrl_construct,
+*	osm_mpr_rcv_ctrl_init
+*********/
+
+/****f* OpenSM: MultiPath Record Receive Controller/osm_mpr_rcv_ctrl_init
+* NAME
+*	osm_mpr_rcv_ctrl_init
+*
+* DESCRIPTION
+*	The osm_mpr_rcv_ctrl_init function initializes a
+*	MultiPath Record Receive Controller object for use.
+*
+* SYNOPSIS
+*/
+ib_api_status_t osm_mpr_rcv_ctrl_init(
+	IN osm_mpr_rcv_ctrl_t* const p_ctrl,
+	IN osm_mpr_rcv_t* const p_rcv,
+	IN osm_log_t* const p_log,
+	IN cl_dispatcher_t* const p_disp );
+/*
+* PARAMETERS
+*	p_ctrl
+*		[in] Pointer to an osm_mpr_rcv_ctrl_t object to initialize.
+*
+*	p_rcv
+*		[in] Pointer to an osm_mpr_t object.
+*
+*	p_log
+*		[in] Pointer to the log object.
+*
+*	p_disp
+*		[in] Pointer to the OpenSM central Dispatcher.
+*
+* RETURN VALUES
+*	CL_SUCCESS if the MultiPath Record Receive Controller object was
+*	initialized successfully.
+*
+* NOTES
+*	Allows calling other MultiPath Record Receive Controller methods.
+*
+* SEE ALSO
+*	MultiPath Record Receive Controller object, osm_pr_rcv_ctrl_construct,
+*	osm_mpr_rcv_ctrl_destroy, osm_mpr_rcv_ctrl_is_inited
+*********/
+
+/****f* OpenSM: MultiPath Record Receive Controller/osm_mpr_rcv_ctrl_is_inited
+* NAME
+*	osm_mpr_rcv_ctrl_is_inited
+*
+* DESCRIPTION
+*	Indicates if the object has been initialized with osm_mpr_rcv_ctrl_init.
+*
+* SYNOPSIS
+*/
+boolean_t osm_mpr_rcv_ctrl_is_inited(
+	IN const osm_mpr_rcv_ctrl_t* const p_ctrl );
+/*
+* PARAMETERS
+*	p_ctrl
+*		[in] Pointer to an osm_mpr_rcv_ctrl_t object.
+*
+* RETURN VALUES
+*	TRUE if the object was initialized successfully,
+*	FALSE otherwise.
+*
+* NOTES
+*	The osm_mpr_rcv_ctrl_construct or osm_mpr_rcv_ctrl_init must be
+*	called before using this function.
+*
+* SEE ALSO
+*	MultiPath Record Receive Controller object, osm_mpr_rcv_ctrl_construct,
+*	osm_mpr_rcv_ctrl_init
+*********/
+
+END_C_DECLS
+
+#endif	/* _OSM_MPRCTRL_H_ */

Property changes on: osm/include/opensm/osm_sa_multipath_record_ctrl.h
___________________________________________________________________
Name: svn:keywords
   + Id

Index: osm/include/Makefile.am
===================================================================
--- osm/include/Makefile.am	(revision 6920)
+++ osm/include/Makefile.am	(working copy)
@@ -8,6 +8,7 @@ EXTRA_DIST = \
 	$(srcdir)/opensm/osm_version.h \
 	$(srcdir)/opensm/osm_sa_portinfo_record_ctrl.h \
 	$(srcdir)/opensm/osm_sa_guidinfo_record_ctrl.h \
+	$(srcdir)/opensm/osm_sa_multipath_record_ctrl.h \
 	$(srcdir)/opensm/osm_sa_path_record.h \
 	$(srcdir)/opensm/osm_lid_mgr.h \
 	$(srcdir)/opensm/osm_vl_arb_rcv.h \
@@ -37,6 +38,7 @@ EXTRA_DIST = \
 	$(srcdir)/opensm/osm_helper.h \
 	$(srcdir)/opensm/osm_sa_portinfo_record.h \
 	$(srcdir)/opensm/osm_sa_guidinfo_record.h \
+	$(srcdir)/opensm/osm_sa_multipath_record.h \
 	$(srcdir)/opensm/osm_sa_service_record.h \
 	$(srcdir)/opensm/osm_sa_response.h \
 	$(srcdir)/opensm/osm_node.h \
Index: osm/include/iba/ib_types.h
===================================================================
--- osm/include/iba/ib_types.h	(revision 6920)
+++ osm/include/iba/ib_types.h	(working copy)
@@ -1615,6 +1615,17 @@ ib_class_is_rmpp(
 * SOURCE
 */
 #define IB_PATH_REC_SELECTOR_MASK			0xC0
+/****d* IBA Base: Constants/IB_MULTIPATH_REC_SELECTOR_MASK
+* NAME
+*       IB_MULTIPATH_REC_SELECTOR_MASK
+*
+* DESCRIPTION
+*       Mask for the selector field for multipath record MTU, rate,
+*       and packet lifetime.
+*
+* SOURCE
+*/
+#define IB_MULTIPATH_REC_SELECTOR_MASK                       0xC0
 /**********/
 /****d* IBA Base: Constants/IB_PATH_REC_BASE_MASK
 * NAME
@@ -1628,6 +1639,18 @@ ib_class_is_rmpp(
 */
 #define IB_PATH_REC_BASE_MASK				0x3F
 /**********/
+/****d* IBA Base: Constants/IB_MULTIPATH_REC_BASE_MASK
+* NAME
+*       IB_MULTIPATH_REC_BASE_MASK
+*
+* DESCRIPTION
+*       Mask for the base value field for multipath record MTU, rate,
+*       and packet lifetime.
+*
+* SOURCE
+*/
+#define IB_MULTIPATH_REC_BASE_MASK                      0x3F
+/**********/
 
 /****h* IBA Base/Type Definitions
 * NAME
@@ -2401,6 +2424,72 @@ typedef struct _ib_path_rec
 #define IB_GIR_COMPMASK_GID6		(CL_HTON64(((uint64_t)1)<<10))
 #define IB_GIR_COMPMASK_GID7		(CL_HTON64(((uint64_t)1)<<11))
 
+/* MultiPath Record Component Masks */
+#define IB_MPR_COMPMASK_RAWTRAFFIC	(CL_HTON64(((uint64_t)1)<<0))
+#define IB_MPR_COMPMASK_RESV0		(CL_HTON64(((uint64_t)1)<<1))
+#define IB_MPR_COMPMASK_FLOWLABEL	(CL_HTON64(((uint64_t)1)<<2))
+#define IB_MPR_COMPMASK_HOPLIMIT	(CL_HTON64(((uint64_t)1)<<3))
+#define IB_MPR_COMPMASK_TCLASS		(CL_HTON64(((uint64_t)1)<<4))
+#define IB_MPR_COMPMASK_REVERSIBLE	(CL_HTON64(((uint64_t)1)<<5))
+#define IB_MPR_COMPMASK_NUMBPATH	(CL_HTON64(((uint64_t)1)<<6))
+#define IB_MPR_COMPMASK_PKEY		(CL_HTON64(((uint64_t)1)<<7))
+#define IB_MPR_COMPMASK_RESV1		(CL_HTON64(((uint64_t)1)<<8))
+#define IB_MPR_COMPMASK_SL		(CL_HTON64(((uint64_t)1)<<9))
+#define IB_MPR_COMPMASK_MTUSELEC	(CL_HTON64(((uint64_t)1)<<10))
+#define IB_MPR_COMPMASK_MTU		(CL_HTON64(((uint64_t)1)<<11))
+#define IB_MPR_COMPMASK_RATESELEC	(CL_HTON64(((uint64_t)1)<<12))
+#define IB_MPR_COMPMASK_RATE		(CL_HTON64(((uint64_t)1)<<13))
+#define IB_MPR_COMPMASK_PKTLIFETIMESELEC (CL_HTON64(((uint64_t)1)<<14))
+#define IB_MPR_COMPMASK_PKTLIFETIME	(CL_HTON64(((uint64_t)1)<<15))
+#define IB_MPR_COMPMASK_RESV2		(CL_HTON64(((uint64_t)1)<<16))
+#define IB_MPR_COMPMASK_INDEPSELEC	(CL_HTON64(((uint64_t)1)<<17))
+#define IB_MPR_COMPMASK_RESV3		(CL_HTON64(((uint64_t)1)<<18))
+#define IB_MPR_COMPMASK_SGIDCOUNT	(CL_HTON64(((uint64_t)1)<<19))
+#define IB_MPR_COMPMASK_DGIDCOUNT	(CL_HTON64(((uint64_t)1)<<20))
+#define IB_MPR_COMPMASK_RESV4		(CL_HTON64(((uint64_t)1)<<21))
+#define IB_MPR_COMPMASK_SDGID1		(CL_HTON64(((uint64_t)1)<<22))
+#define IB_MPR_COMPMASK_SDGID2		(CL_HTON64(((uint64_t)1)<<23))
+#define IB_MPR_COMPMASK_SDGID3		(CL_HTON64(((uint64_t)1)<<24))
+#define IB_MPR_COMPMASK_SDGID4		(CL_HTON64(((uint64_t)1)<<25))
+#define IB_MPR_COMPMASK_SDGID5		(CL_HTON64(((uint64_t)1)<<26))
+#define IB_MPR_COMPMASK_SDGID6		(CL_HTON64(((uint64_t)1)<<27))
+#define IB_MPR_COMPMASK_SDGID7		(CL_HTON64(((uint64_t)1)<<28))
+#define IB_MPR_COMPMASK_SDGID8		(CL_HTON64(((uint64_t)1)<<29))
+#define IB_MPR_COMPMASK_SDGID9		(CL_HTON64(((uint64_t)1)<<30))
+#define IB_MPR_COMPMASK_SDGID10		(CL_HTON64(((uint64_t)1)<<31))
+#define IB_MPR_COMPMASK_SDGID11		(CL_HTON64(((uint64_t)1)<<32))
+#define IB_MPR_COMPMASK_SDGID12		(CL_HTON64(((uint64_t)1)<<33))
+#define IB_MPR_COMPMASK_SDGID13		(CL_HTON64(((uint64_t)1)<<34))
+#define IB_MPR_COMPMASK_SDGID14		(CL_HTON64(((uint64_t)1)<<35))
+#define IB_MPR_COMPMASK_SDGID15		(CL_HTON64(((uint64_t)1)<<36))
+#define IB_MPR_COMPMASK_SDGID16		(CL_HTON64(((uint64_t)1)<<37))
+#define IB_MPR_COMPMASK_SDGID17		(CL_HTON64(((uint64_t)1)<<38))
+#define IB_MPR_COMPMASK_SDGID18		(CL_HTON64(((uint64_t)1)<<39))
+#define IB_MPR_COMPMASK_SDGID19		(CL_HTON64(((uint64_t)1)<<40))
+#define IB_MPR_COMPMASK_SDGID20		(CL_HTON64(((uint64_t)1)<<41))
+#define IB_MPR_COMPMASK_SDGID21		(CL_HTON64(((uint64_t)1)<<42))
+#define IB_MPR_COMPMASK_SDGID22		(CL_HTON64(((uint64_t)1)<<43))
+#define IB_MPR_COMPMASK_SDGID23		(CL_HTON64(((uint64_t)1)<<44))
+#define IB_MPR_COMPMASK_SDGID24		(CL_HTON64(((uint64_t)1)<<45))
+#define IB_MPR_COMPMASK_SDGID25		(CL_HTON64(((uint64_t)1)<<46))
+#define IB_MPR_COMPMASK_SDGID26		(CL_HTON64(((uint64_t)1)<<47))
+#define IB_MPR_COMPMASK_SDGID27		(CL_HTON64(((uint64_t)1)<<48))
+#define IB_MPR_COMPMASK_SDGID28		(CL_HTON64(((uint64_t)1)<<49))
+#define IB_MPR_COMPMASK_SDGID29		(CL_HTON64(((uint64_t)1)<<50))
+#define IB_MPR_COMPMASK_SDGID30		(CL_HTON64(((uint64_t)1)<<51))
+#define IB_MPR_COMPMASK_SDGID31		(CL_HTON64(((uint64_t)1)<<52))
+#define IB_MPR_COMPMASK_SDGID32		(CL_HTON64(((uint64_t)1)<<53))
+#define IB_MPR_COMPMASK_SDGID33		(CL_HTON64(((uint64_t)1)<<54))
+#define IB_MPR_COMPMASK_SDGID34		(CL_HTON64(((uint64_t)1)<<55))
+#define IB_MPR_COMPMASK_SDGID35		(CL_HTON64(((uint64_t)1)<<56))
+#define IB_MPR_COMPMASK_SDGID36		(CL_HTON64(((uint64_t)1)<<57))
+#define IB_MPR_COMPMASK_SDGID37		(CL_HTON64(((uint64_t)1)<<58))
+#define IB_MPR_COMPMASK_SDGID38		(CL_HTON64(((uint64_t)1)<<59))
+#define IB_MPR_COMPMASK_SDGID39		(CL_HTON64(((uint64_t)1)<<60))
+#define IB_MPR_COMPMASK_SDGID40		(CL_HTON64(((uint64_t)1)<<61))
+#define IB_MPR_COMPMASK_SDGID41		(CL_HTON64(((uint64_t)1)<<62))
+#define IB_MPR_COMPMASK_SDGID42		(CL_HTON64(((uint64_t)1)<<63))
+
 /****f* IBA Base: Types/ib_path_rec_init_local
 * NAME
 *	ib_path_rec_init_local
@@ -5508,6 +5597,316 @@ typedef struct _ib_guidinfo_record
 }	PACK_SUFFIX ib_guidinfo_record_t;
 #include <complib/cl_packoff.h>
 
+#define IB_MULTIPATH_MAX_GIDS 11	/* Support max that can fit into first MAD (for now) */
+
+#include <complib/cl_packon.h>
+typedef struct _ib_multipath_rec_t
+{
+	ib_net32_t		hop_flow_raw;
+	uint8_t			tclass;
+	uint8_t			num_path;
+	ib_net16_t		pkey;
+	uint8_t			resv0;
+	uint8_t			sl;
+	uint8_t			mtu;
+	uint8_t			rate;
+	uint8_t			pkt_life;
+	uint8_t			resv1;
+	uint8_t			independence;	/* formerly resv2 */
+	uint8_t			sgid_count;
+	uint8_t			dgid_count;
+	uint8_t			resv3[7];
+	ib_gid_t		gids[IB_MULTIPATH_MAX_GIDS];
+}	PACK_SUFFIX ib_multipath_rec_t;
+#include <complib/cl_packoff.h>
+/*
+* FIELDS
+*       hop_flow_raw
+*               Global routing parameters: hop count, flow label and raw bit.
+*
+*       tclass
+*               Another global routing parameter.
+*
+*       num_path
+*     Reversible path - 1 bit to say if path is reversible.
+*               num_path [6:0] In queries, maximum number of paths to return.
+*               In responses, undefined.
+*
+*       pkey
+*               Partition key (P_Key) to use on this path.
+*
+*       sl
+*               Service level to use on this path.
+*
+*       mtu
+*               MTU and MTU selector fields to use on this path
+*       rate
+*               Rate and rate selector fields to use on this path.
+*
+*       pkt_life
+*               Packet lifetime
+*
+*       preference
+*               Indicates the relative merit of this path versus other path
+*               records returned from the SA.  Lower numbers are better.
+*
+* SEE ALSO
+*********/
+
+/****f* IBA Base: Types/ib_multipath_rec_num_path
+* NAME
+*       ib_multipath_rec_num_path
+*
+* DESCRIPTION
+*       Get max number of paths to return.
+*
+* SYNOPSIS
+*/
+static inline uint8_t
+ib_multipath_rec_num_path(
+        IN              const   ib_multipath_rec_t* const            p_rec )
+{
+        return( p_rec->num_path &0x7F );
+}
+/*
+* PARAMETERS
+*       p_rec
+*               [in] Pointer to the multipath record object.
+*
+* RETURN VALUES
+*       Maximum number of paths to return for each unique SGID_DGID combination.
+*
+* NOTES
+*
+* SEE ALSO
+*       ib_multipath_rec_t
+*********/
+
+/****f* IBA Base: Types/ib_multipath_rec_sl
+* NAME
+*       ib_multipath_rec_sl
+*
+* DESCRIPTION
+*       Get multipath service level.
+*
+* SYNOPSIS
+*/
+static inline uint8_t
+ib_multipath_rec_sl(
+        IN              const   ib_multipath_rec_t* const            p_rec )
+{
+        return( (uint8_t)((cl_ntoh16( p_rec->sl )) & 0xF) );
+}
+/*
+* PARAMETERS
+*       p_rec
+*               [in] Pointer to the multipath record object.
+*
+* RETURN VALUES
+*	SL.
+*
+* NOTES
+*
+* SEE ALSO
+*       ib_multipath_rec_t
+*********/
+
+/****f* IBA Base: Types/ib_multipath_rec_mtu
+* NAME
+*       ib_multipath_rec_mtu
+*
+* DESCRIPTION
+*       Get encoded path MTU.
+*
+* SYNOPSIS
+*/
+static inline uint8_t
+ib_multipath_rec_mtu(
+        IN              const   ib_multipath_rec_t* const            p_rec )
+{
+        return( (uint8_t)(p_rec->mtu & IB_MULTIPATH_REC_BASE_MASK) );
+}
+/*
+* PARAMETERS
+*       p_rec
+*               [in] Pointer to the multipath record object.
+*
+* RETURN VALUES
+*       Encoded path MTU.
+*               1: 256
+*               2: 512
+*               3: 1024
+*               4: 2048
+*               5: 4096
+*               others: reserved
+*
+* NOTES
+*
+* SEE ALSO
+*       ib_multipath_rec_t
+*********/
+
+/****f* IBA Base: Types/ib_multipath_rec_mtu_sel
+* NAME
+*       ib_multipath_rec_mtu_sel
+*
+* DESCRIPTION
+*       Get encoded multipath MTU selector.
+*
+* SYNOPSIS
+*/
+static inline uint8_t
+ib_multipath_rec_mtu_sel(
+        IN              const   ib_multipath_rec_t* const            p_rec )
+{
+        return( (uint8_t)((p_rec->mtu & IB_MULTIPATH_REC_SELECTOR_MASK) >> 6) );
+}
+/*
+* PARAMETERS
+*       p_rec
+*               [in] Pointer to the multipath record object.
+*
+* RETURN VALUES
+*       Encoded path MTU selector value (for queries).
+*               0: greater than MTU specified
+*               1: less than MTU specified
+*               2: exactly the MTU specified
+*               3: largest MTU available
+*
+* NOTES
+*
+* SEE ALSO
+*       ib_multipath_rec_t
+*********/
+
+/****f* IBA Base: Types/ib_multipath_rec_rate
+* NAME
+*       ib_multipath_rec_rate
+*
+* DESCRIPTION
+*       Get encoded multipath rate.
+*
+* SYNOPSIS
+*/
+static inline uint8_t
+ib_multipath_rec_rate(
+        IN              const   ib_multipath_rec_t* const            p_rec )
+{
+        return( (uint8_t)(p_rec->rate & IB_MULTIPATH_REC_BASE_MASK) );
+}
+/*
+* PARAMETERS
+*       p_rec
+*               [in] Pointer to the multipath record object.
+*
+* RETURN VALUES
+*       Encoded multipath rate.
+*               2: 2.5 Gb/sec.
+*               3: 10 Gb/sec.
+*               4: 30 Gb/sec.
+*               others: reserved
+*
+* NOTES
+*
+* SEE ALSO
+*       ib_multipath_rec_t
+*********/
+
+/****f* IBA Base: Types/ib_multipath_rec_rate_sel
+* NAME
+*       ib_multipath_rec_rate_sel
+*
+* DESCRIPTION
+*       Get encoded multipath rate selector.
+*
+* SYNOPSIS
+*/
+static inline uint8_t
+ib_multipath_rec_rate_sel(
+        IN              const   ib_multipath_rec_t* const            p_rec )
+{
+        return( (uint8_t)((p_rec->rate & IB_MULTIPATH_REC_SELECTOR_MASK) >> 6) );
+}
+/*
+* PARAMETERS
+*       p_rec
+*               [in] Pointer to the multipath record object.
+*
+* RETURN VALUES
+*       Encoded path rate selector value (for queries).
+*               0: greater than rate specified
+*               1: less than rate specified
+*               2: exactly the rate specified
+*               3: largest rate available
+*
+* NOTES
+*
+* SEE ALSO
+*       ib_multipath_rec_t
+*********/
+
+/****f* IBA Base: Types/ib_multipath_rec_pkt_life
+* NAME
+*       ib_multipath_rec_pkt_life
+*
+* DESCRIPTION
+*       Get encoded multipath pkt_life.
+*
+* SYNOPSIS
+*/
+static inline uint8_t
+ib_multipath_rec_pkt_life(
+        IN              const   ib_multipath_rec_t* const            p_rec )
+{
+        return( (uint8_t)(p_rec->pkt_life & IB_MULTIPATH_REC_BASE_MASK) );
+}
+/*
+* PARAMETERS
+*       p_rec
+*               [in] Pointer to the multipath record object.
+*
+* RETURN VALUES
+*       Encoded multipath pkt_life = 4.096 ode must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * $Id$
+ */
+
+
+/*
+ * Abstract:
+ * 	Implementation of osm_mpr_rcv_t.
+ *	This object represents the MultiPath Record Receiver object.
+ *	This object is part of the opensm family of objects.
+ *
+ * Environment:
+ * 	Linux User Mode
+ *
+ */
+
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <iba/ib_types.h>
+#include <complib/cl_memory.h>
+#include <complib/cl_qmap.h>
+#include <complib/cl_passivelock.h>
+#include <complib/cl_debug.h>
+#include <complib/cl_qlist.h>
+#include <opensm/osm_sa_multipath_record.h>
+#include <opensm/osm_port.h>
+#include <opensm/osm_node.h>
+#include <opensm/osm_switch.h>
+#include <vendor/osm_vendor.h>
+#include <vendor/osm_vendor_api.h>
+#include <opensm/osm_helper.h>
+
+#define OSM_MPR_RCV_POOL_MIN_SIZE	64
+#define OSM_MPR_RCV_POOL_GROW_SIZE	64
+
+#define OSM_SA_MPR_MAX_NUM_PATH        127
+
+typedef	struct	_osm_mpr_item
+{
+	cl_pool_item_t		 pool_item;
+	const osm_port_t	*p_src_port;
+	const osm_port_t	*p_dest_port;
+	int			 hops;
+	ib_path_rec_t		 path_rec;
+} osm_mpr_item_t;
+
+typedef struct _osm_path_parms
+{
+	ib_net16_t		pkey;
+	uint8_t			mtu;
+	uint8_t			rate;
+	uint8_t			sl;
+	uint8_t			pkt_life;
+	boolean_t		reversible;
+	int			hops;
+} osm_path_parms_t;
+
+
+/**********************************************************************
+ **********************************************************************/
+void
+osm_mpr_rcv_construct(
+  IN osm_mpr_rcv_t* const p_rcv )
+{
+  cl_memclr( p_rcv, sizeof(*p_rcv) );
+  cl_qlock_pool_construct( &p_rcv->pr_pool );
+}
+
+/**********************************************************************
+ **********************************************************************/
+void
+osm_mpr_rcv_destroy(
+  IN osm_mpr_rcv_t* const p_rcv )
+{
+  OSM_LOG_ENTER( p_rcv->p_log, osm_mpr_rcv_destroy );
+  cl_qlock_pool_destroy( &p_rcv->pr_pool );
+  OSM_LOG_EXIT( p_rcv->p_log );
+}
+
+/**********************************************************************
+ **********************************************************************/
+ib_api_status_t
+osm_mpr_rcv_init(
+  IN osm_mpr_rcv_t*	const p_rcv,
+  IN osm_sa_resp_t*	const p_resp,
+  IN osm_mad_pool_t*	const p_mad_pool,
+  IN osm_subn_t*	const p_subn,
+  IN osm_log_t*		const p_log,
+  IN cl_plock_t*	const p_lock )
+{
+  ib_api_status_t status;
+
+  OSM_LOG_ENTER( p_log, osm_mpr_rcv_init );
+
+  osm_mpr_rcv_construct( p_rcv );
+
+  p_rcv->p_log = p_log;
+  p_rcv->p_subn = p_subn;
+  p_rcv->p_lock = p_lock;
+  p_rcv->p_resp = p_resp;
+  p_rcv->p_mad_pool = p_mad_pool;
+
+  status = cl_qlock_pool_init( &p_rcv->pr_pool,
+			       OSM_MPR_RCV_POOL_MIN_SIZE,
+			       0,
+			       OSM_MPR_RCV_POOL_GROW_SIZE,
+			       sizeof(osm_mpr_item_t),
+			       NULL, NULL, NULL );
+
+  OSM_LOG_EXIT( p_rcv->p_log );
+  return( status );
+}
+
+/**********************************************************************
+ **********************************************************************/
+static ib_api_status_t
+__osm_mpr_rcv_get_path_parms(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const ib_multipath_rec_t*	const p_mpr,
+  IN const osm_port_t*		const p_src_port,
+  IN const osm_port_t*		const p_dest_port,
+  IN const uint16_t		dest_lid_ho,
+  IN const ib_net64_t		comp_mask,
+  OUT osm_path_parms_t*		const p_parms )
+{
+  ib_net64_t			node_guid;
+  const osm_node_t*		p_node;
+  const osm_physp_t*		p_physp;
+  const osm_physp_t*		p_dest_physp;
+  const osm_switch_t*		p_sw;
+  const ib_port_info_t*		p_pi;
+  const cl_qmap_t*		p_sw_tbl;
+  ib_slvl_table_t*            	p_slvl_tbl;
+  ib_api_status_t		status = IB_SUCCESS;
+  uint8_t			mtu;
+  uint8_t			rate;
+  uint8_t			pkt_life;
+  uint8_t			required_mtu;
+  uint8_t			required_rate;
+  uint16_t                	required_pkey;
+  uint8_t                 	required_sl;
+  uint8_t			required_pkt_life;
+  ib_net16_t			dest_lid;
+  int                     	hops = 0;
+  int                     	in_port_num = 0;
+  uint8_t			vl;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_get_path_parms );
+
+  dest_lid = cl_hton16( dest_lid_ho );
+
+  p_dest_physp = osm_port_get_default_phys_ptr( p_dest_port );
+  p_physp = osm_port_get_default_phys_ptr( p_src_port );
+  p_pi = osm_physp_get_port_info_ptr( p_physp );
+  p_sw_tbl = &p_rcv->p_subn->sw_guid_tbl;
+
+  mtu = ib_port_info_get_neighbor_mtu( p_pi );
+  rate = ib_port_info_compute_rate( p_pi );
+
+  if ( comp_mask & IB_MPR_COMPMASK_SL )
+    required_sl = ib_multipath_rec_sl( p_mpr );
+  else
+    required_sl = OSM_DEFAULT_SL;
+
+  if ( comp_mask & IB_MPR_COMPMASK_PKEY ) {
+    required_pkey = p_mpr->pkey;
+    if ( !osm_physp_has_pkey( p_rcv->p_log, required_pkey, p_physp ) ||
+         !osm_physp_has_pkey( p_rcv->p_log, required_pkey, p_dest_physp ) ) {
+      osm_log( p_rcv->p_log, OSM_LOG_VERBOSE,
+	       "__osm_mpr_rcv_get_path_parms: "
+	       "path not found for PKEY = 0x%x\n"
+	       "\t\tsrc %Lx dst %Lx\n",
+	       required_pkey,
+	       osm_physp_get_port_guid( p_physp ),
+	       osm_physp_get_port_guid( p_dest_physp ) );
+
+      status = IB_NOT_FOUND;
+      goto Exit;
+    }
+  } else
+    required_pkey = IB_DEFAULT_PKEY;
+
+  /*
+    Walk the subnet object from source to destination,
+    tracking the most restrictive rate and mtu values along the way...
+
+    If source port node is a switch, then p_physp should
+    point to the port that routes the destination lid
+  */
+
+  p_node = osm_physp_get_node_ptr( p_physp );
+
+  if ( osm_node_get_type( p_node ) == IB_NODE_TYPE_SWITCH )
+  {
+    p_sw = (osm_switch_t *)cl_qmap_get( p_sw_tbl,
+					osm_node_get_node_guid( p_node ) );
+
+    if( p_sw == (osm_switch_t *)cl_qmap_end( p_sw_tbl ) )
+    {
+      status = IB_ERROR;
+      goto Exit;
+    }
+
+    /*
+     * If the dest_lid_ho is equal to the lid of the switch pointed by
+     * p_sw then p_physp will be the physical port of the switch port zero.
+     */
+    p_physp = osm_switch_get_route_by_lid( p_sw, cl_ntoh16( dest_lid_ho ) );
+    if ( p_physp == 0 )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+               "__osm_mpr_rcv_get_path_parms: ERR 4514: "
+               "Can't find routing to LID 0x%X from switch for guid 0x%016" PRIx64 "\n",
+               dest_lid_ho,
+               cl_ntoh64( osm_node_get_node_guid( p_node ) ) );
+      status = IB_ERROR;
+      goto Exit;
+    }
+  }
+
+  /*
+   * Same as above
+   */
+  p_node = osm_physp_get_node_ptr( p_dest_physp );
+	
+  if ( osm_node_get_type( p_node ) == IB_NODE_TYPE_SWITCH )
+  {
+    p_sw = (osm_switch_t *)cl_qmap_get( p_sw_tbl,
+					osm_node_get_node_guid( p_node ) );
+
+    if ( p_sw == (osm_switch_t *)cl_qmap_end( p_sw_tbl ) )
+    {
+      status = IB_ERROR;
+      goto Exit;
+    }
+
+    p_dest_physp = osm_switch_get_route_by_lid( p_sw, cl_ntoh16( dest_lid_ho ) );
+
+    if ( p_dest_physp == 0 )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+               "__osm_mpr_rcv_get_path_parms: ERR 4515: "
+               "Can't find routing to LID 0x%X from switch for guid 0x%016" PRIx64 "\n",
+               dest_lid_ho,
+               cl_ntoh64( osm_node_get_node_guid( p_node ) ) );
+      status = IB_ERROR;
+      goto Exit;
+    }
+
+  }
+
+  while ( p_physp != p_dest_physp )
+  {
+    p_physp = osm_physp_get_remote( p_physp );
+
+    if ( p_physp == 0 )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+               "__osm_mpr_rcv_get_path_parms: ERR 4505: "
+               "Can't find remote phys port when routing to LID 0x%X from node guid 0x%016" PRIx64 "\n",
+               dest_lid_ho,
+               cl_ntoh64( osm_node_get_node_guid( p_node ) ) );
+      status = IB_ERROR;
+      goto Exit;
+    }
+
+    hops++;
+
+    /*
+      This is point to point case (no switch in between)
+    */
+    if ( p_physp == p_dest_physp )
+      break;
+
+    p_node = osm_physp_get_node_ptr( p_physp );
+
+    if ( osm_node_get_type( p_node ) != IB_NODE_TYPE_SWITCH )
+    {
+      /*
+        There is some sort of problem in the subnet object!
+        If this isn't a switch, we should have reached
+        the destination by now!
+      */
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+	       "__osm_mpr_rcv_get_path_parms: ERR 4503: "
+	       "Internal error, bad path\n" );
+      status = IB_ERROR;
+      goto Exit;
+    }
+
+    node_guid = osm_node_get_node_guid( p_node );
+    p_sw = (osm_switch_t*)cl_qmap_get( p_sw_tbl, node_guid );
+
+    if ( p_sw == (osm_switch_t*)cl_qmap_end( p_sw_tbl ) )
+    {
+      /*
+        There is some sort of problem in the subnet object!
+      */
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+	       "__osm_mpr_rcv_get_path_parms: ERR 4504: "
+	       "Internal error, no switch for guid 0x%016" PRIx64 "\n",
+	       cl_ntoh64( node_guid ) );
+      status = IB_ERROR;
+      goto Exit;
+    }
+
+    /*
+      Check parameters for the ingress port in this switch.
+    */
+    p_pi = osm_physp_get_port_info_ptr( p_physp );
+
+    if ( mtu > ib_port_info_get_mtu_cap( p_pi ) )
+    {
+      mtu = ib_port_info_get_mtu_cap( p_pi );
+      if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+      {
+        osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+		 "__osm_mpr_rcv_get_path_parms: "
+		 "New smallest MTU = %u at intervening port 0x%016" PRIx64 "\n",
+		 mtu,
+		 osm_physp_get_port_guid( p_physp ) );
+      }
+    }
+
+    if ( rate > ib_port_info_compute_rate( p_pi ) )
+    {
+      rate = ib_port_info_compute_rate( p_pi );
+      if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+      {
+        osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+                 "__osm_mpr_rcv_get_path_parms: "
+		 "New smallest rate = %u at intervening port 0x%016" PRIx64 "\n",
+		 rate,
+		 osm_physp_get_port_guid( p_physp ) );
+      }
+    }
+
+    /*
+      Continue with the egress port on this switch.
+    */
+    p_physp = osm_switch_get_route_by_lid( p_sw, dest_lid );
+
+    if ( p_physp == 0 )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+               "__osm_mpr_rcv_get_path_parms: ERR 4516: "
+               "Dead end on path to LID 0x%X from switch for guid 0x%016" PRIx64 "\n",
+               dest_lid_ho,
+               cl_ntoh64( node_guid ) );
+      status = IB_ERROR;
+      goto Exit;
+    }
+
+    CL_ASSERT( p_physp );
+    CL_ASSERT( osm_physp_is_valid( p_physp ) );
+
+    if ( comp_mask & IB_MPR_COMPMASK_SL ) {
+      in_port_num = osm_physp_get_port_num( p_physp );
+      p_slvl_tbl = osm_physp_get_slvl_tbl( p_physp, in_port_num );
+      vl = ib_slvl_table_get( p_slvl_tbl, required_sl );
+      if (vl == IB_DROP_VL) {	/* discard packet */
+        osm_log( p_rcv->p_log, OSM_LOG_VERBOSE,
+                 "__osm_mpr_rcv_get_path_parms: Path not found for SL %d\n"
+		 "\t\tin_port_num %d port_guid %Lx\n",
+		 required_sl, in_port_num,
+		 osm_physp_get_port_guid( p_physp ) );
+        status = IB_NOT_FOUND;
+        goto Exit;
+      }
+    }
+
+    p_pi = osm_physp_get_port_info_ptr( p_physp );
+
+    if ( mtu > ib_port_info_get_mtu_cap( p_pi ) )
+    {
+      mtu = ib_port_info_get_mtu_cap( p_pi );
+      if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+      {
+        osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+		 "__osm_mpr_rcv_get_path_parms: "
+		 "New smallest MTU = %u at intervening port 0x%016" PRIx64 "\n",
+		 mtu,
+		 osm_physp_get_port_guid( p_physp ) );
+      }
+    }
+
+    if ( rate > ib_port_info_compute_rate( p_pi ) )
+    {
+      rate = ib_port_info_compute_rate( p_pi );
+      if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+      {
+        osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+		 "__osm_mpr_rcv_get_path_parms: "
+		 "New smallest rate = %u at intervening port 0x%016" PRIx64 "\n",
+		 rate,
+		 osm_physp_get_port_guid( p_physp ) );
+      }
+    }
+
+  }
+
+  /*
+    p_physp now points to the destination
+  */
+  p_pi = osm_physp_get_port_info_ptr( p_physp );
+
+  if ( mtu > ib_port_info_get_mtu_cap( p_pi ) )
+  {
+    mtu = ib_port_info_get_mtu_cap( p_pi );
+    if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	       "__osm_mpr_rcv_get_path_parms: "
+	       "New smallest MTU = %u at destination port 0x%016" PRIx64 "\n",
+	       mtu,
+	       osm_physp_get_port_guid( p_physp ) );
+    }
+  }
+
+  if ( rate > ib_port_info_compute_rate( p_pi ) )
+  {
+    rate = ib_port_info_compute_rate( p_pi );
+    if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	       "__osm_mpr_rcv_get_path_parms: "
+	       "New smallest rate = %u at destination port 0x%016" PRIx64 "\n",
+	       rate,
+	       osm_physp_get_port_guid( p_physp ) );
+    }
+  }
+
+  if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	     "__osm_mpr_rcv_get_path_parms: "
+	     "Path min MTU = %u, min rate = %u\n", mtu, rate );
+  }
+
+  /*
+    Determine if these values meet the user criteria
+  */
+
+  /* we silently ignore cases where only the MTU selector is defined */
+  if ( ( comp_mask & IB_MPR_COMPMASK_MTUSELEC ) &&
+       ( comp_mask & IB_MPR_COMPMASK_MTU ) )
+  {
+    required_mtu = ib_multipath_rec_mtu( p_mpr );
+    switch ( ib_multipath_rec_mtu_sel( p_mpr ) )
+    {
+      case 0:	/* must be greater than */
+        if ( mtu <= required_mtu )
+          status = IB_NOT_FOUND;
+        break;
+
+      case 1:	/* must be less than */
+        if ( mtu >= required_mtu )
+          status = IB_NOT_FOUND;
+        break;
+
+      case 2:	/* exact match */
+        if ( mtu != required_mtu )
+          status = IB_NOT_FOUND;
+        break;
+
+      case 3:	/* largest available */
+        /* can't be disqualified by this one */
+        break;
+
+      default:
+        /* if we're here, there's a bug in ib_multipath_rec_mtu_sel() */
+        CL_ASSERT( FALSE );
+        status = IB_ERROR;
+        break;
+    }
+  }
+
+  /* we silently ignore cases where only the Rate selector is defined */
+  if ( ( comp_mask & IB_MPR_COMPMASK_RATESELEC ) &&
+       ( comp_mask & IB_PR_COMPMASK_RATE ) )
+  {
+    required_rate = ib_multipath_rec_rate( p_mpr );
+    switch ( ib_multipath_rec_rate_sel( p_mpr ) )
+    {
+      case 0:	/* must be greater than */
+        if ( rate <= required_rate )
+          status = IB_NOT_FOUND;
+        break;
+
+      case 1:	/* must be less than */
+        if ( rate >= required_rate )
+          status = IB_NOT_FOUND;
+        break;
+
+      case 2:	/* exact match */
+        if ( rate != required_rate )
+          status = IB_NOT_FOUND;
+        break;
+
+      case 3:	/* largest available */
+        /* can't be disqualified by this one */
+        break;
+
+      default:
+        /* if we're here, there's a bug in ib_multipath_rec_mtu_sel() */
+        CL_ASSERT( FALSE );
+        status = IB_ERROR;
+        break;
+    }
+  }
+
+  /* Verify the pkt_life_time */
+  /* According to spec definition IBA 1.2 Table 205 PacketLifeTime description,
+     for loopback paths, packetLifeTime shall be zero. */
+  if ( p_src_port == p_dest_port )
+    pkt_life = 0;	/* loopback */
+  else
+    pkt_life = OSM_DEFAULT_SUBNET_TIMEOUT;
+
+  /* we silently ignore cases where only the PktLife selector is defined */
+  if ( ( comp_mask & IB_MPR_COMPMASK_PKTLIFETIMESELEC ) &&
+       ( comp_mask & IB_MPR_COMPMASK_PKTLIFETIME ) )
+  {
+    required_pkt_life = ib_multipath_rec_pkt_life( p_mpr );
+    switch ( ib_multipath_rec_pkt_life_sel( p_mpr ) )
+    {
+    case 0:    /* must be greater than */
+      if ( pkt_life <= required_pkt_life )
+        status = IB_NOT_FOUND;
+      break;
+
+    case 1:    /* must be less than */
+      if ( pkt_life >= required_pkt_life )
+        status = IB_NOT_FOUND;
+      break;
+
+    case 2:    /* exact match */
+      if ( pkt_life != required_pkt_life )
+        status = IB_NOT_FOUND;
+      break;
+
+    case 3:    /* smallest available */
+      /* can't be disqualified by this one */
+      break;
+
+    default:
+      /* if we're here, there's a bug in ib_path_rec_pkt_life_sel() */
+      CL_ASSERT( FALSE );
+      status = IB_ERROR;
+      break;
+    }
+  }
+
+  if (status != IB_SUCCESS)
+    goto Exit;
+
+  p_parms->mtu = mtu;
+  p_parms->rate = rate;
+  p_parms->pkey = required_pkey;
+  p_parms->pkt_life = pkt_life;
+  p_parms->sl = required_sl;
+  p_parms->hops = hops;
+
+ Exit:
+  OSM_LOG_EXIT( p_rcv->p_log );
+  return( status );
+}
+
+/**********************************************************************
+ **********************************************************************/
+static void
+__osm_mpr_rcv_build_pr(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const osm_port_t*		const p_src_port,
+  IN const osm_port_t*		const p_dest_port,
+  IN const uint16_t		src_lid_ho,
+  IN const uint16_t		dest_lid_ho,
+  IN const uint8_t		preference,
+  IN const osm_path_parms_t*	const p_parms,
+  OUT ib_path_rec_t*		const p_pr )
+{
+  const osm_physp_t*		p_src_physp;
+  const osm_physp_t*		p_dest_physp;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_build_pr );
+
+  p_src_physp = osm_port_get_default_phys_ptr( p_src_port );
+  p_dest_physp = osm_port_get_default_phys_ptr( p_dest_port );
+
+  p_pr->dgid.unicast.prefix = osm_physp_get_subnet_prefix( p_dest_physp );
+  p_pr->dgid.unicast.interface_id = osm_physp_get_port_guid( p_dest_physp );
+
+  p_pr->sgid.unicast.prefix = osm_physp_get_subnet_prefix( p_src_physp );
+  p_pr->sgid.unicast.interface_id = osm_physp_get_port_guid( p_src_physp );
+
+  p_pr->dlid = cl_hton16( dest_lid_ho );
+  p_pr->slid = cl_hton16( src_lid_ho );
+
+  p_pr->pkey = p_parms->pkey;
+  p_pr->sl = p_parms->sl;
+  p_pr->mtu = (uint8_t)( p_parms->mtu | 0x80 );
+  p_pr->rate = (uint8_t)( p_parms->rate | 0x80 );
+
+  /* According to 1.2 spec definition Table 205 PacketLifeTime description,
+     for loopback paths, packetLifeTime shall be zero. */
+  if ( p_src_port == p_dest_port )
+    p_pr->pkt_life = 0x80;	/* loopback */
+  else
+    p_pr->pkt_life = (uint8_t)( p_parms->pkt_life | 0x80 );
+
+  p_pr->preference = preference;
+
+  /* always return num_path = 0 so this is only the reversible component */
+  if ( p_parms->reversible )
+    p_pr->num_path = 0x80;
+
+  OSM_LOG_EXIT( p_rcv->p_log );
+}
+
+/**********************************************************************
+ **********************************************************************/
+static osm_mpr_item_t*
+__osm_mpr_rcv_get_lid_pair_path(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const ib_multipath_rec_t*	const p_mpr,
+  IN const osm_port_t*		const p_src_port,
+  IN const osm_port_t*		const p_dest_port,
+  IN const uint16_t		src_lid_ho,
+  IN const uint16_t		dest_lid_ho,
+  IN const ib_net64_t		comp_mask,
+  IN const uint8_t		preference )
+{
+  osm_path_parms_t		path_parms;
+  osm_path_parms_t		rev_path_parms;
+  osm_mpr_item_t		*p_pr_item;
+  ib_api_status_t		status, rev_path_status;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_get_lid_pair_path );
+
+  if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	     "__osm_mpr_rcv_get_lid_pair_path: "
+	     "Src LID 0x%X, Dest LID 0x%X\n",
+	     src_lid_ho, dest_lid_ho );
+  }
+
+  p_pr_item = (osm_mpr_item_t*)cl_qlock_pool_get( &p_rcv->pr_pool );
+  if ( p_pr_item == NULL )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+	     "__osm_mpr_rcv_get_lid_pair_path: ERR 4501: "
+	     "Unable to allocate path record\n" );
+    goto Exit;
+  }
+
+  status = __osm_mpr_rcv_get_path_parms( p_rcv, p_mpr, p_src_port,
+					 p_dest_port, dest_lid_ho,
+                                         comp_mask, &path_parms );
+
+  if ( status != IB_SUCCESS )
+  {
+    cl_qlock_pool_put( &p_rcv->pr_pool, &p_pr_item->pool_item );
+    p_pr_item = NULL;
+    goto Exit;
+  }
+
+  /* now try the reversible path */
+  rev_path_status = __osm_mpr_rcv_get_path_parms( p_rcv, p_mpr, p_dest_port,
+                                                  p_src_port, src_lid_ho,
+                                                  comp_mask, &rev_path_parms );
+  path_parms.reversible = ( rev_path_status == IB_SUCCESS );
+
+  /* did we get a Reversible Path compmask ? */
+  /* 
+     NOTE that if the reversible component = 0, it is a don't care
+     rather then requiring non-reversible paths ... 
+     see Vol1 Ver1.2 p900 l16
+  */
+  if ( comp_mask & IB_MPR_COMPMASK_REVERSIBLE )
+    if ( (! path_parms.reversible && ( p_mpr->num_path & 0x80 ) ) )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+               "__osm_mpr_rcv_get_lid_pair_path: "
+               "Requested reversible path but failed to get one\n");
+
+      cl_qlock_pool_put( &p_rcv->pr_pool, &p_pr_item->pool_item );
+      p_pr_item = NULL;
+      goto Exit;
+    };
+
+  p_pr_item->p_src_port = p_src_port;
+  p_pr_item->p_dest_port = p_dest_port;
+  p_pr_item->hops = path_parms.hops;
+
+  __osm_mpr_rcv_build_pr( p_rcv, p_src_port, p_dest_port, src_lid_ho,
+			  dest_lid_ho, preference, &path_parms,
+                          &p_pr_item->path_rec );
+
+ Exit:
+  OSM_LOG_EXIT( p_rcv->p_log );
+  return( p_pr_item );
+}
+
+/**********************************************************************
+ **********************************************************************/
+static uint32_t 
+__osm_mpr_rcv_get_port_pair_paths(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const ib_multipath_rec_t*  const p_mpr,
+  IN const osm_port_t*		const p_req_port,
+  IN const osm_port_t*		const p_src_port,
+  IN const osm_port_t*		const p_dest_port,
+  IN const uint32_t		rem_paths,
+  IN const ib_net64_t		comp_mask,
+  IN cl_qlist_t*		const p_list )
+{
+  osm_mpr_item_t*		p_pr_item;
+  uint16_t			src_lid_min_ho;
+  uint16_t			src_lid_max_ho;
+  uint16_t			dest_lid_min_ho;
+  uint16_t			dest_lid_max_ho;
+  uint16_t			src_lid_ho;
+  uint16_t			dest_lid_ho;
+  uint32_t			path_num = 0;
+  uint8_t			preference;
+  uintn_t			src_offset;
+  uintn_t			dest_offset;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_get_port_pair_paths );
+
+  if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	     "__osm_mpr_rcv_get_port_pair_paths: "
+	     "Src port 0x%016" PRIx64 ", "
+	     "Dst port 0x%016" PRIx64 "\n",
+	     cl_ntoh64( osm_port_get_guid( p_src_port ) ),
+	     cl_ntoh64( osm_port_get_guid( p_dest_port ) ) );
+  }
+
+  /* Check that the req_port, src_port and dest_port all share a
+     pkey. The check is done on the default physical port of the ports. */
+  if ( osm_port_share_pkey(p_rcv->p_log, p_req_port, p_src_port ) == FALSE ||
+       osm_port_share_pkey(p_rcv->p_log, p_req_port, p_dest_port ) == FALSE ||
+       osm_port_share_pkey(p_rcv->p_log, p_src_port, p_dest_port ) == FALSE )
+  {
+    /* One of the pairs doesn't share a pkey so the path is disqualified. */
+    goto Exit;
+  }
+
+  /*
+    We shouldn't be here if the paths are disqualified in some way...
+    Thus, we assume every possible connection is valid.
+
+    We desire to return high-quality paths first.
+    In OpenSM, higher quality mean least overlap with other paths.
+    This is acheived in practice by returning paths with
+    different LID value on each end, which means these
+    paths are more redundent that paths with the same LID repeated
+    on one side.  For example, in OpenSM the paths between two
+    endpoints with LMC = 1 might be as follows:
+
+    Port A, LID 1 <-> Port B, LID 3
+    Port A, LID 1 <-> Port B, LID 4
+    Port A, LID 2 <-> Port B, LID 3
+    Port A, LID 2 <-> Port B, LID 4
+
+    The OpenSM unicast routing algorithms attempt to disperse each path
+    to as varied a physical path as is reasonable.  1<->3 and 1<->4 have
+    more physical overlap (hence less redundancy) than 1<->3 and 2<->4.
+
+    OpenSM ranks paths in three preference groups:
+
+    Preference Value		Description
+    ----------------		-------------------------------------------
+    0			Redundant in both directions with other
+    pref value = 0 paths
+
+    1			Redundant in one direction with other
+    pref value = 0 and pref value = 1 paths
+
+    2			Not redundant in either direction with
+    other paths
+
+    3-FF			Unused
+
+
+    SA clients don't need to know these details, only that the lower
+    preference paths are preferred, as stated in the spec.  The paths
+    may not actually be physically redundant depending on the topology
+    of the subnet, but the point of LMC > 0 is to offer redundancy,
+    so I assume the subnet is physically appropriate for the specified
+    LMC value.  A more advanced implementation could inspect for physical
+    redundancy, but I'm not going to bother with that now.
+  */
+
+  osm_port_get_lid_range_ho( p_src_port, &src_lid_min_ho, &src_lid_max_ho );
+  osm_port_get_lid_range_ho( p_dest_port, &dest_lid_min_ho, &dest_lid_max_ho );
+
+  if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	     "__osm_mpr_rcv_get_port_pair_paths: "
+	     "Src LID [0x%X-0x%X], "
+	     "Dest LID [0x%X-0x%X]\n",
+	     src_lid_min_ho, src_lid_max_ho,
+	     dest_lid_min_ho, dest_lid_max_ho );
+  }
+
+  src_lid_ho = src_lid_min_ho;
+  dest_lid_ho = dest_lid_min_ho;
+
+  /*
+    Preferred paths come first in OpenSM
+  */
+  preference = 0;
+
+  while ( path_num < rem_paths )
+  {
+    /*
+      These paths are "fully redundant"
+    */
+     p_pr_item = __osm_mpr_rcv_get_lid_pair_path( p_rcv, p_mpr,
+						  p_src_port, p_dest_port,
+                                                  src_lid_ho, dest_lid_ho,
+						  comp_mask, preference );
+
+    if ( p_pr_item )
+    {
+      cl_qlist_insert_tail( p_list,
+			    (cl_list_item_t*)&p_pr_item->pool_item );
+      ++path_num;
+    }
+
+    if ( ++src_lid_ho > src_lid_max_ho )
+      break;
+
+    if ( ++dest_lid_ho > dest_lid_max_ho )
+      break;
+  }
+
+  /*
+    Check if we've accumulated all the paths that the user cares to see
+  */
+  if ( path_num == rem_paths )
+    goto Exit;
+
+  /*
+    Don't bother reporting preference 1 paths for now.
+    It's more trouble than it's worth and can only occur
+    if ports have different LMC values, which isn't supported
+    by OpenSM right now anyway.
+  */
+  preference = 2;
+  src_lid_ho = src_lid_min_ho;
+  dest_lid_ho = dest_lid_min_ho;
+  src_offset = 0;
+  dest_offset = 0;
+
+  /*
+    Iterate over the remaining paths
+  */
+  while ( path_num < rem_paths )
+  {
+    dest_offset++;
+    dest_lid_ho++;
+
+    if ( dest_lid_ho > dest_lid_max_ho )
+    {
+      src_offset++;
+      src_lid_ho++;
+
+      if ( src_lid_ho > src_lid_max_ho )
+        break;	/* done */
+
+      dest_offset = 0;
+      dest_lid_ho = dest_lid_min_ho;
+    }
+
+    /*
+      These paths are "fully non-redundant" with paths already
+      identified above and consequently not of much value.
+
+      Don't return paths we already identified above, as indicated
+      by the offset values being equal.
+    */
+    if ( src_offset == dest_offset )
+      continue;	/* already reported */
+
+    p_pr_item = __osm_mpr_rcv_get_lid_pair_path( p_rcv, p_mpr,
+						 p_src_port, p_dest_port,
+                                                 src_lid_ho, dest_lid_ho,
+						 comp_mask, preference );
+
+    if ( p_pr_item )
+    {
+      cl_qlist_insert_tail( p_list,
+			    (cl_list_item_t*)&p_pr_item->pool_item );
+      ++path_num;
+    }
+  }
+
+ Exit:
+  OSM_LOG_EXIT( p_rcv->p_log );
+  return path_num;
+}
+
+#undef min
+#define min(x,y)	(((x) < (y)) ? (x) : (y))
+
+/**********************************************************************
+ **********************************************************************/
+static osm_mpr_item_t*
+__osm_mpr_rcv_get_apm_port_pair_paths(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const ib_multipath_rec_t*	const p_mpr,
+  IN const osm_port_t*		const p_src_port,
+  IN const osm_port_t*		const p_dest_port,
+  IN int 			base_offs,
+  IN const ib_net64_t		comp_mask,
+  IN cl_qlist_t*		const p_list )
+{
+  osm_mpr_item_t*		p_pr_item = 0;
+  uint16_t			src_lid_min_ho;
+  uint16_t			src_lid_max_ho;
+  uint16_t			dest_lid_min_ho;
+  uint16_t			dest_lid_max_ho;
+  uint16_t			src_lid_ho;
+  uint16_t			dest_lid_ho;
+  uintn_t			iterations;
+  int				src_lids, dest_lids;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_get_apm_port_pair_paths );
+ 
+  if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	     "__osm_mpr_rcv_get_apm_port_pair_paths: "
+	     "Src port 0x%016" PRIx64 ", "
+	     "Dst port 0x%016" PRIx64 ", base offs %d\n",
+	     cl_ntoh64( osm_port_get_guid( p_src_port ) ),
+	     cl_ntoh64( osm_port_get_guid( p_dest_port ) ),
+	     base_offs );
+  }
+
+  osm_port_get_lid_range_ho( p_src_port, &src_lid_min_ho, &src_lid_max_ho );
+  osm_port_get_lid_range_ho( p_dest_port, &dest_lid_min_ho, &dest_lid_max_ho );
+
+  src_lid_ho = src_lid_min_ho;
+  dest_lid_ho = dest_lid_min_ho;
+
+  src_lids = src_lid_max_ho - src_lid_min_ho + 1;
+  dest_lids = dest_lid_max_ho - dest_lid_min_ho + 1;
+
+  src_lid_ho += base_offs % src_lids;
+  dest_lid_ho += base_offs % dest_lids;
+
+  osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	  "__osm_mpr_rcv_get_apm_port_pair_paths: "
+	  "Src LIDs [0x%X-0x%X] hashed %d, "
+	  "Dest LIDs [0x%X-0x%X] hashed %d\n",
+	  src_lid_min_ho, src_lid_max_ho, src_lid_ho,
+	  dest_lid_min_ho, dest_lid_max_ho, dest_lid_ho );
+
+  iterations = min( src_lids, dest_lids );
+
+  while ( iterations-- )
+  {
+    /*
+      These paths are "fully redundant"
+    */
+    p_pr_item = __osm_mpr_rcv_get_lid_pair_path( p_rcv, p_mpr,
+						 p_src_port, p_dest_port,
+						 src_lid_ho, dest_lid_ho,
+						 comp_mask, 0 );
+
+    if ( p_pr_item )
+    {
+      osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	       "__osm_mpr_rcv_get_apm_port_pair_paths: "
+	       "Found matching path from Src LID 0x%X to Dest LID 0x%X with %d hops\n",
+	       src_lid_ho, dest_lid_ho, p_pr_item->hops);
+      break;
+    }
+
+    if ( ++src_lid_ho > src_lid_max_ho )
+      src_lid_ho = src_lid_min_ho;
+
+    if ( ++dest_lid_ho > dest_lid_max_ho )
+      dest_lid_ho = dest_lid_min_ho;
+  }
+
+  OSM_LOG_EXIT( p_rcv->p_log );
+  return p_pr_item;
+}
+
+/**********************************************************************
+ **********************************************************************/
+static ib_net16_t
+__osm_mpr_rcv_get_gids(
+  IN osm_mpr_rcv_t*	const p_rcv,
+  IN const ib_gid_t *	gids,
+  IN int		ngids,
+  OUT osm_port_t**	pp_port )
+{
+  osm_port_t *p_port;
+  ib_net16_t ib_status = IB_SUCCESS;
+  int i;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_get_gids );
+
+  for ( i = 0; i < ngids; i++, gids++ ) {
+    p_port = (osm_port_t *)cl_qmap_get( &p_rcv->p_subn->port_guid_tbl,
+					 gids->unicast.interface_id );
+    if ( !p_port ||
+         p_port == (osm_port_t *)cl_qmap_end( &p_rcv->p_subn->port_guid_tbl ) ) {
+      /*
+        This 'error' is the client's fault (bad gid) so
+        don't enter it as an error in our own log.
+        Return an error response to the client.
+      */
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+	       "__osm_mpr_rcv_get_gids: ERR 4506: "
+	       "No port with GUID = 0x%016" PRIx64 "\n",
+	       cl_ntoh64( gids->unicast.interface_id ) );
+
+      ib_status = IB_SA_MAD_STATUS_INVALID_GID;
+      goto Exit;
+    }
+
+    pp_port[i] = p_port;
+  }
+
+ Exit:
+  OSM_LOG_EXIT(p_rcv->p_log);
+
+  return ib_status;
+}
+
+/**********************************************************************
+ **********************************************************************/
+static ib_net16_t
+__osm_mpr_rcv_get_end_points(
+  IN osm_mpr_rcv_t*	const p_rcv,
+  IN const osm_madw_t*	const p_madw,
+  OUT osm_port_t **	pp_ports,
+  OUT int *		nsrc,
+  OUT int *		ndest )
+{
+  const ib_multipath_rec_t*	p_mpr;
+  const ib_sa_mad_t*		p_sa_mad;
+  ib_net64_t			comp_mask;
+  ib_net16_t			sa_status = IB_SA_MAD_STATUS_SUCCESS;
+  ib_gid_t *			gids;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_get_end_points );
+
+  /*
+    Determine what fields are valid and then get a pointer
+    to the source and destination port objects, if possible.
+  */
+  p_sa_mad = osm_madw_get_sa_mad_ptr( p_madw );
+  p_mpr = (ib_multipath_rec_t*)ib_sa_mad_get_payload_ptr( p_sa_mad );
+  gids = (ib_gid_t *)p_mpr->gids;
+
+  comp_mask = p_sa_mad->comp_mask;
+
+  /*
+    Check a few easy disqualifying cases up front before getting
+    into the endpoints.
+  */
+
+  /* SDGIDs could be checked for multicast disqualification. */
+
+  *nsrc = *ndest = 0;
+
+  if ( comp_mask & IB_MPR_COMPMASK_SGIDCOUNT ) {
+    *nsrc = p_mpr->sgid_count;
+    if ( *nsrc > IB_MULTIPATH_MAX_GIDS )
+      *nsrc = IB_MULTIPATH_MAX_GIDS;
+    sa_status = __osm_mpr_rcv_get_gids( p_rcv, gids, *nsrc, pp_ports );
+    if ( sa_status != IB_SUCCESS )
+      goto Exit;
+  }
+
+  if ( comp_mask & IB_MPR_COMPMASK_DGIDCOUNT ) {
+    *ndest = p_mpr->dgid_count;
+    if ( *ndest + *nsrc > IB_MULTIPATH_MAX_GIDS )
+      *ndest = IB_MULTIPATH_MAX_GIDS - *nsrc;
+    sa_status = __osm_mpr_rcv_get_gids( p_rcv, gids + *nsrc, *ndest,
+					pp_ports + *nsrc );
+  }
+
+ Exit:
+  OSM_LOG_EXIT( p_rcv->p_log );
+  return( sa_status );
+}
+
+#define __hash_lids(a, b, lmc)	\
+	(((((a) >> (lmc)) << 4) | ((b) >> (lmc))) % 103)
+
+/**********************************************************************
+ **********************************************************************/
+static void
+__osm_mpr_rcv_get_apm_paths(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const ib_multipath_rec_t*	const p_mpr,
+  IN const osm_port_t*		const p_req_port,
+  IN osm_port_t **		_pp_ports,
+  IN const ib_net64_t		comp_mask,
+  IN cl_qlist_t*		const p_list )
+{
+  osm_port_t *pp_ports[4];
+  osm_mpr_item_t *matrix[2][2];
+  int base_offs, src_lid_ho, dest_lid_ho;
+  int sumA, sumB, minA, minB;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_get_apm_paths );
+
+  /*
+   * We want to:
+   * 	1. use different lid offsets (from base) for the resultant paths
+   *	to increase the probability of redundant paths or in case
+   *	of Clos - to ensure it (different offset => different spine!)
+   *	2. keep consistent paths no matter of direction and order of ports
+   *	3. distibute the lid offsets to balance the load
+   * So, we sort the ports (within the srcs, and within the dests),
+   * hash the lids of S0, D0 (after the sort), and call __osm_mpr_rcv_get_apm_port_pair_paths
+   * with base_lid for S0, D0 and base_lid + 1 for S1, D1. This way we will get
+   * always the same offsets - order indepentent, and make sure different spines are used.
+   * Note that the diagonals on a Clos have the same number of hops, so it doesn't
+   * really matter which diagonal we use.
+   */
+  if ( _pp_ports[0]->guid < _pp_ports[1]->guid ) {
+    pp_ports[0] = _pp_ports[0];
+    pp_ports[1] = _pp_ports[1];
+  } else {
+    pp_ports[0] = _pp_ports[1];
+    pp_ports[1] = _pp_ports[0];
+  }
+  if ( _pp_ports[2]->guid < _pp_ports[3]->guid ) {
+    pp_ports[2] = _pp_ports[2];
+    pp_ports[3] = _pp_ports[3];
+  } else {
+    pp_ports[2] = _pp_ports[3];
+    pp_ports[3] = _pp_ports[2];
+  }
+
+  src_lid_ho = osm_port_get_base_lid( pp_ports[0] );
+  dest_lid_ho = osm_port_get_base_lid( pp_ports[2] );
+
+  base_offs = src_lid_ho < dest_lid_ho ?
+	      __hash_lids( src_lid_ho, dest_lid_ho, p_rcv->p_subn->opt.lmc ) :
+	      __hash_lids( dest_lid_ho, src_lid_ho, p_rcv->p_subn->opt.lmc );
+
+  matrix[0][0] = __osm_mpr_rcv_get_apm_port_pair_paths( p_rcv, p_mpr, pp_ports[0],
+							pp_ports[2], base_offs, comp_mask , p_list );
+  matrix[0][1] = __osm_mpr_rcv_get_apm_port_pair_paths( p_rcv, p_mpr, pp_ports[0],
+							pp_ports[3], base_offs, comp_mask, p_list );
+  matrix[1][0] = __osm_mpr_rcv_get_apm_port_pair_paths( p_rcv, p_mpr, pp_ports[1],
+							pp_ports[2], base_offs+1, comp_mask, p_list );
+  matrix[1][1] = __osm_mpr_rcv_get_apm_port_pair_paths( p_rcv, p_mpr, pp_ports[1],
+							pp_ports[3], base_offs+1, comp_mask, p_list );
+
+  osm_log( p_rcv->p_log, OSM_LOG_DEBUG, "__osm_mpr_rcv_get_apm_paths: "
+	   "APM matrix:\n"
+	   "\t{0,0} 0x%X->0x%X (%d)\t| {0,1} 0x%X->0x%X (%d)\n"
+	   "\t{1,0} 0x%X->0x%X (%d)\t| {1,1} 0x%X->0x%X (%d)\n",
+	   matrix[0][0]->path_rec.slid, matrix[0][0]->path_rec.dlid, matrix[0][0]->hops,
+	   matrix[0][1]->path_rec.slid, matrix[0][1]->path_rec.dlid, matrix[0][1]->hops,
+	   matrix[1][0]->path_rec.slid, matrix[1][0]->path_rec.dlid, matrix[1][0]->hops,
+	   matrix[1][1]->path_rec.slid, matrix[1][1]->path_rec.dlid, matrix[1][1]->hops );
+
+  /* check diagonal A {(0,0), (1,1)} */
+  sumA = matrix[0][0]->hops + matrix[1][1]->hops;
+  minA = min( matrix[0][0]->hops, matrix[1][1]->hops );
+
+  /* check diagonal B {(0,1), (1,0)} */
+  sumB = matrix[0][1]->hops + matrix[1][0]->hops;
+  minB = min( matrix[0][1]->hops, matrix[1][0]->hops );
+
+  /* and the winner is... */
+  if ( minA <= minB || ( minA == minB && sumA < sumB ) ) {
+    /* Diag A */
+    osm_log( p_rcv->p_log, OSM_LOG_DEBUG, "__osm_mpr_rcv_get_apm_paths: "
+	     "Diag {0,0} & {1,1} is the best:\n"
+	     "\t{0,0} 0x%X->0x%X (%d)\t & {1,1} 0x%X->0x%X (%d)\n",
+	     matrix[0][0]->path_rec.slid, matrix[0][0]->path_rec.dlid, matrix[0][0]->hops,
+	     matrix[1][1]->path_rec.slid, matrix[1][1]->path_rec.dlid, matrix[1][1]->hops );
+    cl_qlist_insert_tail( p_list,
+			 (cl_list_item_t*)&matrix[0][0]->pool_item );
+    cl_qlist_insert_tail( p_list,
+			 (cl_list_item_t*)&matrix[1][1]->pool_item );
+    cl_qlock_pool_put( &p_rcv->pr_pool, &matrix[0][1]->pool_item );
+    cl_qlock_pool_put( &p_rcv->pr_pool, &matrix[1][0]->pool_item );
+  } else {
+    /* Diag B */
+    osm_log( p_rcv->p_log, OSM_LOG_DEBUG, "__osm_mpr_rcv_get_apm_paths: "
+	     "Diag {0,1} & {1,0} is the best:\n"
+	     "\t{0,1} 0x%X->0x%X (%d)\t & {1,0} 0x%X->0x%X (%d)\n",
+	     matrix[0][1]->path_rec.slid, matrix[0][1]->path_rec.dlid, matrix[0][1]->hops,
+	     matrix[1][0]->path_rec.slid, matrix[1][0]->path_rec.dlid, matrix[1][0]->hops );
+    cl_qlist_insert_tail( p_list,
+			 (cl_list_item_t*)&matrix[0][1]->pool_item );
+    cl_qlist_insert_tail( p_list,
+			 (cl_list_item_t*)&matrix[1][0]->pool_item );
+    cl_qlock_pool_put( &p_rcv->pr_pool, &matrix[0][0]->pool_item );
+    cl_qlock_pool_put( &p_rcv->pr_pool, &matrix[1][1]->pool_item );
+  }
+
+  OSM_LOG_EXIT( p_rcv->p_log );
+}
+
+/**********************************************************************
+ **********************************************************************/
+static void
+__osm_mpr_rcv_process_pairs(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const ib_multipath_rec_t*	const p_mpr,
+  IN osm_port_t*		const p_req_port,
+  IN osm_port_t **		pp_ports,
+  IN const int			nsrc,
+  IN const int			ndest,
+  IN const ib_net64_t		comp_mask,
+  IN cl_qlist_t*		const p_list )
+{
+  osm_port_t **pp_src_port, **pp_es;
+  osm_port_t **pp_dest_port, **pp_ed;
+  uint32_t max_paths, num_paths, total_paths = 0;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_process_pairs );
+
+  if ( comp_mask & IB_MPR_COMPMASK_NUMBPATH )
+    max_paths = p_mpr->num_path & 0x7F;
+  else
+    max_paths = OSM_SA_MPR_MAX_NUM_PATH;
+
+  for ( pp_src_port = pp_ports, pp_es = pp_ports + nsrc; pp_src_port < pp_es; pp_src_port++ )
+  {
+    for ( pp_dest_port = pp_es, pp_ed = pp_es + ndest; pp_dest_port < pp_ed; pp_dest_port++ )
+    {
+      num_paths = __osm_mpr_rcv_get_port_pair_paths( p_rcv, p_mpr, p_req_port,
+                                                    *pp_src_port, *pp_dest_port,
+                                                     max_paths - total_paths,
+					             comp_mask, p_list );
+      total_paths += num_paths;
+      osm_log( p_rcv->p_log, OSM_LOG_DEBUG, "__osm_mpr_rcv_process_pairs: "
+               "%d paths %d total paths %d max paths\n",
+               num_paths, total_paths, max_paths );
+      /* Just take first NumbPaths found */
+      if (total_paths >= max_paths)
+        goto Exit;
+    }
+  }
+
+ Exit:
+  OSM_LOG_EXIT( p_rcv->p_log );
+}
+
+/**********************************************************************
+ **********************************************************************/
+static void
+__osm_mpr_rcv_respond(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN const osm_madw_t*		const p_madw,
+  IN cl_qlist_t*		const p_list )
+{
+  osm_madw_t*			p_resp_madw;
+  const ib_sa_mad_t*		p_sa_mad;
+  ib_sa_mad_t*			p_resp_sa_mad;
+  size_t			num_rec;
+  size_t			mad_size;
+  ib_path_rec_t*		p_resp_pr;
+  ib_multipath_rec_t*		p_mpr;
+  ib_api_status_t		status;
+  osm_mpr_item_t*		p_mpr_item;
+  uint32_t			i;
+
+  OSM_LOG_ENTER( p_rcv->p_log, __osm_mpr_rcv_respond );
+
+  p_sa_mad = osm_madw_get_sa_mad_ptr( p_madw );
+  p_mpr = (ib_multipath_rec_t*)ib_sa_mad_get_payload_ptr( p_sa_mad );
+
+  num_rec = cl_qlist_count( p_list );
+
+  osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
+	   "__osm_mpr_rcv_respond: "
+	   "Generating response with %u records\n", num_rec );
+
+  mad_size =  IB_SA_MAD_HDR_SIZE + num_rec * sizeof(ib_path_rec_t);
+
+  /*
+    Get a MAD to reply. Address of Mad is in the received mad_wrapper
+  */
+  p_resp_madw = osm_mad_pool_get( p_rcv->p_mad_pool, p_madw->h_bind,
+            			  mad_size, &p_madw->mad_addr );
+
+  if ( !p_resp_madw )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+	     "__osm_mpr_rcv_respond: "
+	     "ERR 4502: Unable to allocate MAD\n" );
+
+    for ( i = 0; i < num_rec; i++ )
+    {
+      p_mpr_item = (osm_mpr_item_t*)cl_qlist_remove_head( p_list );
+      cl_qlock_pool_put( &p_rcv->pr_pool, &p_mpr_item->pool_item );
+    }
+
+    osm_sa_send_error( p_rcv->p_resp, p_madw, IB_SA_MAD_STATUS_NO_RESOURCES );
+    goto Exit;
+  }
+
+  p_resp_sa_mad = osm_madw_get_sa_mad_ptr( p_resp_madw );
+
+  cl_memcpy( p_resp_sa_mad, p_sa_mad, IB_SA_MAD_HDR_SIZE );
+  p_resp_sa_mad->method |= IB_MAD_METHOD_RESP_MASK;
+  /* C15-0.1.5 - always return SM_Key = 0 (table 185 p 884) */
+  p_resp_sa_mad->sm_key = 0;
+
+  /*
+    o15-0.2.7: If MultiPath is supported, then SA shall respond to a
+    SubnAdmGetMulti() containing a valid MultiPathRecord attribute with
+    a set of zero or more PathRecords satisfying the constraints indicated
+    in the MultiPathRecord received. The PathRecord Attribute ID shall be
+    used in the response.
+  */
+  p_resp_sa_mad->attr_id = IB_MAD_ATTR_PATH_RECORD;
+  p_resp_sa_mad->attr_offset = ib_get_attr_offset( sizeof(ib_path_rec_t) );
+
+  p_resp_sa_mad->rmpp_flags = IB_RMPP_FLAG_ACTIVE;
+
+  p_resp_pr = (ib_path_rec_t*)ib_sa_mad_get_payload_ptr( p_resp_sa_mad );
+
+  for ( i = 0; i < num_rec; i++ )
+  {
+    p_mpr_item = (osm_mpr_item_t*)cl_qlist_remove_head( p_list );
+
+    /* Copy the Path Records from the list into the MAD */
+    *p_resp_pr = p_mpr_item->path_rec;
+
+    cl_qlock_pool_put( &p_rcv->pr_pool, &p_mpr_item->pool_item );
+    p_resp_pr++;
+  }
+
+  CL_ASSERT( cl_is_qlist_empty( p_list ) );
+
+  osm_dump_sa_mad( p_rcv->p_log, p_resp_sa_mad, OSM_LOG_FRAMES );
+
+  status = osm_vendor_send( p_resp_madw->h_bind, p_resp_madw, FALSE );
+
+  if ( status != IB_SUCCESS )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+	     "__osm_mpr_rcv_respond: ERR 4507: "
+	     "Unable to send MAD (%s)\n", ib_get_err_str( status ) );
+    /*  osm_mad_pool_put( p_rcv->p_mad_pool, p_resp_madw ); */
+  }
+
+ Exit:
+  OSM_LOG_EXIT( p_rcv->p_log );
+}
+
+/**********************************************************************
+ **********************************************************************/
+void
+osm_mpr_rcv_process(
+  IN osm_mpr_rcv_t*		const p_rcv,
+  IN osm_madw_t*		const p_madw )
+{
+  const ib_multipath_rec_t*	p_mpr;
+  const ib_sa_mad_t*		p_sa_mad;
+  osm_port_t*			requester_port;
+  osm_port_t*			pp_ports[IB_MULTIPATH_MAX_GIDS];
+  cl_qlist_t			pr_list;
+  ib_net16_t			sa_status;
+  int				nsrc, ndest;
+
+  OSM_LOG_ENTER( p_rcv->p_log, osm_mpr_rcv_process );
+
+  CL_ASSERT( p_madw );
+
+  /* update the requester physical port. */
+  requester_port = osm_get_port_by_mad_addr( p_rcv->p_log,  p_rcv->p_subn,
+                                             osm_madw_get_mad_addr_ptr( p_madw ) );
+  if ( requester_port == NULL )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+             "osm_mpr_rcv_process: ERR 4516: "
+             "Cannot find requester physical port\n" );
+    goto Exit;
+  }
+
+  p_sa_mad = osm_madw_get_sa_mad_ptr( p_madw );
+  p_mpr = (ib_multipath_rec_t*)ib_sa_mad_get_payload_ptr( p_sa_mad );
+
+  CL_ASSERT( p_sa_mad->attr_id == IB_MAD_ATTR_MULTIPATH_RECORD );
+
+  if ( ( p_sa_mad->rmpp_flags & IB_RMPP_FLAG_ACTIVE ) != IB_RMPP_FLAG_ACTIVE )
+  {
+    osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+             "osm_mpr_rcv_process: ERR 4510: "
+             "Invalid request as RMPP_FLAG_ACTIVE is not set\n" );
+    osm_sa_send_error( p_rcv->p_resp, p_madw, IB_SA_MAD_STATUS_REQ_INVALID );
+    goto Exit;
+  }
+
+  if ( p_sa_mad->method != IB_MAD_METHOD_GETMULTI ) {
+    osm_log( p_rcv->p_log, OSM_LOG_ERROR,
+	     "osm_mpr_rcv_process: ERR 4513: "
+	     "Unsupported Method (%s)\n",
+	     ib_get_sa_method_str( p_sa_mad->method ) );
+    osm_sa_send_error( p_rcv->p_resp, p_madw, IB_SA_MAD_STATUS_REQ_INVALID );
+    goto Exit;
+  }
+
+  if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
+    osm_dump_multipath_record( p_rcv->p_log, p_mpr, OSM_LOG_DEBUG );
+
+  cl_qlist_init( &pr_list ); 
+
+  /*
+    Most SA functions (including this one) are read-only on the
+    subnet object, so we grab the lock non-exclusively.
+   */
+  cl_plock_acquire( p_rcv->p_lock );
+
+  sa_status = __osm_mpr_rcv_get_end_points( p_rcv, p_madw, pp_ports,
+                                            &nsrc, &ndest );
+    
+  if ( sa_status != IB_SA_MAD_STATUS_SUCCESS || !nsrc || !ndest )
+  {
+    if ( sa_status == IB_SA_MAD_STATUS_SUCCESS && ( !nsrc || !ndest ) )
+      osm_log( p_rcv->p_log, OSM_LOG_ERROR, "osm_mpr_rcv_process_cb: ERR 4512: "
+               "__osm_mpr_rcv_get_end_points failed, not enough GIDs "
+               "(nsrc %d ndest %d)\n",
+               nsrc, ndest);
+    cl_plock_release( p_rcv->p_lock );
+    if ( sa_status == IB_SA_MAD_STATUS_SUCCESS )
+      osm_sa_send_error( p_rcv->p_resp, p_madw, IB_SA_MAD_STATUS_REQ_INVALID );
+    else
+      osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status );
+    goto Exit;
+  }
+
+  /* APM request */
+  if ( nsrc == 2 && ndest == 2 && ( p_mpr->num_path & 0x7F ) == 2 )
+    __osm_mpr_rcv_get_apm_paths( p_rcv, p_mpr, requester_port, pp_ports,
+				 p_sa_mad->comp_mask, &pr_list );
+  else
+    __osm_mpr_rcv_process_pairs( p_rcv, p_mpr, requester_port, pp_ports,
+				 nsrc, ndest,
+				 p_sa_mad->comp_mask, &pr_list );
+
+  cl_plock_release( p_rcv->p_lock );
+  __osm_mpr_rcv_respond( p_rcv, p_madw, &pr_list );
+
+ Exit:
+  OSM_LOG_EXIT( p_rcv->p_log );
+}
+#endif

Property changes on: osm/opensm/osm_sa_multipath_record.c
___________________________________________________________________
Name: svn:keywords
   + Id

Index: osm/opensm/osm_helper.c
===================================================================
--- osm/opensm/osm_helper.c	(revision 6920)
+++ osm/opensm/osm_helper.c	(working copy)
@@ -952,6 +952,79 @@ osm_dump_path_record(
 /**********************************************************************
  **********************************************************************/
 void
+osm_dump_multipath_record(
+  IN osm_log_t* const p_log,
+  IN const ib_multipath_rec_t* const p_mpr,
+  IN const osm_log_level_t log_level )
+{
+  int i;
+  char buf_line[1024];
+  ib_gid_t const *p_gid;
+
+  if( osm_log_is_active( p_log, log_level ) )
+  {
+    cl_memclr(buf_line, sizeof(buf_line));
+    p_gid = p_mpr->gids;
+    if ( p_mpr->sgid_count )
+    {
+      for (i = 0; i < p_mpr->sgid_count; i++)
+      {
+        sprintf( buf_line, "%s\t\t\t\tsgid%02d.................."
+                 "0x%016" PRIx64 " : 0x%016" PRIx64 "\n",
+                 buf_line, i + 1, cl_ntoh64( p_gid->unicast.prefix ),
+                 cl_ntoh64( p_gid->unicast.interface_id ) );
+        p_gid++;
+      }
+    }
+    if ( p_mpr->dgid_count )
+    {
+      for (i = 0; i < p_mpr->dgid_count; i++)
+      {
+        sprintf( buf_line, "%s\t\t\t\tdgid%02d.................."
+                 "0x%016" PRIx64 " : 0x%016" PRIx64 "\n",
+                 buf_line, i + 1, cl_ntoh64( p_gid->unicast.prefix ),
+                 cl_ntoh64( p_gid->unicast.interface_id ) );
+        p_gid++;
+      }
+    }
+    osm_log( p_log, log_level,
+             "MultiPathRecord dump:\n"
+             "\t\t\t\thop_flow_raw............0x%X\n"
+             "\t\t\t\ttclass..................0x%X\n"
+             "\t\t\t\tnum_path_revers.........0x%X\n"
+             "\t\t\t\tpkey....................0x%X\n"
+             "\t\t\t\tresv0...................0x%X\n"
+             "\t\t\t\tsl......................0x%X\n"
+             "\t\t\t\tmtu.....................0x%X\n"
+             "\t\t\t\trate....................0x%X\n"
+             "\t\t\t\tpkt_life................0x%X\n"
+             "\t\t\t\tresv1...................0x%X\n"
+             "\t\t\t\tindependence............0x%X\n"
+             "\t\t\t\tsgid_count..............0x%X\n"
+             "\t\t\t\tdgid_count..............0x%X\n"
+             "%s\n"
+             "",
+             cl_ntoh32( p_mpr->hop_flow_raw ),
+             p_mpr->tclass,
+             p_mpr->num_path,
+             cl_ntoh16( p_mpr->pkey ),
+             p_mpr->resv0,
+             cl_ntoh16( p_mpr->sl ),
+             p_mpr->mtu,
+             p_mpr->rate,
+             p_mpr->pkt_life,
+             p_mpr->resv1,
+             p_mpr->independence,
+             p_mpr->sgid_count,
+             p_mpr->dgid_count,
+             buf_line
+             );
+  }
+}
+
+/**********************************************************************
+ **********************************************************************/
+void
 osm_dump_mc_record(
   IN osm_log_t* const p_log,
   IN const ib_member_rec_t* const p_mcmr,
Index: osm/opensm/osm_sa_response.c
===================================================================
--- osm/opensm/osm_sa_response.c	(revision 6920)
+++ osm/opensm/osm_sa_response.c	(working copy)
@@ -154,6 +154,13 @@ osm_sa_send_error(
    */
   p_resp_sa_mad->sm_key = 0;
 
+  /*
+   * o15-0.2.7 - The PathRecord Attribute ID shall be used in
+   * the response (to a SubnAdmGetMulti(MultiPathRecord) 
+   */
+  if( p_resp_sa_mad->attr_id == IB_MAD_ATTR_MULTIPATH_RECORD )
+    p_resp_sa_mad->attr_id = IB_MAD_ATTR_PATH_RECORD;
+
   if( osm_log_is_active( p_resp->p_log, OSM_LOG_FRAMES ) )
     osm_dump_sa_mad( p_resp->p_log, p_resp_sa_mad, OSM_LOG_FRAMES );
 
Index: osm/opensm/osm_sa.c
===================================================================
--- osm/opensm/osm_sa.c	(revision 6920)
+++ osm/opensm/osm_sa.c	(working copy)
@@ -98,6 +98,11 @@ osm_sa_construct(
   osm_pr_rcv_construct( &p_sa->pr_rcv );
   osm_pr_rcv_ctrl_construct( &p_sa->pr_rcv_ctrl );
 
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+  osm_mpr_rcv_construct( &p_sa->mpr_rcv );
+  osm_mpr_rcv_ctrl_construct( &p_sa->mpr_rcv_ctrl );
+#endif
+
   osm_smir_rcv_construct( &p_sa->smir_rcv );
   osm_smir_ctrl_construct( &p_sa->smir_ctrl );
 
@@ -141,6 +146,9 @@ osm_sa_shutdown(
   osm_gir_rcv_ctrl_destroy( &p_sa->gir_rcv_ctrl );
   osm_lr_rcv_ctrl_destroy( &p_sa->lr_rcv_ctrl );
   osm_pr_rcv_ctrl_destroy( &p_sa->pr_rcv_ctrl );
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+  osm_mpr_rcv_ctrl_destroy( &p_sa->mpr_rcv_ctrl );
+#endif
   osm_smir_ctrl_destroy( &p_sa->smir_ctrl );
   osm_mcmr_rcv_ctrl_destroy( &p_sa->mcmr_rcv_ctlr);
   osm_sr_rcv_ctrl_destroy( &p_sa->sr_rcv_ctrl );
@@ -169,6 +177,9 @@ osm_sa_destroy(
   osm_gir_rcv_destroy( &p_sa->gir_rcv );
   osm_lr_rcv_destroy( &p_sa->lr_rcv );
   osm_pr_rcv_destroy( &p_sa->pr_rcv );
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+  osm_mpr_rcv_destroy( &p_sa->mpr_rcv );
+#endif
   osm_smir_rcv_destroy( &p_sa->smir_rcv );
   osm_mcmr_rcv_destroy(&p_sa->mcmr_rcv);
   osm_sr_rcv_destroy( &p_sa->sr_rcv );
@@ -335,6 +346,26 @@ osm_sa_init(
   if( status != IB_SUCCESS )
     goto Exit;
 
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+  status = osm_mpr_rcv_init(
+    &p_sa->mpr_rcv,
+    &p_sa->resp,
+    p_sa->p_mad_pool, 
+    p_subn,
+    p_log,
+    p_lock );
+  if( status != IB_SUCCESS )
+    goto Exit;
+
+  status = osm_mpr_rcv_ctrl_init(
+    &p_sa->mpr_rcv_ctrl,
+    &p_sa->mpr_rcv,
+    p_log,
+    p_disp );
+  if( status != IB_SUCCESS )
+    goto Exit;
+#endif
+
   status = osm_smir_rcv_init(
     &p_sa->smir_rcv,
     &p_sa->resp,
Index: osm/opensm/osm_sa_class_port_info.c
===================================================================
--- osm/opensm/osm_sa_class_port_info.c	(revision 6920)
+++ osm/opensm/osm_sa_class_port_info.c	(working copy)
@@ -219,8 +219,14 @@ __osm_cpi_rcv_respond(
   */
 
   /* Note host notation replaced later */
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
   p_resp_cpi->cap_mask = OSM_CAP_IS_SUBN_GET_SET_NOTICE_SUP |
-                         OSM_CAP_IS_PORT_INFO_CAPMASK_MATCH_SUPPORTED;
+                         OSM_CAP_IS_PORT_INFO_CAPMASK_MATCH_SUPPORTED |
+                         OSM_CAP_IS_MULTIPATH_SUP;
+#else
+  p_resp_cpi->cap_mask = OSM_CAP_IS_SUBN_GET_SET_NOTICE_SUP |
+			 OSM_CAP_IS_PORT_INFO_CAPMASK_MATCH_SUPPORTED;
+#endif
   if (p_rcv->p_subn->opt.no_multicast_option != TRUE)
     p_resp_cpi->cap_mask |= OSM_CAP_IS_UD_MCAST_SUP;
   p_resp_cpi->cap_mask = cl_hton16(p_resp_cpi->cap_mask);
Index: osm/opensm/libopensm.map
===================================================================
--- osm/opensm/libopensm.map	(revision 6920)
+++ osm/opensm/libopensm.map	(working copy)
@@ -21,6 +21,7 @@ OPENSM_1.0 {
 		osm_dump_node_info;
 		osm_dump_node_record;
 		osm_dump_path_record;
+		osm_dump_multipath_record;
 		osm_dump_mc_record;
 		osm_dump_service_record;
 		osm_dump_inform_info;
Index: osm/opensm/osm_sa_mad_ctrl.c
===================================================================
--- osm/opensm/osm_sa_mad_ctrl.c	(revision 6920)
+++ osm/opensm/osm_sa_mad_ctrl.c	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
  * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
  *
@@ -209,6 +209,12 @@ __osm_sa_mad_ctrl_process(
     msg_id = OSM_MSG_MAD_GUIDINFO_RECORD;
     break;
 
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+  case IB_MAD_ATTR_MULTIPATH_RECORD:
+    msg_id = OSM_MSG_MAD_MULTIPATH_RECORD;
+    break;
+#endif
+
   default:
     osm_log( p_ctrl->p_log, OSM_LOG_ERROR,
              "__osm_sa_mad_ctrl_process: ERR 1A01: "
@@ -370,6 +376,9 @@ __osm_sa_mad_ctrl_rcv_callback(
 
   case IB_MAD_METHOD_GET:
   case IB_MAD_METHOD_GETTABLE:
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+  case IB_MAD_METHOD_GETMULTI:
+#endif
   case IB_MAD_METHOD_SET:
   case IB_MAD_METHOD_DELETE:
     __osm_sa_mad_ctrl_process( p_ctrl, p_madw );
Index: osm/opensm/osm_sa_multipath_record_ctrl.c
===================================================================
--- osm/opensm/osm_sa_multipath_record_ctrl.c	(revision 0)
+++ osm/opensm/osm_sa_multipath_record_ctrl.c	(revision 0)
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
+ * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * $Id$
+ */
+
+
+/*
+ * Abstract:
+ *    Implementation of osm_mpr_rcv_ctrl_t.
+ * This object represents the MultiPathRecord request controller object.
+ * This object is part of the opensm family of objects.
+ *
+ * Environment:
+ *    Linux User Mode
+ *
+ */
+
+/*
+  Next available error code: 0x203
+*/
+
+#if defined (VENDOR_RMPP_SUPPORT) && defined (DUAL_SIDED_RMPP)
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <complib/cl_memory.h>
+#include <opensm/osm_sa_multipath_record_ctrl.h>
+#include <opensm/osm_msgdef.h>
+
+/**********************************************************************
+ **********************************************************************/
+static void
+__osm_mpr_rcv_ctrl_disp_callback(
+  IN  void *context,
+  IN  void *p_data )
+{
+  /* ignore return status when invoked via the dispatcher */
+  osm_mpr_rcv_process( ((osm_mpr_rcv_ctrl_t*)context)->p_rcv,
+                      (osm_madw_t*)p_data );
+}
+
+/**********************************************************************
+ **********************************************************************/
+void
+osm_mpr_rcv_ctrl_construct(
+  IN osm_mpr_rcv_ctrl_t* const p_ctrl )
+{
+  cl_memclr( p_ctrl, sizeof(*p_ctrl) );
+  p_ctrl->h_disp = CL_DISP_INVALID_HANDLE;
+}
+
+/**********************************************************************
+ **********************************************************************/
+void
+osm_mpr_rcv_ctrl_destroy(
+  IN osm_mpr_rcv_ctrl_t* const p_ctrl )
+{
+  CL_ASSERT( p_ctrl );
+  cl_disp_unregister( p_ctrl->h_disp );
+}
+
+/**********************************************************************
+ **********************************************************************/
+ib_api_status_t
+osm_mpr_rcv_ctrl_init(
+  IN osm_mpr_rcv_ctrl_t* const p_ctrl,
+  IN osm_mpr_rcv_t* const p_rcv,
+  IN osm_log_t* const p_log,
+  IN cl_dispatcher_t* const p_disp )
+{
+  ib_api_status_t status = IB_SUCCESS;
+
+  OSM_LOG_ENTER( p_log, osm_mpr_rcv_ctrl_init );
+
+  osm_mpr_rcv_ctrl_construct( p_ctrl );
+  p_ctrl->p_log = p_log;
+  p_ctrl->p_rcv = p_rcv;
+  p_ctrl->p_disp = p_disp;
+
+  p_ctrl->h_disp = cl_disp_register(
+    p_disp,
+    OSM_MSG_MAD_MULTIPATH_RECORD,
+    __osm_mpr_rcv_ctrl_disp_callback,
+    p_ctrl );
+
+  if( p_ctrl->h_disp == CL_DISP_INVALID_HANDLE )
+  {
+    osm_log( p_log, OSM_LOG_ERROR,
+             "osm_mpr_rcv_ctrl_init: ERR 4B01: "
+             "Dispatcher registration failed\n" );
+    status = IB_INSUFFICIENT_RESOURCES;
+    goto Exit;
+  }
+
+ Exit:
+  OSM_LOG_EXIT( p_log );
+  return( status );
+}
+
+#endif

Property changes on: osm/opensm/osm_sa_multipath_record_ctrl.c
___________________________________________________________________
Name: svn:keywords
   + Id

Index: osm/opensm/Makefile.am
===================================================================
--- osm/opensm/Makefile.am	(revision 6920)
+++ osm/opensm/Makefile.am	(working copy)
@@ -69,6 +73,7 @@ opensm_SOURCES = main.c osm_console.c os
 		 osm_sa_pkey_record.c osm_sa_pkey_record_ctrl.c \
 		 osm_sa_portinfo_record.c osm_sa_portinfo_record_ctrl.c \
 		 osm_sa_guidinfo_record.c osm_sa_guidinfo_record_ctrl.c \
+		 osm_sa_multipath_record.c osm_sa_multipath_record_ctrl.c \
 		 osm_sa_response.c osm_sa_service_record.c \
 		 osm_sa_service_record_ctrl.c osm_sa_slvl_record.c \
 		 osm_sa_slvl_record_ctrl.c osm_sa_sminfo_record.c \







More information about the general mailing list