/*
 * ibmf_i_send_single_pkt():
 *	Send a single IB packet.  Only used to send non-RMPP packets.
 */
int
ibmf_i_send_single_pkt(ibmf_client_t *clientp, ibmf_qp_handle_t ibmf_qp_handle,
    ibmf_msg_impl_t *msgimplp, int block)
{
	int	status;

	IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_single_pkt_start,
	    IBMF_TNF_TRACE, "",
	    "ibmf_i_send_single_pkt(): clientp = 0x%p, qp_hdl = 0x%p, "
	    "msgp = 0x%p, block = %d\n", tnf_opaque, clientp, clientp,
	    tnf_opaque, qp_hdl, ibmf_qp_handle, tnf_opaque, msg, msgimplp,
	    tnf_uint, block, block);

	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));

	status = ibmf_i_send_pkt(clientp, ibmf_qp_handle, msgimplp, block);
	if (status != IBMF_SUCCESS) {
		IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
		    ibmf_i_send_single_pkt_err, IBMF_TNF_ERROR, "",
		    "ibmf_i_send_single_pkt(): %s, msgp = 0x%p\n",
		    tnf_string, msg, "unable to send packet",
		    tnf_uint, status, status);
		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
		    ibmf_i_send_single_pkt_end, IBMF_TNF_TRACE, "",
		    "ibmf_i_send_single_pkt() exit\n");
		return (status);
	}

	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,	ibmf_i_send_single_pkt_end,
	    IBMF_TNF_TRACE, "", "ibmf_i_send_single_pkt() exit\n");
	return (IBMF_SUCCESS);
}
/* ARGSUSED */
static void
ibmf_i_do_send_compl(ibmf_handle_t ibmf_handle, ibmf_msg_impl_t *msgimplp,
    ibmf_send_wqe_t *send_wqep)
{
	IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_do_send_compl_start,
	    IBMF_TNF_TRACE, "", "ibmf_i_do_send_compl(): ibmf_hdl = 0x%p "
	    "msgp = %p, send_wqep = 0x%p, msg_flags = 0x%x\n",
	    tnf_opaque, ibmf_hdl, ibmf_handle, tnf_opaque, msgimplp, msgimplp,
	    tnf_opaque, send_wqep, send_wqep,
	    tnf_opaque, msg_flags, msgimplp->im_flags);

	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));

	/*
	 * For RMPP transactions, we only care about the final packet of the
	 * transaction.  For others, the code does not need to wait for the send
	 * completion (although bad things can happen if it never occurs).
	 * The final packets of a transaction are sent when the state is either
	 * ABORT or RECEVR_TERMINATE.
	 * Don't mark the transaction as send_done if there are still more
	 * packets to be sent, including doing the second part of a double-sided
	 * transaction.
	 */
	if ((msgimplp->im_flags & IBMF_MSG_FLAGS_RECV_RMPP) ||
	    (msgimplp->im_flags & IBMF_MSG_FLAGS_SEND_RMPP)) {

		IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_do_send_compl, IBMF_TNF_TRACE, "",
		    "ibmf_i_do_send_compl(): %s msgp = %p, rmpp_state = 0x%x\n",
		    tnf_string, msg, "Received send callback for RMPP trans",
		    tnf_opaque, msg, msgimplp,
		    tnf_opaque, rmpp_state, msgimplp->im_rmpp_ctx.rmpp_state);

		/*
		 * For ABORT state, we should not return control to
		 * the client from the send completion handler.
		 * Control should be returned in the error timeout handler.
		 *
		 * The exception is when the IBMF_TRANS_STATE_FLAG_RECV_DONE
		 * flag has already been set. This flag is set when
		 * ibmf_i_terminate_transaction is called from one of the
		 * three timeout handlers. In this case return control from
		 * here.
		 */
		if (msgimplp->im_rmpp_ctx.rmpp_state == IBMF_RMPP_STATE_ABORT) {
			msgimplp->im_trans_state_flags |=
			    IBMF_TRANS_STATE_FLAG_SEND_DONE;
			if (msgimplp->im_trans_state_flags &
			    IBMF_TRANS_STATE_FLAG_RECV_DONE) {
				msgimplp->im_trans_state_flags |=
				    IBMF_TRANS_STATE_FLAG_DONE;
			}
		}

		if ((msgimplp->im_rmpp_ctx.rmpp_state ==
		    IBMF_RMPP_STATE_RECEVR_TERMINATE) ||
		    (msgimplp->im_rmpp_ctx.rmpp_state ==
		    IBMF_RMPP_STATE_DONE)) {
			msgimplp->im_trans_state_flags |=
			    IBMF_TRANS_STATE_FLAG_SEND_DONE;
			if (msgimplp->im_trans_state_flags  &
			    IBMF_TRANS_STATE_FLAG_RECV_DONE) {
				msgimplp->im_trans_state_flags |=
				    IBMF_TRANS_STATE_FLAG_DONE;
			}
		}

		/*
		 * If the transaction is a send-only RMPP, then
		 * set the SEND_DONE flag on every send completion
		 * as long as there are no outstanding ones.
		 * This is needed so that the transaction can return
		 * in the receive path, where ibmf_i_terminate_transaction
		 * is called from ibmf_i_rmpp_sender_active_flow,
		 * after checking if the SEND_DONE flag is set.
		 * When a new MAD is sent as part of the RMPP transaction,
		 * the SEND_DONE flag will get reset.
		 * The RECV_DONE indicates that the last ACK was received.
		 */
		if ((msgimplp->im_flags & IBMF_MSG_FLAGS_SEQUENCED) == 0) {
			if (msgimplp->im_pending_send_compls == 0) {
				msgimplp->im_trans_state_flags |=
				    IBMF_TRANS_STATE_FLAG_SEND_DONE;
				if (msgimplp->im_trans_state_flags  &
				    IBMF_TRANS_STATE_FLAG_RECV_DONE) {
					msgimplp->im_trans_state_flags |=
					    IBMF_TRANS_STATE_FLAG_DONE;
				}
			}
		}

		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
		    ibmf_i_do_send_compl_end, IBMF_TNF_TRACE, "",
		    "ibmf_i_do_send_compl() exit\n");
		return;
	}

	/*
	 * Only non-RMPP send completion gets here.
	 * If the send is a single-packet send that does not use RMPP, and if
	 * the transaction is not a sequenced transaction, call the transaction
	 * callback handler after flagging the transaction as done.  If the
	 * message is sequenced, start a timer to bound the wait for the first
	 * data packet of the response.
	 */
	if (msgimplp->im_flags & IBMF_MSG_FLAGS_SEQUENCED) {

		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_do_send_compl, IBMF_TNF_TRACE, "",
		    "ibmf_i_do_send_compl(): %s msgp = %p\n", tnf_string, msg,
		    "Sequenced transaction, setting response timer",
		    tnf_opaque, msg, msgimplp);

		/*
		 * Check if the send completion already occured,
		 * which could imply that this is a send completion
		 * for some previous transaction that has come in very late.
		 * In this case exit here.
		 */
		if (msgimplp->im_trans_state_flags  &
		    IBMF_TRANS_STATE_FLAG_SEND_DONE) {
			IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
			    ibmf_i_do_send_compl_end, IBMF_TNF_TRACE, "",
			    "ibmf_i_do_send_compl() exit, "
			    "Duplicate SEND completion\n");
			return;
		}

		/* mark as send_compl happened */
		msgimplp->im_trans_state_flags |=
		    IBMF_TRANS_STATE_FLAG_SEND_DONE;

		if (msgimplp->im_trans_state_flags  &
		    IBMF_TRANS_STATE_FLAG_RECV_DONE) {
			msgimplp->im_trans_state_flags |=
			    IBMF_TRANS_STATE_FLAG_DONE;
			IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
			    ibmf_i_do_send_compl_end, IBMF_TNF_TRACE, "",
			    "ibmf_i_do_send_compl() exit, RECV_DONE\n");
			return;
		}

		/*
		 * check if response was received before send
		 * completion
		 */
		if (((msgimplp->im_trans_state_flags &
		    IBMF_TRANS_STATE_FLAG_DONE) == 0) &&
		    ((msgimplp->im_trans_state_flags &
		    IBMF_TRANS_STATE_FLAG_RECV_ACTIVE) == 0)) {
			/* set timer for first packet of response */
			ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp,
			    IBMF_RESP_TIMER);
		}
	} else {
		msgimplp->im_msg_status = IBMF_SUCCESS;
		msgimplp->im_trans_state_flags |=
		    IBMF_TRANS_STATE_FLAG_SEND_DONE;
		msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_DONE;
	}

	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_do_send_compl_end,
	    IBMF_TNF_TRACE, "", "ibmf_i_do_send_compl() exit\n");
}
/*
 * ibmf_i_issue_pkt():
 *	Post an IB packet on the specified QP's send queue
 */
int
ibmf_i_issue_pkt(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp,
    ibmf_qp_handle_t ibmf_qp_handle, ibmf_send_wqe_t *send_wqep)
{
	int			ret;
	ibt_status_t		status;
	ibt_wr_ds_t		sgl[1];
	ibt_qp_hdl_t		ibt_qp_handle;

	_NOTE(ASSUMING_PROTECTED(*send_wqep))
	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*send_wqep))

	IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4,
	    ibmf_i_issue_pkt_start, IBMF_TNF_TRACE, "",
	    "ibmf_i_issue_pkt() enter, clientp = %p, msg = %p, "
	    "qp_hdl = %p,  swqep = %p\n", tnf_opaque, clientp, clientp,
	    tnf_opaque, msg, msgimplp, tnf_opaque, ibmf_qp_handle,
	    ibmf_qp_handle, tnf_opaque, send_wqep, send_wqep);

	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));
	ASSERT(MUTEX_NOT_HELD(&clientp->ic_mutex));

	/*
	 * if the qp handle provided in ibmf_send_pkt()
	 * is not the default qp handle for this client,
	 * then the wqe must be sent on this qp,
	 * else use the default qp handle set up during ibmf_register()
	 */
	if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
		ibt_qp_handle = clientp->ic_qp->iq_qp_handle;
	} else {
		ibt_qp_handle =
		    ((ibmf_alt_qp_t *)ibmf_qp_handle)->isq_qp_handle;
	}

	/* initialize the send WQE */
	ibmf_i_init_send_wqe(clientp, msgimplp, sgl, send_wqep,
	    msgimplp->im_ud_dest, ibt_qp_handle, ibmf_qp_handle);

	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*send_wqep))

	/*
	 * Issue the wqe to the transport.
	 * NOTE: ibt_post_send() will not block, so, it is ok
	 * to hold the msgimpl mutex across this call.
	 */
	status = ibt_post_send(send_wqep->send_qp_handle, &send_wqep->send_wr,
	    1, NULL);
	if (status != IBT_SUCCESS) {
		mutex_enter(&clientp->ic_kstat_mutex);
		IBMF_ADD32_KSTATS(clientp, send_pkt_failed, 1);
		mutex_exit(&clientp->ic_kstat_mutex);
		IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
		    ibmf_i_issue_pkt_err, IBMF_TNF_TRACE, "",
		    "ibmf_i_issue_pkt(): %s, status = %d\n",
		    tnf_string, msg, "post send failure",
		    tnf_uint, ibt_status, status);
		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_issue_pkt_end,
		    IBMF_TNF_TRACE, "", "ibmf_i_issue_pkt(() exit\n");
		return (IBMF_TRANSPORT_FAILURE);
	}

	ret = IBMF_SUCCESS;

	/* bump the number of active sends */
	if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
		mutex_enter(&clientp->ic_mutex);
		clientp->ic_sends_active++;
		mutex_exit(&clientp->ic_mutex);
		mutex_enter(&clientp->ic_kstat_mutex);
		IBMF_ADD32_KSTATS(clientp, sends_active, 1);
		mutex_exit(&clientp->ic_kstat_mutex);
	} else {
		ibmf_alt_qp_t *qpp = (ibmf_alt_qp_t *)ibmf_qp_handle;
		mutex_enter(&qpp->isq_mutex);
		qpp->isq_sends_active++;
		mutex_exit(&qpp->isq_mutex);
		mutex_enter(&clientp->ic_kstat_mutex);
		IBMF_ADD32_KSTATS(clientp, sends_active, 1);
		mutex_exit(&clientp->ic_kstat_mutex);
	}

	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_issue_pkt_end,
	    IBMF_TNF_TRACE, "", "ibmf_i_issue_pkt() exit\n");
	return (ret);
}
/*
 * ibmf_i_do_send_cb():
 *	Do the send completion processing
 */
static void
ibmf_i_do_send_cb(void *taskq_arg)
{
	ibmf_ci_t		*cip;
	ibmf_msg_impl_t		*msgimplp;
	ibmf_client_t		*clientp;
	ibmf_send_wqe_t		*send_wqep;
	boolean_t		found;
	int			msg_trans_state_flags, msg_flags;
	uint_t			ref_cnt;
	ibmf_qp_handle_t	ibmf_qp_handle;
	struct kmem_cache	*kmem_cachep;
	timeout_id_t		msg_rp_unset_id, msg_tr_unset_id;
	timeout_id_t		msg_rp_set_id, msg_tr_set_id;
	ibmf_alt_qp_t		*altqp;
	boolean_t		inc_refcnt;

	send_wqep = taskq_arg;

	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
	    ibmf_i_do_send_cb_start, IBMF_TNF_TRACE, "",
	    "ibmf_i_do_send_cb() enter, send_wqep = %p\n",
	    tnf_opaque, send_wqep, send_wqep);

	clientp = send_wqep->send_client;
	cip = clientp->ic_myci;
	msgimplp = send_wqep->send_msg;

	/* get the QP handle */
	ibmf_qp_handle = send_wqep->send_ibmf_qp_handle;

	/* Get the WQE kmem cache pointer based on the QP type */
	if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT)
		kmem_cachep = cip->ci_send_wqes_cache;
	else {
		altqp = (ibmf_alt_qp_t *)ibmf_qp_handle;
		kmem_cachep = altqp->isq_send_wqes_cache;
	}

	/* Look for a message in the client's message list */
	inc_refcnt = B_TRUE;
	found = ibmf_i_find_msg_client(clientp, msgimplp, inc_refcnt);

	/*
	 * If the message context was not found, then it's likely
	 * been freed up. So, do nothing in this timeout handler
	 */
	if (found == B_FALSE) {
		kmem_cache_free(kmem_cachep, send_wqep);
		mutex_enter(&cip->ci_mutex);
		IBMF_SUB32_PORT_KSTATS(cip, send_wqes_alloced, 1);
		mutex_exit(&cip->ci_mutex);
		if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
			mutex_enter(&cip->ci_mutex);
			cip->ci_wqes_alloced--;
			if (cip->ci_wqes_alloced == 0)
				cv_signal(&cip->ci_wqes_cv);
			mutex_exit(&cip->ci_mutex);
		} else {
			mutex_enter(&altqp->isq_mutex);
			altqp->isq_wqes_alloced--;
			if (altqp->isq_wqes_alloced == 0)
				cv_signal(&altqp->isq_wqes_cv);
			mutex_exit(&altqp->isq_mutex);
		}
		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_do_send_cb, IBMF_TNF_TRACE, "",
		    "ibmf_i_do_send_cb(): %s\n", tnf_string, msg,
		    "Message not found, return without processing send cb");
		return;
	}

	/* Grab the message context lock */
	mutex_enter(&msgimplp->im_mutex);

	/*
	 * Decrement the count of pending send completions for
	 * this transaction
	 */
	msgimplp->im_pending_send_compls -= 1;

	/*
	 * If the pending send completions is not zero, then we must
	 * not attempt to notify the client of a transaction completion
	 * in this instance of the send completion handler. Notification
	 * of transaction completion should be provided only by the
	 * last send completion so that all send completions are accounted
	 * for before the client is notified and subsequently attempts to
	 * reuse the message for an other transaction.
	 * If this is not done, the message may be reused while the
	 * send WR from the old transaction is still active in the QP's WQ.
	 * This could result in an attempt to modify the address handle with
	 * information for the new transaction which could be potentially
	 * incompatible, such as an incorrect port number. Such an
	 * incompatible modification of the address handle of the old
	 * transaction could result in a QP error.
	 */
	if (msgimplp->im_pending_send_compls != 0) {
		IBMF_MSG_DECR_REFCNT(msgimplp);
		mutex_exit(&msgimplp->im_mutex);
		kmem_cache_free(kmem_cachep, send_wqep);
		mutex_enter(&cip->ci_mutex);
		IBMF_SUB32_PORT_KSTATS(cip, send_wqes_alloced, 1);
		mutex_exit(&cip->ci_mutex);
		if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
			mutex_enter(&cip->ci_mutex);
			cip->ci_wqes_alloced--;
			if (cip->ci_wqes_alloced == 0)
				cv_signal(&cip->ci_wqes_cv);
			mutex_exit(&cip->ci_mutex);
		} else {
			mutex_enter(&altqp->isq_mutex);
			altqp->isq_wqes_alloced--;
			if (altqp->isq_wqes_alloced == 0)
				cv_signal(&altqp->isq_wqes_cv);
			mutex_exit(&altqp->isq_mutex);
		}
		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_do_send_cb, IBMF_TNF_TRACE, "",
		    "ibmf_i_do_send_cb(): %s\n", tnf_string, msg,
		    "Message found with pending send completions, "
		    "return without processing send cb");
		return;
	}

	/*
	 * If the message has been marked unitialized or done
	 * release the message mutex and return
	 */
	if ((msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_UNINIT) ||
	    (msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE)) {
		IBMF_MSG_DECR_REFCNT(msgimplp);
		msg_trans_state_flags = msgimplp->im_trans_state_flags;
		msg_flags = msgimplp->im_flags;
		ref_cnt = msgimplp->im_ref_count;
		mutex_exit(&msgimplp->im_mutex);
		/*
		 * This thread may notify the client only if the
		 * transaction is done, the message has been removed
		 * from the client's message list, and the message
		 * reference count is 0.
		 * If the transaction is done, and the message reference
		 * count = 0, there is still a possibility that a
		 * packet could arrive for the message and its reference
		 * count increased if the message is still on the list.
		 * If the message is still on the list, it will be
		 * removed by a call to ibmf_i_client_rem_msg() at
		 * the completion point of the transaction.
		 * So, the reference count should be checked after the
		 * message has been removed.
		 */
		if ((msg_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE) &&
		    !(msg_flags & IBMF_MSG_FLAGS_ON_LIST) &&
		    (ref_cnt == 0)) {

			ibmf_i_notify_sequence(clientp, msgimplp, msg_flags);

		}
		kmem_cache_free(kmem_cachep, send_wqep);
		mutex_enter(&cip->ci_mutex);
		IBMF_SUB32_PORT_KSTATS(cip, send_wqes_alloced, 1);
		mutex_exit(&cip->ci_mutex);
		if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
			mutex_enter(&cip->ci_mutex);
			cip->ci_wqes_alloced--;
			if (cip->ci_wqes_alloced == 0)
				cv_signal(&cip->ci_wqes_cv);
			mutex_exit(&cip->ci_mutex);
		} else {
			mutex_enter(&altqp->isq_mutex);
			altqp->isq_wqes_alloced--;
			if (altqp->isq_wqes_alloced == 0)
				cv_signal(&altqp->isq_wqes_cv);
			mutex_exit(&altqp->isq_mutex);
		}
		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_do_send_cb, IBMF_TNF_TRACE, "",
		    "ibmf_i_do_send_cb(): %s, msg = %p\n", tnf_string, msg,
		    "Message marked for removal, return without processing "
		    "send cb", tnf_opaque, msgimplp, msgimplp);
		return;
	}

	/* Perform send completion processing of the message context */
	ibmf_i_do_send_compl((ibmf_handle_t)clientp, msgimplp, send_wqep);

	msg_rp_unset_id = msg_tr_unset_id = msg_rp_set_id = msg_tr_set_id = 0;

	/* Save the message flags before releasing the mutex */
	msg_trans_state_flags = msgimplp->im_trans_state_flags;
	msg_flags = msgimplp->im_flags;
	msg_rp_unset_id = msgimplp->im_rp_unset_timeout_id;
	msg_tr_unset_id = msgimplp->im_tr_unset_timeout_id;
	msgimplp->im_rp_unset_timeout_id = 0;
	msgimplp->im_tr_unset_timeout_id = 0;

	/*
	 * Decrement the message reference count
	 * This count was inceremented when the message was found on the
	 * client's message list
	 */
	IBMF_MSG_DECR_REFCNT(msgimplp);

	if (msg_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE) {
		if (msgimplp->im_rp_timeout_id != 0) {
			msg_rp_set_id = msgimplp->im_rp_timeout_id;
			msgimplp->im_rp_timeout_id = 0;
		}
		if (msgimplp->im_tr_timeout_id != 0) {
			msg_tr_set_id = msgimplp->im_tr_timeout_id;
			msgimplp->im_tr_timeout_id = 0;
		}
	}

	mutex_exit(&msgimplp->im_mutex);

	if (msg_rp_unset_id != 0) {
		(void) untimeout(msg_rp_unset_id);
	}

	if (msg_tr_unset_id != 0) {
		(void) untimeout(msg_tr_unset_id);
	}

	if (msg_rp_set_id != 0) {
		(void) untimeout(msg_rp_set_id);
	}

	if (msg_tr_set_id != 0) {
		(void) untimeout(msg_tr_set_id);
	}

	IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
	    ibmf_i_do_send_cb, IBMF_TNF_TRACE, "",
	    "ibmf_i_do_send_cb(): %s, msg = %p\n",
	    tnf_string, msg, "Send callback done.  Dec ref count",
	    tnf_opaque, msgimplp, msgimplp);

	/*
	 * If the transaction is done, signal the block thread if the
	 * transaction is blocking, or call the client's transaction done
	 * notification callback
	 */
	if (msg_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE) {

		/* Remove the message from the client's message list */
		ibmf_i_client_rem_msg(clientp, msgimplp, &ref_cnt);

		/*
		 * Notify the client if the message reference count is zero.
		 * At this point, we know that the transaction is done and
		 * the message has been removed from the client's message list.
		 * So, we only need to make sure the reference count is zero
		 * before notifying the client.
		 */
		if (ref_cnt == 0) {

			ibmf_i_notify_sequence(clientp, msgimplp, msg_flags);

		}
	}

	kmem_cache_free(kmem_cachep, send_wqep);
	mutex_enter(&cip->ci_mutex);
	IBMF_SUB32_PORT_KSTATS(cip, send_wqes_alloced, 1);
	mutex_exit(&cip->ci_mutex);
	if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
		mutex_enter(&cip->ci_mutex);
		cip->ci_wqes_alloced--;
		if (cip->ci_wqes_alloced == 0)
			cv_signal(&cip->ci_wqes_cv);
		mutex_exit(&cip->ci_mutex);
	} else {
		mutex_enter(&altqp->isq_mutex);
		altqp->isq_wqes_alloced--;
		if (altqp->isq_wqes_alloced == 0)
			cv_signal(&altqp->isq_wqes_cv);
		mutex_exit(&altqp->isq_mutex);
	}

	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
	    ibmf_i_do_send_cb_end, IBMF_TNF_TRACE, "",
	    "ibmf_i_do_send_cb() exit\n");
}
/* ARGSUSED */
void
ibmf_i_handle_send_completion(ibmf_ci_t *cip, ibt_wc_t *wcp)
{
	ibmf_client_t		*clientp, *cclientp;
	ibmf_send_wqe_t		*send_wqep;
	ibmf_qp_handle_t	ibmf_qp_handle;
	ibmf_alt_qp_t		*qpp;
	int			ret;

	IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
	    ibmf_i_handle_send_completion_start, IBMF_TNF_TRACE, "",
	    "ibmf_i_handle_send_completion() enter, cip = %p, wcp = %p\n",
	    tnf_opaque, cip, cip, tnf_opaque, wcp, wcp);

	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*send_wqep))

	ASSERT(wcp->wc_id != NULL);

	ASSERT(IBMF_IS_SEND_WR_ID(wcp->wc_id));

	/* get the IBMF send WQE context */
	IBMF_SEND_WR_ID_TO_ADDR(wcp->wc_id, send_wqep);

	ASSERT(send_wqep != NULL);

	/* get the client context */
	cclientp =  clientp = send_wqep->send_client;

	/* Check if this is a completion for a BUSY MAD sent by IBMF */
	if (clientp == NULL) {
		ibmf_msg_impl_t		*msgimplp;

		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_handle_send_completion, IBMF_TNF_TRACE, "",
		    "ibmf_i_handle_send_completion(): NULL client\n");

		msgimplp = send_wqep->send_msg;

		/*
		 * Deregister registered memory and free it, and
		 * free up the send WQE context
		 */
		(void) ibt_deregister_mr(cip->ci_ci_handle,
		    send_wqep->send_mem_hdl);
		kmem_free(send_wqep->send_mem, IBMF_MEM_PER_WQE);
		kmem_free(send_wqep, sizeof (ibmf_send_wqe_t));

		/* Free up the message context */
		ibmf_i_put_ud_dest(cip, msgimplp->im_ibmf_ud_dest);
		ibmf_i_clean_ud_dest_list(cip, B_FALSE);
		kmem_free(msgimplp, sizeof (ibmf_msg_impl_t));
		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
		    ibmf_i_handle_send_completion_end, IBMF_TNF_TRACE, "",
		    "ibmf_i_handle_send_completion() exit\n");
		return;
	}

	/* get the QP handle */
	ibmf_qp_handle = send_wqep->send_ibmf_qp_handle;
	qpp = (ibmf_alt_qp_t *)ibmf_qp_handle;

	ASSERT(clientp != NULL);

	/* decrement the number of active sends */
	if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
		mutex_enter(&clientp->ic_mutex);
		clientp->ic_sends_active--;
		mutex_exit(&clientp->ic_mutex);
	} else {
		mutex_enter(&qpp->isq_mutex);
		qpp->isq_sends_active--;
		mutex_exit(&qpp->isq_mutex);
	}

	mutex_enter(&clientp->ic_kstat_mutex);
	IBMF_SUB32_KSTATS(clientp, sends_active, 1);
	mutex_exit(&clientp->ic_kstat_mutex);

	send_wqep->send_status = ibmf_i_ibt_wc_to_ibmf_status(wcp->wc_status);

	/*
	 * issue the callback using taskq. If no taskq or if the
	 * dispatch fails, we do the send processing in the callback context
	 * which is the interrupt context
	 */
	if (cclientp->ic_send_taskq == NULL) {
		/* Do the processing in callback context */
		mutex_enter(&clientp->ic_kstat_mutex);
		IBMF_ADD32_KSTATS(clientp, send_cb_active, 1);
		mutex_exit(&clientp->ic_kstat_mutex);
		ibmf_i_do_send_cb((void *)send_wqep);
		mutex_enter(&clientp->ic_kstat_mutex);
		IBMF_SUB32_KSTATS(clientp, send_cb_active, 1);
		mutex_exit(&clientp->ic_kstat_mutex);
		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
		    ibmf_i_handle_send_err, IBMF_TNF_ERROR, "",
		    "ibmf_i_handle_send_completion(): %s\n",
		    tnf_string, msg, "ci_send_taskq == NULL");
		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
		    ibmf_i_handle_send_completion_end, IBMF_TNF_TRACE, "",
		    "ibmf_i_handle_send_completion() exit\n");
		return;
	}

	mutex_enter(&clientp->ic_kstat_mutex);
	IBMF_ADD32_KSTATS(clientp, send_cb_active, 1);
	mutex_exit(&clientp->ic_kstat_mutex);

	/* Use taskq for processing if the IBMF_REG_FLAG_NO_OFFLOAD isn't set */
	if ((clientp->ic_reg_flags & IBMF_REG_FLAG_NO_OFFLOAD) == 0) {
		ret = taskq_dispatch(cclientp->ic_send_taskq, ibmf_i_do_send_cb,
		    send_wqep, TQ_NOSLEEP);
		if (ret == 0) {
			IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
			    ibmf_i_handle_send_err, IBMF_TNF_ERROR, "",
			    "ibmf_i_handle_send_completion(): %s\n",
			    tnf_string, msg, "send: dispatch failed");
			ibmf_i_do_send_cb((void *)send_wqep);
		}
	} else {
		ibmf_i_do_send_cb((void *)send_wqep);
	}

	mutex_enter(&clientp->ic_kstat_mutex);
	IBMF_SUB32_KSTATS(clientp, send_cb_active, 1);
	mutex_exit(&clientp->ic_kstat_mutex);

	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*send_wqep))

	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
	    ibmf_i_handle_send_completion_end, IBMF_TNF_TRACE, "",
	    "ibmf_i_handle_send_completion() exit\n");
}
/*
 * ibmf_i_send_pkt()
 *	Send an IB packet after allocating send resources
 */
int
ibmf_i_send_pkt(ibmf_client_t *clientp, ibmf_qp_handle_t ibmf_qp_handle,
    ibmf_msg_impl_t *msgimplp, int block)
{
	ibmf_send_wqe_t	*send_wqep;
	int		status;

	IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_pkt_start,
	    IBMF_TNF_TRACE, "",
	    "ibmf_i_send_pkt(): clientp = 0x%p, qp_hdl = 0x%p, "
	    "msgp = 0x%p, block = %d\n", tnf_opaque, clientp, clientp,
	    tnf_opaque, qp_hdl, ibmf_qp_handle, tnf_opaque, msg, msgimplp,
	    tnf_uint, block, block);

	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));

	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*send_wqep))

	/*
	 * Reset send_done to indicate we have not received the completion
	 * for this send yet.
	 */
	msgimplp->im_trans_state_flags &= ~IBMF_TRANS_STATE_FLAG_SEND_DONE;

	/*
	 * Allocate resources needed to send a UD packet including the
	 * send WQE context
	 */
	status = ibmf_i_alloc_send_resources(clientp->ic_myci,
	    msgimplp, block, &send_wqep);
	if (status != IBMF_SUCCESS) {
		IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_send_pkt_err,
		    IBMF_TNF_ERROR, "", "ibmf_i_send_pkt(): %s, status = %d\n",
		    tnf_string, msg, "unable to allocate send resources",
		    tnf_uint, status, status);
		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,	ibmf_i_send_pkt_end,
		    IBMF_TNF_TRACE, "", "ibmf_i_send_pkt() exit\n");
		return (status);
	}

	/* Set the segment number in the send WQE context */
	if (msgimplp->im_flags & IBMF_MSG_FLAGS_SEND_RMPP)
		send_wqep->send_rmpp_segment = msgimplp->im_rmpp_ctx.rmpp_ns;

	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*send_wqep))

	/*
	 * Increment the count of pending send completions.
	 * Only when this count is zero should the client be notified
	 * of completion of the transaction.
	 */
	msgimplp->im_pending_send_compls += 1;

	/* Send the packet */
	status = ibmf_i_issue_pkt(clientp, msgimplp, ibmf_qp_handle, send_wqep);
	if (status != IBMF_SUCCESS) {
		ibmf_i_free_send_resources(clientp->ic_myci, msgimplp,
		    send_wqep);
		IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_send_pkt_err,
		    IBMF_TNF_ERROR, "", "ibmf_i_send_pkt(): %s, status = %d\n",
		    tnf_string, msg, "unable to issue packet",
		    tnf_uint, status, status);
		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,	ibmf_i_send_pkt_end,
		    IBMF_TNF_TRACE, "", "ibmf_i_send_pkt() exit\n");
		return (status);
	}

	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,	ibmf_i_send_pkt_end,
	    IBMF_TNF_TRACE, "", "ibmf_i_send_pkt() exit, status = %d\n",
	    tnf_uint, status, status);

	return (IBMF_SUCCESS);
}
Exemple #7
0
/*
 * ibmf_i_client_add_msg():
 *	Add the message to the client message list
 */
void
ibmf_i_client_add_msg(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp)
{
    IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
                 ibmf_i_client_add_msg_start, IBMF_TNF_TRACE, "",
                 "ibmf_i_client_add_msg(): clientp = 0x%p, msgp = 0x%p\n",
                 tnf_opaque, clientp, clientp, tnf_opaque, msg, msgimplp);

    ASSERT(MUTEX_NOT_HELD(&msgimplp->im_mutex));

    mutex_enter(&clientp->ic_msg_mutex);

    /*
     * If this is a termination message, add the message to
     * the termination message list else add the message
     * to the regular message list.
     */
    mutex_enter(&msgimplp->im_mutex);
    if (msgimplp->im_flags & IBMF_MSG_FLAGS_TERMINATION) {

        mutex_exit(&msgimplp->im_mutex);
        /* Put the message on the list */
        if (clientp->ic_term_msg_list == NULL) {
            clientp->ic_term_msg_list = clientp->ic_term_msg_last =
                                            msgimplp;
        } else {
            msgimplp->im_msg_prev = clientp->ic_term_msg_last;
            clientp->ic_term_msg_last->im_msg_next = msgimplp;
            clientp->ic_term_msg_last = msgimplp;
        }
    } else {

        mutex_exit(&msgimplp->im_mutex);
        /*
         * Increment the counter and kstats for active messages
         */
        clientp->ic_msgs_active++;
        mutex_enter(&clientp->ic_kstat_mutex);
        IBMF_ADD32_KSTATS(clientp, msgs_active, 1);
        mutex_exit(&clientp->ic_kstat_mutex);

        /* Put the message on the list */
        if (clientp->ic_msg_list == NULL) {
            clientp->ic_msg_list = clientp->ic_msg_last = msgimplp;
        } else {
            msgimplp->im_msg_prev = clientp->ic_msg_last;
            clientp->ic_msg_last->im_msg_next = msgimplp;
            clientp->ic_msg_last = msgimplp;
        }
    }

    msgimplp->im_msg_next = NULL;

    /* Set the message flags to indicate the message is on the list */
    mutex_enter(&msgimplp->im_mutex);
    msgimplp->im_flags |= IBMF_MSG_FLAGS_ON_LIST;
    mutex_exit(&msgimplp->im_mutex);

    mutex_exit(&clientp->ic_msg_mutex);

    IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                 ibmf_i_client_add_msg_end, IBMF_TNF_TRACE, "",
                 "ibmf_i_client_add_msg() exit\n");
}
Exemple #8
0
/*
 * ibmf_i_client_rem_msg():
 *	Remove the message from the client's message list
 *	The refcnt will hold the message reference count at the time
 *	the message was removed from the message list. Any packets
 *	arriving after this point for the message will be dropped.
 *	The message reference count is used by the threads processing
 *	the message to decide which one should notify the client
 *	(the one that decrements the reference count to zero).
 */
void
ibmf_i_client_rem_msg(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp,
                      uint_t *refcnt)
{
    ibmf_msg_impl_t *tmpmsg, *prevmsg = NULL;

    ASSERT(MUTEX_NOT_HELD(&msgimplp->im_mutex));

    IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
                 ibmf_i_client_rem_msg_start, IBMF_TNF_TRACE, "",
                 "ibmf_i_client_rem_msg(): clientp = 0x%p, msgp = 0x%p\n",
                 tnf_opaque, clientp, clientp, tnf_opaque, msg, msgimplp);

    mutex_enter(&clientp->ic_msg_mutex);

    /*
     * If this is a termination message, remove the message from
     * the termination message list else remove the message
     * from the regular message list.
     */
    mutex_enter(&msgimplp->im_mutex);
    if (msgimplp->im_flags & IBMF_MSG_FLAGS_TERMINATION) {

        mutex_exit(&msgimplp->im_mutex);
        tmpmsg = clientp->ic_term_msg_list;

        while (tmpmsg != NULL) {
            if (tmpmsg == msgimplp)
                break;
            prevmsg = tmpmsg;
            tmpmsg = tmpmsg->im_msg_next;
        }

        ASSERT(tmpmsg != NULL);

        if (tmpmsg->im_msg_next == NULL)
            clientp->ic_term_msg_last = prevmsg;
        else
            tmpmsg->im_msg_next->im_msg_prev = prevmsg;

        if (prevmsg != NULL)
            prevmsg->im_msg_next = tmpmsg->im_msg_next;
        else
            clientp->ic_term_msg_list = tmpmsg->im_msg_next;
    } else {

        mutex_exit(&msgimplp->im_mutex);
        /*
         * Decrement the counter and kstats for active messages
         */
        ASSERT(clientp->ic_msgs_active != 0);
        clientp->ic_msgs_active--;
        mutex_enter(&clientp->ic_kstat_mutex);
        IBMF_SUB32_KSTATS(clientp, msgs_active, 1);
        mutex_exit(&clientp->ic_kstat_mutex);

        tmpmsg = clientp->ic_msg_list;

        while (tmpmsg != NULL) {
            if (tmpmsg == msgimplp)
                break;
            prevmsg = tmpmsg;
            tmpmsg = tmpmsg->im_msg_next;
        }

        ASSERT(tmpmsg != NULL);

        if (tmpmsg->im_msg_next == NULL)
            clientp->ic_msg_last = prevmsg;
        else
            tmpmsg->im_msg_next->im_msg_prev = prevmsg;

        if (prevmsg != NULL)
            prevmsg->im_msg_next = tmpmsg->im_msg_next;
        else
            clientp->ic_msg_list = tmpmsg->im_msg_next;
    }

    /* Save away the message reference count and clear the list flag */
    mutex_enter(&msgimplp->im_mutex);
    *refcnt = msgimplp->im_ref_count;
    msgimplp->im_flags &= ~IBMF_MSG_FLAGS_ON_LIST;
    mutex_exit(&msgimplp->im_mutex);

    mutex_exit(&clientp->ic_msg_mutex);

    IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                 ibmf_i_client_rem_msg_end, IBMF_TNF_TRACE, "",
                 "ibmf_i_client_rem_msg() exit\n");
}
Exemple #9
0
/*
 * ibmf_i_terminate_transaction():
 *	Do transaction termination processing.
 */
void
ibmf_i_terminate_transaction(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp,
    uint32_t status)
{

	IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4,
	    ibmf_i_terminate_transaction_start, IBMF_TNF_TRACE, "",
	    "ibmf_i_terminate_transaction(): clientp = 0x%p, msgp = 0x%p, "
	    "status = 0x%x\n", tnf_opaque, clientp, clientp,
	    tnf_opaque, msg, msgimplp, tnf_uint, status, status);

	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));

	msgimplp->im_msg_status = status;

	/*
	 * Cancel the transaction timer. timer is probably only active if status
	 * was not success and this is a recv operation, but unset_timer() will
	 * check.
	 */
	ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER);

	/*
	 * For unsolicited messages, do not notify the client
	 * if an error was encontered in the transfer.
	 * For solicited messages, call the transaction callback
	 * provided by the client in the message context.
	 */
	if (msgimplp->im_unsolicited == B_TRUE) {

		msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_DONE;

	} else {

		IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_terminate_transaction, IBMF_TNF_TRACE, "",
		    "ibmf_i_terminate_transaction(): %s, "
		    "trans_state_flags = 0x%x, msg_flags = 0x%x\n",
		    tnf_string, msg, "solicted message callback",
		    tnf_opaque, trans_state_flags,
		    msgimplp->im_trans_state_flags,
		    tnf_opaque, flags, msgimplp->im_flags);

		/* mark as recv_compl happened */
		msgimplp->im_trans_state_flags |=
		    IBMF_TRANS_STATE_FLAG_RECV_DONE;

		/*
		 * Check if last send is done before marking as done.
		 * We should get here for sequenced transactions and
		 * non-sequenced send RMPP transaction.
		 */
		if (msgimplp->im_trans_state_flags &
		    IBMF_TRANS_STATE_FLAG_SEND_DONE) {
			msgimplp->im_trans_state_flags |=
			    IBMF_TRANS_STATE_FLAG_DONE;
		}
	}

	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
	    ibmf_i_terminate_transaction_end, IBMF_TNF_TRACE, "",
	    "ibmf_i_terminate_transaction() exit\n");
}
Exemple #10
0
/*
 * ibmf_i_notify_client():
 * 	If the transaction is done, call the appropriate callback
 */
void
ibmf_i_notify_client(ibmf_msg_impl_t *msgimplp)
{
	ibmf_client_t	*clientp;
	ibmf_msg_cb_t	async_cb;
	void		*async_cb_arg;

	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_notify_client_start,
	    IBMF_TNF_TRACE, "", "ibmf_i_notify_client(): msgp = 0x%p\n",
	    tnf_opaque, msgimplp, msgimplp);

	clientp = msgimplp->im_client;

	/*
	 * message is removed so no more threads will find message;
	 * wait for any current clients to finish
	 */
	mutex_enter(&msgimplp->im_mutex);

	ASSERT(msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE);

	/*
	 * If the message reference count is not zero, then some duplicate
	 * MAD has arrived for this message. The thread processing the MAD
	 * found the message on the client's list before this thread was able
	 * to remove the message from the list. Since, we should not notify
	 * the client of the transaction completion until all the threads
	 * working on this message have completed (we don't want the client
	 * to free the message while a thread is working on it), we let one
	 * of the other threads notify the client of the completion once
	 * the message reference count is zero.
	 */
	if (msgimplp->im_ref_count != 0) {
		mutex_exit(&msgimplp->im_mutex);
		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
		    ibmf_i_notify_client_err, IBMF_TNF_TRACE,
		    "", "ibmf_i_notify_client(): %s\n",
		    tnf_string, msg, "message reference count != 0");
		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
		    ibmf_i_notify_client_end, IBMF_TNF_TRACE, "",
		    "ibmf_i_notify_client() exit\n");
		return;
	}

	mutex_exit(&msgimplp->im_mutex);

	/*
	 * Free up the UD dest resource so it is not tied down by
	 * the message in case the message is not freed immediately.
	 * Clean up the UD dest list as well so that excess UD dest
	 * resources are returned to the CI.
	 */
	if (msgimplp->im_ibmf_ud_dest != NULL) {
		ibmf_i_free_ud_dest(clientp, msgimplp);
		ibmf_i_clean_ud_dest_list(clientp->ic_myci, B_FALSE);
	}

	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*msgimplp))

	if (msgimplp->im_unsolicited == B_TRUE) {

		/*
		 * Do nothing if error status
		 */
		if (msgimplp->im_msg_status != IBMF_SUCCESS) {

			if (msgimplp->im_qp_hdl == IBMF_QP_HANDLE_DEFAULT) {
				mutex_enter(&clientp->ic_mutex);
				IBMF_RECV_CB_CLEANUP(clientp);
				mutex_exit(&clientp->ic_mutex);
			} else {
				ibmf_alt_qp_t *qpp =
				    (ibmf_alt_qp_t *)msgimplp->im_qp_hdl;
				mutex_enter(&qpp->isq_mutex);
				IBMF_ALT_RECV_CB_CLEANUP(qpp);
				mutex_exit(&qpp->isq_mutex);
			}

			IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
			    ibmf_i_notify_client_err, IBMF_TNF_ERROR, "",
			    "ibmf_i_notify_client(): %s, status = %d\n",
			    tnf_string, msg, "message status not success",
			    tnf_opaque, status, msgimplp->im_msg_status);

			ibmf_i_free_msg(msgimplp);

			IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
			    ibmf_i_notify_client_end, IBMF_TNF_TRACE, "",
			    "ibmf_i_notify_client() exit\n");

			return;
		}

		/*
		 * Check to see if
		 * a callback has been resgistered with the client
		 * for this unsolicited message.
		 * If one has been registered, up the recvs active
		 * count to get the teardown routine to wait until
		 * this callback is complete.
		 */
		if (msgimplp->im_qp_hdl == IBMF_QP_HANDLE_DEFAULT) {

			mutex_enter(&clientp->ic_mutex);

			if ((clientp->ic_recv_cb == NULL) ||
			    (clientp->ic_flags & IBMF_CLIENT_TEAR_DOWN_CB)) {
				IBMF_RECV_CB_CLEANUP(clientp);
				mutex_exit(&clientp->ic_mutex);
				ibmf_i_free_msg(msgimplp);
				IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
				    ibmf_i_notify_client_err, IBMF_TNF_ERROR,
				    "", "ibmf_i_notify_client(): %s\n",
				    tnf_string, msg,
				    "ibmf_tear_down_recv_cb already occurred");
				IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
				    ibmf_i_notify_client_end,
				    IBMF_TNF_TRACE, "",
				    "ibmf_i_notify_client() exit\n");
				return;
			}

			clientp->ic_msgs_alloced++;
			mutex_enter(&clientp->ic_kstat_mutex);
			IBMF_ADD32_KSTATS(clientp, msgs_alloced, 1);
			mutex_exit(&clientp->ic_kstat_mutex);

			async_cb = clientp->ic_recv_cb;
			async_cb_arg = clientp->ic_recv_cb_arg;

			mutex_exit(&clientp->ic_mutex);

			async_cb((ibmf_handle_t)clientp, (ibmf_msg_t *)msgimplp,
			    async_cb_arg);

			mutex_enter(&clientp->ic_mutex);
			IBMF_RECV_CB_CLEANUP(clientp);
			mutex_exit(&clientp->ic_mutex);

		} else {
			ibmf_alt_qp_t *qpp =
			    (ibmf_alt_qp_t *)msgimplp->im_qp_hdl;

			mutex_enter(&qpp->isq_mutex);

			if ((qpp->isq_recv_cb == NULL) ||
			    (qpp->isq_flags & IBMF_CLIENT_TEAR_DOWN_CB)) {
				IBMF_ALT_RECV_CB_CLEANUP(qpp);
				mutex_exit(&qpp->isq_mutex);
				ibmf_i_free_msg(msgimplp);
				IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
				    ibmf_i_notify_client_err, IBMF_TNF_ERROR,
				    "", "ibmf_i_notify_client(): %s\n",
				    tnf_string, msg,
				    "ibmf_tear_down_recv_cb already occurred");
				IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
				    ibmf_i_notify_client_end,
				    IBMF_TNF_TRACE, "",
				    "ibmf_i_notify_client() exit\n");
				return;
			}

			async_cb = qpp->isq_recv_cb;
			async_cb_arg = qpp->isq_recv_cb_arg;

			mutex_exit(&qpp->isq_mutex);

			mutex_enter(&clientp->ic_mutex);

			clientp->ic_msgs_alloced++;

			mutex_exit(&clientp->ic_mutex);

			mutex_enter(&clientp->ic_kstat_mutex);
			IBMF_ADD32_KSTATS(clientp, msgs_alloced, 1);
			mutex_exit(&clientp->ic_kstat_mutex);

			async_cb((ibmf_handle_t)clientp, (ibmf_msg_t *)msgimplp,
			    async_cb_arg);

			mutex_enter(&qpp->isq_mutex);
			IBMF_ALT_RECV_CB_CLEANUP(qpp);
			mutex_exit(&qpp->isq_mutex);
		}
	} else {

		/* Solicited transaction processing */

		if (msgimplp->im_trans_cb == NULL) {

			/* Processing for a blocking transaction */

			mutex_enter(&msgimplp->im_mutex);

			if (msgimplp->im_trans_state_flags &
			    IBMF_TRANS_STATE_FLAG_WAIT) {

				IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
				    ibmf_i_notify_client, IBMF_TNF_TRACE, "",
				    "ibmf_i_notify_client(): %s, msg = 0x%p\n",
				    tnf_string, msg, "Awaking thread",
				    tnf_opaque, msgimplp, msgimplp);

				cv_signal(&msgimplp->im_trans_cv);
			} else {
				IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
				    ibmf_i_notify_client, IBMF_TNF_TRACE, "",
				    "ibmf_i_notify_client(): %s, msg = 0x%p\n",
				    tnf_string, msg, "Notify client, no wait",
				    tnf_opaque, msgimplp, msgimplp);
			}

			msgimplp->im_trans_state_flags |=
			    IBMF_TRANS_STATE_FLAG_SIGNALED;

			mutex_exit(&msgimplp->im_mutex);

		} else {

			/* Processing for a non-blocking transaction */

			mutex_enter(&msgimplp->im_mutex);
			msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY;
			mutex_exit(&msgimplp->im_mutex);

			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
			    ibmf_i_notify_client, IBMF_TNF_TRACE, "",
			    "ibmf_i_notify_client(): %s, msg = 0x%p\n",
			    tnf_string, msg, "No thread is blocking",
			    tnf_opaque, msgimplp, msgimplp);

			if (msgimplp->im_trans_cb != NULL) {
				msgimplp->im_trans_cb(
				    (ibmf_handle_t)clientp,
				    (ibmf_msg_t *)msgimplp,
				    msgimplp->im_trans_cb_arg);
			}
		}
	}

	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,	ibmf_i_notify_client_end,
	    IBMF_TNF_TRACE, "", "ibmf_i_notify_client() exit\n");
}