ib_api_status_t
osmv_txn_init_rmpp_sender(IN osm_bind_handle_t h_bind,
			  IN osmv_txn_ctx_t * p_txn, IN osm_madw_t * p_madw)
{
	ib_api_status_t st;

	CL_ASSERT(p_txn);

	/* Double-Sided RMPP Direction Switch */
	osmv_txn_remove_timeout_ev(h_bind, osmv_txn_get_key(p_txn));

	p_txn->rmpp_txfr.rmpp_state = OSMV_TXN_RMPP_SENDER;
	p_txn->rmpp_txfr.p_rmpp_send_ctx = malloc(sizeof(osmv_rmpp_send_ctx_t));

	if (!p_txn->rmpp_txfr.p_rmpp_send_ctx) {
		return IB_INSUFFICIENT_MEMORY;
	}

	memset(p_txn->rmpp_txfr.p_rmpp_send_ctx, 0,
	       sizeof(osmv_rmpp_send_ctx_t));

	st = osmv_rmpp_send_ctx_init(p_txn->rmpp_txfr.p_rmpp_send_ctx,
				     (void *)p_madw->p_mad,
				     p_madw->mad_size, p_txn->p_log);
	return st;
}
Beispiel #2
0
ib_api_status_t
osmv_simple_send_madw(IN osm_bind_handle_t h_bind,
		      IN osm_madw_t * const p_madw,
		      IN osmv_txn_ctx_t * p_txn, IN boolean_t is_retry)
{
	ib_api_status_t ret;
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
	osm_mad_addr_t *p_mad_addr = osm_madw_get_mad_addr_ptr(p_madw);
	uint8_t mad_buf[MAD_BLOCK_SIZE];
	ib_mad_t *p_mad = (ib_mad_t *) mad_buf;
	uint64_t key = 0;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	CL_ASSERT(p_madw->mad_size <= MAD_BLOCK_SIZE);

	memset(p_mad, 0, MAD_BLOCK_SIZE);
	memcpy(p_mad, osm_madw_get_mad_ptr(p_madw), p_madw->mad_size);

	if (NULL != p_txn) {
		/* Push a fake txn id to the MAD */
		key = osmv_txn_get_key(p_txn);
		p_mad->trans_id = cl_hton64(key);
	}

	/*
	   Add call for packet drop randomizer.
	   This is a testing feature. If run_randomizer flag is set to TRUE,
	   the randomizer will be called, and randomally will drop
	   a packet. This is used for simulating unstable fabric.
	 */
	if (p_bo->p_vendor->run_randomizer == TRUE) {
		/* Try the randomizer */
		if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
						p_bo->p_vendor->
						p_pkt_randomizer,
						p_mad) == TRUE) {
			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
				"The MAD will not be sent. \n");
			ret = IB_SUCCESS;
		} else {
			ret =
			    osmv_transport_mad_send(h_bind, p_mad, p_mad_addr);
		}
	} else {
		ret = osmv_transport_mad_send(h_bind, p_mad, p_mad_addr);
	}

	if ((IB_SUCCESS == ret) && (NULL != p_txn) && (!is_retry)) {
		/* Set the timeout for receiving the response MAD */
		ret = osmv_txn_set_timeout_ev(h_bind, key,
					      p_bo->p_vendor->resp_timeout);
	}

	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
	return ret;
}
static void
__osmv_dispatch_simple_mad(IN osm_bind_handle_t h_bind,
			   IN const ib_mad_t * p_mad,
			   IN osmv_txn_ctx_t * p_txn,
			   IN const osm_mad_addr_t * p_mad_addr)
{
	osm_madw_t *p_madw;
	ib_mad_t *p_mad_buf;
	osm_madw_t *p_req_madw = NULL;
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	/* Build the MAD wrapper to be returned to the user.
	 * The actual storage for the MAD is allocated there.
	 */
	p_madw =
	    osm_mad_pool_get(p_bo->p_osm_pool, h_bind, MAD_BLOCK_SIZE,
			     p_mad_addr);

	if (NULL == p_madw) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
			"__osmv_dispatch_simple_mad: ERR 6501: "
			"Out Of Memory - could not allocate a buffer of size %d\n",
			MAD_BLOCK_SIZE);

		goto dispatch_simple_mad_done;
	}

	p_mad_buf = osm_madw_get_mad_ptr(p_madw);
	/* Copy the payload to the MAD buffer */
	memcpy((void *)p_mad_buf, (void *)p_mad, MAD_BLOCK_SIZE);

	if (NULL != p_txn) {
		/* This is a RESPONSE MAD. Pair it with the REQUEST MAD, pass upstream */
		p_req_madw = p_txn->p_madw;
		CL_ASSERT(NULL != p_req_madw);

		p_mad_buf->trans_id = cl_hton64(osmv_txn_get_tid(p_txn));
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"Restoring the original TID to 0x%" PRIx64 "\n",
			cl_ntoh64(p_mad_buf->trans_id));

		/* Reply matched, transaction complete */
		osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
	} else {
		/* This is a REQUEST  MAD. Don't create a context, pass upstream */
	}

	/* Do the job ! */
	p_bo->recv_cb(p_madw, p_bo->cb_context, p_req_madw);

dispatch_simple_mad_done:
	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
}
ib_api_status_t
osmv_txn_init_rmpp_receiver(IN osm_bind_handle_t h_bind,
			    IN osmv_txn_ctx_t * p_txn,
			    IN boolean_t is_init_by_peer)
{
	ib_api_status_t st;
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
	uint64_t key = osmv_txn_get_key(p_txn);

	CL_ASSERT(p_txn);

	/* Double-Sided RMPP Direction Switch */
	osmv_txn_remove_timeout_ev(h_bind, key);

	/* Set the Transaction Timeout value */
	st = osmv_txn_set_timeout_ev(h_bind, key,
				     p_bo->p_vendor->ttime_timeout);
	if (IB_SUCCESS != st) {

		return st;
	}

	p_txn->rmpp_txfr.rmpp_state = OSMV_TXN_RMPP_RECEIVER;
	p_txn->rmpp_txfr.is_rmpp_init_by_peer = is_init_by_peer;

	p_txn->rmpp_txfr.p_rmpp_recv_ctx = malloc(sizeof(osmv_rmpp_recv_ctx_t));

	if (!p_txn->rmpp_txfr.p_rmpp_recv_ctx) {

		osmv_txn_remove_timeout_ev(h_bind, key);
		return IB_INSUFFICIENT_MEMORY;
	}

	memset(p_txn->rmpp_txfr.p_rmpp_recv_ctx, 0,
	       sizeof(osmv_rmpp_recv_ctx_t));

	st = osmv_rmpp_recv_ctx_init(p_txn->rmpp_txfr.p_rmpp_recv_ctx,
				     p_txn->p_log);

	return st;
}
void __osmv_txn_all_done(osm_bind_handle_t h_bind)
{
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
	cl_map_item_t *p_item;
	cl_map_obj_t *p_obj;
	osmv_txn_ctx_t *p_txn;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	p_item = cl_qmap_head(p_bo->txn_mgr.p_txn_map);
	while (p_item != cl_qmap_end(p_bo->txn_mgr.p_txn_map)) {

		p_obj = PARENT_STRUCT(p_item, cl_map_obj_t, item);
		p_txn = (osmv_txn_ctx_t *) cl_qmap_obj(p_obj);
		osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
		free(p_obj);
		/* assuming osmv_txn_done has removed the txn from the map */
		p_item = cl_qmap_head(p_bo->txn_mgr.p_txn_map);
	}

	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
}
ib_api_status_t
__osmv_txnmgr_insert_txn(IN osmv_txn_mgr_t * p_tx_mgr,
			 IN osmv_txn_ctx_t * p_txn, IN uint64_t key)
{
	cl_map_obj_t *p_obj = NULL;
	cl_map_item_t *p_item;
	uint64_t tmp_key;

	CL_ASSERT(p_tx_mgr);
	CL_ASSERT(p_txn);

	key = osmv_txn_get_key(p_txn);
	p_obj = malloc(sizeof(cl_map_obj_t));
	if (NULL == p_obj)
		return IB_INSUFFICIENT_MEMORY;

	osm_log(p_tx_mgr->p_log, OSM_LOG_DEBUG,
		"__osmv_txnmgr_insert_txn: "
		"Inserting key: 0x%llX to map ptr:%p\n", key,
		p_tx_mgr->p_txn_map);

	memset(p_obj, 0, sizeof(cl_map_obj_t));

	cl_qmap_set_obj(p_obj, p_txn);
	/* assuming lookup with this key was made and the result was IB_NOT_FOUND */
	cl_qmap_insert(p_tx_mgr->p_txn_map, key, &p_obj->item);

	p_item = cl_qmap_head(p_tx_mgr->p_txn_map);
	while (p_item != cl_qmap_end(p_tx_mgr->p_txn_map)) {
		tmp_key = cl_qmap_key(p_item);
		osm_log(p_tx_mgr->p_log, OSM_LOG_DEBUG,
			"__osmv_txnmgr_insert_txn: "
			"Found key 0x%llX \n", tmp_key);
		p_item = cl_qmap_next(p_item);
	}

	return IB_SUCCESS;
}
Beispiel #7
0
static ib_api_status_t
__osmv_rmpp_send_segment(IN osm_bind_handle_t h_bind,
			 IN osmv_txn_ctx_t * p_txn, IN uint32_t seg_num)
{
	ib_api_status_t ret;
	osmv_rmpp_send_ctx_t *p_send_ctx;
	uint8_t mad_buf[MAD_BLOCK_SIZE];
	ib_mad_t *p_mad = (ib_mad_t *) mad_buf;
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
	osm_mad_addr_t *p_mad_addr =
	    osm_madw_get_mad_addr_ptr(osmv_txn_get_madw(p_txn));
	uint32_t timeout = p_bo->p_vendor->resp_timeout;
	uint64_t key;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

#ifdef OSMV_RANDOM_DROP
	if (TRUE == osmv_random_drop()) {

		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"Error injection - simulating the RMPP segment drop\n");
		return IB_SUCCESS;
	}
#endif

	p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
	key = osmv_txn_get_key(p_txn);

	if (0 != seg_num) {
		ret =
		    osmv_rmpp_send_ctx_get_seg(p_send_ctx, seg_num, timeout,
					       p_mad);
		CL_ASSERT(IB_SUCCESS == ret);

		/* Put the segment to the wire ! */
		p_mad->trans_id = cl_hton64(key);

		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"Sending RMPP segment #%d, on-wire TID=0x%llX\n",
			seg_num, p_mad->trans_id);

		/*
		   Add call for packet drop randomizer.
		   This is a testing feature. If run_randomizer flag is set to TRUE,
		   the randomizer will be called, and randomally will drop
		   a packet. This is used for simulating unstable fabric.
		 */
		if (p_bo->p_vendor->run_randomizer == TRUE) {
			/* Try the randomizer */
			if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
							p_bo->p_vendor->
							p_pkt_randomizer,
							p_mad) == TRUE) {
				osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
					"The MAD will not be sent. \n");
				ret = IB_SUCCESS;
			} else {
				ret =
				    osmv_transport_mad_send((osm_bind_handle_t)
							    p_bo, p_mad,
							    p_mad_addr);
			}
		} else {
			ret =
			    osmv_transport_mad_send((osm_bind_handle_t) p_bo,
						    p_mad, p_mad_addr);
		}
	} else {
		/* This is an ACK for double-sided handshake. Give it a special treatment. */

		/* It doesn't really matter which data to put. Only the header matters. */
		ret = osmv_rmpp_send_ctx_get_seg(p_send_ctx, 1, timeout, p_mad);
		CL_ASSERT(IB_SUCCESS == ret);

		p_mad->trans_id = cl_hton64(key);
		ret =
		    osmv_rmpp_send_ack((osm_bind_handle_t) p_bo, p_mad,
				       0 /* segnum */ ,
				       OSMV_RMPP_RECV_WIN /* NWL */ ,
				       p_mad_addr);
	}

	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
	return ret;
}
Beispiel #8
0
ib_api_status_t
osmv_rmpp_send_madw(IN osm_bind_handle_t h_bind,
		    IN osm_madw_t * const p_madw,
		    IN osmv_txn_ctx_t * p_txn, IN boolean_t is_rmpp_ds)
{
	ib_api_status_t ret = IB_SUCCESS;
	uint32_t i, total_segs;

	osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx);
	CL_ASSERT(total_segs >= 1);

	/* In the double-sided transfer, wait for ACK 0 */

	for (;;) {

		if (p_send_ctx->window_first > total_segs) {

			/* Every segment is acknowledged */
			break;
		}

		/* Send the next burst. */
		for (i = p_send_ctx->window_first; i <= p_send_ctx->window_last;
		     i++) {

			/* Send a segment and setup a timeout timer */
			ret = __osmv_rmpp_send_segment(h_bind, p_txn, i);
			if (IB_SUCCESS != ret) {
				goto send_done;
			}
		}

		/* Set the Response Timeout for the ACK on the last DATA segment */
		ret = osmv_txn_set_timeout_ev(h_bind, osmv_txn_get_key(p_txn),
					      p_bo->p_vendor->resp_timeout);
		if (IB_SUCCESS != ret) {
			goto send_done;
		}

		/* Going to sleep. Let the others access the transaction DB */
		osmv_txn_unlock(p_bo);

		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"RMPP Sender thread (madw=%p) going to sleep ...\n",
			p_madw);

		/* Await the next event to happen */
		cl_event_wait_on(&p_send_ctx->event,
				 EVENT_NO_TIMEOUT, TRUE /* interruptible */ );

		/* Got a signal from the MAD dispatcher/timeout handler */
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"RMPP Sender thread (madw=%p) waking up on a signal ...\n",
			p_madw);

		/* Let's see what changed... Make this atomic - re-acquire the lock. */
		osmv_txn_lock(p_bo);

		if (TRUE == p_bo->is_closing) {
			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
				"osmv_rmpp_send_madw: ERR 6601: "
				"The bind handle %p is being closed. "
				"Stopping the RMPP Send of MADW %p\n",
				h_bind, p_madw);

			ret = IB_TIMEOUT;
			return IB_INTERRUPTED;
		}

		/* STOP? ABORT? TIMEOUT? */
		if (IB_SUCCESS != p_send_ctx->status) {
			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
				"osmv_rmpp_send_madw: ERR 6602: "
				"An error (%s) happened during the RMPP send of %p. Bailing out.\n",
				ib_get_err_str(p_send_ctx->status), p_madw);
			ret = p_send_ctx->status;
			goto send_done;
		}
	}

	if (TRUE == is_rmpp_ds) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"Double-sided RMPP - switching to be the receiver.\n");

		ret = osmv_txn_init_rmpp_receiver(h_bind, p_txn, FALSE
						  /*Send was initiated by me */
						  );

		if (IB_SUCCESS == ret) {
			/* Send ACK on the 0 segment */
			ret = __osmv_rmpp_send_segment(h_bind, p_txn, 0);
		}
	}

send_done:
	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
	return ret;
}
ib_api_status_t
osm_vendor_send(IN osm_bind_handle_t h_bind,
		IN osm_madw_t * const p_madw, IN boolean_t const resp_expected)
{
	ib_api_status_t ret = IB_SUCCESS;
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
	boolean_t is_rmpp = FALSE, is_rmpp_ds = FALSE;
	osmv_txn_ctx_t *p_txn = NULL;
	ib_mad_t *p_mad;
	osm_log_t *p_log = p_bo->p_vendor->p_log;
	osm_mad_pool_t *p_mad_pool = p_bo->p_osm_pool;
	OSM_LOG_ENTER(p_log);

	if (NULL == h_bind || NULL == p_madw ||
	    NULL == (p_mad = osm_madw_get_mad_ptr(p_madw)) ||
	    NULL == osm_madw_get_mad_addr_ptr(p_madw)) {

		return IB_INVALID_PARAMETER;
	}

	is_rmpp = (p_madw->mad_size > MAD_BLOCK_SIZE
		   || osmv_mad_is_rmpp(p_mad));
	/* is this rmpp double sided? This means we expect a response that can be
	   an rmpp or not */
	is_rmpp_ds = (TRUE == is_rmpp && TRUE == resp_expected);

	/* Make our operations with the send context atomic */
	osmv_txn_lock(p_bo);

	if (TRUE == p_bo->is_closing) {

		osm_log(p_log, OSM_LOG_ERROR,
			"osm_vendor_send: ERR 7310: "
			"The handle %p is being unbound, cannot send.\n",
			h_bind);
		ret = IB_INTERRUPTED;
		/* When closing p_bo could be detroyed or is going to , thus could not refer to it */
		goto send_done;
	}

	if (TRUE == resp_expected || TRUE == is_rmpp) {

		/* We must run under a transaction framework.
		 * Get the transaction object (old or new) */
		ret = __osmv_get_send_txn(h_bind, p_madw, is_rmpp,
					  resp_expected, &p_txn);
		if (IB_SUCCESS != ret) {
			goto send_done;
		}
	}

	if (TRUE == is_rmpp) {
		/* Do the job - RMPP!
		 * The call returns as all the packets are ACK'ed/upon error
		 * The txn lock will be released each time the function sleeps
		 * and re-acquired when it wakes up
		 */
		ret = osmv_rmpp_send_madw(h_bind, p_madw, p_txn, is_rmpp_ds);
	} else {

		/* Do the job - single MAD!
		 * The call returns as soon as the MAD is put on the wire
		 */
		ret = osmv_simple_send_madw(h_bind, p_madw, p_txn, FALSE);
	}

	if (IB_SUCCESS == ret) {

		if ((TRUE == is_rmpp) && (FALSE == is_rmpp_ds)) {
			/* For double-sided sends, the txn continues to live */
			osmv_txn_done(h_bind, osmv_txn_get_key(p_txn),
				      FALSE /*not in callback */ );
		}

		if (FALSE == resp_expected) {
			osm_mad_pool_put(p_mad_pool, p_madw);
		}
	} else if (IB_INTERRUPTED != ret) {
		if (NULL != p_txn) {
			osmv_txn_done(h_bind, osmv_txn_get_key(p_txn),
				      FALSE /*not in callback */ );
		}

		osm_log(p_log, OSM_LOG_ERROR,
			"osm_vendor_send: ERR 7311: failed to send MADW %p\n",
			p_madw);

		if (TRUE == resp_expected) {
			/* Change the status on the p_madw */
			p_madw->status = ret;
			/* Only the requester expects the error callback */
			p_bo->send_err_cb(p_bo->cb_context, p_madw);
		} else {
			/* put back the mad - it is useless ... */
			osm_mad_pool_put(p_mad_pool, p_madw);
		}
	} else {		/* the transaction was aborted due to p_bo exit */

		osm_mad_pool_put(p_mad_pool, p_madw);
		goto aborted;
	}
send_done:

	osmv_txn_unlock(p_bo);
aborted:
	OSM_LOG_EXIT(p_log);
	return ret;
}
static ib_api_status_t
__osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind,
			 IN const ib_mad_t * p_mad,
			 IN osmv_txn_ctx_t * p_txn,
			 IN const osm_mad_addr_t * p_mad_addr)
{
	ib_api_status_t status = IB_SUCCESS;
	osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
	boolean_t is_last1 = FALSE, is_last2 = FALSE;
	osm_madw_t *p_new_madw = NULL, *p_req_madw = NULL;
	ib_mad_t *p_mad_buf;
	uint32_t size = 0;
	uint64_t key = osmv_txn_get_key(p_txn);
	uint64_t tid = osmv_txn_get_tid(p_txn);

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	if (TRUE == osmv_rmpp_is_ack(p_mad)) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"Not supposed to receive ACK's --> dropping the MAD\n");

		goto dispatch_rmpp_rcv_done;
	}

	if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"__osmv_dispatch_rmpp_rcv: ERR 6504: "
			"The Remote Side stopped sending\n");

		status = IB_REMOTE_ERROR;
		goto dispatch_rmpp_rcv_done;
	}

	status = __osmv_dispatch_accept_seg(h_bind, p_txn, p_mad);
	switch (status) {

	case IB_SUCCESS:

		/* Check wheter this is the legal last MAD */
		/* Criteria #1: the received MAD is marked last */
		is_last1 = osmv_rmpp_is_last(p_mad);

		/* Criteria #2: the total accumulated length hits the advertised one */
		is_last2 = is_last1;

		size = osmv_rmpp_recv_ctx_get_byte_num_from_first(p_recv_ctx);
		if (size > 0) {
			is_last2 =
			    (osmv_rmpp_recv_ctx_get_cur_byte_num(p_recv_ctx) >=
			     size);
		}

		if (is_last1 != is_last2) {

			osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
					   IB_RMPP_TYPE_ABORT,
					   IB_RMPP_STATUS_BAD_LEN);

			status = IB_ERROR;
			goto dispatch_rmpp_rcv_done;
		}

		/* TBD Consider an optimization - sending an ACK
		 * only for the last segment in the window
		 */
		__osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
		break;

	case IB_INSUFFICIENT_RESOURCES:
		/* An out-of-order segment received. Send the ACK anyway */
		__osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
		status = IB_SUCCESS;
		goto dispatch_rmpp_rcv_done;

	case IB_INSUFFICIENT_MEMORY:
		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
				   IB_RMPP_TYPE_STOP, IB_RMPP_STATUS_RESX);
		goto dispatch_rmpp_rcv_done;

	default:
		/* Illegal return code */
		CL_ASSERT(FALSE);
	}

	if (TRUE != is_last1) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"RMPP MADW assembly continues, TID=0x%" PRIx64 "\n", tid);
		goto dispatch_rmpp_rcv_done;
	}

	/* This is the last packet. */
	if (0 == size) {
		/* The total size was not advertised in the first packet */
		size = osmv_rmpp_recv_ctx_get_byte_num_from_last(p_recv_ctx);
	}

	/*
	   NOTE: the received mad might not be >= 256 bytes.
	   some MADs might contain several SA records but still be
	   less then a full MAD.
	   We have to use RMPP to send them over since on a regular
	   "simple" MAD there is no way to know how many records were sent
	 */

	/* Build the MAD wrapper to be returned to the user.
	 * The actual storage for the MAD is allocated there.
	 */
	p_new_madw =
	    osm_mad_pool_get(p_bo->p_osm_pool, h_bind, size, p_mad_addr);
	if (NULL == p_new_madw) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
			"__osmv_dispatch_rmpp_rcv: ERR 6506: "
			"Out Of Memory - could not allocate %d bytes for the MADW\n",
			size);

		status = IB_INSUFFICIENT_MEMORY;
		goto dispatch_rmpp_rcv_done;
	}

	p_req_madw = osmv_txn_get_madw(p_txn);
	p_mad_buf = osm_madw_get_mad_ptr(p_new_madw);
	status = osmv_rmpp_recv_ctx_reassemble_arbt_mad(p_recv_ctx, size,
							(uint8_t *) p_mad_buf);
	if (IB_SUCCESS != status) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
			"__osmv_dispatch_rmpp_rcv: ERR 6507: "
			"Internal error - could not reassemble the result MAD\n");
		goto dispatch_rmpp_rcv_done;	/* What can happen here? */
	}

	/* The MAD is assembled, we are about to apply the callback.
	 * Delete the transaction context, unless the transaction is double sided */
	if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)
	    || FALSE == osmv_mad_is_multi_resp(p_mad)) {

		osmv_txn_done(h_bind, key, FALSE);
	}

	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
		"RMPP MADW %p assembly complete, TID=0x%" PRIx64 "\n", p_new_madw,
		tid);

	p_mad_buf->trans_id = cl_hton64(tid);
	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
		"Restoring the original TID to 0x%" PRIx64 "\n",
		cl_ntoh64(p_mad_buf->trans_id));

	/* Finally, do the job! */
	p_bo->recv_cb(p_new_madw, p_bo->cb_context, p_req_madw);

dispatch_rmpp_rcv_done:
	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
	return status;
}
static void
__osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind,
			 IN const ib_mad_t * p_mad,
			 IN osmv_txn_ctx_t * p_txn,
			 IN const osm_mad_addr_t * p_mad_addr)
{
	osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);

	uint32_t old_wl = p_send_ctx->window_last;
	uint32_t total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx);
	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
	uint32_t new_wl = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->paylen_newwin);
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {

		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
			"__osmv_dispatch_rmpp_snd: ERR 6502: "
			"The remote side sent an ABORT/STOP indication.\n");
		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
		goto dispatch_rmpp_snd_done;
	}

	if (FALSE == osmv_rmpp_is_ack(p_mad)) {

		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"Not supposed to receive DATA packets --> dropping the MAD\n");
		goto dispatch_rmpp_snd_done;
	}

	/* Continue processing the ACK */
	if (seg_num > old_wl) {

		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
			"__osmv_dispatch_rmpp_snd: ERR 6503: "
			"ACK received for a non-sent segment %d\n", seg_num);

		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
				   IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_S2B);

		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
		goto dispatch_rmpp_snd_done;
	}

	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
		"__osmv_dispatch_rmpp_snd: "
		"New WL = %u Old WL = %u Total Segs = %u\n",
		new_wl, old_wl, total_segs);

	if (new_wl < old_wl) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
			"__osmv_dispatch_rmpp_snd: ERR 6508: "
			"The receiver requests a smaller WL (%d) than before (%d)\n",
			new_wl, old_wl);

		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
				   IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_W2S);

		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
		goto dispatch_rmpp_snd_done;
	}

	/* Update the sender's window, and optionally wake up the sender thread
	 * Note! A single ACK can acknowledge a whole range of segments: [WF..SEG_NUM]
	 */
	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
		"ACK for seg_num #%d accepted.\n", seg_num);

	if (seg_num == old_wl) {

		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"The send window [%d:%d] is totally acknowledged.\n",
			p_send_ctx->window_first, old_wl);

		p_send_ctx->window_first = seg_num + 1;
		p_send_ctx->window_last =
		    (new_wl < total_segs) ? new_wl : total_segs;

		/* Remove the response timeout event for the window */
		osmv_txn_remove_timeout_ev(h_bind, osmv_txn_get_key(p_txn));

		/* Wake up the sending thread */
		cl_event_signal(&p_send_ctx->event);
	}

dispatch_rmpp_snd_done:
	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
}
static void
__osmv_dispatch_rmpp_mad(IN osm_bind_handle_t h_bind,
			 IN const ib_mad_t * p_mad,
			 IN osmv_txn_ctx_t * p_txn,
			 IN const osm_mad_addr_t * p_mad_addr)
{
	ib_api_status_t status = IB_SUCCESS;
	uint64_t key = cl_ntoh64(p_mad->trans_id);
	boolean_t is_init_by_peer = FALSE;
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
	osm_madw_t *p_madw;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	if (NULL == p_txn) {
		if (FALSE == osmv_rmpp_is_data(p_mad)
		    || FALSE == osmv_rmpp_is_first(p_mad)) {
			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
				"The MAD does not match any transaction "
				"and does not start a sender-initiated RMPP transfer.\n");
			goto dispatch_rmpp_mad_done;
		}

		/* IB Spec 13.6.2.2. This is a Sender Initiated Transfer.
		   My peer is the requester and RMPP Sender. I am the RMPP Receiver.
		 */
		status = osmv_txn_init(h_bind, /*tid==key */ key, key, &p_txn);
		if (IB_SUCCESS != status) {
			goto dispatch_rmpp_mad_done;
		}

		is_init_by_peer = TRUE;
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"A new sender-initiated transfer (TID=0x%" PRIx64 ") started\n",
			key);
	}

	if (OSMV_TXN_RMPP_NONE == osmv_txn_get_rmpp_state(p_txn)) {
		/* Case 1: Fall through from above.
		 * Case 2: When the transaction was initiated by me
		 *         (a single request MAD), there was an uncertainty
		 *         whether the reply will be RMPP. Now it's resolved,
		 *         since the reply is RMPP!
		 */
		status =
		    osmv_txn_init_rmpp_receiver(h_bind, p_txn, is_init_by_peer);
		if (IB_SUCCESS != status) {
			goto dispatch_rmpp_mad_done;
		}
	}

	switch (osmv_txn_get_rmpp_state(p_txn)) {

	case OSMV_TXN_RMPP_RECEIVER:
		status =
		    __osmv_dispatch_rmpp_rcv(h_bind, p_mad, p_txn, p_mad_addr);
		if (IB_SUCCESS != status) {
			if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)) {
				/* This is a requester, still waiting for the reply. Apply the callback */
				/* update the status of the p_madw */
				p_madw = osmv_txn_get_madw(p_txn);
				p_madw->status = status;
				p_bo->send_err_cb(p_bo->cb_context, p_madw);
			}

			/* ABORT/STOP/LOCAL ERROR */
			osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
		}
		break;

	case OSMV_TXN_RMPP_SENDER:
		__osmv_dispatch_rmpp_snd(h_bind, p_mad, p_txn, p_mad_addr);
		/* If an error happens here, it's the sender thread to cleanup the txn */
		break;

	default:
		CL_ASSERT(FALSE);
	}

dispatch_rmpp_mad_done:
	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
}