[openib-general] [PATCH] Initial CM implementation

Sean Hefty mshefty at ichips.intel.com
Mon Jan 17 13:16:16 PST 2005


The following patch provides an initial CM implementation.  The code builds,
but has not been tested.  It is not included with the build at this time.

General code is in place to send all messages, including SIDR and path
migration, but receive handling is not in place for REJ or MRA.  Error
handling on the receive side is limited to dropping the received MAD.
See todo's for details on missing functionality.

I would definitely welcome any comments on this code.

- Sean

Index: include/ib_cm.h
===================================================================
--- include/ib_cm.h	(revision 1548)
+++ include/ib_cm.h	(working copy)
@@ -51,10 +51,6 @@
 	IB_CM_MRA_REP_SENT,
 	IB_CM_MRA_REP_RCVD,
 	IB_CM_ESTABLISHED,
-	IB_CM_LAP_SENT,
-	IB_CM_LAP_RCVD,
-	IB_CM_MRA_LAP_SENT,
-	IB_CM_MRA_LAP_RCVD,
 	IB_CM_DREQ_SENT,
 	IB_CM_DREQ_RCVD,
 	IB_CM_TIMEWAIT,
@@ -62,32 +58,225 @@
 	IB_CM_SIDR_REQ_RCVD
 };
 
+enum ib_cm_lap_state {
+	IB_CM_LAP_IDLE,
+	IB_CM_LAP_SENT,
+	IB_CM_LAP_RCVD,
+	IB_CM_MRA_LAP_SENT,
+	IB_CM_MRA_LAP_RCVD,
+};
+
 enum ib_cm_event_type {
-	IB_CM_REQ_TIMEOUT,
+	IB_CM_REQ_ERROR,
 	IB_CM_REQ_RECEIVED,
-	IB_CM_REP_TIMEOUT,
+	IB_CM_REP_ERROR,
 	IB_CM_REP_RECEIVED,
 	IB_CM_RTU_RECEIVED,
-	IB_CM_DREQ_TIMEOUT,
+	IB_CM_DREQ_ERROR,
 	IB_CM_DREQ_RECEIVED,
 	IB_CM_DREP_RECEIVED,
 	IB_CM_MRA_RECEIVED,
-	IB_CM_LAP_TIMEOUT,
+	IB_CM_REJ_RECEIVED,
+	IB_CM_LAP_ERROR,
 	IB_CM_LAP_RECEIVED,
-	IB_CM_APR_RECEIVED
+	IB_CM_APR_RECEIVED,
+	IB_CM_SIDR_REQ_ERROR,
+	IB_CM_SIDR_REQ_RECEIVED,
+	IB_CM_SIDR_REP_RECEIVED
+};
+
+enum ib_cm_data_size {
+	IB_CM_REQ_PRIVATE_DATA_SIZE	 = 92,
+	IB_CM_MRA_PRIVATE_DATA_SIZE	 = 222,
+	IB_CM_REJ_PRIVATE_DATA_SIZE	 = 148,
+	IB_CM_REP_PRIVATE_DATA_SIZE	 = 196,
+	IB_CM_RTU_PRIVATE_DATA_SIZE	 = 224,
+	IB_CM_DREQ_PRIVATE_DATA_SIZE	 = 220,
+	IB_CM_DREP_PRIVATE_DATA_SIZE	 = 224,
+	IB_CM_REJ_ARI_LENGTH		 = 72,
+	IB_CM_LAP_PRIVATE_DATA_SIZE	 = 168,
+	IB_CM_APR_PRIVATE_DATA_SIZE	 = 148,
+	IB_CM_APR_INFO_LENGTH		 = 72,
+	IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE = 216,
+	IB_CM_SIDR_REP_PRIVATE_DATA_SIZE = 136,
+	IB_CM_SIDR_REP_INFO_LENGTH	 = 72
+};
+
+struct ib_cm_id;
+
+struct ib_cm_req_event_param {
+	struct ib_cm_id		*listen_id;
+	struct ib_device	*device;
+	u8			port;
+
+	struct ib_sa_path_rec	*primary_path;
+	struct ib_sa_path_rec	*alternate_path;
+
+	u64			remote_ca_guid;
+	u32			remote_qkey;
+	u32			remote_qpn;
+	enum ib_qp_type		qp_type;
+
+	u32	starting_psn;
+	u8	responder_resources;
+	u8	initiator_depth;
+	int	local_cm_response_timeout:5;
+	int	flow_control:1;
+	int	remote_cm_response_timeout:5;
+	int	retry_count:3;
+	int	rnr_retry_count:3;
+	int	srq:1;
+};
+
+struct ib_cm_rep_event_param {
+	u64	remote_ca_guid;
+	u32	remote_qkey;
+	u32	remote_qpn;
+	u32	starting_psn;
+	u8	responder_resources;
+	u8	initiator_depth;
+	int	target_ack_delay:5;
+	int	failover_accepted:2;
+	int	flow_control:1;
+	int	rnr_retry_count:3;
+	int	srq:1;
+};
+
+enum ib_cm_rej_reason {
+	IB_CM_REJ_NO_QP				= __constant_htons(1),
+	IB_CM_REJ_NO_EEC			= __constant_htons(2),
+	IB_CM_REJ_NO_RESOURCES			= __constant_htons(3),
+	IB_CM_REJ_TIMEOUT			= __constant_htons(4),
+	IB_CM_REJ_UNSUPPORTED			= __constant_htons(5),
+	IB_CM_REJ_INVALID_COMM_ID		= __constant_htons(6),
+	IB_CM_REJ_INVALID_COMM_INSTANCE		= __constant_htons(7),
+	IB_CM_REJ_INVALID_SERVICE_ID		= __constant_htons(8),
+	IB_CM_REJ_INVALID_TRANSPORT_TYPE	= __constant_htons(9),
+	IB_CM_REJ_STALE_CONN			= __constant_htons(10),
+	IB_CM_REJ_RDC_NOT_EXIST			= __constant_htons(11),
+	IB_CM_REJ_INVALID_GID			= __constant_htons(12),
+	IB_CM_REJ_INVALID_LID			= __constant_htons(13),
+	IB_CM_REJ_INVALID_SL			= __constant_htons(14),
+	IB_CM_REJ_INVALID_TRAFFIC_CLASS		= __constant_htons(15),
+	IB_CM_REJ_INVALID_HOP_LIMIT		= __constant_htons(16),
+	IB_CM_REJ_INVALID_PACKET_RATE		= __constant_htons(17),
+	IB_CM_REJ_INVALID_ALT_GID		= __constant_htons(18),
+	IB_CM_REJ_INVALID_ALT_LID		= __constant_htons(19),
+	IB_CM_REJ_INVALID_ALT_SL		= __constant_htons(20),
+	IB_CM_REJ_INVALID_ALT_TRAFFIC_CLASS	= __constant_htons(21),
+	IB_CM_REJ_INVALID_ALT_HOP_LIMIT		= __constant_htons(22),
+	IB_CM_REJ_INVALID_ALT_PACKET_RATE	= __constant_htons(23),
+	IB_CM_REJ_PORT_REDIRECT			= __constant_htons(24),
+	IB_CM_REJ_INVALID_MTU			= __constant_htons(26),
+	IB_CM_REJ_INSUFFICIENT_RESP_RESOURCES	= __constant_htons(27),
+	IB_CM_REJ_CONSUMER_DEFINED		= __constant_htons(28),
+	IB_CM_REJ_INVALID_RNR_RETRY		= __constant_htons(29),
+	IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID	= __constant_htons(30),
+	IB_CM_REJ_INVALID_CLASS_VERSION		= __constant_htons(31),
+	IB_CM_REJ_INVALID_FLOW_LABEL		= __constant_htons(32),
+	IB_CM_REJ_INVALID_ALT_FLOW_LABEL	= __constant_htons(33)
+};
+
+struct ib_cm_rej_event_param {
+	enum ib_cm_rej_reason	reason;
+	void			*ari;
+	u8			ari_length;
+};
+
+struct ib_cm_mra_event_param {
+	u8	service_timeout;
+};
+
+struct ib_cm_lap_event_param {
+	struct ib_sa_path_rec	*alternate_path;
+};
+
+enum ib_cm_apr_status {
+	IB_CM_APR_SUCCESS,
+	IB_CM_APR_INVALID_COMM_ID,
+	IB_CM_APR_UNSUPPORTED,
+	IB_CM_APR_REJECT,
+	IB_CM_APR_REDIRECT,
+	IB_CM_APR_IS_CURRENT,
+	IB_CM_APR_INVALID_QPN_EECN,
+	IB_CM_APR_INVALID_LID,
+	IB_CM_APR_INVALID_GID,
+	IB_CM_APR_INVALID_FLOW_LABEL,
+	IB_CM_APR_INVALID_TCLASS,
+	IB_CM_APR_INVALID_HOP_LIMIT,
+	IB_CM_APR_INVALID_PACKET_RATE,
+	IB_CM_APR_INVALID_SL
+};
+
+struct ib_cm_apr_event_param {
+	enum ib_cm_apr_status	ap_status;
+	void			*apr_info;
+	u8			info_len;
+};
+
+struct ib_cm_sidr_req_event_param {
+	struct ib_cm_id		*listen_id;
+	struct ib_device	*device;
+	u8			port;
+
+	u16	pkey;
+};
+
+enum ib_cm_sidr_status {
+	IB_SIDR_SUCCESS,
+	IB_SIDR_UNSUPPORTED,
+	IB_SIDR_REJECT,
+	IB_SIDR_NO_QP,
+	IB_SIDR_REDIRECT,
+	IB_SIDR_UNSUPPORTED_VERSION
+};
+
+struct ib_cm_sidr_rep_event_param {
+	enum ib_cm_sidr_status	status;
+	u32			qkey;
+	u32			qpn;
+	void			*info;
+	u8			info_len;
+
 };
 
 struct ib_cm_event {
-	/* a whole lot more stuff goes here */
-	void			*private_data;
-	u8			private_data_len;
 	enum ib_cm_event_type	event;
-};
+	union {
+		struct ib_cm_req_event_param	req_rcvd;
+		struct ib_cm_rep_event_param	rep_rcvd;
+		/* No data for RTU received events. */
+		struct ib_cm_rej_event_param	rej_rcvd;
+		struct ib_cm_mra_event_param	mra_rcvd;
+		struct ib_cm_lap_event_param	lap_rcvd;
+		struct ib_cm_apr_event_param	apr_rcvd;
+		/* No data for DREQ/DREP received events. */
+		struct ib_cm_sidr_req_event_param sidr_req_rcvd;
+		struct ib_cm_sidr_rep_event_param sidr_rep_rcvd;
+		enum ib_wc_status		send_status;
+	} param;
 
-struct ib_cm_id;
+	void			*private_data;
+};
 
-typedef void (*ib_cm_handler)(struct ib_cm_id *cm_id,
-			      struct ib_cm_event *event);
+/**
+ * ib_cm_handler - User-defined callback to process communication events.
+ * @cm_id: Communication identifier associated with the reported event.
+ * @event: Information about the communication event.
+ *
+ * IB_CM_REQ_RECEIVED and IB_CM_SIDR_REQ_RECEIVED communication events 
+ * generated as a result of listen requests result in the allocation of a 
+ * new @cm_id.  The new @cm_id is returned to the user through this callback.
+ * Clients are responsible for destroying the new @cm_id.  For peer-to-peer
+ * IB_CM_REQ_RECEIVED and all other events, the returned @cm_id corresponds
+ * to a user's existing communication identifier.
+ *
+ * Users may not call ib_destroy_cm_id while in the context of this callback;
+ * however, returning a non-zero value instructs the communication manager to
+ * destroy the @cm_id after the callback completes. 
+ */
+typedef int (*ib_cm_handler)(struct ib_cm_id *cm_id,
+			     struct ib_cm_event *event);
 
 struct ib_cm_id {
 	ib_cm_handler		cm_handler;
@@ -95,16 +284,19 @@
 	u64			service_id;
 	u64			service_mask;
 	enum ib_cm_state	state;
+	enum ib_cm_lap_state	lap_state;
+	u32			local_id;
+	u32			remote_id;
 };
 
 /**
- * ib_create_cm_id - Allocate a connection identifier.
+ * ib_create_cm_id - Allocate a communication identifier.
  * @cm_handler: Callback invoked to notify the user of CM events.
- * @context: User specified context associated with the connection
+ * @context: User specified context associated with the communication
  *   identifier.
  *
- * Connection identifiers are used to track connection states and
- * listen requests.
+ * Communication identifiers are used to track connection states, service
+ * ID resolution requests, and listen requests.
  */
 struct ib_cm_id *ib_create_cm_id(ib_cm_handler cm_handler,
 				 void *context);
@@ -116,15 +308,14 @@
  * This call blocks until the connection identifier is destroyed.
  */
 int ib_destroy_cm_id(struct ib_cm_id *cm_id);
-//*** TBD : add flags to allow calling routine from CM callback...
-//***       or return value from callback to destroy cm_id...
 
 /**
  * ib_cm_listen - Initiates listening on the specified service ID for
  *   connection and service ID resolution requests.
  * @cm_id: Connection identifier associated with the listen request.
  * @service_id: Service identifier matched against incoming connection
- *   and service ID resolution requests.
+ *   and service ID resolution requests.  The service ID should be specified
+ *   network-byte order.
  * @service_mask: Mask applied to service ID used to listen across a
  *   range of service IDs.  If set to 0, the service ID is matched
  *   exactly.
@@ -135,9 +326,10 @@
 
 struct ib_cm_req_param {
 	struct ib_qp		*qp;
-	struct ib_path_record	*primary_path;
-	struct ib_path_record	*alternate_path;
+	struct ib_sa_path_rec	*primary_path;
+	struct ib_sa_path_rec	*alternate_path;
 	u64			service_id;
+	u32			starting_send_psn;
 	void			*private_data;
 	u8			private_data_len;
 	u8			peer_to_peer;
@@ -163,8 +355,9 @@
 
 struct ib_cm_rep_param {
 	struct ib_qp	*qp;
+	u32		starting_send_psn;
 	void		*private_data;
-	u8		reply_private_data_len;
+	u8		private_data_len;
 	u8		responder_resources;
 	u8		initiator_depth;
 	u8		target_ack_delay;
@@ -182,7 +375,7 @@
  *   connection.
  */
 int ib_send_cm_rep(struct ib_cm_id *cm_id,
-		   struct ib_cm_req_param *param);
+		   struct ib_cm_rep_param *param);
 
 /**
  * ib_send_cm_rtu - Sends a connection ready to use message in response
@@ -228,42 +421,7 @@
  * This routine should be invoked by users who receive messages on a
  * connected QP before an RTU has been received.
  */
-int ib_cm_establish(struct ib_cm_id *id);
-
-enum ib_cm_rej_reason {
-	IB_CM_REJ_NO_QP				= __constant_htons(1),
-	IB_CM_REJ_NO_EEC			= __constant_htons(2),
-	IB_CM_REJ_NO_RESOURCES			= __constant_htons(3),
-	IB_CM_REJ_TIMEOUT			= __constant_htons(4),
-	IB_CM_REJ_UNSUPPORTED			= __constant_htons(5),
-	IB_CM_REJ_INVALID_COMM_ID		= __constant_htons(6),
-	IB_CM_REJ_INVALID_COMM_INSTANCE		= __constant_htons(7),
-	IB_CM_REJ_INVALID_SERVICE_ID		= __constant_htons(8),
-	IB_CM_REJ_INVALID_TRANSPORT_TYPE	= __constant_htons(9),
-	IB_CM_REJ_STALE_CONN			= __constant_htons(10),
-	IB_CM_REJ_RDC_NOT_EXIST			= __constant_htons(11),
-	IB_CM_REJ_INVALID_GID			= __constant_htons(12),
-	IB_CM_REJ_INVALID_LID			= __constant_htons(13),
-	IB_CM_REJ_INVALID_SL			= __constant_htons(14),
-	IB_CM_REJ_INVALID_TRAFFIC_CLASS		= __constant_htons(15),
-	IB_CM_REJ_INVALID_HOP_LIMIT		= __constant_htons(16),
-	IB_CM_REJ_INVALID_PACKET_RATE		= __constant_htons(17),
-	IB_CM_REJ_INVALID_ALT_GID		= __constant_htons(18),
-	IB_CM_REJ_INVALID_ALT_LID		= __constant_htons(19),
-	IB_CM_REJ_INVALID_ALT_SL		= __constant_htons(20),
-	IB_CM_REJ_INVALID_ALT_TRAFFIC_CLASS	= __constant_htons(21),
-	IB_CM_REJ_INVALID_ALT_HOP_LIMIT		= __constant_htons(22),
-	IB_CM_REJ_INVALID_ALT_PACKET_RATE	= __constant_htons(23),
-	IB_CM_REJ_PORT_REDIRECT			= __constant_htons(24),
-	IB_CM_REJ_INVALID_MTU			= __constant_htons(26),
-	IB_CM_REJ_INSUFFICIENT_RESP_RESOURCES	= __constant_htons(27),
-	IB_CM_REJ_CONSUMER_DEFINED		= __constant_htons(28),
-	IB_CM_REJ_INVALID_RNR_RETRY		= __constant_htons(29),
-	IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID	= __constant_htons(30),
-	IB_CM_REJ_INVALID_CLASS_VERSION		= __constant_htons(31),
-	IB_CM_REJ_INVALID_FLOW_LABEL		= __constant_htons(32),
-	IB_CM_REJ_INVALID_ALT_FLOW_LABEL	= __constant_htons(33)
-};
+int ib_cm_establish(struct ib_cm_id *cm_id);
 
 /**
  * ib_send_cm_rej - Sends a connection rejection message to the
@@ -310,27 +468,10 @@
  * @private_data_len: Size of the private data buffer, in bytes.
  */
 int ib_send_cm_lap(struct ib_cm_id *cm_id,
-		   struct ib_path_record *alternate_path,
+		   struct ib_sa_path_rec *alternate_path,
 		   void *private_data,
 		   u8 private_data_len);
 
-enum ib_cm_apr_status {
-	IB_CM_APR_SUCCESS,
-	IB_CM_APR_INVALID_COMM_ID,
-	IB_CM_APR_UNSUPPORTED,
-	IB_CM_APR_REJECT,
-	IB_CM_APR_REDIRECT,
-	IB_CM_APR_IS_CURRENT,
-	IB_CM_APR_INVALID_QPN_EECN,
-	IB_CM_APR_INVALID_LID,
-	IB_CM_APR_INVALID_GID,
-	IB_CM_APR_INVALID_FLOW_LABEL,
-	IB_CM_APR_INVALID_TCLASS,
-	IB_CM_APR_INVALID_HOP_LIMIT,
-	IB_CM_APR_INVALID_PACKET_RATE,
-	IB_CM_APR_INVALID_SL
-};
-
 /**
  * ib_send_cm_apr - Sends an alternate path response message in response to
  *   a load alternate path request.
@@ -351,11 +492,12 @@
 		   u8 private_data_len);
 
 struct ib_cm_sidr_req_param {
-	struct ib_path_record	*path;
+	struct ib_sa_path_rec	*path;
 	u64			service_id;
 	int			timeout_ms;
 	void			*private_data;
 	u8			private_data_len;
+	u8			max_cm_retries;
 	u16			pkey;
 };
 
@@ -369,15 +511,6 @@
 int ib_send_cm_sidr_req(struct ib_cm_id *cm_id,
 			struct ib_cm_sidr_req_param *param);
 
-enum ib_cm_sidr_status {
-	IB_SIDR_SUCCESS,
-	IB_SIDR_UNSUPPORTED,
-	IB_SIDR_REJECT,
-	IB_SIDR_NO_QP,
-	IB_SIDR_REDIRECT,
-	IB_SIDR_UNSUPPORTED_VERSION
-};
-
 struct ib_cm_sidr_rep_param {
 	u32			qp_num;
 	u32			qkey;
Index: core/cm.c
===================================================================
--- core/cm.c	(revision 1548)
+++ core/cm.c	(working copy)
@@ -33,11 +33,17 @@
  *
  * $Id$
  */
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/pci.h>
 #include <linux/rbtree.h>
 #include <linux/spinlock.h>
+#include <linux/workqueue.h>
 
+#include <ib_cache.h>
 #include <ib_cm.h>
+#include "cm_msgs.h"
 
 MODULE_AUTHOR("Sean Hefty");
 MODULE_DESCRIPTION("InfiniBand CM");
@@ -54,32 +60,245 @@
 
 static struct ib_cm {
 	spinlock_t lock;
+	/* todo: (high priority) walk through table insertion/removal paths */
 	struct rb_root service_table;
+	struct rb_root remote_qp_table;
+	struct rb_root remote_id_table;
+	struct rb_root remote_sidr_table;
+	struct idr local_id_table;
+	struct workqueue_struct *wq;
 } cm;
 
 struct cm_port {
 	struct ib_mad_agent *mad_agent;
+	u64 ca_guid;
+	spinlock_t lock;
+	struct ib_mr *mr;
+	u8 port_num;
+};
+
+struct cm_msg {
+	struct cm_id_private *cm_id_priv;
+	struct ib_send_wr send_wr;
+	struct ib_sge sge;
+	u8 retry;
+	DECLARE_PCI_UNMAP_ADDR(mapping)
+	struct ib_mad mad;
 };
 
-struct ib_cm_id_private {
+struct cm_id_private {
 	struct ib_cm_id	id;
 
-	struct rb_node node;
+	struct rb_node service_node;
+	struct rb_node remote_qp_node;
+	struct rb_node remote_id_node;
 	spinlock_t lock;
 	wait_queue_head_t wait;
 	atomic_t refcount;
+
+	/* todo: use alternate port on send failure */
+	struct cm_port *port;
+	struct cm_msg *msg;
+
+	struct ib_ah_attr ah_attr;
+	u16 pkey_index;
+	u32 local_qpn;
+	u32 remote_qpn;
+	u64 remote_ca_guid;
+	union ib_gid remote_port_gid;
+	int timeout_ms;
+	u8 max_cm_retries;
+};
+
+struct cm_recv_work {
+	struct work_struct work;
+	struct cm_port *port;
+	struct ib_mad_recv_wc *mad_recv_wc;
+	/* todo: allocate event when allocating work structure */
+	/* struct ib_cm_event cm_event; */
 };
 
-static struct ib_cm_id_private *find_cm_service(u64 service_id)
+static inline void cm_deref_id(struct cm_id_private *cm_id_priv)
+{
+	if (atomic_dec_and_test(&cm_id_priv->refcount))
+		wake_up(&cm_id_priv->wait);
+}
+
+static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
+			struct cm_msg **msg)
+{
+	struct ib_mad_agent *mad_agent;
+	struct cm_msg *m;
+	int ret;
+
+	m = kmalloc(sizeof **msg, GFP_KERNEL);
+	if (!m)
+		return -ENOMEM;
+	memset(m, 0, sizeof *m);
+
+	mad_agent = cm_id_priv->port->mad_agent;
+	m->send_wr.wr.ud.ah = ib_create_ah(mad_agent->qp->pd,
+					   &cm_id_priv->ah_attr);
+	if (IS_ERR(m->send_wr.wr.ud.ah)) {
+		ret = PTR_ERR(m->send_wr.wr.ud.ah);
+		kfree(m);
+		return ret;
+	}
+
+	m->sge.addr = dma_map_single(mad_agent->device->dma_device,
+				     &m->mad, sizeof m->mad,
+				     DMA_TO_DEVICE);
+	pci_unmap_addr_set(msg, mapping, m->sge.addr);
+	m->sge.length = sizeof m->mad;
+	m->sge.lkey = cm_id_priv->port->mr->lkey;
+
+	m->send_wr.wr_id = (unsigned long) msg;
+	m->send_wr.sg_list = &m->sge;
+	m->send_wr.num_sge = 1;
+	m->send_wr.opcode = IB_WR_SEND;
+	m->send_wr.send_flags = IB_SEND_SIGNALED;
+	m->send_wr.wr.ud.mad_hdr = &m->mad.mad_hdr;
+	m->send_wr.wr.ud.remote_qpn = 1;
+	m->send_wr.wr.ud.remote_qkey = IB_QP1_QKEY;
+	m->send_wr.wr.ud.timeout_ms = cm_id_priv->timeout_ms;
+	m->send_wr.wr.ud.pkey_index = cm_id_priv->pkey_index;
+
+	atomic_inc(&cm_id_priv->refcount);
+	m->cm_id_priv = cm_id_priv;
+	*msg = m;
+	return 0;
+}
+
+static void cm_free_msg(struct cm_msg *msg)
+{
+	ib_destroy_ah(msg->send_wr.wr.ud.ah);
+	dma_unmap_single(msg->cm_id_priv->port->mad_agent->device->dma_device,
+			 pci_unmap_addr(msg, mapping), sizeof msg->mad,
+			 DMA_TO_DEVICE);
+
+	cm_deref_id(msg->cm_id_priv);
+	kfree(msg);
+}
+
+static void cm_set_ah_attr(struct ib_ah_attr *ah_attr, u8 port_num,
+			   u16 dlid, u8 sl, u16 src_path_bits)
+{
+	memset(ah_attr, 0, sizeof ah_attr);
+	ah_attr->dlid = be16_to_cpu(dlid);
+	ah_attr->sl = sl;
+	ah_attr->src_path_bits = src_path_bits;
+	ah_attr->port_num = port_num;
+}
+
+static struct cm_port * cm_find_port(struct ib_device *device,
+				     union ib_gid *gid)
+{
+	struct cm_port *port;
+	int ret;
+	u8 p;
+
+	port = (struct cm_port *)ib_get_client_data(device, &cm_client);
+	if (!port)
+		return NULL;
+	
+	ret = ib_find_cached_gid(device, gid, &p, NULL);
+	if (ret)
+		port = NULL;
+	else
+		port = &port[p-1];
+
+	return port;
+}
+
+static int cm_find_device(union ib_gid *gid, struct ib_device **device,
+			  struct cm_port **port)
+{
+	int ret;
+	u8 p;
+
+	/* todo: (high priority if SIDR is needed, low otherwise)
+		write me - need call in ib_cache_* stuff? */
+	/* see static device_list in device.c */
+	/* ret = ib_find_cached_device_gid(gid, device, &p, NULL); */
+	ret = -EINVAL;
+	if (ret)
+		return ret;
+
+	*port = (struct cm_port *)ib_get_client_data(*device, &cm_client);
+	if (!*port)
+		return -EINVAL;
+
+	*port = &(*port[p-1]);
+	return 0;
+}
+
+static int cm_alloc_id(struct cm_id_private *cm_id_priv)
+{
+	unsigned long flags;
+	int ret;
+
+	do {
+		spin_lock_irqsave(&cm.lock, flags);
+		ret = idr_get_new_above(&cm.local_id_table, cm_id_priv, 1,
+					(int *) &cm_id_priv->id);
+		spin_unlock_irqrestore(&cm.lock, flags);
+	} while( (ret == -EAGAIN) && idr_pre_get(&cm.local_id_table, GFP_KERNEL) );
+	return ret;
+}
+
+static void cm_free_id(u32 local_id)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cm.lock, flags);
+	idr_remove(&cm.local_id_table, (int) local_id);
+	spin_unlock_irqrestore(&cm.lock, flags);
+}
+
+static struct cm_id_private * cm_acquire_id_by_local_id(u32 local_id)
+{
+	struct cm_id_private *cm_id_priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cm.lock, flags);
+	cm_id_priv = idr_find(&cm.local_id_table, (int) local_id);
+	if (cm_id_priv)
+		atomic_inc(&cm_id_priv->refcount);
+	spin_unlock_irqrestore(&cm.lock, flags);
+
+	return cm_id_priv;
+}
+
+static void cm_insert_service(struct cm_id_private *cm_id_priv)
+{
+	struct rb_node **link = &cm.service_table.rb_node;
+	struct rb_node *parent = NULL;
+	struct cm_id_private *cur_cm_id_priv;
+	u64 service_id = cm_id_priv->id.service_id;
+
+	while (*link) {
+		parent = *link;
+		cur_cm_id_priv = rb_entry(parent, struct cm_id_private,
+					  service_node);
+		if (service_id < cur_cm_id_priv->id.service_id)
+			link = &(*link)->rb_left;
+		else
+			link = &(*link)->rb_right;
+	}
+	rb_link_node(&cm_id_priv->service_node, parent, link);
+	rb_insert_color(&cm_id_priv->service_node, &cm.service_table);
+}
+
+static struct cm_id_private * cm_find_service(u64 service_id)
 {
 	struct rb_node *node = cm.service_table.rb_node;
-	struct ib_cm_id_private *cm_id_priv;
+	struct cm_id_private *cm_id_priv;
 
 	while (node) {
-		cm_id_priv = rb_entry(node, struct ib_cm_id_private, node);
+		cm_id_priv = rb_entry(node, struct cm_id_private, service_node);
 		if ((cm_id_priv->id.service_mask & service_id) ==
 		    (cm_id_priv->id.service_mask & cm_id_priv->id.service_id))
-		    return cm_id_priv;
+			return cm_id_priv;
 
 		if (service_id < cm_id_priv->id.service_id)
 			node = node->rb_left;
@@ -89,30 +308,229 @@
 	return NULL;
 }
 
-static void insert_cm_service(struct ib_cm_id_private *cm_id_priv)
+static struct cm_id_private * cm_acquire_id_by_service(u64 service_id)
 {
-	struct rb_node **link = &cm.service_table.rb_node;
+	struct cm_id_private *cm_id_priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cm.lock, flags);
+	cm_id_priv = cm_find_service(service_id);
+	if (cm_id_priv)
+		atomic_inc(&cm_id_priv->refcount);
+	spin_unlock_irqrestore(&cm.lock, flags);
+
+	return cm_id_priv;
+}
+
+static struct cm_id_private * cm_insert_remote_id(struct cm_id_private
+						  *cm_id_priv)
+{
+	struct rb_node **link = &cm.remote_id_table.rb_node;
 	struct rb_node *parent = NULL;
-	struct ib_cm_id_private *cur_cm_id_priv;
-	u64 service_id = cm_id_priv->id.service_id;
+	struct cm_id_private *cur_cm_id_priv;
+	u64 remote_ca_guid = cm_id_priv->remote_ca_guid;
+	u32 remote_id = cm_id_priv->id.remote_id;
 
 	while (*link) {
 		parent = *link;
-		cur_cm_id_priv = rb_entry(parent, struct ib_cm_id_private,
-					  node);
-		if (service_id < cur_cm_id_priv->id.service_id)
+		cur_cm_id_priv = rb_entry(parent, struct cm_id_private,
+					  remote_id_node);
+		if (remote_id < cur_cm_id_priv->id.remote_id)
+			link = &(*link)->rb_left;
+		else if (remote_id > cur_cm_id_priv->id.remote_id)
+			link = &(*link)->rb_right;
+		else if (remote_ca_guid < cur_cm_id_priv->remote_ca_guid)
 			link = &(*link)->rb_left;
+		else if (remote_ca_guid > cur_cm_id_priv->remote_ca_guid)
+			link = &(*link)->rb_right;
+		else
+			return cur_cm_id_priv;
+	}
+	rb_link_node(&cm_id_priv->remote_id_node, parent, link);
+	rb_insert_color(&cm_id_priv->remote_id_node, &cm.remote_id_table);
+	return NULL;
+}
+
+static struct cm_id_private * cm_find_id_by_remote_id(u64 remote_ca_guid,
+						      u32 remote_id)
+{
+	struct rb_node *node = cm.remote_id_table.rb_node;
+	struct cm_id_private *cm_id_priv;
+
+	while (node) {
+		cm_id_priv = rb_entry(node, struct cm_id_private, remote_id_node);
+		if (remote_id < cm_id_priv->id.remote_id)
+			node = node->rb_left;
+		else if (remote_id > cm_id_priv->id.remote_id)
+			node = node->rb_right;
+		else if (remote_ca_guid < cm_id_priv->remote_ca_guid)
+			node = node->rb_left;
+		else if (remote_ca_guid > cm_id_priv->remote_ca_guid)
+			node = node->rb_right;
 		else
+			return cm_id_priv;
+	}
+	return NULL;
+}
+
+static struct cm_id_private * cm_acquire_id_by_remote_id(u64 remote_ca_guid,
+							 u32 remote_id)
+{
+	struct cm_id_private *cm_id_priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cm.lock, flags);
+	cm_id_priv = cm_find_id_by_remote_id(remote_ca_guid, remote_id);
+	if (cm_id_priv)
+		atomic_inc(&cm_id_priv->refcount);
+	spin_unlock_irqrestore(&cm.lock, flags);
+
+	return cm_id_priv;
+}
+
+static struct cm_id_private * cm_insert_remote_qpn(struct cm_id_private
+						   *cm_id_priv)
+{
+	struct rb_node **link = &cm.remote_qp_table.rb_node;
+	struct rb_node *parent = NULL;
+	struct cm_id_private *cur_cm_id_priv;
+	union ib_gid *port_gid = &cm_id_priv->remote_port_gid;
+	u32 remote_qpn = cm_id_priv->remote_qpn;
+
+	while (*link) {
+		parent = *link;
+		cur_cm_id_priv = rb_entry(parent, struct cm_id_private,
+					  remote_qp_node);
+		if (remote_qpn < cur_cm_id_priv->remote_qpn)
+			link = &(*link)->rb_left;
+		else if (remote_qpn > cur_cm_id_priv->remote_qpn)
+			link = &(*link)->rb_right;
+		else {
+			int cmp;
+			cmp = memcmp(port_gid, &cur_cm_id_priv->remote_port_gid,
+				     sizeof *port_gid);
+			if (cmp < 0)
+				link = &(*link)->rb_left;
+			else if (cmp > 0)
+				link = &(*link)->rb_right;
+			else
+				return cur_cm_id_priv;
+		}
+	}
+	rb_link_node(&cm_id_priv->remote_qp_node, parent, link);
+	rb_insert_color(&cm_id_priv->remote_qp_node, &cm.remote_qp_table);
+	return NULL;
+}
+
+static struct cm_id_private * cm_find_id_by_remote_qpn(union ib_gid *port_gid,
+						       u32 remote_qpn)
+{
+	struct rb_node *node = cm.remote_qp_table.rb_node;
+	struct cm_id_private *cm_id_priv;
+
+	while (node) {
+		cm_id_priv = rb_entry(node, struct cm_id_private, remote_qp_node);
+
+		if (remote_qpn < cm_id_priv->remote_qpn)
+			node = node->rb_left;
+		else if (remote_qpn > cm_id_priv->remote_qpn)
+			node = node->rb_right;
+		else {
+			int cmp;
+			cmp = memcmp(port_gid, &cm_id_priv->remote_port_gid,
+				     sizeof *port_gid);
+			if (cmp < 0)
+				node = node->rb_left;
+			else if (cmp > 0)
+				node = node->rb_right;
+			else
+				return cm_id_priv;
+		}
+	}
+	return NULL;
+}
+
+static struct cm_id_private * cm_acquire_id_by_remote_qpn(union ib_gid *port_gid,
+							  u32 remote_qpn)
+{
+	struct cm_id_private *cm_id_priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cm.lock, flags);
+	cm_id_priv = cm_find_id_by_remote_qpn(port_gid, remote_qpn);
+	if (cm_id_priv)
+		atomic_inc(&cm_id_priv->refcount);
+	spin_unlock_irqrestore(&cm.lock, flags);
+
+	return cm_id_priv;
+}
+
+static struct cm_id_private * cm_insert_remote_sidr(struct cm_id_private
+						    *cm_id_priv)
+{
+	struct rb_node **link = &cm.remote_sidr_table.rb_node;
+	struct rb_node *parent = NULL;
+	struct cm_id_private *cur_cm_id_priv;
+	union ib_gid *port_gid = &cm_id_priv->remote_port_gid;
+	u32 remote_id = cm_id_priv->id.remote_id;
+
+	while (*link) {
+		parent = *link;
+		cur_cm_id_priv = rb_entry(parent, struct cm_id_private,
+					  remote_id_node);
+		if (remote_id < cur_cm_id_priv->id.remote_id)
+			link = &(*link)->rb_left;
+		else if (remote_id > cur_cm_id_priv->id.remote_id)
 			link = &(*link)->rb_right;
+		else {
+			int cmp;
+			cmp = memcmp(port_gid, &cur_cm_id_priv->remote_port_gid,
+				     sizeof *port_gid);
+			if (cmp < 0)
+				link = &(*link)->rb_left;
+			else if (cmp > 0)
+				link = &(*link)->rb_right;
+			else
+				return cur_cm_id_priv;
+		}
 	}
-	rb_link_node(&cm_id_priv->node, parent, link);
-	rb_insert_color(&cm_id_priv->node, &cm.service_table);
+	rb_link_node(&cm_id_priv->remote_id_node, parent, link);
+	rb_insert_color(&cm_id_priv->remote_id_node, &cm.remote_sidr_table);
+	return NULL;
+}
+
+static struct cm_id_private * cm_find_id_by_remote_sidr(union ib_gid *port_gid,
+							u32 remote_id)
+{
+	struct rb_node *node = cm.remote_sidr_table.rb_node;
+	struct cm_id_private *cm_id_priv;
+
+	while (node) {
+		cm_id_priv = rb_entry(node, struct cm_id_private, remote_id_node);
+		if (remote_id < cm_id_priv->id.remote_id)
+			node = node->rb_left;
+		else if (remote_id > cm_id_priv->id.remote_id)
+			node = node->rb_right;
+		else {
+			int cmp;
+			cmp = memcmp(port_gid, &cm_id_priv->remote_port_gid,
+				     sizeof *port_gid);
+			if (cmp < 0)
+				node = node->rb_left;
+			else if (cmp > 0)
+				node = node->rb_right;
+			else
+				return cm_id_priv;
+		}
+	}
+	return NULL;
 }
 
 struct ib_cm_id *ib_create_cm_id(ib_cm_handler cm_handler,
 				 void *context)
 {
-	struct ib_cm_id_private *cm_id_priv;
+	struct cm_id_private *cm_id_priv;
+	int ret;
 
 	cm_id_priv = kmalloc(sizeof *cm_id_priv, GFP_KERNEL);
 	if (!cm_id_priv)
@@ -122,47 +540,42 @@
 	cm_id_priv->id.state = IB_CM_IDLE;
 	cm_id_priv->id.cm_handler = cm_handler;
 	cm_id_priv->id.context = context;
+	ret = cm_alloc_id(cm_id_priv);
+	if (ret)
+		goto error;
 
 	spin_lock_init(&cm_id_priv->lock);
 	init_waitqueue_head(&cm_id_priv->wait);
 	atomic_set(&cm_id_priv->refcount, 1);
-
 	return &cm_id_priv->id;
-}
-EXPORT_SYMBOL(ib_create_cm_id);
 
-static void reset_cm_state(struct ib_cm_id_private *cm_id_priv)
-{
-	/* reject connections if establishing */
-	/* disconnect established connections */
-	/* update timewait info */
+error:
+	kfree(cm_id_priv);
+	return ERR_PTR(-ENOMEM);
 }
+EXPORT_SYMBOL(ib_create_cm_id);
 
 int ib_destroy_cm_id(struct ib_cm_id *cm_id)
 {
-	struct ib_cm_id_private *cm_id_priv;
+	struct cm_id_private *cm_id_priv;
 	unsigned long flags, flags2;
 
-	cm_id_priv = container_of(cm_id, struct ib_cm_id_private, id);
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
 
 	spin_lock_irqsave(&cm_id_priv->lock, flags);
-	switch(cm_id->state) {
+	/* todo: enter timewait state */
+	switch (cm_id->state) {
 	case IB_CM_LISTEN:
 		spin_lock_irqsave(&cm.lock, flags2);
-		rb_erase(&cm_id_priv->node, &cm.service_table);
-		spin_lock_irqrestore(&cm.lock, flags2);
-		break;
-	case IB_CM_IDLE:
-		break;
-	case IB_CM_TIMEWAIT:
-		break;
+		rb_erase(&cm_id_priv->service_node, &cm.service_table);
+		spin_unlock_irqrestore(&cm.lock, flags2);
 	default:
-		reset_cm_state(cm_id_priv);
 		break;
 	}
 	cm_id->state = IB_CM_IDLE;
 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
 
+	cm_free_id(cm_id->local_id);
 	atomic_dec(&cm_id_priv->refcount);
 	wait_event(cm_id_priv->wait, !atomic_read(&cm_id_priv->refcount));
 	kfree(cm_id_priv);
@@ -174,11 +587,11 @@
 		 u64 service_id,
 		 u64 service_mask)
 {
-	struct ib_cm_id_private *cm_id_priv;
+	struct cm_id_private *cm_id_priv;
 	unsigned long flags;
 	int ret = 0;
 
-	cm_id_priv = container_of(cm_id, struct ib_cm_id_private, id);
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
 	spin_lock_irqsave(&cm_id_priv->lock, flags);
 	if (cm_id->state != IB_CM_IDLE) {
 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
@@ -192,185 +605,1824 @@
 	cm_id->service_mask = service_mask ? service_mask : ~0ULL;
 
 	spin_lock_irqsave(&cm.lock, flags);
-	if (find_cm_service(service_id)) {
-		/* No one else is able to change the cm_id state. */
-		cm_id->state = IB_CM_IDLE;
+	if (cm_find_service(service_id)) {
+		/* No one else is able to change the cm_id_priv state. */
+		cm_id_priv->id.state = IB_CM_IDLE;
 		ret = -EBUSY;
 	} else
-		insert_cm_service(cm_id_priv);
+		cm_insert_service(cm_id_priv);
 	spin_unlock_irqrestore(&cm.lock, flags);
 out:
 	return ret;
 }
 EXPORT_SYMBOL(ib_cm_listen);
 
-int ib_send_cm_req(struct ib_cm_id *cm_id,
-		   struct ib_cm_req_param *param)
+static inline int cm_convert_to_ms(u8 iba_time)
 {
-	return -EINVAL;
+	/* approximate conversion to ms from 4.096us x 2^iba_time */
+	return (1 << ((uint)min(iba_time, (u8) 8) - (uint)8));
 }
-EXPORT_SYMBOL(ib_send_cm_req);
 
-int ib_send_cm_rep(struct ib_cm_id *cm_id,
-		   struct ib_cm_req_param *param)
-{
-	return -EINVAL;
-}
-EXPORT_SYMBOL(ib_send_cm_rep);
+static void cm_format_mad_hdr(struct ib_mad_hdr *hdr,
+			      struct cm_id_private *cm_id_priv,
+			      enum cm_msg_attr_id attr_id,
+			      enum cm_msg_sequence msg_seq)
+{
+	u64 hi_tid, low_tid;
+
+	hdr->base_version  = IB_MGMT_BASE_VERSION;
+	hdr->mgmt_class	   = IB_MGMT_CLASS_CM;
+	hdr->class_version = IB_CM_CLASS_VERSION;
+	hdr->method	   = IB_MGMT_METHOD_SEND;
+	hdr->attr_id	   = attr_id;
+
+	hi_tid   = ((u64) cm_id_priv->port->mad_agent->hi_tid) << 32;
+	low_tid  = (u64) (cm_id_priv->id.local_id | (msg_seq << 30));
+	hdr->tid = cpu_to_be64(hi_tid | low_tid);
+}
+
+static void cm_format_req(struct cm_req_msg *req_msg,
+			  struct cm_id_private *cm_id_priv,
+			  struct ib_cm_req_param *param)
+{
+	cm_format_mad_hdr(&req_msg->hdr, cm_id_priv,
+			  CM_REQ_ATTR_ID, CM_MSG_SEQUENCE_REQ);
+
+	req_msg->local_comm_id = cm_id_priv->id.local_id;
+	req_msg->service_id = param->service_id;
+	req_msg->local_ca_guid = cm_id_priv->port->ca_guid;
+	cm_req_set_local_qpn(req_msg, cpu_to_be32(param->qp->qp_num));
+	cm_req_set_resp_res(req_msg, param->responder_resources);
+	cm_req_set_init_depth(req_msg, param->initiator_depth);
+	cm_req_set_remote_resp_timeout(req_msg,
+				       param->remote_cm_response_timeout);
+	cm_req_set_qp_type(req_msg, param->qp->qp_type);
+	cm_req_set_flow_ctrl(req_msg, param->flow_control);
+	cm_req_set_starting_psn(req_msg, param->starting_send_psn);
+	cm_req_set_local_resp_timeout(req_msg,
+				      param->local_cm_response_timeout);
+	cm_req_set_retry_count(req_msg, param->retry_count);
+	req_msg->pkey = param->primary_path->pkey;
+	cm_req_set_path_mtu(req_msg, param->primary_path->mtu);
+	cm_req_set_rnr_retry_count(req_msg, param->rnr_retry_count);
+	cm_req_set_max_cm_retries(req_msg, param->max_cm_retries);
+	cm_req_set_srq(req_msg, (param->qp->srq != NULL));
+
+	req_msg->primary_local_lid = param->primary_path->slid;
+	req_msg->primary_remote_lid = param->primary_path->dlid;
+	req_msg->primary_local_gid = param->primary_path->sgid;
+	req_msg->primary_remote_gid = param->primary_path->dgid;
+	cm_req_set_primary_flow_label(req_msg, param->primary_path->flow_label);
+	cm_req_set_primary_packet_rate(req_msg, param->primary_path->rate);
+	req_msg->primary_traffic_class = param->primary_path->traffic_class;
+	req_msg->primary_hop_limit = param->primary_path->hop_limit;
+	cm_req_set_primary_sl(req_msg, param->primary_path->sl);
+	cm_req_set_primary_subnet_local(req_msg, 1); /* local only... */
+	cm_req_set_primary_local_ack_timeout(req_msg,
+		min(31, param->primary_path->packet_life_time + 1));
+
+	if (param->alternate_path) {
+		req_msg->alt_local_lid = param->alternate_path->slid;
+		req_msg->alt_remote_lid = param->alternate_path->dlid;
+		req_msg->alt_local_gid = param->alternate_path->sgid;
+		req_msg->alt_remote_gid = param->alternate_path->dgid;
+		cm_req_set_alt_flow_label(req_msg,
+					  param->alternate_path->flow_label);
+		cm_req_set_alt_packet_rate(req_msg, param->alternate_path->rate);
+		req_msg->alt_traffic_class = param->alternate_path->traffic_class;
+		req_msg->alt_hop_limit = param->alternate_path->hop_limit;
+		cm_req_set_alt_sl(req_msg, param->alternate_path->sl);
+		cm_req_set_alt_subnet_local(req_msg, 1); /* local only... */
+		cm_req_set_alt_local_ack_timeout(req_msg,
+			min(31, param->alternate_path->packet_life_time + 1));
+	}
 
-int ib_send_cm_rtu(struct ib_cm_id *cm_id,
-		   void *private_data,
-		   u8 private_data_len)
-{
-	return -EINVAL;
+	if (param->private_data && param->private_data_len)
+		memcpy(req_msg->private_data, param->private_data,
+		       param->private_data_len);
 }
-EXPORT_SYMBOL(ib_send_cm_rtu);
 
-int ib_send_cm_dreq(struct ib_cm_id *cm_id,
-		    void *private_data,
-		    u8 private_data_len)
+static inline int cm_validate_req_param(struct ib_cm_req_param *param)
 {
-	return -EINVAL;
+	if (!param->qp || !param->primary_path)
+		return -EINVAL;
+
+	if (param->qp->qp_type != IB_QPT_RC && param->qp->qp_type != IB_QPT_UC)
+		return -EINVAL;
+
+	if (param->private_data &&
+	    param->private_data_len > IB_CM_REQ_PRIVATE_DATA_SIZE)
+		return -EINVAL;
+
+	return 0;
 }
-EXPORT_SYMBOL(ib_send_cm_dreq);
 
-int ib_send_cm_drep(struct ib_cm_id *cm_id,
-		    void *private_data,
-		    u8 private_data_len)
+int ib_send_cm_req(struct ib_cm_id *cm_id,
+		   struct ib_cm_req_param *param)
 {
-	return -EINVAL;
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	struct ib_sa_path_rec *path;
+	unsigned long flags, flags2;
+	int ret;
+
+	ret = cm_validate_req_param(param);
+	if (ret)
+		goto out;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	cm_id_priv->port = cm_find_port(param->qp->device,
+					&param->primary_path->sgid);
+	if (!cm_id_priv->port) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = ib_find_cached_pkey(param->qp->device,
+				  cm_id_priv->port->port_num,
+				  param->primary_path->pkey,
+				  &cm_id_priv->pkey_index);
+	if (ret)
+		goto out;
+
+	cm_id_priv->timeout_ms = cm_convert_to_ms(
+				    param->primary_path->packet_life_time) * 2 +
+				 cm_convert_to_ms(
+				    param->remote_cm_response_timeout);
+	cm_id_priv->max_cm_retries = param->max_cm_retries;
+	cm_id_priv->local_qpn = cpu_to_be32(param->qp->qp_num);
+	path = param->primary_path;
+	cm_set_ah_attr(&cm_id_priv->ah_attr, cm_id_priv->port->port_num,
+		       path->dlid, path->sl, path->slid & 0x7F);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_req((struct cm_req_msg *)&msg->mad, cm_id_priv, param);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_IDLE)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+	cm_id->state = IB_CM_REQ_SENT;
+	cm_id_priv->msg = msg;
+
+	if (param->peer_to_peer) {
+		spin_lock_irqsave(&cm.lock, flags2);
+		cm_insert_service(cm_id_priv);
+		spin_unlock_irqrestore(&cm.lock, flags2);
+	}
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
 }
-EXPORT_SYMBOL(ib_send_cm_drep);
+EXPORT_SYMBOL(ib_send_cm_req);
 
-int ib_cm_establish(struct ib_cm_id *id)
+static inline int cm_is_active_peer(u64 local_ca_guid, u64 remote_ca_guid,
+				    u32 local_qpn, u32 remote_qpn)
 {
-	return -EINVAL;
+	return (be64_to_cpu(local_ca_guid) > be64_to_cpu(remote_ca_guid) ||
+		((local_ca_guid == remote_ca_guid) &&
+		 (be32_to_cpu(local_qpn) > be32_to_cpu(remote_qpn))));
+}
+
+static inline void cm_format_paths_from_req(struct ib_sa_path_rec *primary_path,
+					    struct ib_sa_path_rec *alt_path,
+					    struct cm_req_msg *req_msg)
+{
+	memset(primary_path, 0, sizeof *primary_path);
+	primary_path->dgid = req_msg->primary_local_gid;
+	primary_path->sgid = req_msg->primary_remote_gid;
+	primary_path->dlid = req_msg->primary_local_lid;
+	primary_path->slid = req_msg->primary_remote_lid;
+	primary_path->flow_label = cm_req_get_primary_flow_label(req_msg);
+	primary_path->hop_limit = req_msg->primary_hop_limit;
+	primary_path->traffic_class = req_msg->primary_traffic_class;
+	primary_path->reversible = 1;
+	primary_path->pkey = req_msg->pkey;
+	primary_path->sl = cm_req_get_primary_sl(req_msg);
+	primary_path->mtu_selector = IB_SA_EQ;
+	primary_path->mtu = cm_req_get_mtu(req_msg);
+	primary_path->rate_selector = IB_SA_EQ;
+	primary_path->rate = cm_req_get_primary_packet_rate(req_msg);
+	primary_path->packet_life_time_selector = IB_SA_EQ;
+	primary_path->packet_life_time =
+		cm_req_get_primary_local_ack_timeout(req_msg);
+
+	memset(alt_path, 0, sizeof *alt_path);
+	alt_path->dgid = req_msg->alt_local_gid;
+	alt_path->sgid = req_msg->alt_remote_gid;
+	alt_path->dlid = req_msg->alt_local_lid;
+	alt_path->slid = req_msg->alt_remote_lid;
+	alt_path->flow_label = cm_req_get_alt_flow_label(req_msg);
+	alt_path->hop_limit = req_msg->alt_hop_limit;
+	alt_path->traffic_class = req_msg->alt_traffic_class;
+	alt_path->reversible = 1;
+	alt_path->pkey = req_msg->pkey;
+	alt_path->sl = cm_req_get_alt_sl(req_msg);
+	alt_path->mtu_selector = IB_SA_EQ;
+	alt_path->mtu = cm_req_get_mtu(req_msg);
+	alt_path->rate_selector = IB_SA_EQ;
+	alt_path->rate = cm_req_get_alt_packet_rate(req_msg);
+	alt_path->packet_life_time_selector = IB_SA_EQ;
+	alt_path->packet_life_time =
+		cm_req_get_alt_local_ack_timeout(req_msg);
+}
+
+static void cm_req_handler(struct cm_port *port,
+			   struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct ib_cm_id *cm_id;
+	struct cm_id_private *cm_id_priv, *cur_cm_id_priv;
+	struct cm_req_msg *req_msg;
+	struct ib_cm_event *cm_event;
+	unsigned long flags, flags2;
+	struct ib_cm_req_event_param *param;
+	struct ib_wc *wc;
+	int ret;
+
+	cm_event = kmalloc(sizeof *cm_event +
+			   sizeof *cm_event->param.req_rcvd.primary_path +
+			   sizeof *cm_event->param.req_rcvd.alternate_path,
+			   GFP_KERNEL);
+	if (!cm_event)
+		return;
+	
+	cm_id = ib_create_cm_id(NULL, NULL);
+	if (IS_ERR(cm_id))
+		goto out;
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+
+	req_msg = (struct cm_req_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv->remote_ca_guid = req_msg->local_ca_guid;
+	cm_id_priv->id.remote_id = req_msg->local_comm_id;
+
+	spin_lock_irqsave(&cm.lock, flags);
+	/* Check for duplicate REQ. */
+	cur_cm_id_priv = cm_find_id_by_remote_id(req_msg->local_ca_guid,
+						 req_msg->local_comm_id);
+	if (cur_cm_id_priv) {
+		spin_unlock_irqrestore(&cm.lock, flags);
+		goto destroy_id; /* Duplicate message. */
+	}
+	/* Find matching listen/peer request. */
+	cur_cm_id_priv = cm_find_service(req_msg->service_id);
+	if (!cur_cm_id_priv) {
+		spin_unlock_irqrestore(&cm.lock, flags);
+		/* todo: reject with no match */
+		goto destroy_id; /* No match. */
+	}
+	spin_lock_irqsave(&cur_cm_id_priv->lock, flags2);
+	if (cur_cm_id_priv->id.state == IB_CM_LISTEN) {
+		atomic_inc(&cur_cm_id_priv->refcount);
+		spin_unlock_irqrestore(&cur_cm_id_priv->lock, flags2);
+		cm_insert_remote_id(cm_id_priv);
+		spin_unlock_irqrestore(&cm.lock, flags);
+
+		cm_id_priv->id.cm_handler = cur_cm_id_priv->id.cm_handler;
+		cm_id_priv->id.context = cur_cm_id_priv->id.context;
+		cm_id_priv->id.service_id = req_msg->service_id;
+		cm_id_priv->id.service_mask = ~0ULL;
+		cm_id_priv->id.state = IB_CM_REQ_RCVD;
+	} else {
+		/* Process peer requests. */
+		if (cm_is_active_peer(port->ca_guid, req_msg->local_ca_guid,
+				      cur_cm_id_priv->local_qpn,
+				      cm_req_get_local_qpn(req_msg))) {
+			spin_unlock_irqrestore(&cm.lock, flags);
+			goto destroy_id; /* Stay active. */
+		}
+		atomic_inc(&cur_cm_id_priv->refcount);
+		cur_cm_id_priv->id.state = IB_CM_REQ_RCVD;
+		spin_unlock_irqrestore(&cur_cm_id_priv->lock, flags2);
+		rb_erase(&cur_cm_id_priv->service_node, &cm.service_table);
+		cur_cm_id_priv->remote_ca_guid = req_msg->local_ca_guid;
+		cur_cm_id_priv->id.remote_id = req_msg->local_comm_id;
+		cm_insert_remote_id(cur_cm_id_priv);
+		spin_unlock_irqrestore(&cm.lock, flags);
+
+		ib_cancel_mad(cur_cm_id_priv->port->mad_agent,
+			      (unsigned long)cur_cm_id_priv->msg);
+		ib_destroy_cm_id(&cm_id_priv->id);
+		cm_id_priv = cur_cm_id_priv;
+	}
+	cm_id_priv->port = port;
+	cm_id_priv->max_cm_retries = cm_req_get_max_cm_retries(req_msg);
+	cm_id_priv->remote_qpn = cm_req_get_local_qpn(req_msg);
+	cm_id_priv->remote_port_gid = req_msg->primary_local_gid;
+	wc = mad_recv_wc->wc;
+	cm_id_priv->pkey_index = wc->pkey_index;
+	cm_set_ah_attr(&cm_id_priv->ah_attr, port->port_num,
+		       wc->slid, wc->sl, wc->dlid_path_bits);
+
+	cm_event->event = IB_CM_REQ_RECEIVED;
+	param = &cm_event->param.req_rcvd;
+	param->listen_id = &cur_cm_id_priv->id;
+	param->device = port->mad_agent->device;
+	param->port = port->port_num;
+	param->primary_path = (struct ib_sa_path_rec *)
+				(u8*)cm_event + sizeof *cm_event;
+	param->alternate_path = (struct ib_sa_path_rec *)
+				(u8*)param->primary_path +
+				sizeof *param->primary_path;
+	cm_format_paths_from_req(param->primary_path, param->alternate_path,
+				 req_msg);
+	param->remote_ca_guid = req_msg->local_ca_guid;
+	param->remote_qkey = be32_to_cpu(req_msg->local_qkey);
+	param->remote_qpn = be32_to_cpu(cm_id_priv->remote_qpn);
+	param->qp_type = cm_req_get_qp_type(req_msg);
+	param->starting_psn = be32_to_cpu(cm_req_get_starting_psn(req_msg));
+	param->responder_resources = cm_req_get_resp_res(req_msg);
+	param->initiator_depth = cm_req_get_init_depth(req_msg);
+	param->local_cm_response_timeout = cm_req_get_remote_resp_timeout(req_msg);
+	param->flow_control = cm_req_get_flow_ctrl(req_msg);
+	param->remote_cm_response_timeout = cm_req_get_local_resp_timeout(req_msg);
+	param->retry_count = cm_req_get_retry_count(req_msg);
+	param->rnr_retry_count = cm_req_get_rnr_retry_count(req_msg);
+	param->srq = cm_req_get_srq(req_msg);
+	cm_event->private_data = &req_msg->private_data;
+	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+	cm_deref_id(cur_cm_id_priv);
+	if (!ret)
+		goto out;
+destroy_id:
+	ib_destroy_cm_id(&cm_id_priv->id);
+out:
+	kfree(cm_event);
 }
-EXPORT_SYMBOL(ib_cm_establish);
 
-int ib_send_cm_rej(struct ib_cm_id *cm_id,
-		   enum ib_cm_rej_reason reason,
-		   void *ari,
-		   u8 ari_length,
-		   void *private_data,
-		   u8 private_data_len)
+static void cm_format_rep(struct cm_rep_msg *rep_msg,
+			  struct cm_id_private *cm_id_priv,
+			  struct ib_cm_rep_param *param)
 {
-	return -EINVAL;
+	cm_format_mad_hdr(&rep_msg->hdr, cm_id_priv,
+			  CM_REP_ATTR_ID, CM_MSG_SEQUENCE_REQ);
+
+	rep_msg->local_comm_id = cm_id_priv->id.local_id;
+	rep_msg->remote_comm_id = cm_id_priv->id.remote_id;
+	cm_rep_set_local_qpn(rep_msg, param->qp->qp_num);
+	cm_rep_set_starting_psn(rep_msg, param->starting_send_psn);
+	rep_msg->resp_resources = param->responder_resources;
+	rep_msg->initiator_depth = param->initiator_depth;
+	cm_rep_set_target_ack_delay(rep_msg, param->target_ack_delay);
+	cm_rep_set_failover(rep_msg, param->failover_accepted);
+	cm_rep_set_flow_ctrl(rep_msg, param->flow_control);
+	cm_rep_set_rnr_retry_count(rep_msg, param->rnr_retry_count);
+	cm_rep_set_srq(rep_msg, (param->qp->srq != NULL));
+	rep_msg->local_ca_guid = cm_id_priv->port->ca_guid;
+
+	if (param->private_data && param->private_data_len)
+		memcpy(rep_msg->private_data, param->private_data,
+		       param->private_data_len);
 }
-EXPORT_SYMBOL(ib_send_cm_rej);
 
-int ib_send_cm_mra(struct ib_cm_id *cm_id,
-		   u8 service_timeout,
-		   void *private_data,
-		   u8 private_data_len)
+static inline int cm_validate_rep_param(struct ib_cm_rep_param *param)
 {
-	return -EINVAL;
+	if (!param->qp)
+		return -EINVAL;
+
+	if (param->qp->qp_type != IB_QPT_RC && param->qp->qp_type != IB_QPT_UC)
+		return -EINVAL;
+
+	if (param->private_data &&
+	    param->private_data_len > IB_CM_REP_PRIVATE_DATA_SIZE)
+		return -EINVAL;
+
+	return 0;
 }
-EXPORT_SYMBOL(ib_send_cm_mra);
 
-int ib_send_cm_lap(struct ib_cm_id *cm_id,
-		   struct ib_path_record *alternate_path,
-		   void *private_data,
-		   u8 private_data_len)
+int ib_send_cm_rep(struct ib_cm_id *cm_id,
+		   struct ib_cm_rep_param *param)
 {
-	return -EINVAL;
-}
-EXPORT_SYMBOL(ib_send_cm_lap);
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
 
-int ib_send_cm_apr(struct ib_cm_id *cm_id,
-		   enum ib_cm_apr_status status,
+	ret = cm_validate_rep_param(param);
+	if (ret)
+		goto out;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_rep((struct cm_rep_msg *)&msg->mad, cm_id_priv, param);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_REQ_RCVD ||
+	    cm_id->state == IB_CM_MRA_REQ_SENT)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	cm_id->state = IB_CM_REP_SENT;
+	cm_id_priv->msg = msg;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(ib_send_cm_rep);
+
+static void cm_format_rtu(struct cm_rtu_msg *rtu_msg,
+			  struct cm_id_private *cm_id_priv,
+			  void *private_data,
+			  u8 private_data_len)
+{
+	cm_format_mad_hdr(&rtu_msg->hdr, cm_id_priv,
+			  CM_RTU_ATTR_ID, CM_MSG_SEQUENCE_REQ);
+
+	rtu_msg->local_comm_id = cm_id_priv->id.local_id;
+	rtu_msg->remote_comm_id = cm_id_priv->id.remote_id;
+
+	if (private_data && private_data_len)
+		memcpy(rtu_msg->private_data, private_data, private_data_len);
+}
+
+static void cm_resend_rtu(struct cm_id_private *cm_id_priv)
+{
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	int ret;
+
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		return;
+
+	cm_format_rtu((struct cm_rtu_msg *)&msg->mad, cm_id_priv, NULL, 0);
+	ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+			       &msg->send_wr, &bad_send_wr);
+	if (ret)
+		cm_free_msg(cm_id_priv->msg);
+}
+
+int ib_send_cm_rtu(struct ib_cm_id *cm_id,
+		   void *private_data,
+		   u8 private_data_len)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if (private_data && private_data_len > IB_CM_RTU_PRIVATE_DATA_SIZE)
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_rtu((struct cm_rtu_msg *)&msg->mad, cm_id_priv,
+		      private_data, private_data_len);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_REP_RCVD ||
+	    cm_id->state == IB_CM_MRA_REP_SENT)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	cm_id->state = IB_CM_ESTABLISHED;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(ib_send_cm_rtu);
+
+static void cm_rep_handler(struct cm_port *port,
+			   struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_rep_msg *rep_msg;
+	struct ib_cm_event *cm_event;
+	struct ib_cm_rep_event_param *param;
+	unsigned long flags;
+	int ret;
+
+	rep_msg = (struct cm_rep_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv = cm_acquire_id_by_local_id(rep_msg->remote_comm_id);
+	if (!cm_id_priv)
+		return;
+
+	cm_event = kmalloc(sizeof *cm_event, GFP_KERNEL);
+	if (!cm_event)
+		goto out;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	switch (cm_id_priv->id.state) {
+	case IB_CM_REQ_SENT:
+	case IB_CM_MRA_REQ_RCVD:
+		break;
+	case IB_CM_ESTABLISHED:
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_resend_rtu(cm_id_priv);
+		goto out;
+	default:
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		goto out;
+	}
+	cm_id_priv->id.state = IB_CM_REP_RCVD;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	cm_id_priv->id.remote_id = rep_msg->local_comm_id;
+	cm_id_priv->remote_ca_guid = rep_msg->local_ca_guid;
+	cm_id_priv->remote_qpn = cm_rep_get_local_qpn(rep_msg);
+
+	cm_event->event = IB_CM_REP_RECEIVED;
+	param = &cm_event->param.rep_rcvd;
+	param->remote_ca_guid = rep_msg->local_ca_guid;
+	param->remote_qkey = be32_to_cpu(rep_msg->local_qkey);
+	param->remote_qpn = be32_to_cpu(cm_id_priv->remote_qpn);
+	param->starting_psn = be32_to_cpu(cm_rep_get_starting_psn(rep_msg));
+	param->responder_resources = rep_msg->resp_resources;
+	param->initiator_depth = rep_msg->initiator_depth;
+	param->target_ack_delay = cm_rep_get_target_ack_delay(rep_msg);
+	param->failover_accepted = cm_rep_get_failover(rep_msg);
+	param->flow_control = cm_rep_get_flow_ctrl(rep_msg);
+	param->rnr_retry_count = cm_rep_get_rnr_retry_count(rep_msg);
+	param->srq = cm_rep_get_srq(rep_msg);
+	cm_event->private_data = &rep_msg->private_data;
+	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+	if (ret) {
+		kfree(cm_event);
+		atomic_dec(&cm_id_priv->refcount);
+		ib_destroy_cm_id(&cm_id_priv->id);
+		return;
+	}
+out:
+	kfree(cm_event);
+	cm_deref_id(cm_id_priv);
+}
+
+static void cm_rtu_handler(struct cm_port *port,
+			   struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_rtu_msg *rtu_msg;
+	struct ib_cm_event *cm_event;
+	unsigned long flags;
+
+	rtu_msg = (struct cm_rtu_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv = cm_acquire_id_by_local_id(rtu_msg->remote_comm_id);
+	if (!cm_id_priv)
+		return;
+
+	cm_event = kmalloc(sizeof *cm_event, GFP_KERNEL);
+	if (!cm_event)
+		goto out;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id_priv->id.state != IB_CM_REP_SENT &&
+	    cm_id_priv->id.state != IB_CM_MRA_REP_RCVD) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		goto out;
+	}
+	cm_id_priv->id.state = IB_CM_ESTABLISHED;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	cm_event->event = IB_CM_RTU_RECEIVED;
+	cm_event->private_data = &rtu_msg->private_data;
+	cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+out:
+	kfree(cm_event);
+	cm_deref_id(cm_id_priv);
+}
+
+int ib_cm_establish(struct ib_cm_id *cm_id)
+{
+	struct cm_id_private *cm_id_priv;
+	unsigned long flags;
+	int ret = 0;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	switch (cm_id->state)
+	{
+	case IB_CM_REP_SENT:
+	case IB_CM_MRA_REP_RCVD:
+		cm_id->state = IB_CM_ESTABLISHED;
+		break;
+	case IB_CM_ESTABLISHED:
+		ret = -EISCONN;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(ib_cm_establish);
+
+static void cm_format_dreq(struct cm_dreq_msg *dreq_msg,
+			  struct cm_id_private *cm_id_priv,
+			  void *private_data,
+			  u8 private_data_len)
+{
+	cm_format_mad_hdr(&dreq_msg->hdr, cm_id_priv,
+			  CM_DREQ_ATTR_ID, CM_MSG_SEQUENCE_DREQ);
+
+	dreq_msg->local_comm_id = cm_id_priv->id.local_id;
+	dreq_msg->remote_comm_id = cm_id_priv->id.remote_id;
+	cm_dreq_set_remote_qpn(dreq_msg, cm_id_priv->remote_qpn);
+
+	if (private_data && private_data_len)
+		memcpy(dreq_msg->private_data, private_data, private_data_len);
+}
+
+int ib_send_cm_dreq(struct ib_cm_id *cm_id,
+		    void *private_data,
+		    u8 private_data_len)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if (private_data && private_data_len > IB_CM_DREQ_PRIVATE_DATA_SIZE)
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_dreq((struct cm_dreq_msg *)&msg->mad, cm_id_priv,
+		       private_data, private_data_len);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_ESTABLISHED)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	cm_id->state = IB_CM_DREQ_SENT;
+	cm_id_priv->msg = msg;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(ib_send_cm_dreq);
+
+static void cm_dreq_handler(struct cm_port *port,
+			    struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_dreq_msg *dreq_msg;
+	struct ib_cm_event *cm_event;
+	unsigned long flags;
+	int ret;
+
+	dreq_msg = (struct cm_dreq_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv = cm_acquire_id_by_local_id(dreq_msg->remote_comm_id);
+	if (!cm_id_priv)
+		return;
+
+	cm_event = kmalloc(sizeof *cm_event, GFP_KERNEL);
+	if (!cm_event)
+		goto out;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id_priv->id.state != IB_CM_ESTABLISHED ||
+	    cm_id_priv->local_qpn != cm_dreq_get_remote_qpn(dreq_msg)) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		/* todo: resend DREP */
+		goto out;
+	}
+	cm_id_priv->id.state = IB_CM_DREQ_RCVD;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	cm_event->event = IB_CM_DREQ_RECEIVED;
+	cm_event->private_data = &dreq_msg->private_data;
+	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+	if (ret) {
+		kfree(cm_event);
+		atomic_dec(&cm_id_priv->refcount);
+		ib_destroy_cm_id(&cm_id_priv->id);
+		return;
+	}
+out:
+	kfree(cm_event);
+	cm_deref_id(cm_id_priv);
+}
+
+static void cm_format_drep(struct cm_drep_msg *drep_msg,
+			  struct cm_id_private *cm_id_priv,
+			  void *private_data,
+			  u8 private_data_len)
+{
+	cm_format_mad_hdr(&drep_msg->hdr, cm_id_priv,
+			  CM_DREP_ATTR_ID, CM_MSG_SEQUENCE_DREQ);
+
+	drep_msg->local_comm_id = cm_id_priv->id.local_id;
+	drep_msg->remote_comm_id = cm_id_priv->id.remote_id;
+
+	if (private_data && private_data_len)
+		memcpy(drep_msg->private_data, private_data, private_data_len);
+}
+
+int ib_send_cm_drep(struct ib_cm_id *cm_id,
+		    void *private_data,
+		    u8 private_data_len)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if (private_data && private_data_len > IB_CM_DREP_PRIVATE_DATA_SIZE)
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_drep((struct cm_drep_msg *)&msg->mad, cm_id_priv,
+		       private_data, private_data_len);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_DREQ_RCVD)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	cm_id->state = IB_CM_TIMEWAIT;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(ib_send_cm_drep);
+
+static void cm_drep_handler(struct cm_port *port,
+			    struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_drep_msg *drep_msg;
+	struct ib_cm_event *cm_event;
+	unsigned long flags;
+	int ret;
+
+	drep_msg = (struct cm_drep_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv = cm_acquire_id_by_local_id(drep_msg->remote_comm_id);
+	if (!cm_id_priv)
+		return;
+
+	cm_event = kmalloc(sizeof *cm_event, GFP_KERNEL);
+	if (!cm_event)
+		goto out;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id_priv->id.state != IB_CM_DREQ_SENT) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		goto out;
+	}
+	cm_id_priv->id.state = IB_CM_TIMEWAIT;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	cm_event->event = IB_CM_DREP_RECEIVED;
+	cm_event->private_data = &drep_msg->private_data;
+	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+	if (ret) {
+		kfree(cm_event);
+		atomic_dec(&cm_id_priv->refcount);
+		ib_destroy_cm_id(&cm_id_priv->id);
+		return;
+	}
+out:
+	kfree(cm_event);
+	cm_deref_id(cm_id_priv);
+}
+
+static void cm_format_rej(struct cm_rej_msg *rej_msg,
+			  struct cm_id_private *cm_id_priv,
+			  enum ib_cm_rej_reason reason,
+			  void *ari,
+			  u8 ari_length,
+			  void *private_data,
+			  u8 private_data_len)
+{
+	cm_format_mad_hdr(&rej_msg->hdr, cm_id_priv,
+			  CM_REJ_ATTR_ID, CM_MSG_SEQUENCE_REQ);
+
+	rej_msg->local_comm_id = cm_id_priv->id.local_id;
+	rej_msg->remote_comm_id = cm_id_priv->id.remote_id;
+
+	switch(cm_id_priv->id.state) {
+	case IB_CM_REQ_RCVD:
+		cm_rej_set_msg_rejected(rej_msg, CM_MSG_RESPONSE_REQ);
+		break;
+	case IB_CM_REP_RCVD:
+		cm_rej_set_msg_rejected(rej_msg, CM_MSG_RESPONSE_REP);
+		break;
+	default:
+		cm_rej_set_msg_rejected(rej_msg, CM_MSG_RESPONSE_OTHER);
+		break;
+	}
+
+	rej_msg->reason = reason;
+	if (ari && ari_length) {
+		cm_rej_set_reject_info_len(rej_msg, ari_length);
+		memcpy(rej_msg->ari, ari, ari_length);
+	}
+
+	if (private_data && private_data_len)
+		memcpy(rej_msg->private_data, private_data, private_data_len);
+}
+
+int ib_send_cm_rej(struct ib_cm_id *cm_id,
+		   enum ib_cm_rej_reason reason,
+		   void *ari,
+		   u8 ari_length,
+		   void *private_data,
+		   u8 private_data_len)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if ((private_data && private_data_len > IB_CM_REJ_PRIVATE_DATA_SIZE) ||
+	    (ari && ari_length > IB_CM_REJ_ARI_LENGTH))
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_rej((struct cm_rej_msg *)&msg->mad, cm_id_priv, reason,
+		      ari, ari_length, private_data, private_data_len);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	switch (cm_id->state) {
+	case IB_CM_REQ_RCVD:
+	case IB_CM_MRA_REQ_SENT:
+	case IB_CM_REP_RCVD:
+	case IB_CM_MRA_REP_SENT:
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	cm_id->state = IB_CM_IDLE;
+	cm_id_priv->msg = msg;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(ib_send_cm_rej);
+
+static void cm_rej_handler(struct cm_port *port,
+			   struct ib_mad_recv_wc *mad_recv_wc)
+{
+	/* todo: write reject handler */
+}
+
+static void cm_format_mra(struct cm_mra_msg *mra_msg,
+			  struct cm_id_private *cm_id_priv,
+			  u8 service_timeout,
+			  void *private_data,
+			  u8 private_data_len)
+{
+	enum cm_msg_sequence msg_sequence;
+	unsigned long flags;
+	enum cm_msg_response msg_mraed;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	switch(cm_id_priv->id.state) {
+	case IB_CM_REQ_RCVD:
+		msg_sequence = CM_MSG_SEQUENCE_REQ;
+		msg_mraed = CM_MSG_RESPONSE_REQ;
+		break;
+	case IB_CM_REP_RCVD:
+		msg_sequence = CM_MSG_SEQUENCE_REQ;
+		msg_mraed = CM_MSG_RESPONSE_REP;
+		break;
+	case IB_CM_ESTABLISHED:
+		msg_sequence = CM_MSG_SEQUENCE_LAP;
+		msg_mraed = CM_MSG_RESPONSE_OTHER;
+		break;
+	default:
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+	cm_format_mad_hdr(&mra_msg->hdr, cm_id_priv,
+			  CM_MRA_ATTR_ID, msg_sequence);
+	cm_mra_set_msg_mraed(mra_msg, msg_mraed);
+
+	mra_msg->local_comm_id = cm_id_priv->id.local_id;
+	mra_msg->remote_comm_id = cm_id_priv->id.remote_id;
+	cm_mra_set_service_timeout(mra_msg, service_timeout);
+
+	if (private_data && private_data_len)
+		memcpy(mra_msg->private_data, private_data, private_data_len);
+}
+
+int ib_send_cm_mra(struct ib_cm_id *cm_id,
+		   u8 service_timeout,
+		   void *private_data,
+		   u8 private_data_len)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if (private_data && private_data_len > IB_CM_MRA_PRIVATE_DATA_SIZE)
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_mra((struct cm_mra_msg *)&msg->mad, cm_id_priv,
+		      service_timeout, private_data, private_data_len);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id_priv->id.state != IB_CM_REQ_RCVD &&
+	    cm_id_priv->id.state != IB_CM_REP_RCVD &&
+	    (cm_id_priv->id.state != IB_CM_ESTABLISHED ||
+	     cm_id_priv->id.lap_state != IB_CM_LAP_RCVD)) {
+
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	} else {
+		ret = -EINVAL;
+	}
+
+	if (ret)  {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	switch (cm_id_priv->id.state) {
+	case IB_CM_REQ_RCVD:
+		cm_id->state = IB_CM_MRA_REQ_SENT;
+		break;
+	case IB_CM_REP_RCVD:
+		cm_id->state = IB_CM_MRA_REP_SENT;
+		break;
+	case IB_CM_ESTABLISHED:
+		cm_id_priv->id.lap_state = IB_CM_MRA_LAP_SENT;
+		break;
+	default:
+		break;
+	}
+	cm_id_priv->msg = msg;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(ib_send_cm_mra);
+
+static void cm_mra_handler(struct cm_port *port,
+			   struct ib_mad_recv_wc *mad_recv_wc)
+{
+	/* todo: write MRA handler */
+	/* todo: add timeout mechanism separate from retries for
+		receiver of MRA */
+}
+
+static void cm_format_lap(struct cm_lap_msg *lap_msg,
+			  struct cm_id_private *cm_id_priv,
+			  struct ib_sa_path_rec *alternate_path,
+			  void *private_data,
+			  u8 private_data_len)
+{
+	cm_format_mad_hdr(&lap_msg->hdr, cm_id_priv,
+			  CM_LAP_ATTR_ID, CM_MSG_SEQUENCE_LAP);
+
+	lap_msg->local_comm_id = cm_id_priv->id.local_id;
+	lap_msg->remote_comm_id = cm_id_priv->id.remote_id;
+	cm_lap_set_remote_qpn(lap_msg, cm_id_priv->remote_qpn);
+	/* todo: need remote CM response timeout */
+	cm_lap_set_remote_resp_timeout(lap_msg, 0x1F);
+	lap_msg->alt_local_lid = alternate_path->slid;
+	lap_msg->alt_remote_lid = alternate_path->dlid;
+	lap_msg->alt_local_gid = alternate_path->sgid;
+	lap_msg->alt_remote_gid = alternate_path->dgid;
+	cm_lap_set_flow_label(lap_msg, alternate_path->flow_label);
+	cm_lap_set_traffic_class(lap_msg, alternate_path->traffic_class);
+	lap_msg->alt_hop_limit = alternate_path->hop_limit;
+	cm_lap_set_packet_rate(lap_msg, alternate_path->rate);
+	cm_lap_set_sl(lap_msg, alternate_path->sl);
+	cm_lap_set_subnet_local(lap_msg, 1); /* local only... */
+	cm_lap_set_local_ack_timeout(lap_msg,
+		min(31, alternate_path->packet_life_time + 1));
+
+	if (private_data && private_data_len)
+		memcpy(lap_msg->private_data, private_data, private_data_len);
+}
+
+int ib_send_cm_lap(struct ib_cm_id *cm_id,
+		   struct ib_sa_path_rec *alternate_path,
+		   void *private_data,
+		   u8 private_data_len)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if (private_data && private_data_len > IB_CM_LAP_PRIVATE_DATA_SIZE)
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_lap((struct cm_lap_msg *)&msg->mad, cm_id_priv,
+		      alternate_path, private_data, private_data_len);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_ESTABLISHED &&
+	    cm_id->lap_state == IB_CM_LAP_IDLE)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	cm_id->lap_state = IB_CM_LAP_SENT;
+	cm_id_priv->msg = msg;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(ib_send_cm_lap);
+
+static void cm_format_path_from_lap(struct ib_sa_path_rec *path,
+				    struct cm_lap_msg *lap_msg)
+{
+	memset(path, 0, sizeof *path);
+	path->dgid = lap_msg->alt_local_gid;
+	path->sgid = lap_msg->alt_remote_gid;
+	path->dlid = lap_msg->alt_local_lid;
+	path->slid = lap_msg->alt_remote_lid;
+	path->flow_label = cm_lap_get_flow_label(lap_msg);
+	path->hop_limit = lap_msg->alt_hop_limit;
+	path->traffic_class = cm_lap_get_traffic_class(lap_msg);
+	path->reversible = 1;
+	/* pkey is same as in REQ */
+	path->sl = cm_lap_get_sl(lap_msg);
+	path->mtu_selector = IB_SA_EQ;
+	/* mtu is same as in REQ */
+	path->rate_selector = IB_SA_EQ;
+	path->rate = cm_lap_get_packet_rate(lap_msg);
+	path->packet_life_time_selector = IB_SA_EQ;
+	path->packet_life_time = cm_lap_get_local_ack_timeout(lap_msg);
+}
+
+static void cm_lap_handler(struct cm_port *port,
+			   struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_lap_msg *lap_msg;
+	struct ib_cm_event *cm_event;
+	unsigned long flags;
+	int ret;
+
+	/* todo: verify LAP request and send reject APR if invalid. */
+	lap_msg = (struct cm_lap_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv = cm_acquire_id_by_local_id(lap_msg->remote_comm_id);
+	if (!cm_id_priv)
+		return;
+
+	cm_event = kmalloc(sizeof *cm_event + 
+			   sizeof *(cm_event->param.lap_rcvd.alternate_path),
+			   GFP_KERNEL);
+	if (!cm_event)
+		goto out;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id_priv->id.state != IB_CM_ESTABLISHED &&
+	    cm_id_priv->id.lap_state != IB_CM_LAP_IDLE) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		goto out;
+	}
+	cm_id_priv->id.lap_state = IB_CM_LAP_RCVD;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	cm_event->event = IB_CM_LAP_RECEIVED;
+	cm_event->param.lap_rcvd.alternate_path = (struct ib_sa_path_rec *)
+							(u8*)cm_event +
+							sizeof *cm_event;
+	cm_format_path_from_lap(cm_event->param.lap_rcvd.alternate_path,
+				lap_msg);
+	cm_event->private_data = &lap_msg->private_data;
+	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+	if (ret) {
+		kfree(cm_event);
+		atomic_dec(&cm_id_priv->refcount);
+		ib_destroy_cm_id(&cm_id_priv->id);
+		return;
+	}
+out:
+	kfree(cm_event);
+	cm_deref_id(cm_id_priv);
+}
+
+static void cm_format_apr(struct cm_apr_msg *apr_msg,
+			  struct cm_id_private *cm_id_priv,
+			  enum ib_cm_apr_status status,
+			  void *info,
+			  u8 info_length,
+			  void *private_data,
+			  u8 private_data_len)
+{
+	cm_format_mad_hdr(&apr_msg->hdr, cm_id_priv,
+			  CM_APR_ATTR_ID, CM_MSG_SEQUENCE_LAP);
+
+	apr_msg->local_comm_id = cm_id_priv->id.local_id;
+	apr_msg->remote_comm_id = cm_id_priv->id.remote_id;
+	apr_msg->ap_status = (u8) status;
+
+	if (info && info_length) {
+		apr_msg->info_length = info_length;
+		memcpy(apr_msg->info, info, info_length);
+	}
+
+	if (private_data && private_data_len)
+		memcpy(apr_msg->private_data, private_data, private_data_len);
+}
+
+int ib_send_cm_apr(struct ib_cm_id *cm_id,
+		   enum ib_cm_apr_status status,
 		   void *info,
 		   u8 info_length,
 		   void *private_data,
 		   u8 private_data_len)
 {
-	return -EINVAL;
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if ((private_data && private_data_len > IB_CM_APR_PRIVATE_DATA_SIZE) ||
+	    (info && info_length > IB_CM_APR_INFO_LENGTH))
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_apr((struct cm_apr_msg *)&msg->mad, cm_id_priv, status,
+		      info, info_length, private_data, private_data_len);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_ESTABLISHED &&
+	    (cm_id->lap_state == IB_CM_LAP_RCVD ||
+	     cm_id->lap_state == IB_CM_MRA_LAP_SENT))
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+
+	cm_id->lap_state = IB_CM_LAP_IDLE;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
 }
 EXPORT_SYMBOL(ib_send_cm_apr);
 
+static void cm_apr_handler(struct cm_port *port,
+			   struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_apr_msg *apr_msg;
+	struct ib_cm_event *cm_event;
+	unsigned long flags;
+
+	apr_msg = (struct cm_apr_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv = cm_acquire_id_by_local_id(apr_msg->remote_comm_id);
+	if (!cm_id_priv)
+		return;	/* Unmatched reply. */
+
+	cm_event = kmalloc(sizeof *cm_event, GFP_KERNEL);
+	if (!cm_event)
+		goto out;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id_priv->id.state != IB_CM_ESTABLISHED || 
+	    (cm_id_priv->id.lap_state != IB_CM_LAP_SENT &&
+	     cm_id_priv->id.lap_state != IB_CM_MRA_LAP_RCVD)) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		goto out;
+	}
+	cm_id_priv->id.lap_state = IB_CM_LAP_IDLE;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	ib_cancel_mad(cm_id_priv->port->mad_agent,
+		      (unsigned long)cm_id_priv->msg);
+
+	cm_event->event = IB_CM_APR_RECEIVED;
+	cm_event->param.apr_rcvd.ap_status = apr_msg->ap_status;
+	cm_event->param.apr_rcvd.apr_info = &apr_msg->info;
+	cm_event->param.apr_rcvd.info_len = apr_msg->info_length;
+	cm_event->private_data = &apr_msg->private_data;
+	cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+out:
+	kfree(cm_event);
+	cm_deref_id(cm_id_priv);
+}
+
+static void cm_format_sidr_req(struct cm_sidr_req_msg *sidr_req_msg,
+			       struct cm_id_private *cm_id_priv,
+			       struct ib_cm_sidr_req_param *param)
+{
+	cm_format_mad_hdr(&sidr_req_msg->hdr, cm_id_priv,
+			  CM_SIDR_REQ_ATTR_ID, CM_MSG_SEQUENCE_SIDR);
+
+	sidr_req_msg->request_id = cm_id_priv->id.local_id;
+	sidr_req_msg->pkey = param->pkey;
+	sidr_req_msg->service_id = param->service_id;
+
+	if (param->private_data && param->private_data_len)
+		memcpy(sidr_req_msg->private_data, param->private_data,
+		       param->private_data_len);
+}
+
 int ib_send_cm_sidr_req(struct ib_cm_id *cm_id,
 			struct ib_cm_sidr_req_param *param)
 {
-	return -EINVAL;
+	struct ib_device *device;
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if (!param->path || (param->private_data && 
+	     param->private_data_len > IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE))
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_find_device(&param->path->sgid, &device, &cm_id_priv->port);
+	if (ret)
+		goto out;
+
+	ret = ib_find_cached_pkey(device, cm_id_priv->port->port_num,
+				  param->path->pkey, &cm_id_priv->pkey_index);
+	if (ret)
+		goto out;
+
+	cm_id_priv->timeout_ms = param->timeout_ms;
+	cm_id_priv->max_cm_retries = param->max_cm_retries;
+	cm_set_ah_attr(&cm_id_priv->ah_attr, cm_id_priv->port->port_num,
+		       param->path->dlid, param->path->sl,
+		       param->path->slid & 0x7F);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_sidr_req((struct cm_sidr_req_msg *)&msg->mad, cm_id_priv,
+			   param);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_IDLE)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+	cm_id->state = IB_CM_SIDR_REQ_SENT;
+	cm_id_priv->msg = msg;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+out:
+	return ret;
 }
 EXPORT_SYMBOL(ib_send_cm_sidr_req);
 
+static void cm_sidr_req_handler(struct cm_port *port,
+				struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct ib_cm_id *cm_id;
+	struct cm_id_private *cm_id_priv, *cur_cm_id_priv;
+	struct cm_sidr_req_msg *sidr_req_msg;
+	struct ib_cm_event *cm_event;
+	struct ib_wc *wc;
+	unsigned long flags;
+	int ret;
+
+	cm_event = kmalloc(sizeof *cm_event, GFP_KERNEL);
+	if (!cm_event)
+		return;
+	
+	cm_id = ib_create_cm_id(NULL, NULL);
+	if (IS_ERR(cm_id))
+		goto out;
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+
+	/* Record SGID/SLID and request ID for lookup. */
+	sidr_req_msg = (struct cm_sidr_req_msg *)mad_recv_wc->recv_buf.mad;
+	wc = mad_recv_wc->wc;
+	cm_id_priv->remote_port_gid.global.subnet_prefix = wc->slid;
+	cm_id_priv->remote_port_gid.global.interface_id = 0;
+	cm_id_priv->id.remote_id = sidr_req_msg->request_id;
+
+	spin_lock_irqsave(&cm.lock, flags);
+	cur_cm_id_priv = cm_insert_remote_sidr(cm_id_priv);
+	if (cur_cm_id_priv) {
+		spin_unlock_irqrestore(&cm.lock, flags);
+		goto destroy_id; /* Duplicate message. */
+	}
+	cur_cm_id_priv = cm_find_service(sidr_req_msg->service_id);
+	if (!cur_cm_id_priv || cur_cm_id_priv->id.state != IB_CM_LISTEN) {
+		rb_erase(&cm_id_priv->remote_id_node, &cm.remote_sidr_table);
+		spin_unlock_irqrestore(&cm.lock, flags);
+		/* todo: reject with no match */
+		goto destroy_id; /* No match. */
+	}
+	atomic_inc(&cur_cm_id_priv->refcount);
+	spin_unlock_irqrestore(&cm.lock, flags);
+
+	cm_id_priv->id.cm_handler = cur_cm_id_priv->id.cm_handler;
+	cm_id_priv->id.context = cur_cm_id_priv->id.context;
+	cm_id_priv->id.service_id = sidr_req_msg->service_id;
+	cm_id_priv->id.service_mask = ~0ULL;
+	cm_id_priv->id.state = IB_CM_SIDR_REQ_RCVD;
+	cm_id_priv->port = port;
+	cm_id_priv->pkey_index = wc->pkey_index;
+	cm_set_ah_attr(&cm_id_priv->ah_attr, port->port_num, wc->slid,
+		       wc->sl, wc->dlid_path_bits);
+
+	cm_event->event = IB_CM_SIDR_REQ_RECEIVED;
+	cm_event->param.sidr_req_rcvd.pkey = sidr_req_msg->pkey;
+	cm_event->param.sidr_req_rcvd.listen_id = &cur_cm_id_priv->id;
+	cm_event->param.sidr_req_rcvd.device = port->mad_agent->device;
+	cm_event->param.sidr_req_rcvd.port = port->port_num;
+	cm_event->private_data = &sidr_req_msg->private_data;
+	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+	cm_deref_id(cur_cm_id_priv);
+	if (!ret)
+		goto out;
+destroy_id:
+	ib_destroy_cm_id(&cm_id_priv->id);
+out:
+	kfree(cm_event);
+}
+
+static void cm_format_sidr_rep(struct cm_sidr_rep_msg *sidr_rep_msg,
+			       struct cm_id_private *cm_id_priv,
+			       struct ib_cm_sidr_rep_param *param)
+{
+	cm_format_mad_hdr(&sidr_rep_msg->hdr, cm_id_priv,
+			  CM_SIDR_REP_ATTR_ID, CM_MSG_SEQUENCE_SIDR);
+
+	sidr_rep_msg->request_id = cm_id_priv->id.remote_id;
+	sidr_rep_msg->status = param->status;
+	cm_sidr_rep_set_qpn(sidr_rep_msg, cpu_to_be32(param->qp_num));
+	sidr_rep_msg->service_id = cm_id_priv->id.service_id;
+	sidr_rep_msg->qkey = cpu_to_be32(param->qkey);
+
+	if (param->info && param->info_length)
+		memcpy(sidr_rep_msg->info, param->info, param->info_length);
+
+	if (param->private_data && param->private_data_len)
+		memcpy(sidr_rep_msg->private_data, param->private_data,
+		       param->private_data_len);
+}
+
 int ib_send_cm_sidr_rep(struct ib_cm_id *cm_id,
 			struct ib_cm_sidr_rep_param *param)
 {
-	return -EINVAL;
+	struct cm_id_private *cm_id_priv;
+	struct cm_msg *msg;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	if ((param->info && param->info_length > IB_CM_SIDR_REP_INFO_LENGTH) ||
+	    (param->private_data &&
+	     param->private_data_len > IB_CM_SIDR_REP_PRIVATE_DATA_SIZE))
+		return -EINVAL;
+
+	cm_id_priv = container_of(cm_id, struct cm_id_private, id);
+	ret = cm_alloc_msg(cm_id_priv, &msg);
+	if (ret)
+		goto out;
+
+	cm_format_sidr_rep((struct cm_sidr_rep_msg *)&msg->mad, cm_id_priv,
+			   param);
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id->state == IB_CM_SIDR_REQ_RCVD)
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+	else
+		ret = -EINVAL;
+
+	if (ret) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(cm_id_priv->msg);
+		goto out;
+	}
+	cm_id->state = IB_CM_IDLE;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	spin_lock_irqsave(&cm.lock, flags);
+	rb_erase(&cm_id_priv->remote_id_node, &cm.remote_sidr_table);
+	spin_unlock_irqrestore(&cm.lock, flags);
+out:
+	return ret;
 }
 EXPORT_SYMBOL(ib_send_cm_sidr_rep);
 
-static void send_handler(struct ib_mad_agent *mad_agent,
+static void cm_sidr_rep_handler(struct cm_port *port,
+				struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_id_private *cm_id_priv;
+	struct cm_sidr_rep_msg *sidr_rep_msg;
+	struct ib_cm_event *cm_event;
+	unsigned long flags;
+	int ret;
+
+	sidr_rep_msg = (struct cm_sidr_rep_msg *)mad_recv_wc->recv_buf.mad;
+	cm_id_priv = cm_acquire_id_by_local_id(sidr_rep_msg->request_id);
+	if (!cm_id_priv)
+		return;	/* Unmatched reply. */
+
+	cm_event = kmalloc(sizeof *cm_event, GFP_KERNEL);
+	if (!cm_event)
+		goto out;
+
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (cm_id_priv->id.state != IB_CM_SIDR_REQ_SENT) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		goto out;
+	}
+	cm_id_priv->id.state = IB_CM_IDLE;
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+
+	ib_cancel_mad(cm_id_priv->port->mad_agent,
+		      (unsigned long)cm_id_priv->msg);
+
+	cm_event->event = IB_CM_SIDR_REP_RECEIVED;
+	cm_event->param.sidr_rep_rcvd.status = sidr_rep_msg->status;
+	cm_event->param.sidr_rep_rcvd.qkey = be32_to_cpu(sidr_rep_msg->qkey);
+	cm_event->param.sidr_rep_rcvd.qpn = be32_to_cpu(cm_sidr_rep_get_qpn(
+								sidr_rep_msg));
+	cm_event->param.sidr_rep_rcvd.info = &sidr_rep_msg->info;
+	cm_event->param.sidr_rep_rcvd.info_len = sidr_rep_msg->info_length;
+	cm_event->private_data = &sidr_rep_msg->private_data;
+	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, cm_event);
+	if (ret) {
+		kfree(cm_event);
+		atomic_dec(&cm_id_priv->refcount);
+		ib_destroy_cm_id(&cm_id_priv->id);
+		return;
+	}
+out:
+	kfree(cm_event);
+	cm_deref_id(cm_id_priv);
+}
+
+static void cm_process_send_error(struct cm_msg *msg,
+				  enum ib_wc_status wc_status)
+{
+	struct cm_id_private *cm_id_priv;
+	struct ib_cm_event cm_event;
+	unsigned long flags, flags2;
+
+	memset(&cm_event, 0, sizeof cm_event);
+	cm_id_priv = msg->cm_id_priv;
+
+	/* Discard old sends or ones without a response. */
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (msg != cm_id_priv->msg)
+		goto discard;
+
+	switch (cm_id_priv->id.state) {
+	case IB_CM_REQ_SENT:
+		cm_id_priv->id.state = IB_CM_IDLE;
+		cm_event.event = IB_CM_REQ_ERROR;
+		break;
+	case IB_CM_REP_SENT:
+		cm_id_priv->id.state = IB_CM_IDLE;
+		cm_event.event = IB_CM_REP_ERROR;
+		break;
+	case IB_CM_DREQ_SENT:
+		cm_id_priv->id.state = IB_CM_TIMEWAIT;
+		cm_event.event = IB_CM_DREQ_ERROR;
+		break;
+	case IB_CM_SIDR_REQ_SENT:
+		cm_id_priv->id.state = IB_CM_IDLE;
+		cm_event.event = IB_CM_SIDR_REQ_ERROR;
+		spin_lock_irqsave(&cm.lock, flags2);
+		rb_erase(&cm_id_priv->remote_id_node, &cm.remote_sidr_table);
+		spin_unlock_irqrestore(&cm.lock, flags2);
+		break;
+	default:
+		goto discard;
+	}
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+	cm_event.param.send_status = wc_status;
+	cm_id_priv->id.cm_handler(&cm_id_priv->id, &cm_event);
+	cm_free_msg(msg);
+	return;
+
+discard:
+	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+	cm_free_msg(msg);
+}
+
+static void cm_process_send_timeout(struct cm_msg *msg)
+{
+	struct cm_id_private *cm_id_priv;
+	struct ib_send_wr *bad_send_wr;
+	unsigned long flags;
+	int ret;
+
+	cm_id_priv = msg->cm_id_priv;
+
+	/* Discard old sends. */
+	spin_lock_irqsave(&cm_id_priv->lock, flags);
+	if (msg != cm_id_priv->msg) {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_free_msg(msg);
+		return;
+	}
+
+	if (msg->retry++ < cm_id_priv->max_cm_retries) {
+		ret = ib_post_send_mad(cm_id_priv->port->mad_agent,
+				       &msg->send_wr, &bad_send_wr);
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		if (ret)
+			cm_process_send_error(msg, IB_WC_GENERAL_ERR);
+	} else {
+		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+		cm_process_send_error(msg, IB_WC_RESP_TIMEOUT_ERR);
+	}
+}
+
+static void cm_send_handler(struct ib_mad_agent *mad_agent,
 			 struct ib_mad_send_wc *mad_send_wc)
 {
-	struct cm_port *port;
+	struct cm_msg *msg;
+
+	msg = (struct cm_msg *)(unsigned long)mad_send_wc->wr_id;
 
-	port = (struct cm_port *)mad_agent->context;
+	switch (mad_send_wc->status) {
+	case IB_WC_SUCCESS:
+	case IB_WC_WR_FLUSH_ERR:
+		cm_free_msg(msg);
+		break;
+	case IB_WC_RESP_TIMEOUT_ERR:
+		cm_process_send_timeout(msg);
+		break;
+	default:
+		cm_process_send_error(msg, mad_send_wc->status);
+		break;
+	}
 }
 
-static void recv_handler(struct ib_mad_agent *mad_agent,
-			 struct ib_mad_recv_wc *mad_recv_wc)
+static void cm_recv_work_handler(void *data)
 {
-	struct cm_port *port;
+	struct cm_recv_work *recv_work = data;
+
+	switch (recv_work->mad_recv_wc->recv_buf.mad->mad_hdr.attr_id) {
+	case CM_REQ_ATTR_ID:
+		cm_req_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_MRA_ATTR_ID:
+		cm_mra_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_REJ_ATTR_ID:
+		cm_rej_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_REP_ATTR_ID:
+		cm_rep_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_RTU_ATTR_ID:
+		cm_rtu_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_DREQ_ATTR_ID:
+		cm_dreq_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_DREP_ATTR_ID:
+		cm_drep_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_SIDR_REQ_ATTR_ID:
+		cm_sidr_req_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_SIDR_REP_ATTR_ID:
+		cm_sidr_rep_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_LAP_ATTR_ID:
+		cm_lap_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	case CM_APR_ATTR_ID:
+		cm_apr_handler(recv_work->port, recv_work->mad_recv_wc);
+		break;
+	default:
+		break;
+	}
+	ib_free_recv_mad(recv_work->mad_recv_wc);
+	kfree(recv_work);
+}
+
+static void cm_recv_handler(struct ib_mad_agent *mad_agent,
+			    struct ib_mad_recv_wc *mad_recv_wc)
+{
+	struct cm_recv_work *recv_work;
 
-	port = (struct cm_port *)mad_agent->context;
+	recv_work = kmalloc(sizeof *recv_work, GFP_KERNEL);
+	if (!recv_work) {
+		ib_free_recv_mad(mad_recv_wc);
+		return;
+	}
+
+	PREPARE_WORK(&recv_work->work, cm_recv_work_handler, recv_work);
+	recv_work->mad_recv_wc = mad_recv_wc;
+	recv_work->port = (struct cm_port *)mad_agent->context;
+	queue_work(cm.wq, &recv_work->work);
+}
+
+static u64 cm_get_ca_guid(struct ib_device *device)
+{
+	struct ib_device_attr *device_attr;
+	u64 guid;
+	int ret;
+
+	device_attr = kmalloc(sizeof *device_attr, GFP_KERNEL);
+	if (!device_attr)
+		return 0;
+
+	ret = ib_query_device(device, device_attr);
+	guid = ret ? 0 : device_attr->node_guid;
+	kfree(device_attr);
+	return guid;
 }
 
 static void cm_add_one(struct ib_device *device)
 {
-	struct cm_port *port;
+	struct cm_port *port_array, *port;
 	struct ib_mad_reg_req reg_req;
+	u64 ca_guid;
 	u8 i;
 
-	port = kmalloc(sizeof *port * device->phys_port_cnt, GFP_KERNEL);
-	if (!port)
-		goto out;
+	ca_guid = cm_get_ca_guid(device);
+	if (!ca_guid)
+		return;
+
+	port_array = kmalloc(sizeof *port * device->phys_port_cnt, GFP_KERNEL);
+	if (!port_array)
+		return;
 
+	/* todo: (high priority) set CM bit in port info */
 	memset(&reg_req, 0, sizeof reg_req);
 	reg_req.mgmt_class = IB_MGMT_CLASS_CM;
 	reg_req.mgmt_class_version = 1;
-	set_bit(IB_MGMT_METHOD_GET, reg_req.method_mask);
-	set_bit(IB_MGMT_METHOD_SET, reg_req.method_mask);
-	set_bit(IB_MGMT_METHOD_GET_RESP, reg_req.method_mask);
 	set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask);
-	for (i = 1; i <= device->phys_port_cnt; i++) {
-		port[i].mad_agent = ib_register_mad_agent(device, i,
-							  IB_QPT_GSI,
-							  &reg_req,
-							  0,
-							  send_handler,
-							  recv_handler,
-							  &port[i]);
+	for (i = 1, port = port_array; i <= device->phys_port_cnt; i++, port++){
+		spin_lock_init(&port->lock);
+		port->ca_guid = ca_guid;
+		port->port_num = i;
+		port->mad_agent = ib_register_mad_agent(device, i,
+							IB_QPT_GSI,
+							&reg_req,
+							0,
+							cm_send_handler,
+							cm_recv_handler,
+							port);
+		if (IS_ERR(port->mad_agent))
+			goto error;
+
+		port->mr = ib_get_dma_mr(port->mad_agent->qp->pd,
+					 IB_ACCESS_LOCAL_WRITE);
+		if (IS_ERR(port->mr)) {
+			ib_unregister_mad_agent(port->mad_agent);
+			goto error;
+		}
 	}
+	ib_set_client_data(device, &cm_client, port_array);
+	return;
 
-out:
-	ib_set_client_data(device, &cm_client, port);
+error:
+	while (port != port_array) {
+		--port;
+		ib_dereg_mr(port->mr);
+		ib_unregister_mad_agent(port->mad_agent);
+	}
+	kfree(port_array);
 }
 
 static void cm_remove_one(struct ib_device *device)
 {
-	struct cm_port *port;
+	struct cm_port *port_array, *port;
 	int i;
 
-	port = (struct cm_port *)ib_get_client_data(device, &cm_client);
-	if (!port)
+	port_array = (struct cm_port *)ib_get_client_data(device, &cm_client);
+	if (!port_array)
 		return;
 
-	for (i = 1; i <= device->phys_port_cnt; i++) {
-		if (!IS_ERR(port[i].mad_agent))
-			ib_unregister_mad_agent(port[i].mad_agent);
+	for (i = 1, port = port_array; i <= device->phys_port_cnt; i++, port++){
+		ib_dereg_mr(port->mr);
+		ib_unregister_mad_agent(port->mad_agent);
 	}
-	kfree(port);
+	kfree(port_array);
 }
 
 static int __init ib_cm_init(void)
 {
+	int ret;
+
 	memset(&cm, 0, sizeof cm);
 	spin_lock_init(&cm.lock);
 	cm.service_table = RB_ROOT;
-	return ib_register_client(&cm_client);
+	cm.remote_id_table = RB_ROOT;
+	cm.remote_qp_table = RB_ROOT;
+	cm.remote_sidr_table = RB_ROOT;
+	idr_init(&cm.local_id_table);
+	idr_pre_get(&cm.local_id_table, GFP_KERNEL);
+
+	cm.wq = create_workqueue("ib_cm");
+	if (!cm.wq)
+		return -ENOMEM;
+
+	ret = ib_register_client(&cm_client);
+	if (ret)
+		destroy_workqueue(cm.wq);
+	return ret;
 }
 
 static void __exit ib_cm_cleanup(void)
Index: core/cm_msgs.h
===================================================================
--- core/cm_msgs.h	(revision 0)
+++ core/cm_msgs.h	(revision 0)
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire 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 the madirectory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use source and binary forms, with or
+ *     withmodification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retathe above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHWARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS THE
+ * SOFTWARE.
+ */
+#if !defined(CM_MSGS_H)
+#define CM_MSGS_H
+
+#include <ib_mad.h>
+
+/*
+ * Parameters to routines below should be in network-byte order, and values
+ * are returned in network-byte order.
+ */
+
+#define IB_CM_CLASS_VERSION	2 /* IB specification 1.2 */
+
+enum cm_msg_attr_id {
+	CM_REQ_ATTR_ID	    = __constant_htons(0x0010),
+	CM_MRA_ATTR_ID	    = __constant_htons(0x0011),
+	CM_REJ_ATTR_ID	    = __constant_htons(0x0012),
+	CM_REP_ATTR_ID	    = __constant_htons(0x0013),
+	CM_RTU_ATTR_ID	    = __constant_htons(0x0014),
+	CM_DREQ_ATTR_ID	    = __constant_htons(0x0015),
+	CM_DREP_ATTR_ID	    = __constant_htons(0x0016),
+	CM_SIDR_REQ_ATTR_ID = __constant_htons(0x0017),
+	CM_SIDR_REP_ATTR_ID = __constant_htons(0x0018),
+	CM_LAP_ATTR_ID      = __constant_htons(0x0019),
+	CM_APR_ATTR_ID      = __constant_htons(0x001A)
+};
+
+enum cm_msg_sequence {
+	CM_MSG_SEQUENCE_REQ,
+	CM_MSG_SEQUENCE_LAP,
+	CM_MSG_SEQUENCE_DREQ,
+	CM_MSG_SEQUENCE_SIDR
+};
+
+struct cm_req_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 rsvd4;
+	u64 service_id;
+	u64 local_ca_guid;
+	u32 rsvd24;
+	u32 local_qkey;
+	/* local QPN:24, responder resources:8 */
+	u32 offset32;
+	/* local EECN:24, initiator depth:8 */
+	u32 offset36;
+	/*
+	 * remote EECN:24, remote CM response timeout:5,
+	 * transport service type:2, end-to-end flow control:1
+	 */
+	u32 offset40;
+	/* starting PSN:24, local CM response timeout:5, retry count:3 */
+	u32 offset44;
+	u16 pkey;
+	/* path MTU:4, RDC exists:1, RNR retry count:3. */
+	u8 offset50;
+	/* max CM Retries:4, SRQ:1, rsvd:3 */
+	u8 offset51;
+
+	u16 primary_local_lid;
+	u16 primary_remote_lid;
+	union ib_gid primary_local_gid;
+	union ib_gid primary_remote_gid;
+	/* flow label:20, rsvd:6, packet rate:6 */
+	u32 primary_offset88;
+	u8 primary_traffic_class;
+	u8 primary_hop_limit;
+	/* SL:4, subnet local:1, rsvd:3 */
+	u8 primary_offset94;
+	/* local ACK timeout:5, rsvd:3 */
+	u8 primary_offset95;
+
+	u16 alt_local_lid;
+	u16 alt_remote_lid;
+	union ib_gid alt_local_gid;
+	union ib_gid alt_remote_gid;
+	/* flow label:20, rsvd:6, packet rate:6 */
+	u32 alt_offset132;
+	u8 alt_traffic_class;
+	u8 alt_hop_limit;
+	/* SL:4, subnet local:1, rsvd:3 */
+	u8 alt_offset138;
+	/* local ACK timeout:5, rsvd:3 */
+	u8 alt_offset139;
+
+	u8 private_data[IB_CM_REQ_PRIVATE_DATA_SIZE];
+
+} __attribute__ ((packed));
+
+static inline u32 cm_req_get_local_qpn(struct cm_req_msg *req_msg)
+{
+	return cpu_to_be32(be32_to_cpu(req_msg->offset32) >> 8);
+}
+
+static inline void cm_req_set_local_qpn(struct cm_req_msg *req_msg, u32 qpn)
+{
+	req_msg->offset32 = cpu_to_be32((be32_to_cpu(qpn) << 8) |
+					 (be32_to_cpu(req_msg->offset32) &
+					  0x000000FF));
+}
+
+static inline u8 cm_req_get_resp_res(struct cm_req_msg *req_msg)
+{
+	return (u8) be32_to_cpu(req_msg->offset32);
+}
+
+static inline void cm_req_set_resp_res(struct cm_req_msg *req_msg, u8 resp_res)
+{
+	req_msg->offset32 = cpu_to_be32(resp_res |
+					(be32_to_cpu(req_msg->offset32) &
+					 0xFFFFFF00));
+}
+
+static inline u8 cm_req_get_init_depth(struct cm_req_msg *req_msg)
+{
+	return (u8) be32_to_cpu(req_msg->offset36);
+}
+
+static inline void cm_req_set_init_depth(struct cm_req_msg *req_msg,
+					 u8 init_depth)
+{
+	req_msg->offset36 = cpu_to_be32(init_depth |
+					(be32_to_cpu(req_msg->offset36) &
+					 0xFFFFFF00));
+}
+
+static inline u8 cm_req_get_remote_resp_timeout(struct cm_req_msg *req_msg)
+{
+	return (u8) ((be32_to_cpu(req_msg->offset40) & 0xF8) >> 3);
+}
+
+static inline void cm_req_set_remote_resp_timeout(struct cm_req_msg *req_msg,
+						  u8 resp_timeout)
+{
+	req_msg->offset40 = cpu_to_be32((resp_timeout << 3) |
+					 (be32_to_cpu(req_msg->offset40) &
+					  0xFFFFFF07));
+}
+
+static inline enum ib_qp_type cm_req_get_qp_type(struct cm_req_msg *req_msg)
+{
+	u8 transport_type = (u8) (be32_to_cpu(req_msg->offset40) & 0x06) >> 1;
+	switch(transport_type) {
+	case 0: return IB_QPT_RC;
+	case 1: return IB_QPT_UC;
+	default: return 0;
+	}
+}
+
+static inline void cm_req_set_qp_type(struct cm_req_msg *req_msg,
+				      enum ib_qp_type qp_type)
+{
+	switch(qp_type) {
+	case IB_QPT_UC:
+		req_msg->offset40 = cpu_to_be32((be32_to_cpu(
+						  req_msg->offset40) &
+						   0xFFFFFFF9) | 0x2);
+	default: 
+		req_msg->offset40 = cpu_to_be32(be32_to_cpu(
+						 req_msg->offset40) &
+						  0xFFFFFFF9);
+	}
+}
+
+static inline u8 cm_req_get_flow_ctrl(struct cm_req_msg *req_msg)
+{
+	return be32_to_cpu(req_msg->offset40) & 0x1;
+}
+
+static inline void cm_req_set_flow_ctrl(struct cm_req_msg *req_msg,
+					u8 flow_ctrl)
+{
+	req_msg->offset40 = cpu_to_be32((flow_ctrl & 0x1) |
+					 (be32_to_cpu(req_msg->offset40) &
+					  0xFFFFFFFE));
+}
+
+static inline u32 cm_req_get_starting_psn(struct cm_req_msg *req_msg)
+{
+	return be32_to_cpu(req_msg->offset44) >> 8;
+}
+
+static inline void cm_req_set_starting_psn(struct cm_req_msg *req_msg,
+					   u32 starting_psn)
+{
+	req_msg->offset44 = cpu_to_be32((be32_to_cpu(starting_psn) << 8) |
+			    (be32_to_cpu(req_msg->offset44) & 0x000000FF));
+}
+
+static inline u8 cm_req_get_local_resp_timeout(struct cm_req_msg *req_msg)
+{
+	return (u8) ((be32_to_cpu(req_msg->offset44) & 0xF8) >> 3);
+}
+
+static inline void cm_req_set_local_resp_timeout(struct cm_req_msg *req_msg,
+						 u8 resp_timeout)
+{
+	req_msg->offset44 = cpu_to_be32((resp_timeout << 3) |
+			    (be32_to_cpu(req_msg->offset44) & 0xFFFFFF07));
+}
+
+static inline u8 cm_req_get_retry_count(struct cm_req_msg *req_msg)
+{
+	return (u8) (be32_to_cpu(req_msg->offset44) & 0x7);
+}
+
+static inline void cm_req_set_retry_count(struct cm_req_msg *req_msg,
+					  u8 retry_count)
+{
+	req_msg->offset44 = cpu_to_be32((retry_count & 0x7) |
+			    (be32_to_cpu(req_msg->offset44) & 0xFFFFFFF8));
+}
+
+static inline u8 cm_req_get_mtu(struct cm_req_msg *req_msg)
+{
+	return req_msg->offset50 >> 4;
+}
+
+static inline void cm_req_set_path_mtu(struct cm_req_msg *req_msg, u8 path_mtu)
+{
+	req_msg->offset50 = (u8) ((req_msg->offset50 & 0xF) | (path_mtu << 4));
+}
+
+static inline u8 cm_req_get_rnr_retry_count(struct cm_req_msg *req_msg)
+{
+	return req_msg->offset50 & 0x7;
+}
+
+static inline void cm_req_set_rnr_retry_count(struct cm_req_msg *req_msg,
+					      u8 rnr_retry_count)
+{
+	req_msg->offset50 = (u8) ((req_msg->offset50 & 0xF8) |
+				  (rnr_retry_count & 0x7));
+}
+
+static inline u8 cm_req_get_max_cm_retries(struct cm_req_msg *req_msg)
+{
+	return req_msg->offset51 >> 4;
+}
+
+static inline void cm_req_set_max_cm_retries(struct cm_req_msg *req_msg,
+					     u8 retries)
+{
+	req_msg->offset51 = (u8) ((req_msg->offset51 & 0xF) | (retries << 4));
+}
+
+static inline u8 cm_req_get_srq(struct cm_req_msg *req_msg)
+{
+	return (req_msg->offset51 & 0x8) >> 3;
+}
+
+static inline void cm_req_set_srq(struct cm_req_msg *req_msg, u8 srq)
+{
+	req_msg->offset51 = (u8) ((req_msg->offset51 & 0xF7) |
+				  ((srq & 0x1) << 3));
+}
+
+static inline u32 cm_req_get_primary_flow_label(struct cm_req_msg *req_msg)
+{
+	return cpu_to_be32((be32_to_cpu(req_msg->primary_offset88) >> 12));
+}
+
+static inline void cm_req_set_primary_flow_label(struct cm_req_msg *req_msg,
+						 u32 flow_label)
+{
+	req_msg->primary_offset88 = cpu_to_be32(
+				    (be32_to_cpu(req_msg->primary_offset88) &
+				     0x00000FFF) |
+				     (be32_to_cpu(flow_label) << 12));
+}
+
+static inline u8 cm_req_get_primary_packet_rate(struct cm_req_msg *req_msg)
+{
+	return (u8) (be32_to_cpu(req_msg->primary_offset88) & 0x3F);
+}
+
+static inline void cm_req_set_primary_packet_rate(struct cm_req_msg *req_msg,
+						  u8 rate)
+{
+	req_msg->primary_offset88 = cpu_to_be32(
+				    (be32_to_cpu(req_msg->primary_offset88) &
+				     0xFFFFFFC0) | (rate & 0x3F));
+}
+
+static inline u8 cm_req_get_primary_sl(struct cm_req_msg *req_msg)
+{
+	return (u8) (req_msg->primary_offset94 >> 4);
+}
+
+static inline void cm_req_set_primary_sl(struct cm_req_msg *req_msg, u8 sl)
+{
+	req_msg->primary_offset94 = (u8) ((req_msg->primary_offset94 & 0x0F) |
+					  (sl << 4));
+}
+
+static inline u8 cm_req_get_primary_subnet_local(struct cm_req_msg *req_msg)
+{
+	return (u8) ((req_msg->primary_offset94 & 0x08) >> 3);
+}
+
+static inline void cm_req_set_primary_subnet_local(struct cm_req_msg *req_msg,
+						   u8 subnet_local)
+{
+	req_msg->primary_offset94 = (u8) ((req_msg->primary_offset94 & 0xF7) |
+					  ((subnet_local & 0x1) << 3));
+}
+
+static inline u8 cm_req_get_primary_local_ack_timeout(struct cm_req_msg *req_msg)
+{
+	return (u8) (req_msg->primary_offset95 >> 3);
+}
+
+static inline void cm_req_set_primary_local_ack_timeout(struct cm_req_msg *req_msg,
+							u8 local_ack_timeout)
+{
+	req_msg->primary_offset95 = (u8) ((req_msg->primary_offset95 & 0x07) |
+					  (local_ack_timeout << 3));
+}
+
+static inline u32 cm_req_get_alt_flow_label(struct cm_req_msg *req_msg)
+{
+	return cpu_to_be32((be32_to_cpu(req_msg->alt_offset132) >> 12));
+}
+
+static inline void cm_req_set_alt_flow_label(struct cm_req_msg *req_msg,
+					     u32 flow_label)
+{
+	req_msg->alt_offset132 = cpu_to_be32(
+				 (be32_to_cpu(req_msg->alt_offset132) &
+				  0x00000FFF) |
+				  (be32_to_cpu(flow_label) << 12));
+}
+
+static inline u8 cm_req_get_alt_packet_rate(struct cm_req_msg *req_msg)
+{
+	return (u8) (be32_to_cpu(req_msg->alt_offset132) & 0x3F);
+}
+
+static inline void cm_req_set_alt_packet_rate(struct cm_req_msg *req_msg,
+					      u8 rate)
+{
+	req_msg->alt_offset132 = cpu_to_be32(
+				 (be32_to_cpu(req_msg->alt_offset132) &
+				  0xFFFFFFC0) | (rate & 0x3F));
+}
+
+static inline u8 cm_req_get_alt_sl(struct cm_req_msg *req_msg)
+{
+	return (u8) (req_msg->alt_offset138 >> 4);
+}
+
+static inline void cm_req_set_alt_sl(struct cm_req_msg *req_msg, u8 sl)
+{
+	req_msg->alt_offset138 = (u8) ((req_msg->alt_offset138 & 0x0F) |
+				       (sl << 4));
+}
+
+static inline u8 cm_req_get_alt_subnet_local(struct cm_req_msg *req_msg)
+{
+	return (u8) ((req_msg->alt_offset138 & 0x08) >> 3);
+}
+
+static inline void cm_req_set_alt_subnet_local(struct cm_req_msg *req_msg,
+					       u8 subnet_local)
+{
+	req_msg->alt_offset138 = (u8) ((req_msg->alt_offset138 & 0xF7) |
+				       ((subnet_local & 0x1) << 3));
+}
+
+static inline u8 cm_req_get_alt_local_ack_timeout(struct cm_req_msg *req_msg)
+{
+	return (u8) (req_msg->alt_offset139 >> 3);
+}
+
+static inline void cm_req_set_alt_local_ack_timeout(struct cm_req_msg *req_msg,
+						    u8 local_ack_timeout)
+{
+	req_msg->alt_offset139 = (u8) ((req_msg->alt_offset139 & 0x07) |
+				       (local_ack_timeout << 3));
+}
+
+/* Message REJected or MRAed */
+enum cm_msg_response {
+	CM_MSG_RESPONSE_REQ = 0x0,
+	CM_MSG_RESPONSE_REP = 0x1,
+	CM_MSG_RESPONSE_OTHER = 0x2
+};
+
+ struct cm_mra_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+	/* message MRAed:2, rsvd:6 */
+	u8 offset8;
+	/* service timeout:5, rsvd:3 */
+	u8 offset9;
+
+	u8 private_data[IB_CM_MRA_PRIVATE_DATA_SIZE];
+
+} __attribute__ ((packed));
+
+static inline u8 cm_mra_get_msg_mraed(struct cm_mra_msg *mra_msg)
+{
+	return (u8) (mra_msg->offset8 >> 6);
+}
+
+static inline void cm_mra_set_msg_mraed(struct cm_mra_msg *mra_msg, u8 msg)
+{
+	mra_msg->offset8 = (u8) ((mra_msg->offset8 & 0x3F) | (msg << 6));
+}
+
+static inline u8 cm_mra_get_service_timeout(struct cm_mra_msg *mra_msg)
+{
+	return (u8) (mra_msg->offset9 >> 3);
+}
+
+static inline void cm_mra_set_service_timeout(struct cm_mra_msg *mra_msg,
+					      u8 service_timeout)
+{
+	mra_msg->offset9 = (u8) ((mra_msg->offset9 & 0x07) |
+				 (service_timeout << 3));
+}
+
+struct cm_rej_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+	/* message REJected:2, rsvd:6 */
+	u8 offset8;
+	/* reject info length:7, rsvd:1. */
+	u8 offset9;
+	u16 reason;
+	u8 ari[IB_CM_REJ_ARI_LENGTH];
+
+	u8 private_data[IB_CM_REJ_PRIVATE_DATA_SIZE];
+
+} __attribute__ ((packed));
+
+static inline u8 cm_rej_get_msg_rejected(struct cm_rej_msg *rej_msg)
+{
+	return (u8) (rej_msg->offset8 >> 6);
+}
+
+static inline void cm_rej_set_msg_rejected(struct cm_rej_msg *rej_msg, u8 msg)
+{
+	rej_msg->offset8 = (u8) ((rej_msg->offset8 & 0x3F) | (msg << 6));
+}
+
+static inline u8 cm_rej_get_reject_info_len(struct cm_rej_msg *rej_msg)
+{
+	return (u8) (rej_msg->offset9 >> 1);
+}
+
+static inline void cm_rej_set_reject_info_len(struct cm_rej_msg *rej_msg,
+					      u8 len)
+{
+	rej_msg->offset9 = (u8) ((rej_msg->offset9 & 0x1) | (len << 1));
+}
+
+struct cm_rep_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+	u32 local_qkey;
+	/* local QPN:24, rsvd:8 */
+	u32 offset12;
+	/* local EECN:24, rsvd:8 */
+	u32 offset16;
+	/* starting PSN:24 rsvd:8 */
+	u32 offset20;
+	u8 resp_resources;
+	u8 initiator_depth;
+	/* target ACK delay:5, failover accepted:2, end-to-end flow control:1 */
+	u8 offset26;
+	/* RNR retry count:3, SRQ:1, rsvd:5 */
+	u8 offset27;
+	u64 local_ca_guid;
+
+	u8 private_data[IB_CM_REP_PRIVATE_DATA_SIZE];
+
+} __attribute__ ((packed));
+
+static inline u32 cm_rep_get_local_qpn(struct cm_rep_msg *rep_msg)
+{
+	return cpu_to_be32(be32_to_cpu(rep_msg->offset12) >> 8);
+}
+
+static inline void cm_rep_set_local_qpn(struct cm_rep_msg *rep_msg, u32 qpn)
+{
+	rep_msg->offset12 = cpu_to_be32((be32_to_cpu(qpn) << 8) |
+			    (be32_to_cpu(rep_msg->offset12) & 0x000000FF));
+}
+
+static inline u32 cm_rep_get_starting_psn(struct cm_rep_msg *rep_msg)
+{
+	return cpu_to_be32(be32_to_cpu(rep_msg->offset20) >> 8);
+}
+
+static inline void cm_rep_set_starting_psn(struct cm_rep_msg *rep_msg,
+					   u32 starting_psn)
+{
+	rep_msg->offset20 = cpu_to_be32((be32_to_cpu(starting_psn) << 8) |
+			    (be32_to_cpu(rep_msg->offset20) & 0x000000FF));
+}
+
+static inline u8 cm_rep_get_target_ack_delay(struct cm_rep_msg *rep_msg)
+{
+	return (u8) (rep_msg->offset26 >> 3);
+}
+
+static inline void cm_rep_set_target_ack_delay(struct cm_rep_msg *rep_msg,
+					       u8 target_ack_delay)
+{
+	rep_msg->offset26 = (u8) ((rep_msg->offset26 & 0x07) |
+				  (target_ack_delay << 3));
+}
+
+static inline u8 cm_rep_get_failover(struct cm_rep_msg *rep_msg)
+{
+	return (u8) ((rep_msg->offset26 & 0x06) >> 1);
+}
+
+static inline void cm_rep_set_failover(struct cm_rep_msg *rep_msg, u8 failover)
+{
+	rep_msg->offset26 = (u8) ((rep_msg->offset26 & 0xF9) |
+				  ((failover & 0x3) << 1));
+}
+
+static inline u8 cm_rep_get_flow_ctrl(struct cm_rep_msg *rep_msg)
+{
+	return (u8) (rep_msg->offset26 & 0x01);
+}
+
+static inline void cm_rep_set_flow_ctrl(struct cm_rep_msg *rep_msg,
+					    u8 flow_ctrl)
+{
+	rep_msg->offset26 = (u8) ((rep_msg->offset26 & 0xFE) |
+				  (flow_ctrl & 0x1));
+}
+
+static inline u8 cm_rep_get_rnr_retry_count(struct cm_rep_msg *rep_msg)
+{
+	return (u8) (rep_msg->offset27 >> 5);
+}
+
+static inline void cm_rep_set_rnr_retry_count(struct cm_rep_msg *rep_msg,
+					      u8 rnr_retry_count)
+{
+	rep_msg->offset27 = (u8) ((rep_msg->offset27 & 0x1F) |
+				  (rnr_retry_count << 5));
+}
+
+static inline u8 cm_rep_get_srq(struct cm_rep_msg *rep_msg)
+{
+	return (u8) ((rep_msg->offset27 >> 4) & 0x1);
+}
+
+static inline void cm_rep_set_srq(struct cm_rep_msg *rep_msg, u8 srq)
+{
+	rep_msg->offset27 = (u8) ((rep_msg->offset27 & 0xEF) |
+				  ((srq & 0x1) << 4));
+}
+
+struct cm_rtu_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+
+	u8 private_data[IB_CM_RTU_PRIVATE_DATA_SIZE];
+
+} __attribute__ ((packed));
+
+struct cm_dreq_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+	/* remote QPN/EECN:24, rsvd:8 */
+	u32 offset8;
+
+	u8 private_data[IB_CM_DREQ_PRIVATE_DATA_SIZE];
+
+} __attribute__ ((packed));
+
+static inline u32 cm_dreq_get_remote_qpn(struct cm_dreq_msg *dreq_msg)
+{
+	return cpu_to_be32(be32_to_cpu(dreq_msg->offset8) >> 8);
+}
+
+static inline void cm_dreq_set_remote_qpn(struct cm_dreq_msg *dreq_msg, u32 qpn)
+{
+	dreq_msg->offset8 = cpu_to_be32((be32_to_cpu(qpn) << 8) |
+			    (be32_to_cpu(dreq_msg->offset8) & 0x000000FF));
+}
+
+struct cm_drep_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+
+	u8 private_data[IB_CM_DREP_PRIVATE_DATA_SIZE];
+
+} __attribute__ ((packed));
+
+struct cm_lap_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+
+	u32 rsvd8;
+	/* remote QPN/EECN:24, remote CM response timeout:5, rsvd:3 */
+	u32 offset12;
+	u32 rsvd16;
+
+	u16 alt_local_lid;
+	u16 alt_remote_lid;
+	union ib_gid alt_local_gid;
+	union ib_gid alt_remote_gid;
+	/* flow label:20, rsvd:4, traffic class:8 */
+	u32 offset56;
+	u8 alt_hop_limit;
+	/* rsvd:2, packet rate:6 */
+	uint8_t offset61;
+	/* SL:4, subnet local:1, rsvd:3 */
+	uint8_t offset62;
+	/* local ACK timeout:5, rsvd:3 */
+	uint8_t offset63;
+
+	u8 private_data[IB_CM_LAP_PRIVATE_DATA_SIZE];
+} __attribute__  ((packed));
+
+static inline u32 cm_lap_get_remote_qpn(struct cm_lap_msg *lap_msg)
+{
+	return cpu_to_be32(be32_to_cpu(lap_msg->offset12) >> 8);
+}
+
+static inline void cm_lap_set_remote_qpn(struct cm_lap_msg *lap_msg, u32 qpn)
+{
+	lap_msg->offset12 = cpu_to_be32((be32_to_cpu(qpn) << 8) |
+					 (be32_to_cpu(lap_msg->offset12) &
+					  0x000000FF));
+}
+
+static inline u8 cm_lap_get_remote_resp_timeout(struct cm_lap_msg *lap_msg)
+{
+	return (u8) ((be32_to_cpu(lap_msg->offset12) & 0xF8) >> 3);
+}
+
+static inline void cm_lap_set_remote_resp_timeout(struct cm_lap_msg *lap_msg,
+						  u8 resp_timeout)
+{
+	lap_msg->offset12 = cpu_to_be32((resp_timeout << 3) |
+					 (be32_to_cpu(lap_msg->offset12) &
+					  0xFFFFFF07));
+}
+
+static inline u32 cm_lap_get_flow_label(struct cm_lap_msg *lap_msg)
+{
+	return be32_to_cpu(lap_msg->offset56) >> 12;
+}
+
+static inline void cm_lap_set_flow_label(struct cm_lap_msg *lap_msg,
+					 u32 flow_label)
+{
+	lap_msg->offset56 = cpu_to_be32((flow_label << 12) |
+					 (be32_to_cpu(lap_msg->offset56) &
+					  0x00000FFF));
+}
+
+static inline u8 cm_lap_get_traffic_class(struct cm_lap_msg *lap_msg)
+{
+	return (u8) be32_to_cpu(lap_msg->offset56);
+}
+
+static inline void cm_lap_set_traffic_class(struct cm_lap_msg *lap_msg,
+					    u8 traffic_class)
+{
+	lap_msg->offset56 = cpu_to_be32(traffic_class |
+					 (be32_to_cpu(lap_msg->offset56) &
+					  0xFFFFFF00));
+}
+
+static inline u8 cm_lap_get_packet_rate(struct cm_lap_msg *lap_msg)
+{
+	return lap_msg->offset61 & 0x3F;
+}
+
+static inline void cm_lap_set_packet_rate(struct cm_lap_msg *lap_msg,
+					  u8 packet_rate)
+{
+	lap_msg->offset61 = (packet_rate & 0x3F) | (lap_msg->offset61 & 0xC0);
+}
+
+static inline u8 cm_lap_get_sl(struct cm_lap_msg *lap_msg)
+{
+	return lap_msg->offset62 >> 4;
+}
+
+static inline void cm_lap_set_sl(struct cm_lap_msg *lap_msg, u8 sl)
+{
+	lap_msg->offset62 = (sl << 4) | (lap_msg->offset62 & 0x0F);
+}
+
+static inline u8 cm_lap_get_subnet_local(struct cm_lap_msg *lap_msg)
+{
+	return (lap_msg->offset62 >> 3) & 0x1;
+}
+
+static inline void cm_lap_set_subnet_local(struct cm_lap_msg *lap_msg,
+					   u8 subnet_local)
+{
+	lap_msg->offset62 = ((subnet_local & 0x1) << 3) |
+			     (lap_msg->offset61 & 0xF7);
+}
+static inline u8 cm_lap_get_local_ack_timeout(struct cm_lap_msg *lap_msg)
+{
+	return lap_msg->offset63 >> 3;
+}
+
+static inline void cm_lap_set_local_ack_timeout(struct cm_lap_msg *lap_msg,
+						u8 local_ack_timeout)
+{
+	lap_msg->offset63 = (local_ack_timeout << 3) |
+			    (lap_msg->offset63 & 0x07);
+}
+
+struct cm_apr_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 local_comm_id;
+	u32 remote_comm_id;
+
+	u8 info_length;
+	u8 ap_status;
+	u8 info[IB_CM_APR_INFO_LENGTH];
+
+	u8 private_data[IB_CM_APR_PRIVATE_DATA_SIZE];
+} __attribute__ ((packed));
+
+struct cm_sidr_req_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 request_id;
+	u16 pkey;
+	u16 rsvd;
+	u64 service_id;
+
+	u8 private_data[IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE];
+} __attribute__ ((packed));
+
+struct cm_sidr_rep_msg {
+	struct ib_mad_hdr hdr;
+
+	u32 request_id;
+	u8 status;
+	u8 info_length;
+	u16 rsvd;
+	/* QPN:24, rsvd:8 */
+	u32 offset8;
+	u64 service_id;
+	u32 qkey;
+	u8 info[IB_CM_SIDR_REP_INFO_LENGTH];
+
+	u8 private_data[IB_CM_SIDR_REP_PRIVATE_DATA_SIZE];
+} __attribute__ ((packed));
+
+static inline u32 cm_sidr_rep_get_qpn(struct cm_sidr_rep_msg *sidr_rep_msg)
+{
+	return cpu_to_be32(be32_to_cpu(sidr_rep_msg->offset8) >> 8);
+}
+
+static inline void cm_sidr_rep_set_qpn(struct cm_sidr_rep_msg *sidr_rep_msg,
+				       u32 qpn)
+{
+	sidr_rep_msg->offset8 = cpu_to_be32((be32_to_cpu(qpn) << 8) |
+					(be32_to_cpu(sidr_rep_msg->offset8) &
+					 0x000000FF));
+}
+
+#endif /* CM_MSGS_H */



More information about the general mailing list