/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file contains the routines to implement the RMPP protocol. */ #include extern ibmf_state_t *ibmf_statep; extern int ibmf_trace_level; #define IBMF_BUF_PKTS 10 static void ibmf_i_rmpp_sender_active_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad); static void ibmf_i_rmpp_sender_switch_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad); static void ibmf_i_rmpp_recvr_flow_main(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad); static void ibmf_i_rmpp_recvr_active_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad); static void ibmf_i_rmpp_recvr_term_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad); static boolean_t ibmf_i_is_valid_rmpp_status(ibmf_rmpp_hdr_t *rmpp_hdr); /* * ibmf_i_is_rmpp(): * Check if the client and QP context supports RMPP transfers */ boolean_t ibmf_i_is_rmpp(ibmf_client_t *clientp, ibmf_qp_handle_t ibmf_qp_handle) { ibmf_alt_qp_t *qpp = (ibmf_alt_qp_t *)ibmf_qp_handle; boolean_t is_rmpp; IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_is_rmpp_start, IBMF_TNF_TRACE, "", "ibmf_i_is_rmpp(): clientp = %p, " "ibmf_qp_handle = 0x%p\n", tnf_opaque, clientp, clientp, tnf_opaque, ibmf_qp_handle, ibmf_qp_handle); if ((clientp->ic_reg_flags & IBMF_REG_FLAG_RMPP) == 0) { is_rmpp = B_FALSE; } else if ((ibmf_qp_handle != IBMF_QP_HANDLE_DEFAULT) && (qpp->isq_supports_rmpp == B_FALSE)) { is_rmpp = B_FALSE; } else { is_rmpp = B_TRUE; } IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_is_rmpp_end, IBMF_TNF_TRACE, "", "ibmf_i_is_rmpp() exit, is_rmpp = %d\n", tnf_uint, is_rmpp, is_rmpp); return (is_rmpp); } /* * ibmf_i_rmpp_sender_active_flow(): * Perform RMPP processing for the sender side transaction. * Refer to figure 178 "RMPP Sender Main Flow Diagram" of * the InfiniBand Architecture Specification Volume 1, Release 1.1 */ static void ibmf_i_rmpp_sender_active_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; ibmf_rmpp_hdr_t *rmpp_hdr; uint32_t abort_status; int status; IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_start, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): clientp = 0x%p, qp_hdl = 0x%p, " "msgp = 0x%p, madp = 0x%p\n", tnf_opaque, clientp, clientp, tnf_opaque, qp_hdl, qp_hdl, tnf_opaque, msg, msgimplp, tnf_opaque, mad, mad); /* * RMPP header is located just after the MAD header for SA MADs * If this changes for Vendor MADs, we will need some way for * the client to specify the byte offset of the RMPP header * within the MAD. */ rmpp_hdr = (ibmf_rmpp_hdr_t *)(mad + sizeof (ib_mad_hdr_t)); if (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_DATA) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "Data packet received, discarding it"); /* * According to the IB spec, we discard the packet and resend * packets next_seg->window_last. However, next_seg is equal to * window_last so send_rmpp_window() will just reset the timer. */ ibmf_i_send_rmpp_window(msgimplp, IBMF_NO_BLOCK); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); return; } if (rmpp_hdr->rmpp_type != IBMF_RMPP_TYPE_ACK) { if ((rmpp_hdr->rmpp_type != IBMF_RMPP_TYPE_STOP) && (rmpp_hdr->rmpp_type != IBMF_RMPP_TYPE_ABORT)) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "Unrecognized packet received, sending ABORT"); /* abort with status BadT */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); } else { abort_status = rmpp_hdr->rmpp_status; IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s, " "status = %d\n", tnf_string, msg, "STOP or ABORT packet received, terminating", tnf_uint, abort_status, abort_status); } rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); return; } IBMF_TRACE_5(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s, " "msgp = 0x%p, recvd seg = %d wl = %d wf = %d\n", tnf_string, msg, "ACK packet received", tnf_opaque, msgp, msgimplp, tnf_uint, recvd_seg, b2h32(rmpp_hdr->rmpp_segnum), tnf_uint, wl, rmpp_ctx->rmpp_wl, tnf_uint, wf, rmpp_ctx->rmpp_wf); /* only ACK packets get here */ if (b2h32(rmpp_hdr->rmpp_segnum) > rmpp_ctx->rmpp_wl) { /* abort with status S2B */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_S2B, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "Segnum > WL"); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); return; } if (b2h32(rmpp_hdr->rmpp_segnum) < rmpp_ctx->rmpp_wf) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "Segnum < WF"); /* discard the packet by not processing it here */ /* send the window */ ibmf_i_send_rmpp_window(msgimplp, IBMF_NO_BLOCK); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); return; } /* only ACK packets with valid segnum get here */ if (b2h32(rmpp_hdr->rmpp_pyldlen_nwl) < rmpp_ctx->rmpp_wl) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "NWL < WL"); /* abort with status W2S */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_W2S, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); return; } /* is ACK of last packet */ if (b2h32(rmpp_hdr->rmpp_segnum) == rmpp_ctx->rmpp_num_pkts) { IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s, msgp = 0x%p, " "double-sided = %d\n", tnf_string, msg, "Last packet", tnf_opaque, msgimplp, msgimplp, tnf_opaque, double_sided, rmpp_ctx->rmpp_is_ds); if (rmpp_ctx->rmpp_is_ds) { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s, " "msgp = 0x%p\n", tnf_string, msg, "Doublesided,sending ACK and switching to receiver", tnf_opaque, msgimplp, msgimplp); rmpp_ctx->rmpp_is_ds = B_FALSE; rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_SENDER_SWITCH; rmpp_ctx->rmpp_wf = 1; rmpp_ctx->rmpp_wl = 1; rmpp_ctx->rmpp_es = 1; (void) ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ACK, IBMF_RMPP_STATUS_NORMAL, 0, 1, IBMF_NO_BLOCK); /* set the response timer */ ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); /* proceed with sender switch to receiver */ IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); return; } /* successful termination */ rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_DONE; ibmf_i_terminate_transaction(clientp, msgimplp, IBMF_SUCCESS); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); return; } /* update RMPP context and send the next window */ rmpp_ctx->rmpp_wf = b2h32(rmpp_hdr->rmpp_segnum) + 1; rmpp_ctx->rmpp_ns = b2h32(rmpp_hdr->rmpp_segnum) + 1; rmpp_ctx->rmpp_wl = (rmpp_ctx->rmpp_num_pkts < b2h32(rmpp_hdr->rmpp_pyldlen_nwl)) ? rmpp_ctx->rmpp_num_pkts : b2h32(rmpp_hdr->rmpp_pyldlen_nwl); IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow(): %s, " "wf = %d, wl = %d, ns = %d\n", tnf_string, msg, "sending next window", tnf_uint, wf, rmpp_ctx->rmpp_wf, tnf_uint, wl, rmpp_ctx->rmpp_wl, tnf_uint, ns, rmpp_ctx->rmpp_ns); /* send the window */ ibmf_i_send_rmpp_window(msgimplp, IBMF_NO_BLOCK); /* carry on with the protocol */ IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_active_flow() exit\n"); } /* * ibmf_i_rmpp_sender_switch_flow(): * Perform sender to receiver flow processing switch. * Refer to figure 179 "RMPP Sender Direction Switch Flow Diagram" of * the InfiniBand Architecture Specification Volume 1, Release 1.1 */ static void ibmf_i_rmpp_sender_switch_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; ibmf_rmpp_hdr_t *rmpp_hdr; int status; IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_switch_flow_start, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_switch_flow(): clientp = 0x%p, qp_hdl = 0x%p, " "msgp = 0x%p, madp = 0x%p\n", tnf_opaque, clientp, clientp, tnf_opaque, qp_hdl, qp_hdl, tnf_opaque, msg, msgimplp, tnf_opaque, mad, mad); rmpp_hdr = (ibmf_rmpp_hdr_t *)(mad + sizeof (ib_mad_hdr_t)); if (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_ACK) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_switch_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_switch_flow(): %s\n", tnf_string, msg, "ACK packet received, sending ACK"); (void) ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ACK, IBMF_RMPP_STATUS_NORMAL, 0, 1, IBMF_NO_BLOCK); /* set the response timer */ ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); } else if (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_DATA) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_sender_switch_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_switch_flow(): %s\n", tnf_string, msg, "DATA packet received, processing packet"); msgimplp->im_flags |= IBMF_MSG_FLAGS_RECV_RMPP; ibmf_i_rmpp_recvr_flow_main(clientp, qp_hdl, msgimplp, mad); } else { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_switch_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_switch_flow(): %s\n", tnf_string, msg, "Unexpected packet received, sending ABORT BADT"); /* abort with status BadT */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_sender_switch_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_switch_flow(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); } IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_sender_switch_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_sender_switch_flow() exit\n"); } /* * ibmf_i_rmpp_recvr_flow_main(): * Perform RMPP receiver flow processing. * Refer to figure 176 "RMPP Receiver Main Flow Diagram" of * the InfiniBand Architecture Specification Volume 1, Release 1.1 */ static void ibmf_i_rmpp_recvr_flow_main(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; ibmf_rmpp_hdr_t *rmpp_hdr; ib_mad_hdr_t *mad_hdr; uchar_t *msgbufp; uchar_t *datap; uint32_t data_sz, offset, num_pkts; uint32_t cl_hdr_sz, cl_data_sz, cl_hdr_off, cl_hdrdata_sz; size_t buf_sz; int status; IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_start, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): clientp = 0x%p, qp_hdl = 0x%p, " "msgp = 0x%p, madp = 0x%p\n", tnf_opaque, clientp, clientp, tnf_opaque, qp_hdl, qp_hdl, tnf_opaque, msg, msgimplp, tnf_opaque, mad, mad); rmpp_hdr = (ibmf_rmpp_hdr_t *)(mad + sizeof (ib_mad_hdr_t)); IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): " "segnum = %d, es = %d, wl = %d\n", tnf_uint, segnum, b2h32(rmpp_hdr->rmpp_segnum), tnf_uint, es, rmpp_ctx->rmpp_es, tnf_uint, wl, rmpp_ctx->rmpp_wl); /* * check that this is the segment we expected; * assume this check will succeed for the first segment since we cannot * send an ACK if we haven't allocated the rmpp context yet */ if (b2h32(rmpp_hdr->rmpp_segnum) != rmpp_ctx->rmpp_es) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "Unexpected segment number, discarding packet"); /* discard this packet by not processing it here */ /* * If the receive buffer is not yet allocated, this is * probably the first MAD received for the receive context. * We need to set up the receive buffer before calling * ibmf_i_send_rmpp() to send an ACK packet. */ if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { status = ibmf_setup_recvbuf_on_error(msgimplp, mad); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_flow_main_err, IBMF_TNF_ERROR, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "ibmf_setup_recvbuf_on_error() failed"); return; } } /* send an ACK of ES - 1 if ES is greater than 1 */ if (rmpp_ctx->rmpp_es > 1) { (void) ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ACK, IBMF_RMPP_STATUS_NORMAL, rmpp_ctx->rmpp_es - 1, rmpp_ctx->rmpp_es - 1 + IBMF_RMPP_DEFAULT_WIN_SZ, IBMF_NO_BLOCK); } /* * reset the timer if we're still waiting for the first seg; * this is the same timer that is normally set in send_compl * NOTE: this should be in the IB spec's flowchart but isn't */ if (rmpp_ctx->rmpp_es == 1) { ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); } IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main() exit\n"); return; } mad_hdr = (ib_mad_hdr_t *)mad; ibmf_i_mgt_class_to_hdr_sz_off(mad_hdr->MgmtClass, &cl_hdr_sz, &cl_hdr_off); if ((rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_FIRST_PKT) || (b2h32(rmpp_hdr->rmpp_segnum) == 1)) { /* first packet flag should be set and seg num should be 1 */ if (((rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_FIRST_PKT) == 0) || (b2h32(rmpp_hdr->rmpp_segnum) != 1)) { /* * If the receive buffer is not yet allocated, this is * probably the first MAD received for the receive ctx. * We need to set up the receive buffer before calling * ibmf_i_send_rmpp() to send an ABORT packet. */ if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { status = ibmf_setup_recvbuf_on_error(msgimplp, mad); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_flow_main_err, IBMF_TNF_ERROR, "", "ibmf_i_rmpp_recvr_flow_main(): " "%s\n", tnf_string, msg, "ibmf_setup_recvbuf_on_error() " "failed"); return; } } /* abort with status BadT */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_IFSN, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "Inconsistent first and segment " "number detected, sending ABORT IFSN"); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main() exit\n"); return; } IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "Segment number 1 received:", tnf_opaque, msgp, msgimplp); cl_data_sz = MAD_SIZE_IN_BYTES - sizeof (ib_mad_hdr_t) - cl_hdr_off - cl_hdr_sz; cl_hdrdata_sz = MAD_SIZE_IN_BYTES - sizeof (ib_mad_hdr_t) - cl_hdr_off; /* * Calculate the number of packets by dividing the payload * length in the RMPP header by the payload size for * a single packet of that management class (including the * class header). */ buf_sz = b2h32(rmpp_hdr->rmpp_pyldlen_nwl); if ((buf_sz % cl_hdrdata_sz) != 0) num_pkts = (buf_sz / cl_hdrdata_sz) + 1; else { if (buf_sz > 0) num_pkts = buf_sz / cl_hdrdata_sz; else num_pkts = 1; } /* * If the payload length of the message is not specified * in the first packet's RMPP header, we create a * temporary receive buffer with space for data payloads * of IBMF_BUF_PKTS packets. If the number of packets * received exceeds the capacity in the receive buffer, * the temporary receive buffer will be freed up, and * a larger temporary receive buffer will be allocated. * When the last packet is received, the final receive * buffer will be allocated with the real size of the message. * The data will be copied from the old buffer to the new * buffer. */ if (b2h32(rmpp_hdr->rmpp_pyldlen_nwl) != 0) { /* * rmpp_pyld_len is the total length of just the * class data. Class headers from each packet are * not included in this calculation. */ msgimplp->im_msgbufs_recv.im_bufs_cl_data_len = rmpp_ctx->rmpp_pyld_len = b2h32(rmpp_hdr->rmpp_pyldlen_nwl) - (num_pkts * cl_hdr_sz); } else { msgimplp->im_msgbufs_recv.im_bufs_cl_data_len = rmpp_ctx->rmpp_pyld_len = IBMF_BUF_PKTS * cl_data_sz; rmpp_ctx->rmpp_flags |= IBMF_CTX_RMPP_FLAGS_DYN_PYLD; } ASSERT(msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL); /* allocate memory for the message data */ msgimplp->im_msgbufs_recv.im_bufs_mad_hdr = (ib_mad_hdr_t *)kmem_zalloc(sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + rmpp_ctx->rmpp_pyld_len, KM_NOSLEEP); if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "mem allocation failure (known rmpp payload)"); ibmf_i_terminate_transaction( msgimplp->im_client, msgimplp, IBMF_NO_MEMORY); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main() exit\n"); return; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, recv_bufs_alloced, 1); mutex_exit(&clientp->ic_kstat_mutex); msgbufp = (uchar_t *)msgimplp->im_msgbufs_recv.im_bufs_mad_hdr; /* copy the MAD and class header */ bcopy((const void *)mad, (void *)msgbufp, sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz); offset = sizeof (ib_mad_hdr_t) + cl_hdr_off; /* initialize class header pointer */ if (cl_hdr_sz == 0) { msgimplp->im_msgbufs_recv.im_bufs_cl_hdr = NULL; } else { msgimplp->im_msgbufs_recv.im_bufs_cl_hdr = (void *)(msgbufp + offset); } msgimplp->im_msgbufs_recv.im_bufs_cl_hdr_len = cl_hdr_sz; offset += cl_hdr_sz; /* initialize data area pointer */ msgimplp->im_msgbufs_recv.im_bufs_cl_data = (void *)(msgbufp + offset); rmpp_ctx->rmpp_data_offset = 0; cl_data_sz = MAD_SIZE_IN_BYTES - sizeof (ib_mad_hdr_t) - cl_hdr_off - cl_hdr_sz; rmpp_ctx->rmpp_pkt_data_sz = cl_data_sz; /* * calculate number of expected packets for transaction * timeout calculation */ if (rmpp_ctx->rmpp_flags & IBMF_CTX_RMPP_FLAGS_DYN_PYLD) { /* * if the payload length is not specified in * the first packet, just guess how many packets * might arrive */ msgimplp->im_rmpp_ctx.rmpp_num_pkts = 100; } else { msgimplp->im_rmpp_ctx.rmpp_num_pkts = rmpp_ctx->rmpp_pyld_len / cl_data_sz; /* round up */ if ((rmpp_ctx->rmpp_pyld_len % cl_data_sz) != 0) msgimplp->im_rmpp_ctx.rmpp_num_pkts++; } /* set the transaction timer if there are more packets */ if ((rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_LAST_PKT) == 0) { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "First pkt recvd; setting trans timer: ", tnf_opaque, msg, msgimplp); IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): setting trans" " timer %p %d\n", tnf_opaque, msg, msgimplp, tnf_opaque, timeout_id, msgimplp->im_rp_timeout_id); ibmf_i_set_timer(ibmf_i_recv_timeout, msgimplp, IBMF_TRANS_TIMER); } } offset = sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz; /* * copy the data from the packet into the data buffer in * the message. */ if (rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_LAST_PKT) data_sz = b2h32(rmpp_hdr->rmpp_pyldlen_nwl) - cl_hdr_sz; else data_sz = rmpp_ctx->rmpp_pkt_data_sz; /* if a payload length was specified and we've met or exceeded it */ if (((data_sz + rmpp_ctx->rmpp_data_offset) >= rmpp_ctx->rmpp_pyld_len) && ((rmpp_ctx->rmpp_flags & IBMF_CTX_RMPP_FLAGS_DYN_PYLD) == 0)) { /* last packet flag should be set */ if ((rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_LAST_PKT) == 0) { /* abort with status Incon. last and payload length */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_ILPL, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "Inconsistent last and payload length detected," " sending ABORT ILPL, unsetting trans timer"); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main() exit\n"); return; } } else if (((data_sz + rmpp_ctx->rmpp_data_offset) >= rmpp_ctx->rmpp_pyld_len) && ((rmpp_ctx->rmpp_flags & IBMF_CTX_RMPP_FLAGS_DYN_PYLD) != 0) && ((rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_LAST_PKT) == 0)) { /* * If the payload length was not specified in the first * packet's RMPP header, we have a temporary receive buffer * the size of which will be exceeded with this incoming * packet. We need to allocate a new temporary receive buffer * with an additional IBMF_BUF_PKTS data payloads. */ ib_mad_hdr_t *old_buf; size_t prev_pyld_len; old_buf = msgimplp->im_msgbufs_recv.im_bufs_mad_hdr; prev_pyld_len = rmpp_ctx->rmpp_pyld_len; rmpp_ctx->rmpp_pyld_len += IBMF_BUF_PKTS * rmpp_ctx->rmpp_pkt_data_sz; msgimplp->im_msgbufs_recv.im_bufs_cl_data_len = rmpp_ctx->rmpp_pyld_len; msgimplp->im_msgbufs_recv.im_bufs_mad_hdr = (ib_mad_hdr_t *)kmem_zalloc(sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + rmpp_ctx->rmpp_pyld_len, KM_NOSLEEP); if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s, allocsz = %d\n", tnf_string, msg, "mem allocation failure (unknown rmpp payload)", tnf_uint, alloc_size, sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + rmpp_ctx->rmpp_pyld_len); ibmf_i_terminate_transaction( msgimplp->im_client, msgimplp, IBMF_NO_MEMORY); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main() exit\n"); return; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, recv_bufs_alloced, 1); mutex_exit(&clientp->ic_kstat_mutex); msgbufp = (uchar_t *)msgimplp->im_msgbufs_recv.im_bufs_mad_hdr; /* copy the MAD and class header */ bcopy((const void *)old_buf, (void *)msgbufp, sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + prev_pyld_len); kmem_free(old_buf, sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + prev_pyld_len); } /* don't overflow buffer */ if (rmpp_ctx->rmpp_data_offset + data_sz > rmpp_ctx->rmpp_pyld_len) { data_sz = rmpp_ctx->rmpp_pyld_len - rmpp_ctx->rmpp_data_offset; } datap = (uchar_t *)msgimplp->im_msgbufs_recv.im_bufs_cl_data; bcopy((void *)&mad[offset], (void *)(datap + rmpp_ctx->rmpp_data_offset), data_sz); rmpp_ctx->rmpp_data_offset += data_sz; rmpp_ctx->rmpp_es++; IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): es = %d\n", tnf_uint, es, rmpp_ctx->rmpp_es); if (rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_LAST_PKT) { /* * Since this is the last packet, we finally know the * size of the receive buffer we need to allocate. * Allocate the needed size and free the temporary receive * buffer. */ if ((rmpp_ctx->rmpp_flags & IBMF_CTX_RMPP_FLAGS_DYN_PYLD) != 0) { ib_mad_hdr_t *old_buf; size_t prev_pyld_len; rmpp_ctx->rmpp_flags &= ~IBMF_CTX_RMPP_FLAGS_DYN_PYLD; old_buf = msgimplp->im_msgbufs_recv.im_bufs_mad_hdr; prev_pyld_len = rmpp_ctx->rmpp_pyld_len; rmpp_ctx->rmpp_pyld_len = rmpp_ctx->rmpp_data_offset; msgimplp->im_msgbufs_recv.im_bufs_cl_data_len = rmpp_ctx->rmpp_pyld_len; msgimplp->im_msgbufs_recv.im_bufs_mad_hdr = (ib_mad_hdr_t *)kmem_zalloc(sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + rmpp_ctx->rmpp_pyld_len, KM_NOSLEEP); if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "mem allocation failure (final payload)"); ibmf_i_terminate_transaction( msgimplp->im_client, msgimplp, IBMF_NO_MEMORY); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main() exit\n"); return; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, recv_bufs_alloced, 1); mutex_exit(&clientp->ic_kstat_mutex); msgbufp = (uchar_t *) msgimplp->im_msgbufs_recv.im_bufs_mad_hdr; /* copy the data to the new buffer */ bcopy((const void *)old_buf, (void *)msgbufp, sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + rmpp_ctx->rmpp_pyld_len); offset = sizeof (ib_mad_hdr_t) + cl_hdr_off; /* initialize class header pointer */ if (cl_hdr_sz == 0) { msgimplp->im_msgbufs_recv.im_bufs_cl_hdr = NULL; } else { msgimplp->im_msgbufs_recv.im_bufs_cl_hdr = (void *)(msgbufp + offset); } msgimplp->im_msgbufs_recv.im_bufs_cl_hdr_len = cl_hdr_sz; offset += cl_hdr_sz; /* initialize data area pointer */ msgimplp->im_msgbufs_recv.im_bufs_cl_data = (void *)(msgbufp + offset); kmem_free(old_buf, sizeof (ib_mad_hdr_t) + cl_hdr_off + cl_hdr_sz + prev_pyld_len); } IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p\n", tnf_string, msg, "Last pkt rcvd; state to recv_term, sending ack", tnf_opaque, msgp, msgimplp); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_RECEVR_TERMINATE; status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ACK, IBMF_RMPP_STATUS_NORMAL, rmpp_ctx->rmpp_es - 1, rmpp_ctx->rmpp_es - 1 + IBMF_RMPP_DEFAULT_WIN_SZ, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "RMPP ACK send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } /* unset the transaction timer if it's not the first segment */ if ((rmpp_hdr->rmpp_flags & IBMF_RMPP_FLAGS_FIRST_PKT) == 0) { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p\n", tnf_string, msg, "Last, but not first segment", tnf_opaque, msgp, msgimplp); IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): unsetting timer " "%p %d\n", tnf_opaque, msgp, msgimplp, tnf_opaque, timeout_id, msgimplp->im_rp_timeout_id); ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); } IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p\n", tnf_string, msg, "Last pkt rcvd; setting resp timer", tnf_opaque, msgp, msgimplp); /* * The RMPP receive transaction has been broken * up into two parts. At this point in the * transaction, all the data has been received. * From the perspective of the client, the transaction * is complete. So, control is returned to the client * at this point. However, the RMPP protocol requires * a wait after receiving the last data packet, so that, * duplicate packets may be absorbed. This wait is * implemented in the second part of the transaction under * a duplicate message context. * The regular message context is marked as done in * ibmf_i_terminate_transaction(). * The IBMF_MSG_FLAGS_SET_TERMINATION flag indicates * that the duplicate message context needs to be created * to handle the termination loop. */ IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): last packet, " " returning data to client for message %p\n", tnf_opaque, msgp, msgimplp); ibmf_i_terminate_transaction(clientp, msgimplp, IBMF_SUCCESS); /* Mark this message for early termination */ msgimplp->im_flags |= IBMF_MSG_FLAGS_SET_TERMINATION; return; } if (b2h32(rmpp_hdr->rmpp_segnum) == rmpp_ctx->rmpp_wl) { IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p" "segnum = %d, wl = %d\n", tnf_string, msg, "Last packet in window received", tnf_opaque, msgimplp, msgimplp, tnf_opaque, seg, b2h32(rmpp_hdr->rmpp_segnum), tnf_opaque, wl, rmpp_ctx->rmpp_wl); (void) ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ACK, IBMF_RMPP_STATUS_NORMAL, rmpp_ctx->rmpp_es - 1, rmpp_ctx->rmpp_es - 1 + IBMF_RMPP_DEFAULT_WIN_SZ, IBMF_NO_BLOCK); /* update the window */ rmpp_ctx->rmpp_wl += IBMF_RMPP_DEFAULT_WIN_SZ; } else { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_flow_main, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main(): %s\n", tnf_string, msg, "Packet in window received"); } IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_flow_main_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_flow_main() exit\n"); } /* * ibmf_i_rmpp_recvr_active_flow(): * Perform RMPP receiver flow initiation processing. * Refer to figure 176 "RMPP Receiver Main Flow Diagram" of * the InfiniBand Architecture Specification Volume 1, Release 1.1 */ static void ibmf_i_rmpp_recvr_active_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; ibmf_rmpp_hdr_t *rmpp_hdr; uint32_t abort_status; int status; IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_active_flow_start, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_active_flow(): clientp = 0x%p, qp_hdl = 0x%p, " "msgp = 0x%p, madp = 0x%p\n", tnf_opaque, clientp, clientp, tnf_opaque, qp_hdl, qp_hdl, tnf_opaque, msg, msgimplp, tnf_opaque, mad, mad); rmpp_hdr = (ibmf_rmpp_hdr_t *)(mad + sizeof (ib_mad_hdr_t)); if (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_ACK) { /* discard this packet by not processing it here */ IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_active_flow(): %s\n", tnf_string, msg, "ACK packet received, discarding packet"); /* * reset the timer if we're still waiting for the first seg; * this is the same timer that is normally set in send_compl * NOTE: this should be in the IB spec's flowchart but isn't */ if (rmpp_ctx->rmpp_es == 1) { ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); } return; } if (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_DATA) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_active_flow(): %s\n", tnf_string, msg, "DATA packet received, processing packet"); ibmf_i_rmpp_recvr_flow_main(clientp, qp_hdl, msgimplp, mad); } else if ((rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_STOP) || (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_ABORT)) { abort_status = rmpp_hdr->rmpp_status; IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_active_flow(): %s, status = %d\n", tnf_string, msg, "STOP/ABORT packet received, terminating transaction", tnf_uint, abort_status, abort_status); /* discard the packet and terminate the transaction */ rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); } else { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_active_flow(): %s\n", tnf_string, msg, "Unrecognized packet received, terminating transaction"); /* abort with status BadT */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_active_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_active_flow(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); } IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_active_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_active_flow() exit\n"); } /* * ibmf_i_rmpp_recvr_term_flow(): * Perform RMPP receiver termination flow processing. * Refer to figure 177 "RMPP Receiver Termination Flow Diagram" of * the InfiniBand Architecture Specification Volume 1, Release 1.1 */ static void ibmf_i_rmpp_recvr_term_flow(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *mad) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; ibmf_rmpp_hdr_t *rmpp_hdr; int status; IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_term_flow_start, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): clientp = 0x%p, qp_hdl = 0x%p, " "msgp = 0x%p, madp = 0x%p\n", tnf_opaque, clientp, clientp, tnf_opaque, qp_hdl, qp_hdl, tnf_opaque, msg, msgimplp, tnf_opaque, mad, mad); rmpp_hdr = (ibmf_rmpp_hdr_t *)(mad + sizeof (ib_mad_hdr_t)); if (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_DATA) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_term_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): %s\n", tnf_string, msg, "Data packet received, resending ACK"); (void) ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ACK, IBMF_RMPP_STATUS_NORMAL, rmpp_ctx->rmpp_es - 1, rmpp_ctx->rmpp_es - 1 + IBMF_RMPP_DEFAULT_WIN_SZ, IBMF_NO_BLOCK); IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_term_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): setting resp timer %d %p\n", tnf_opaque, msgimplp, msgimplp, tnf_opaque, timeout_id, msgimplp->im_rp_timeout_id); /* set the response timer */ ibmf_i_set_timer(ibmf_i_recv_timeout, msgimplp, IBMF_RESP_TIMER); } else if (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_ACK) { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_term_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): %s, msgp = 0x%p\n", tnf_string, msg, "ACK packet received", tnf_opaque, msgimplp, msgimplp); if (rmpp_ctx->rmpp_is_ds) { /* * received ACK from sender which is indication that * we can send response; notify client that data has * arrived; it will call msg_transport to send response */ IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_rmpp_recvr_term_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): %s, msgp = 0x%p\n", tnf_string, msg, "Received final ack for double-sided trans", tnf_opaque, msgimplp, msgimplp); /* * successful termination */ rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_DONE; ibmf_i_terminate_transaction(clientp, msgimplp, IBMF_SUCCESS); } else { IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_term_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): %s, msgp = 0x%p\n", tnf_string, msg, "Received ACK while in recv_term " "state for single sided trans", tnf_opaque, msgimplp, msgimplp); /* abort with status BadT */ (void) ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); } } else { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_term_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): %s\n", tnf_string, msg, "Unexpected packet received, sending ABORT BADT"); /* abort with status BadT */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_rmpp_recvr_term_flow, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); } IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_rmpp_recvr_term_flow_end, IBMF_TNF_TRACE, "", "ibmf_i_rmpp_recvr_term_flow() exit\n"); } /* * ibmf_i_is_valid_rmpp_status(): * Check for a valid RMPP status */ static boolean_t ibmf_i_is_valid_rmpp_status(ibmf_rmpp_hdr_t *rmpp_hdr) { boolean_t found = B_TRUE; IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_is_valid_rmpp_status_start, IBMF_TNF_TRACE, "", "ibmf_i_is_valid_rmpp_status(): rmpp_hdr = 0x%p\n", tnf_opaque, rmpp_hdr, rmpp_hdr); if (((rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_DATA) || (rmpp_hdr->rmpp_type == IBMF_RMPP_TYPE_ACK)) && (rmpp_hdr->rmpp_status != IBMF_RMPP_STATUS_NORMAL)) found = B_FALSE; if ((rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_RESX) && (rmpp_hdr->rmpp_type != IBMF_RMPP_TYPE_STOP)) found = B_FALSE; if (((rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_T2L) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_ILPL) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_IFSN) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_BADT) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_W2S) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_S2B) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_IS) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_UNV) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_TMR) || (rmpp_hdr->rmpp_status == IBMF_RMPP_STATUS_USP)) && (rmpp_hdr->rmpp_type != IBMF_RMPP_TYPE_ABORT)) found = B_FALSE; IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_is_valid_rmpp_status_end, IBMF_TNF_TRACE, "", "ibmf_i_is_valid_rmpp_status_flow() exit\n"); return (found); } /* * ibmf_i_handle_rmpp(): * Handle RMPP processing of an incoming IB packet */ void ibmf_i_handle_rmpp(ibmf_client_t *clientp, ibmf_qp_handle_t qp_hdl, ibmf_msg_impl_t *msgimplp, uchar_t *madp) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; ibmf_rmpp_hdr_t *rmpp_hdr; int status; IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_handle_rmpp_start, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): clientp = 0x%p, qp_hdl = 0x%p, " "msgp = 0x%p, madp = 0x%p\n", tnf_opaque, clientp, clientp, tnf_opaque, qp_hdl, qp_hdl, tnf_opaque, msg, msgimplp, tnf_opaque, mad, madp); ASSERT(MUTEX_HELD(&msgimplp->im_mutex)); rmpp_hdr = (ibmf_rmpp_hdr_t *)(madp + sizeof (ib_mad_hdr_t)); /* * Check the version in the RMPP header */ if (rmpp_hdr->rmpp_version != IBMF_RMPP_VERSION) { /* * If the receive buffer is not yet allocated, this is * probably the first MAD received for the receive context. * We need to set up the receive buffer before calling * ibmf_i_send_rmpp() to send an ABORT packet. */ if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { status = ibmf_setup_recvbuf_on_error(msgimplp, madp); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L2, ibmf_i_handle_rmpp_err, IBMF_TNF_ERROR, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "ibmf_setup_recvbuf_on_error() failed"); return; } } /* * Drop the message if the transaction has not yet * been identified as a send or receive RMPP transaction. * This is because the send completion of an abort packet * will hit the non-rmpp code which attempts to reset the * RESP timer set after sending the abort packet, causing * an assert. */ if (((msgimplp->im_flags & IBMF_MSG_FLAGS_RECV_RMPP) == 0) && (msgimplp->im_flags & IBMF_MSG_FLAGS_SEND_RMPP) == 0) { /* * Reset the response timer since we're still * waiting for the first response MAD, provided * that the send completion has occured */ if (msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_SEND_DONE) { ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); } IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_handle_rmpp, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "BAD version detected, dropping MAD"); return; } /* abort with status BadT */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_UNV, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L2, ibmf_i_handle_rmpp_err, IBMF_TNF_ERROR, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_handle_rmpp, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "Unsupported RMPP version detected, sending ABORT UNV"); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_handle_rmpp_end, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp() exit\n"); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); return; } /* * Check for a valid status in the RMPP header */ if (ibmf_i_is_valid_rmpp_status(rmpp_hdr) != B_TRUE) { /* * If the receive buffer is not yet allocated, this is * probably the first MAD received for the receive context. * We need to set up the receive buffer before calling * ibmf_i_send_rmpp() to send an ABORT packet. */ if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { status = ibmf_setup_recvbuf_on_error(msgimplp, madp); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L2, ibmf_i_handle_rmpp_err, IBMF_TNF_ERROR, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "ibmf_setup_recvbuf_on_error() failed"); return; } } /* * Drop the message if the transaction has not yet * been identified as a send or receive RMPP transaction. * This is because the send completion of an abort packet * will hit the non-rmpp code which attempts to reset the * RESP timer set after sending the abort packet, causing * an assert. */ if (((msgimplp->im_flags & IBMF_MSG_FLAGS_RECV_RMPP) == 0) && (msgimplp->im_flags & IBMF_MSG_FLAGS_SEND_RMPP) == 0) { /* * Reset the response timer since we're still * waiting for the first response MAD, provided * that the send completion has occured */ if (msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_SEND_DONE) { ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); } IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_handle_rmpp, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "Invalid RMPP status detected, dropping MAD"); return; } /* abort with status BadT */ status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_IS, 0, 0, IBMF_NO_BLOCK); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L2, ibmf_i_handle_rmpp_err, IBMF_TNF_ERROR, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "RMPP ABORT send failed"); msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_SEND_DONE; } mutex_enter(&clientp->ic_kstat_mutex); IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1); mutex_exit(&clientp->ic_kstat_mutex); IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_handle_rmpp, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "Invalid RMPP status detected, sending ABORT IS"); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_handle_rmpp_end, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp() exit\n"); rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT; ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER); ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); return; } /* * We could check the MAD here and do an optional abort. * This abort if the MAD header is bad is not required by the spec. * Also, we should account for RRespTime here. */ /* * The RMPP engine has four execution flow paths corresponding * to the four states the RMPP state machine can be in at any * given time. The packet will be dropped if the context is not in any * of these four states. */ switch (rmpp_ctx->rmpp_state) { case IBMF_RMPP_STATE_SENDER_ACTIVE : ibmf_i_rmpp_sender_active_flow(clientp, qp_hdl, msgimplp, madp); break; case IBMF_RMPP_STATE_SENDER_SWITCH : ibmf_i_rmpp_sender_switch_flow(clientp, qp_hdl, msgimplp, madp); break; case IBMF_RMPP_STATE_RECEVR_ACTIVE : ibmf_i_rmpp_recvr_active_flow(clientp, qp_hdl, msgimplp, madp); break; case IBMF_RMPP_STATE_RECEVR_TERMINATE : ibmf_i_rmpp_recvr_term_flow(clientp, qp_hdl, msgimplp, madp); break; default: /* Including IBMF_RMPP_STATE_ABORT */ IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2, ibmf_i_handle_rmpp, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): %s, rmpp_state = 0x%x\n", tnf_string, msg, "Dropping packet", tnf_opaque, rmpp_state, rmpp_ctx->rmpp_state); /* Reinitiate the resp timer if the state is ABORT */ if (rmpp_ctx->rmpp_state == IBMF_RMPP_STATE_ABORT) { ibmf_i_set_timer(ibmf_i_err_terminate_timeout, msgimplp, IBMF_RESP_TIMER); return; } /* * Drop the message if the transaction has not yet * been identified as a send or receive RMPP transaction. */ if (((msgimplp->im_flags & IBMF_MSG_FLAGS_RECV_RMPP) == 0) && (msgimplp->im_flags & IBMF_MSG_FLAGS_SEND_RMPP) == 0) { /* * Reset the response timer since we're still * waiting for the first response MAD, provided * that the send completion has occured */ if (msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_SEND_DONE) { ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); } IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_handle_rmpp, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): %s\n", tnf_string, msg, "BAD 1st RMPP packet, dropping MAD"); return; } } IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_handle_rmpp_end, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp() exit\n"); } /* * ibmf_i_send_rmpp(): * ibmf_i_send_rmpp() is called to send any * type RMPP packet. The RMPP status is passed in as an argument. * In addition, the segment field and the payload length / new window last * field are passed in as arguments. */ int ibmf_i_send_rmpp(ibmf_msg_impl_t *msgimplp, uint8_t rmpp_type, uint8_t rmpp_status, uint32_t segno, uint32_t nwl, int block) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; int status; IBMF_TRACE_5(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_start, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp(): msgp = 0x%p, rmpp_type = 0x%x, " "rmpp_status = %d, segno = %d, nwl = %d\n", tnf_opaque, msg, msgimplp, tnf_uint, rmpp_type, rmpp_type, tnf_uint, rmpp_status, rmpp_status, tnf_uint, segno, segno, tnf_uint, nwl, nwl); IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_send_rmpp, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp(): msgp = 0x%p, " "next_seg = 0x%x, num_pkts = %d\n", tnf_opaque, msg, msgimplp, tnf_uint, next_seg, msgimplp->im_rmpp_ctx.rmpp_ns, tnf_uint, num_pkts, msgimplp->im_rmpp_ctx.rmpp_num_pkts); ASSERT(MUTEX_HELD(&msgimplp->im_mutex)); rmpp_ctx->rmpp_type = rmpp_type; rmpp_ctx->rmpp_status = rmpp_status; rmpp_ctx->rmpp_word3 = segno; rmpp_ctx->rmpp_word4 = nwl; /* * send packet without blocking */ status = ibmf_i_send_pkt(msgimplp->im_client, msgimplp->im_qp_hdl, msgimplp, block); if (status != IBMF_SUCCESS) { IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_send_rmpp_err, IBMF_TNF_ERROR, "", "ibmf_i_send_rmpp(): %s, status = %d\n", tnf_string, msg, "unable to send packet", tnf_uint, status, status); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_end, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp() exit\n"); return (status); } IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_end, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp() exit\n"); return (IBMF_SUCCESS); } /* * ibmf_i_send_rmpp_window(): * Send an RMPP protocol window of packets */ void ibmf_i_send_rmpp_window(ibmf_msg_impl_t *msgimplp, int block) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; int status, i, numpkts = rmpp_ctx->rmpp_wl - rmpp_ctx->rmpp_ns + 1; uint32_t payload_length, cl_hdr_sz, cl_hdr_off; IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_window_start, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp_window(): msgp = 0x%p\n", tnf_opaque, msg, msgimplp); IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_send_rmpp_window, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): wl = %d " "ns = %d, num_pkts = %d\n", tnf_uint, wl, rmpp_ctx->rmpp_wl, tnf_uint, ns, rmpp_ctx->rmpp_ns, tnf_uint, num_pkts, rmpp_ctx->rmpp_num_pkts); ASSERT(MUTEX_HELD(&msgimplp->im_mutex)); ibmf_i_mgt_class_to_hdr_sz_off( msgimplp->im_msgbufs_send.im_bufs_mad_hdr->MgmtClass, &cl_hdr_sz, &cl_hdr_off); for (i = 1; i <= numpkts; i++) { if (rmpp_ctx->rmpp_ns == 1) payload_length = rmpp_ctx->rmpp_pyld_len + (rmpp_ctx->rmpp_num_pkts * cl_hdr_sz); else if (rmpp_ctx->rmpp_ns == rmpp_ctx->rmpp_num_pkts) payload_length = rmpp_ctx->rmpp_last_pkt_sz + cl_hdr_sz; else payload_length = rmpp_ctx->rmpp_pkt_data_sz; IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_send_rmpp_window, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): ns = %d, " "pl = %d\n", tnf_uint, ns, rmpp_ctx->rmpp_ns, tnf_uint, pl, payload_length); status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_DATA, IBMF_RMPP_STATUS_NORMAL, rmpp_ctx->rmpp_ns, payload_length, block); if (status != IBMF_SUCCESS) { IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_send_rmpp_window_err, IBMF_TNF_ERROR, "", "ibmf_i_send_rmpp_window(): %s\n", tnf_string, msg, "Send rmpp window failed"); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_window_end, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp_window() exit\n"); return; } rmpp_ctx->rmpp_ns++; rmpp_ctx->rmpp_data_offset += rmpp_ctx->rmpp_pkt_data_sz; } /* Set the response timer */ IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_send_rmpp_window, IBMF_TNF_TRACE, "", "ibmf_i_handle_rmpp(): setting timer %p %d\n", tnf_opaque, msgimplp, msgimplp, tnf_opaque, timeout_id, msgimplp->im_rp_timeout_id); ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp, IBMF_RESP_TIMER); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_window_end, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp_window() exit\n"); } /* * ibmf_i_send_rmpp_pkts(): * Send a message using the RMPP protocol */ int ibmf_i_send_rmpp_pkts(ibmf_client_t *clientp, ibmf_qp_handle_t ibmf_qp_handle, ibmf_msg_impl_t *msgimplp, boolean_t isDS, int block) { ibmf_rmpp_ctx_t *rmpp_ctx = &msgimplp->im_rmpp_ctx; size_t buf_sz = msgimplp->im_msgbufs_send.im_bufs_cl_data_len; uint32_t num_pkts, resid; uint32_t cl_hdr_sz, cl_data_sz, cl_hdr_off; IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_pkts_start, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp_pkts(): clientp = 0x%p, " "qphdl = 0x%p, msgp = 0x%p, block = %d\n", tnf_opaque, clientp, clientp, tnf_opaque, qphdl, ibmf_qp_handle, tnf_opaque, msg, msgimplp, tnf_uint, block, block); ASSERT(MUTEX_HELD(&msgimplp->im_mutex)); ibmf_i_mgt_class_to_hdr_sz_off( msgimplp->im_msgbufs_send.im_bufs_mad_hdr->MgmtClass, &cl_hdr_sz, &cl_hdr_off); cl_data_sz = MAD_SIZE_IN_BYTES - sizeof (ib_mad_hdr_t) - cl_hdr_off - cl_hdr_sz; if ((resid = (buf_sz % cl_data_sz)) != 0) num_pkts = (buf_sz / cl_data_sz) + 1; else { if (buf_sz > 0) num_pkts = buf_sz / cl_data_sz; else num_pkts = 1; } rmpp_ctx->rmpp_wf = 1; rmpp_ctx->rmpp_wl = 1; rmpp_ctx->rmpp_ns = 1; rmpp_ctx->rmpp_is_ds = isDS; rmpp_ctx->rmpp_pyld_len = buf_sz; rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_SENDER_ACTIVE; rmpp_ctx->rmpp_type = IBMF_RMPP_TYPE_DATA; rmpp_ctx->rmpp_respt = IBMF_RMPP_TERM_RRESPT; rmpp_ctx->rmpp_status = IBMF_RMPP_STATUS_NORMAL; rmpp_ctx->rmpp_num_pkts = num_pkts; rmpp_ctx->rmpp_pkt_data_sz = (buf_sz < cl_data_sz) ? buf_sz : cl_data_sz; rmpp_ctx->rmpp_last_pkt_sz = (resid == 0) ? ((buf_sz == 0) ? 0 : cl_data_sz) : resid; rmpp_ctx->rmpp_data_offset = 0; ibmf_i_send_rmpp_window(msgimplp, block); IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_rmpp_pkts_end, IBMF_TNF_TRACE, "", "ibmf_i_send_rmpp_pkts() exit\n"); return (IBMF_SUCCESS); }