/* 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_notify_sequence() * Checks for the need to create a termination context before * notifying the client. */ void ibmf_i_notify_sequence(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp, int msg_flags) { int status; IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_notify_sequence_start, IBMF_TNF_TRACE, "", "ibmf_i_notify_sequence() enter, clientp = %p, msgimplp = %p\n", tnf_opaque, clientp, clientp, tnf_opaque, msgimplp, msgimplp); if (msg_flags & IBMF_MSG_FLAGS_TERMINATION) { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_notify_sequence, IBMF_TNF_TRACE, "", "ibmf_i_notify_sequence(): %s, " "msgimplp = %p\n", tnf_string, msg, "IBMF_MSG_FLAGS_TERMINATION already set", tnf_opaque, msgimplp, msgimplp); return; } if (msg_flags & IBMF_MSG_FLAGS_SET_TERMINATION) { /* * In some cases, we need to check if the termination context * needs to be set up for early termination of non-double-sided * RMPP receiver transactions. In these cases we set up the * termination context, and then notify the client. * If the set up of the termination context fails, attempt to * reverse state to the regular context, and set the response * timer for the termination timeout and exit without notifying * the client in this failure case. If the setting of the * response timer fails, simply notify the client without * going through the process of timing out in the response * timer. */ status = ibmf_setup_term_ctx(clientp, msgimplp); if (status != IBMF_SUCCESS) { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_notify_sequence, IBMF_TNF_TRACE, "", "ibmf_i_notify_sequence(): %s, " "msgimplp = %p\n", tnf_string, msg, "ibmf_setup_term_ctx() failed," "reversing to regular termination", tnf_opaque, msgimplp, msgimplp); mutex_enter(&msgimplp->im_mutex); ibmf_i_set_timer(ibmf_i_recv_timeout, msgimplp, IBMF_RESP_TIMER); /* * Set the flags cleared in * ibmf_i_terminate_transaction() */ msgimplp->im_trans_state_flags &= ~IBMF_TRANS_STATE_FLAG_DONE; msgimplp->im_trans_state_flags &= ~IBMF_TRANS_STATE_FLAG_RECV_DONE; mutex_exit(&msgimplp->im_mutex); /* Re-add the message to the list */ ibmf_i_client_add_msg(clientp, msgimplp); } else { /* * The termination context has been * set up. Notify the client that the * regular message is done. */ ibmf_i_notify_client(msgimplp); } } else { ibmf_i_notify_client(msgimplp); } IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_notify_sequence_end, IBMF_TNF_TRACE, "", "ibmf_i_notify_sequence() exit, msgimplp = %p\n", tnf_opaque, msgimplp, msgimplp); }