static ib_api_status_t
__osmv_dispatch_accept_seg(IN osm_bind_handle_t h_bind,
			   IN osmv_txn_ctx_t * p_txn, IN const ib_mad_t * p_mad)
{
	ib_api_status_t ret = IB_SUCCESS;
	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
	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;
	uint64_t tid = osmv_txn_get_tid(p_txn);

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	if (seg_num != p_recv_ctx->expected_seg) {
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"TID 0x%" PRIx64 ": can't accept this segment (%d) - "
			"this is a Go-Back-N implementation\n", tid, seg_num);
		return IB_INSUFFICIENT_RESOURCES;
	}

	/* Store the packet's copy in the reassembly list.
	 * Promote the expected segment counter.
	 */
	ret = osmv_rmpp_recv_ctx_store_mad_seg(p_recv_ctx, (uint8_t *) p_mad);
	if (IB_SUCCESS != ret) {
		return ret;
	}

	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
		"TID 0x%" PRIx64 ": segment %d accepted\n", tid, seg_num);
	p_recv_ctx->expected_seg = seg_num + 1;

	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
	return IB_SUCCESS;
}
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);
}
static uint64_t
__osmv_txn_timeout_cb(IN uint64_t key,
		      IN uint32_t num_regs, IN void *cb_context)
{
	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) cb_context;
	uint64_t ret = 0;
	osmv_txn_ctx_t *p_txn;
	osmv_rmpp_send_ctx_t *p_send_ctx;
	osm_madw_t *p_madw = NULL;
	ib_mad_t *p_mad;
	osm_mad_addr_t *p_mad_addr;
	boolean_t invoke_err_cb = FALSE;

	OSM_LOG_ENTER(p_bo->p_vendor->p_log);

	/* Don't try to acquire a lock on the Bind Object -
	 * it's taken by the mechanism that drives the timeout based events!
	 * (Recall the special constructor that the Event Wheel is applied with)
	 */
	if (p_bo->is_closing) {
		goto txn_done;
	}

	ret = osmv_txn_lookup(p_bo, key, &p_txn);
	if (IB_NOT_FOUND == ret) {
		/* Prevent a race - the transaction is already destroyed */
		goto txn_done;
	}

	p_madw = p_txn->p_madw;

	switch (osmv_txn_get_rmpp_state(p_txn)) {

	case OSMV_TXN_RMPP_NONE:
		if (num_regs <= OSMV_MAX_RETRANSMIT) {
			/* We still did not exceed the limit of retransmissions.
			 * Set the next timeout's value.
			 */
			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
				"__osmv_txn_timeout_cb: "
				"The transaction request (tid=0x%llX) timed out %d times. "
				"Retrying the send.\n",
				osmv_txn_get_tid(p_txn), num_regs);

			/* resend this mad */
			ret = osmv_simple_send_madw((osm_bind_handle_t *) p_bo,
						    p_madw, p_txn, TRUE);
			if (ret != IB_SUCCESS) {
				osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
					"__osmv_txn_timeout_cb: "
					"Fail to send retry for transaction request (tid=0x%llX).\n",
					osmv_txn_get_tid(p_txn));

				osmv_txn_done((osm_bind_handle_t) p_bo, key,
					      TRUE /*in timeout callback */ );

				/* This is a requester. Always apply the callback */
				invoke_err_cb = TRUE;
			} else {
				uint64_t next_timeout_ms;
				next_timeout_ms =
				    p_bo->p_vendor->resp_timeout * (num_regs +
								    1) *
				    (num_regs + 1);
				/* when do we need to timeout again */
				ret =
				    cl_get_time_stamp() +
				    (uint64_t) (1000 * next_timeout_ms);

				osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
					"__osmv_txn_timeout_cb: "
					"Retry request timout in : %lu [msec].\n",
					next_timeout_ms);
			}
		} else {
			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
				"__osmv_txn_timeout_cb: ERR 6702: "
				"The transaction request (tid=0x%llX) timed out (after %d retries). "
				"Invoking the error callback.\n",
				osmv_txn_get_tid(p_txn), num_regs);

			osmv_txn_done((osm_bind_handle_t) p_bo, key,
				      TRUE /*in timeout callback */ );

			/* This is a requester. Always apply the callback */
			invoke_err_cb = TRUE;
		}
		break;

	case OSMV_TXN_RMPP_SENDER:
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"RMPP sender (tid=0x%llX) did not receive ACK "
			"on every segment in the current send window.\n",
			osmv_txn_get_tid(p_txn));

		p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
		if (num_regs <= OSMV_MAX_RETRANSMIT) {
			/* We still did not exceed the limit of retransmissions.
			 * Set the next timeout's value.
			 */
			ret =
			    cl_get_time_stamp() +
			    1000 * p_bo->p_vendor->resp_timeout;
		} else {
			p_send_ctx->status = IB_TIMEOUT;

			p_mad = osm_madw_get_mad_ptr(p_madw);
			p_mad_addr = osm_madw_get_mad_addr_ptr(p_madw);

			/* Send an ABORT to the other side */
			osmv_rmpp_send_nak((osm_bind_handle_t) p_bo, p_mad,
					   p_mad_addr, IB_RMPP_TYPE_ABORT,
					   IB_RMPP_STATUS_T2L);
		}

		/* Wake the RMPP sender thread up */
		cl_event_signal(&p_send_ctx->event);
		break;

	case OSMV_TXN_RMPP_RECEIVER:
		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
			"Transaction timeout on an RMPP receiver (tid=0x%llX). "
			"Dropping the transaction.\n", osmv_txn_get_tid(p_txn));

		osmv_txn_done((osm_bind_handle_t) p_bo, key,
			      TRUE /*in timeout callback */ );

		if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)) {
			/* This is a requester, still waiting for the reply. Apply the callback */
			invoke_err_cb = TRUE;
		}

		break;

	default:
		CL_ASSERT(FALSE);
	}

	if (TRUE == invoke_err_cb) {
		CL_ASSERT(NULL != p_madw);
		/* update the status in the p_madw */
		p_madw->status = IB_TIMEOUT;
		p_bo->send_err_cb(p_bo->cb_context, p_madw);
		/* no re-registration */
		ret = 0;
	}

txn_done:
	OSM_LOG_EXIT(p_bo->p_vendor->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;
}