/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file implements the MAD receive logic in IBMF.
*/
extern ibmf_state_t *ibmf_statep;
extern int ibmf_recv_wqes_per_port;
extern int ibmf_send_wqes_posted_per_qp;
extern int ibmf_recv_wqes_posted_per_qp;
#define IBMF_QP0_NUM 0
/*
* Structure defintion of entries in the module names table
*/
typedef struct _ibmf_mod_names_t {
typedef struct _ibmf_mod_load_args_t {
char *modname;
extern int ibmf_trace_level;
extern int ibmf_send_wqes_posted_per_qp;
extern int ibmf_recv_wqes_posted_per_qp;
static void ibmf_i_do_recv_cb(void *taskq_arg);
char *modname);
static void ibmf_module_load(void *taskq_arg);
/*
* ibmf_i_handle_recv_completion():
* Process the WQE from the RQ, obtain the management class of the
* packet and retrieve the corresponding client context
*/
void
{
int ret;
"ibmf_i_handle_recv_completion() enter, cip = %p, wcp = %p\n",
if (ret == 0) {
"", "ibmf_i_handle_recv_completion(): %s\n",
"population thread failed");
}
}
/* Retrieve the QP handle from the receive WQE context */
/* Get the WQE kmem cache pointer based on the QP type */
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
} else {
}
/*
* if the wqe is being flushed due to shutting down of the qp, free
* the wqe and return.
*/
sizeof (ibt_wr_ds_t));
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
cip->ci_wqes_alloced--;
if (cip->ci_wqes_alloced == 0)
} else {
if (altqp->isq_wqes_alloced == 0)
}
"", "ibmf_i_handle_recv_completion(): %s\n",
"", "ibmf_i_handle_recv_completion() exit\n");
return;
}
/*
* Dynamic Posting of WQEs to the Receive Queue (RQ) of the QP:
* If the number of RQ WQEs posted to the QP drops below half
* the initial number of RQ WQEs posted to the QP, then, one additional
* WQE is posted to the RQ of the QP while processing this CQE.
*/
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
qpp->iq_rwqes_posted--;
"ibmf_i_handle_recv_compl(): %s, "
"Posting more RQ WQEs",
/* Post an additional WQE to the RQ */
if (ret != IBMF_SUCCESS) {
"", "ibmf_i_handle_recv_compl(): %s, "
"ibmf_i_post_recv_buffer() failed",
}
}
} else {
"ibmf_i_handle_recv_compl(): %s, "
"Posting more RQ WQEs",
/* Post an additional WQE to the RQ */
if (ret != IBMF_SUCCESS) {
"", "ibmf_i_handle_recv_compl(): %s, "
"ibmf_i_post_recv_buffer() failed",
}
}
}
/*
* for all other completion errors, repost the wqe, and if that
* fails, free the wqe and return.
*/
"", "ibmf_i_handle_recv_completion(): %s, wc_status = %d\n",
"", "ibmf_i_handle_recv_completion() exit\n");
return;
}
/* find the client corresponding to this recv cqe */
sizeof (ib_grh_t));
/* drop packet if MAD Base Version is not as expected */
"", "ibmf_i_handle_recv_completion(): %s\n",
"", "ibmf_i_handle_recv_completion() exit\n");
return;
}
/* bad class & type? */
#ifdef DEBUG
#endif
"", "ibmf_i_handle_recv_completion(): %s\n",
"", "ibmf_i_handle_recv_completion() exit\n");
return;
}
if (ret == IBMF_SUCCESS) {
/*
* Increment the kstats for the number of active receiver side
* callbacks
*/
/* Dispatch the taskq thread to do further processing */
if (ret == 0) {
IBMF_TNF_ERROR, "",
"ibmf_i_handle_recv_completion(): %s\n",
(void) ibmf_i_repost_recv_buffer(cip,
IBMF_TNF_TRACE, "",
"ibmf_i_handle_recv_completion() exit\n");
return;
}
} else {
ibmf_i_do_recv_cb((void *)recv_wqep);
}
/*
* Decrement the kstats for the number of active receiver side
* callbacks
*/
} else {
/*
* A client has not registered to receive MADs of this
* management class. IBMF must attempt to load the
* client and request a resend of the request MAD.
* The name of the client MAD is derived using a
* convention described in PSARC case 2003/753.
*/
/*
* HCA driver handles the Performance management
* class MAD's. It registers with the IBMF during early
* boot and unregisters during detach and during
* HCA unconfigure operation. We come here
* 1. Before HCA registers with IBMF
* Drop the MAD. Since this is a UD MAD,
* sender will resend the request
* 2. After HCA unregistered with IBMF during DR operation.
* Since HCA is going away, we can safely drop the PMA
* MAD's here.
* Solaris does not support BM_AGENT and so drop the BM MAD's
*/
return;
}
"ibmf_i_handle_recv_completion(): %s, port = %d, "
"class = 0x%x\n",
/* Construct the IBMF client module name */
/* Load the module using a taskq thread */
sizeof (ibmf_mod_load_args_t), KM_NOSLEEP);
if (ret == 0) {
sizeof (ibmf_mod_load_args_t));
IBMF_TNF_TRACE, "",
"ibmf_i_handle_recv_completion(): Failed "
"to dispatch ibmf_module_load taskq\n");
(void) ibmf_i_repost_recv_buffer(cip,
}
} else {
"", "ibmf_i_handle_recv_completion(): "
"Failed to allocate memory for modlargs\n");
}
}
"ibmf_i_handle_recv_completion() exit\n");
}
/*
* ibmf_i_do_recv_cb():
* This routine does the following:
* o looks for a message in the client's message list
* o creates a new message if one does not exist for unsolicited data
* o invoke routines to do specific handling for rmpp and non-rmpp cases
* o on a failure, the receive WQE is reposted to the RQ
*/
static void
{
int status;
/* The taskq_arg argument is a pointer to the receive WQE context */
/* Retrieve the QP handle from the receive WQE context */
"ibmf_i_do_recv_cb() enter, recv_wqep = %p\n",
/* Retrieve the client context pointer from the receive WQE context */
/* Get a pointer to the IBT work completion structure */
/*
* Identify the port by the LID or GID depending on whether the
* Global Route Header is valid or not
*/
grhpresent = B_TRUE;
} else {
}
/* Get a pointer to the MAD header */
sizeof (ib_grh_t));
/* Get a pointer to the RMPP header */
sizeof (ib_grh_t) + sizeof (ib_mad_hdr_t));
"attrID = 0x%x, lid = 0x%x\n",
/*
* Look for the matching message in the client's message list
* NOTE: if the message is found, the message reference count will
* have been increased by 1.
*/
/*
* If the message is not on the regular message list, search
* for it in the termination message list.
*/
}
/* if this packet is from the SA */
/*
* ibmf_saa's callback arg is its saa_portp;
* take advantage of this fact to quickly update the
* port's SA uptime. ibmf_saa uses the up time to
* determine if the SA is still alive
*/
/* update the SA uptime */
}
/*
* Clear timers for transactions of solicited incoming packets
*/
if (msgimplp->im_rp_timeout_id != 0) {
}
/*
* If a MAD is received in the middle of an RMPP receive
* transaction, and the MAD's RMPPFlags.Active bit is 0,
* drop the MAD
*/
"ibmf_i_do_recv_cb(): %s, msg = %p\n",
"Non-RMPP MAD received in RMPP transaction, "
"ibmf_i_do_recv_cb() exit\n");
return;
}
/*
* If the message has been marked unitialized or done
* release the message mutex and return
*/
if ((msgimplp->im_trans_state_flags &
/*
* 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 &
!(msg_flags & IBMF_MSG_FLAGS_ON_LIST) &&
(ref_cnt == 0)) {
}
"ibmf_i_do_recv_cb(): %s, msg = %p\n",
"Message already marked for removal, dropping MAD",
"ibmf_i_do_recv_cb() exit\n");
return;
}
} else {
/* unsolicited message packet */
/*
* Check if the client context, the alternate QP context
* (if not the default QP), and the incoming MAD support RMPP
*/
/* Only unsolicited packets should be data seg 1 */
if ((rmpp_hdr->rmpp_flags &
IBMF_RMPP_FLAGS_FIRST_PKT) == 0) {
(void) ibmf_i_repost_recv_buffer(
"ibmf_i_do_recv_cb(): %s\n",
"unsolicited rmpp packet not first packet");
"ibmf_i_do_recv_cb() exit\n");
return;
}
}
/*
* Before we alloc a message context, check to see if
* a callback has been registered with the client
* for this unsolicited message.
* If one has been registered, increment the recvs active
* count to get the teardown routine to wait until
* this callback is complete.
*/
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
(void) ibmf_i_repost_recv_buffer(
"ibmf_i_do_recv_cb(): %s, class %x\n",
"ibmf_tear_down_recv_cb already occurred",
"ibmf_i_do_recv_cb() exit\n");
return;
}
} else {
(void) ibmf_i_repost_recv_buffer(
"ibmf_i_do_recv_cb(): %s, class %x\n",
"ibmf_tear_down_recv_cb already occurred",
"ibmf_i_do_recv_cb() exit\n");
return;
}
}
/*
* Allocate a message context
*/
sizeof (ibmf_msg_impl_t), KM_NOSLEEP);
/* If we cannot allocate memory, drop the packet and clean up */
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
} else {
}
"mem allocation failure");
"ibmf_i_do_recv_cb() exit\n");
return;
}
/* Get the port's base LID if it's not in the client context */
if ((clientp->ic_base_lid == 0) &&
(void) ibt_get_port_state_byguid(
&clientp->ic_base_lid);
if (clientp->ic_base_lid == 0) {
"ibmf_i_do_recv_cb(): %s\n",
}
}
/* Set up address information */
/* Get the pkey, including the correct partiton membership */
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
/*
* here too we expect the pkey index in the work
* completion belongs to a pkey in the pkey
* table
*/
if (status != IBMF_SUCCESS) {
IBMF_TNF_ERROR, "",
"ibmf_i_do_recv_cb(): "
"get_pkey failed for ix %d,"
"status = %d\n", tnf_uint,
(void) ibmf_i_repost_recv_buffer(
sizeof (ibmf_msg_impl_t));
IBMF_TNF_TRACE, "",
"ibmf_i_do_recv_cb() exit\n");
return;
}
}
} else {
/* For alternate QPs, the pkey is in the QP context */
}
/* Initialize the message context */
/*
* Initialize (and possibly allocate) the IBT UD destination
* address handle.
*/
if (status != IBMF_SUCCESS) {
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
} else {
}
"ibmf_i_do_recv_cb(): %s, status = %d\n",
"ibmf_i_do_recv_cb() exit\n");
return;
}
/* add message to client's list */
/* no one should have touched our state */
/* transition out of uninit state */
}
/* fill in the grh with the contents of the recv wqe */
if (grhpresent == B_TRUE) {
/*
* swap to get byte order back to wire format on little endian
* systems so we can apply the GRH masks
*/
}
/* Perform RMPP or non-RMPP processing */
"flags = 0x%x rmpp_type = %d, rmpp_segnum = %d\n",
/*
* Set the RMPP state to "receiver active" on the first packet
* of all RMPP message, and initialize the
* the expected segment to 1.
*/
"ibmf_i_do_recv_cb(): %s, msgimplp = %p\n",
}
/* set double-sided transfer flag for certain methods */
}
"ibmf_i_do_recv_cb: %s, resp_time %d\n",
}
sizeof (ib_grh_t)));
} else {
sizeof (ib_grh_t)));
}
/*
* Save the transaction state flags and the timeout IDs
* before releasing the mutex as they may be changed after that.
*/
/*
* Decrement the message reference count
* This count was incremented either when the message was found
* on the client's message list (ibmf_i_find_msg()) or when
* a new message was created for unsolicited data
*/
if (msgimplp->im_rp_timeout_id != 0) {
msgimplp->im_rp_timeout_id = 0;
}
if (msgimplp->im_tr_timeout_id != 0) {
msgimplp->im_tr_timeout_id = 0;
}
}
/*
* Call untimeout() after releasing the lock because the
* lock is acquired in the timeout handler as well. Untimeout()
* does not return until the timeout handler has run, if it already
* fired, which would result in a deadlock if we did not first
* release the im_mutex lock.
*/
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);
}
/* Increment the kstats for number of messages received */
/*
* now that we are done gleaning all we want out of the receive
* completion, we repost the receive request.
*/
/*
* If the transaction flags indicate a completed transaction,
* notify the client
*/
"ibmf_i_do_recv_cb(): %s, msgimplp = %p\n",
/* Remove the message from the client's message list */
/*
* 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_do_recv_cb() exit, msgimplp = %p\n",
}
/*
* ibmf_i_handle_non_rmpp():
* Handle non-RMPP processing of an incoming IB packet
*/
void
{
"ibmf_i_handle_non_rmpp(): clientp = 0x%p, "
/* Get the MAD header */
/* Determine the MAD's class header size */
/* Allocate the message receive buffers if not already allocated */
"mem allocation failure (non-rmpp payload)");
"ibmf_i_handle_non_rmpp() exit\n");
return;
}
}
/* Get a pointer to the MAD location in the receive buffer */
/* Copy the incoming MAD into the receive buffer */
/* Get the offset of the class header */
/* initialize class header pointer */
if (clhdrsz == 0) {
} else {
}
/* initialize data area pointer */
}
/*
* ibmf_i_repost_recv_buffer():
* Repost a WQE to the RQ after processing it
*/
/* ARGSUSED */
int
{
int ret;
"ibmf_i_repost_recv_buffer() enter, cip = %p, rwqep = %p\n",
/* Get the WQE kmem cache pointer based on the QP type */
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
} else {
}
/* post recv wqe; free it if the post fails */
1, NULL);
if (ret != IBMF_SUCCESS) {
"ibmf_i_repost_recv_buffer(): %s, status = %d\n",
ibt_status, status);
IBMF_MAX_RQ_WR_SGL_ELEMENTS * sizeof (ibt_wr_ds_t));
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
cip->ci_wqes_alloced--;
if (cip->ci_wqes_alloced == 0)
} else {
if (altqp->isq_wqes_alloced == 0)
}
}
if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
qpp->iq_rwqes_posted++;
} else {
}
return (ret);
}
/*
* ibmf_i_get_class:
* Parses the mad header and determines which class should be notified of the
* notification.
*
* Input Argument
* madhdrp contents of mad header for the packet
*
* Output Argument
* dest_classp pointer to the class type of the client that should be notified
*
* Returns
* status
*/
static int
{
"ibmf_i_get_class() enter, class = 0x%x, method = 0x%x, "
"attribute = 0x%x, dest_qp_hdl = 0x%p\n",
/* set default for error checking */
*dest_classp = 0;
/*
* Determine the class type
*/
switch (class) {
/*
* tavor generates trap by sending mad with slid 0;
* deliver this to SMA
*/
break;
}
/* this is derived from table 109 of IB Spec 1.1, vol1 */
else
break;
case MAD_MGMT_CLASS_SUBN_ADM:
/*
* Deliver to SA client (agent) if packet was sent to default qp
* Deliver to ibmf_saa client (manager) if packet was sent to
* alternate qp
*/
else
break;
case MAD_MGMT_CLASS_PERF:
/* Deliver to PM if response bit is set */
else
break;
case MAD_MGMT_CLASS_BM:
/*
* Deliver to BM if response bit is set, packet is a trap,
* or packet is a BMSend
*/
(method == MAD_METHOD_TRAP) ||
((method == MAD_METHOD_SEND) &&
else
*dest_classp = BM_AGENT;
break;
case MAD_MGMT_CLASS_DEV_MGT:
/* Deliver to DM if response bit is set or packet is a trap */
(method == MAD_METHOD_TRAP))
else
break;
case MAD_MGMT_CLASS_COMM_MGT:
break;
case MAD_MGMT_CLASS_SNMP:
break;
default:
if ((class >= MAD_MGMT_CLASS_VENDOR_START) &&
(class <= MAD_MGMT_CLASS_VENDOR_END)) {
} else if ((class >= MAD_MGMT_CLASS_VENDOR2_START) &&
(class <= MAD_MGMT_CLASS_VENDOR2_END)) {
} else if ((class >= MAD_MGMT_CLASS_APPLICATION_START) &&
(class <= MAD_MGMT_CLASS_APPLICATION_END)) {
}
break;
}
if (*dest_classp == 0) {
"ibmf_i_get_class(): %s, class = 0x%x\n",
return (IBMF_FAILURE);
}
"ibmf_i_get_class() exit, class = 0x%x\n",
return (IBMF_SUCCESS);
}
/*
* ibmf_get_mod_name():
* Constructs the module name based on the naming convention described in
* PSARC case 2003/753.
* The name should be "sunwibmgt<MgtClass><a_m>
* where:
* MgtClass = Management class field in the MAD header.
* Two lower-case characters are used to represent
* this 8-bit value as 2 hex digits.
* a_m = "a" if the client is an agent-only module
* "m" if the client is a manager-only module
* "" if the client is both agent and manager.
*
* Input Argument
* mad_class management class in the MAD header
* class IBMF management class of incoming MAD
*
* Output Argument
* modname pointer to the character array that holds the module name
*
* Status
* None
*/
static void
{
if (AGENT_CLASS(class)) {
} else if (MANAGER_CLASS(class)) {
} else {
/* AGENT+MANAGER class */
}
}
/*
* ibmf_send_busy():
*
* When a MAD request is received for an IB mandatory agent (BMA or PMA),
* which has not yet registered with IBMF, IBMF returns a BUSY MAD
* to the source of the request to solicit a retry while IBMF attempts
* to load the mandatory agent.
* A temporary, alternate QP is allocated for the purpose of sending the
* MAD. This QP is configured to be in the same partition as the manager
* that sent the request.
*
* Input Argument
* modlargsp Pointer to ibmf_mod_load_args_t structure
*
* Output Argument
* None
*
* Status
* None
*/
static void
{
/* setup the qp attrs for the alloc call */
/* request IBT for a qp with the desired attributes */
if (ibtstatus != IBT_SUCCESS) {
return;
}
/* call the IB transport to initialize the QP */
if (ibtstatus != IBT_SUCCESS) {
(void) ibt_free_qp(ibt_qp_handle);
return;
}
/* allocate the message context */
KM_SLEEP);
/*
* Get a UD dest structure from the pool, this will not fail
* because ibmf_i_pop_ud_dest_thread() calls
* ibmf_i_populate_ud_dest_list with the KM_SLEEP flag.
*/
/*
* Reset send_done to indicate we have not received the completion
* for this send yet.
*/
/*
* Allocate resources needed to send a UD packet including the
* send WQE context
*/
KM_SLEEP);
/* Register the allocated memory */
if (ibtstatus != IBT_SUCCESS) {
(void) ibt_free_qp(ibt_qp_handle);
return;
}
/* use send wqe pointer as the WR ID */
/* Initialize the scatter-gather list */
/* Initialize the address vector */
/* modify the address handle with the address vector information */
if (ibtstatus != IBT_SUCCESS) {
return;
}
sizeof (ib_grh_t));
/* Set up the MAD header */
/* Post the MAD to the IBT layer */
num_work_reqs = 1;
if (ibtstatus != IBT_SUCCESS) {
return;
}
}
/*
* ibmf_module_load():
* This function attempts to load a client module that has not yet
* registered with IBMF at the time a request MAD arrives for it.
* Prior to loading the module, it sends a busy MAD to the sender of
* the request MAD, this soliciting a resend of the request MAD.
*
* Input Argument
* modlargsp Pointer to ibmf_mod_load_args_t structure
*
* Output Argument
* None
*
* Status
* None
*/
static void
{
char *modname;
if (IS_MANDATORY_CLASS(class)) {
}
IBMF_TNF_TRACE, "",
"ibmf_module_load(): modload failed for %s\n",
return;
}
}