/*
* 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
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains the routines to implement the RMPP protocol.
*/
extern ibmf_state_t *ibmf_statep;
extern int ibmf_trace_level;
/*
* ibmf_i_is_rmpp():
* Check if the client and QP context supports RMPP transfers
*/
{
} else if ((ibmf_qp_handle != IBMF_QP_HANDLE_DEFAULT) &&
} else {
}
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
{
int status;
"ibmf_i_rmpp_sender_active_flow(): clientp = 0x%p, qp_hdl = 0x%p, "
/*
* 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.
*/
"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_rmpp_sender_active_flow() exit\n");
return;
}
"ibmf_i_rmpp_sender_active_flow(): %s\n",
"Unrecognized packet received, sending ABORT");
/* abort with status BadT */
0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
IBMF_TNF_TRACE, "",
"ibmf_i_rmpp_sender_active_flow(): %s\n",
}
} else {
"ibmf_i_rmpp_sender_active_flow(): %s, "
"STOP or ABORT packet received, terminating",
}
"ibmf_i_rmpp_sender_active_flow() exit\n");
return;
}
"msgp = 0x%p, recvd seg = %d wl = %d wf = %d\n",
/* only ACK packets get here */
/* abort with status S2B */
IBMF_RMPP_STATUS_S2B, 0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
"ibmf_i_rmpp_sender_active_flow(): %s\n",
}
"ibmf_i_rmpp_sender_active_flow(): %s\n",
"ibmf_i_rmpp_sender_active_flow() exit\n");
return;
}
"ibmf_i_rmpp_sender_active_flow(): %s\n",
/* discard the packet by not processing it here */
/* send the window */
"ibmf_i_rmpp_sender_active_flow() exit\n");
return;
}
/* only ACK packets with valid segnum get here */
"ibmf_i_rmpp_sender_active_flow(): %s\n",
/* abort with status W2S */
IBMF_RMPP_STATUS_W2S, 0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
"ibmf_i_rmpp_sender_active_flow(): %s\n",
}
"ibmf_i_rmpp_sender_active_flow() exit\n");
return;
}
/* is ACK of last packet */
"ibmf_i_rmpp_sender_active_flow(): %s, msgp = 0x%p, "
if (rmpp_ctx->rmpp_is_ds) {
"ibmf_i_rmpp_sender_active_flow(): %s, "
"Doublesided,sending ACK and switching to receiver",
/* set the response timer */
/* proceed with sender switch to receiver */
IBMF_TNF_TRACE, "",
"ibmf_i_rmpp_sender_active_flow() exit\n");
return;
}
/* successful termination */
"ibmf_i_rmpp_sender_active_flow() exit\n");
return;
}
/* update RMPP context and send the next window */
"wf = %d, wl = %d, ns = %d\n",
/* send the window */
/* carry on with the protocol */
"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
{
int status;
"ibmf_i_rmpp_sender_switch_flow(): clientp = 0x%p, qp_hdl = 0x%p, "
"ACK packet received, sending ACK");
/* set the response timer */
"DATA packet received, processing packet");
} else {
"Unexpected packet received, sending ABORT BADT");
/* abort with status BadT */
IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
"ibmf_i_rmpp_sender_switch_flow(): %s\n",
}
}
"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
{
int status;
"ibmf_i_rmpp_recvr_flow_main(): clientp = 0x%p, qp_hdl = 0x%p, "
/*
* 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
*/
"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 (status != IBMF_SUCCESS) {
IBMF_TNF_ERROR, "",
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
"ibmf_setup_recvbuf_on_error() failed");
return;
}
}
/* send an ACK of ES - 1 if ES is greater than 1 */
}
/*
* 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
*/
}
"ibmf_i_rmpp_recvr_flow_main() exit\n");
return;
}
&cl_hdr_off);
/* first packet flag should be set and seg num should be 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.
*/
mad);
if (status != IBMF_SUCCESS) {
IBMF_TNF_ERROR, "",
"ibmf_i_rmpp_recvr_flow_main(): "
"ibmf_setup_recvbuf_on_error() "
"failed");
return;
}
}
/* abort with status BadT */
0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
IBMF_TNF_TRACE, "",
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
}
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
"number detected, sending ABORT IFSN");
"", "ibmf_i_rmpp_recvr_flow_main() exit\n");
return;
}
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).
*/
if ((buf_sz % cl_hdrdata_sz) != 0)
else {
if (buf_sz > 0)
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.
*/
/*
* rmpp_pyld_len is the total length of just the
* class data. Class headers from each packet are
* not included in this calculation.
*/
} else {
}
/* allocate memory for the message data */
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
"mem allocation failure (known rmpp payload)");
"", "ibmf_i_rmpp_recvr_flow_main() exit\n");
return;
}
/* copy the MAD and class header */
/* initialize class header pointer */
if (cl_hdr_sz == 0) {
} else {
}
/* initialize data area pointer */
rmpp_ctx->rmpp_data_offset = 0;
/*
* calculate number of expected packets for transaction
* timeout calculation
*/
/*
* if the payload length is not specified in
* the first packet, just guess how many packets
* might arrive
*/
} else {
/* round up */
}
/* set the transaction timer if there are more packets */
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
"First pkt recvd; setting trans timer: ",
"ibmf_i_rmpp_recvr_flow_main(): setting trans"
}
}
/*
* copy the data from the packet into the data buffer in
* the message.
*/
else
/* if a payload length was specified and we've met or exceeded it */
rmpp_ctx->rmpp_pyld_len) &&
/* last packet flag should be set */
/* abort with status Incon. last and payload length */
0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
IBMF_TNF_TRACE, "",
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
}
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
"Inconsistent last and payload length detected,"
" sending ABORT ILPL, unsetting trans timer");
"", "ibmf_i_rmpp_recvr_flow_main() exit\n");
return;
}
rmpp_ctx->rmpp_pyld_len) &&
/*
* 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.
*/
"ibmf_i_rmpp_recvr_flow_main(): %s, allocsz = %d\n",
"mem allocation failure (unknown rmpp payload)",
"", "ibmf_i_rmpp_recvr_flow_main() exit\n");
return;
}
/* copy the MAD and class header */
}
/* don't overflow buffer */
}
/*
* 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.
*/
0) {
IBMF_TNF_TRACE, "",
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
"mem allocation failure (final payload)");
IBMF_TNF_TRACE, "",
"ibmf_i_rmpp_recvr_flow_main() exit\n");
return;
}
/* copy the data to the new buffer */
/* initialize class header pointer */
if (cl_hdr_sz == 0) {
} else {
}
/* initialize data area pointer */
}
"ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p\n",
"Last pkt rcvd; state to recv_term, sending ack",
if (status != IBMF_SUCCESS) {
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
}
/* unset the transaction timer if it's not the first segment */
"ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p\n",
"ibmf_i_rmpp_recvr_flow_main(): unsetting timer "
}
"ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p\n",
"Last pkt rcvd; setting resp timer",
/*
* 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_i_rmpp_recvr_flow_main(): last packet, "
" returning data to client for message %p\n",
/* Mark this message for early termination */
return;
}
"ibmf_i_rmpp_recvr_flow_main(): %s, msgp = 0x%p"
/* update the window */
} else {
"ibmf_i_rmpp_recvr_flow_main(): %s\n",
}
"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
{
int status;
"ibmf_i_rmpp_recvr_active_flow(): clientp = 0x%p, qp_hdl = 0x%p, "
/* discard this packet by not processing it here */
"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
*/
}
return;
}
"DATA packet received, processing packet");
"ibmf_i_rmpp_recvr_active_flow(): %s, status = %d\n",
/* discard the packet and terminate the transaction */
} else {
"Unrecognized packet received, terminating transaction");
/* abort with status BadT */
IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
"ibmf_i_rmpp_recvr_active_flow(): %s\n",
}
}
"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
{
int status;
"ibmf_i_rmpp_recvr_term_flow(): clientp = 0x%p, qp_hdl = 0x%p, "
"Data packet received, resending ACK");
"ibmf_i_rmpp_recvr_term_flow(): setting resp timer %d %p\n",
/* set the response timer */
"ibmf_i_rmpp_recvr_term_flow(): %s, msgp = 0x%p\n",
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_i_rmpp_recvr_term_flow(): %s, msgp = 0x%p\n",
"Received final ack for double-sided trans",
/*
* successful termination
*/
} else {
"ibmf_i_rmpp_recvr_term_flow(): %s, msgp = 0x%p\n",
"state for single sided trans",
/* abort with status BadT */
IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK);
}
} else {
"Unexpected packet received, sending ABORT BADT");
/* abort with status BadT */
IBMF_RMPP_STATUS_BADT, 0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
"ibmf_i_rmpp_recvr_term_flow(): %s\n",
}
}
"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(): rmpp_hdr = 0x%p\n",
"ibmf_i_is_valid_rmpp_status_flow() exit\n");
return (found);
}
/*
* ibmf_i_handle_rmpp():
* Handle RMPP processing of an incoming IB packet
*/
void
{
int status;
"ibmf_i_handle_rmpp(): clientp = 0x%p, qp_hdl = 0x%p, "
/*
* Check the version in the RMPP header
*/
/*
* 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 (status != IBMF_SUCCESS) {
"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.
*/
/*
* 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 &
}
"BAD version detected, dropping MAD");
return;
}
/* abort with status BadT */
IBMF_RMPP_STATUS_UNV, 0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
"RMPP ABORT send failed");
}
"Unsupported RMPP version detected, sending ABORT UNV");
"ibmf_i_handle_rmpp() exit\n");
return;
}
/*
* Check for a valid status in the RMPP header
*/
/*
* 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 (status != IBMF_SUCCESS) {
"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.
*/
/*
* 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 &
}
"Invalid RMPP status detected, dropping MAD");
return;
}
/* abort with status BadT */
IBMF_RMPP_STATUS_IS, 0, 0, IBMF_NO_BLOCK);
if (status != IBMF_SUCCESS) {
"RMPP ABORT send failed");
}
"Invalid RMPP status detected, sending ABORT IS");
"ibmf_i_handle_rmpp() exit\n");
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) {
break;
break;
break;
break;
default:
/* Including IBMF_RMPP_STATE_ABORT */
"ibmf_i_handle_rmpp(): %s, rmpp_state = 0x%x\n",
/* Reinitiate the resp timer if the state is ABORT */
return;
}
/*
* Drop the message if the transaction has not yet
* been identified as a send or receive RMPP transaction.
*/
/*
* 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 &
}
"BAD 1st RMPP packet, dropping MAD");
return;
}
}
"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
{
int status;
IBMF_TNF_TRACE, "",
"ibmf_i_send_rmpp(): msgp = 0x%p, rmpp_type = 0x%x, "
"rmpp_status = %d, segno = %d, nwl = %d\n",
"next_seg = 0x%x, num_pkts = %d\n",
/*
* send packet without blocking
*/
if (status != IBMF_SUCCESS) {
return (status);
}
return (IBMF_SUCCESS);
}
/*
* ibmf_i_send_rmpp_window():
* Send an RMPP protocol window of packets
*/
void
{
&cl_hdr_sz, &cl_hdr_off);
for (i = 1; i <= numpkts; i++) {
else
block);
if (status != IBMF_SUCCESS) {
"Send rmpp window failed");
"ibmf_i_send_rmpp_window() exit\n");
return;
}
}
/* Set the response timer */
}
/*
* ibmf_i_send_rmpp_pkts():
* Send a message using the RMPP protocol
*/
int
{
"qphdl = 0x%p, msgp = 0x%p, block = %d\n",
&cl_hdr_sz, &cl_hdr_off);
else {
if (buf_sz > 0)
else
num_pkts = 1;
}
rmpp_ctx->rmpp_data_offset = 0;
return (IBMF_SUCCESS);
}