void dapl_evd_connection_callback( IN ib_cm_handle_t ib_cm_handle, IN const ib_cm_events_t ib_cm_event, IN const void *private_data_ptr, IN const void *context) { DAPL_EP *ep_ptr; DAPL_EVD *evd_ptr; DAPL_PRIVATE *prd_ptr; DAT_EVENT_NUMBER event_type; dapl_dbg_log( DAPL_DBG_TYPE_CM | DAPL_DBG_TYPE_CALLBACK, "--> dapl_evd_connection_callback: ctxt: %p event: %x" " cm_handle %p\n", context, ib_cm_event, ib_cm_handle); /* * Determine the type of handle passed back to us in the context * and sort out key parameters. */ dapl_os_assert(((DAPL_HEADER *)context)->magic == DAPL_MAGIC_EP || ((DAPL_HEADER *)context)->magic == DAPL_MAGIC_EP_EXIT); /* * Active side of the connection, context is an EP and * PSP is irrelevant. */ ep_ptr = (DAPL_EP *)context; evd_ptr = (DAPL_EVD *)ep_ptr->param.connect_evd_handle; prd_ptr = (DAPL_PRIVATE *)private_data_ptr; switch (ib_cm_event) { case IB_CME_CONNECTED: { /* * If we don't have an EP at this point we are very screwed * up */ DAT_RETURN dat_status; if (ep_ptr->param.ep_state == DAT_EP_STATE_DISCONNECT_PENDING) { /* * If someone pulled the plug on the connection, just * exit */ break; } dapls_ib_connected(ep_ptr); ep_ptr->param.ep_state = DAT_EP_STATE_CONNECTED; ep_ptr->cm_handle = ib_cm_handle; /* copy in the private data */ (void) dapl_os_memcpy(ep_ptr->private_data, prd_ptr->private_data, IB_MAX_REQ_PDATA_SIZE); dat_status = dapls_evd_post_connection_event( evd_ptr, DAT_CONNECTION_EVENT_ESTABLISHED, (DAT_HANDLE) ep_ptr, IB_MAX_REQ_PDATA_SIZE, ep_ptr->private_data); if (dat_status != DAT_SUCCESS) { (void) dapls_ib_disconnect(ep_ptr, DAT_CLOSE_ABRUPT_FLAG); ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECT_PENDING; } /* * If we received any premature DTO completions and * post them to the recv evd now. * there is a race here - if events arrive after we change * the ep state to connected and before we process premature * events */ dapls_evd_post_premature_events(ep_ptr); break; } case IB_CME_DISCONNECTED: case IB_CME_DISCONNECTED_ON_LINK_DOWN: { /* * EP is now fully disconnected; initiate any post processing * to reset the underlying QP and get the EP ready for * another connection */ if (ep_ptr->param.ep_state == DAT_EP_STATE_DISCONNECTED) { /* DTO error caused this */ event_type = DAT_CONNECTION_EVENT_BROKEN; } else { ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_TRUE, ib_cm_event); event_type = DAT_CONNECTION_EVENT_DISCONNECTED; } /* If the EP has been freed, the evd_ptr will be NULL */ if (evd_ptr != NULL) { (void) dapls_evd_post_connection_event( evd_ptr, event_type, (DAT_HANDLE) ep_ptr, 0, 0); } /* * If the user has done an ep_free of the EP, we have been * waiting for the disconnect event; just clean it up now. */ if (ep_ptr->header.magic == DAPL_MAGIC_EP_EXIT) { (void) dapl_ep_free(ep_ptr); } break; } case IB_CME_DESTINATION_REJECT_PRIVATE_DATA: { ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_TRUE, ib_cm_event); (void) dapls_evd_post_connection_event( evd_ptr, DAT_CONNECTION_EVENT_PEER_REJECTED, (DAT_HANDLE) ep_ptr, 0, 0); break; } case IB_CME_DESTINATION_UNREACHABLE: { ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_TRUE, ib_cm_event); (void) dapls_evd_post_connection_event( evd_ptr, DAT_CONNECTION_EVENT_UNREACHABLE, (DAT_HANDLE) ep_ptr, 0, 0); break; } case IB_CME_DESTINATION_REJECT: case IB_CME_TOO_MANY_CONNECTION_REQUESTS: case IB_CME_LOCAL_FAILURE: { ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_TRUE, ib_cm_event); (void) dapls_evd_post_connection_event( evd_ptr, DAT_CONNECTION_EVENT_NON_PEER_REJECTED, (DAT_HANDLE) ep_ptr, 0, 0); break; } case IB_CME_TIMED_OUT: { ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_TRUE, ib_cm_event); (void) dapls_evd_post_connection_event( evd_ptr, DAT_CONNECTION_EVENT_TIMED_OUT, (DAT_HANDLE) ep_ptr, 0, 0); break; } case IB_CME_CONNECTION_REQUEST_PENDING: case IB_CME_CONNECTION_REQUEST_PENDING_PRIVATE_DATA: default: { dapl_os_assert(0); /* shouldn't happen */ break; } } dapl_dbg_log(DAPL_DBG_TYPE_CM | DAPL_DBG_TYPE_CALLBACK, "dapl_evd_connection_callback () returns\n"); }
/* * dapls_cr_callback * * The callback function registered with verbs for passive side of * connection requests. The interface is specified by cm_api.h * * * Input: * ib_cm_handle, Handle to CM * ib_cm_event Specific CM event * instant_data Private data with DAT ADDRESS header * context SP pointer * * Output: * None * */ void dapls_cr_callback(IN dp_ib_cm_handle_t ib_cm_handle, IN const ib_cm_events_t ib_cm_event, IN const void *private_data_ptr, IN const int private_data_size, IN const void *context) { DAPL_EP *ep_ptr; DAPL_EVD *evd_ptr; DAPL_SP *sp_ptr; DAPL_PRIVATE *prd_ptr; DAT_EVENT_NUMBER dat_event_num; DAT_RETURN dat_status; dapl_dbg_log(DAPL_DBG_TYPE_CM | DAPL_DBG_TYPE_CALLBACK, "--> dapl_cr_callback! context: %p event: %x cm_handle %p\n", context, ib_cm_event, (void *)ib_cm_handle); /* * Passive side of the connection, context is a SP and * we need to look up the EP. */ sp_ptr = (DAPL_SP *) context; /* * The context pointer could have been cleaned up in a racing * CM callback, check to see if we should just exit here */ if (sp_ptr->header.magic == DAPL_MAGIC_INVALID) { return; } dapl_os_assert(sp_ptr->header.magic == DAPL_MAGIC_PSP || sp_ptr->header.magic == DAPL_MAGIC_RSP); /* Obtain the event number from the provider layer */ dat_event_num = dapls_ib_get_dat_event(ib_cm_event, DAT_FALSE); /* * CONNECT_REQUEST events create an event on the PSP * EVD, which will trigger connection processing. The * sequence is: * CONNECT_REQUEST Event to SP * CONNECTED Event to EP * DISCONNECT Event to EP * * Obtain the EP if required and set an event up on the correct * EVD. */ if (dat_event_num == DAT_CONNECTION_REQUEST_EVENT) { ep_ptr = NULL; evd_ptr = sp_ptr->evd_handle; } else { /* see if there is an EP connected with this CM handle */ ep_ptr = dapli_get_sp_ep(ib_cm_handle, sp_ptr, dat_event_num); /* if we lost a race with the CM just exit. */ if (ep_ptr == NULL) { return; } evd_ptr = (DAPL_EVD *) ep_ptr->param.connect_evd_handle; /* if something has happened to our EVD, bail. */ if (evd_ptr == NULL) { return; } } prd_ptr = (DAPL_PRIVATE *) private_data_ptr; dat_status = DAT_INTERNAL_ERROR; /* init to ERR */ switch (dat_event_num) { case DAT_CONNECTION_REQUEST_EVENT: { /* * Requests arriving on a disabled SP are immediatly rejected */ dapl_os_lock(&sp_ptr->header.lock); if (sp_ptr->listening == DAT_FALSE) { dapl_os_unlock(&sp_ptr->header.lock); dapl_log(DAPL_DBG_TYPE_CM_WARN, " cr_callback: CR event on non-listening SP\n"); (void)dapls_ib_reject_connection(ib_cm_handle, DAT_CONNECTION_EVENT_UNREACHABLE, 0, NULL); return; } if (sp_ptr->header.handle_type == DAT_HANDLE_TYPE_RSP) { /* * RSP connections only allow a single connection. Close * it down NOW so we reject any further connections. */ sp_ptr->listening = DAT_FALSE; } dapl_os_unlock(&sp_ptr->header.lock); /* * Only occurs on the passive side of a connection * dapli_connection_request will post the connection * event if appropriate. */ dat_status = dapli_connection_request(ib_cm_handle, sp_ptr, prd_ptr, private_data_size, evd_ptr); /* Set evd_ptr = NULL so we don't generate an event below */ evd_ptr = NULL; break; } case DAT_CONNECTION_EVENT_ESTABLISHED: { /* This is just a notification the connection is now * established, there isn't any private data to deal with. * * Update the EP state and cache a copy of the cm handle, * then let the user know we are ready to go. */ dapl_os_lock(&ep_ptr->header.lock); if (ep_ptr->header.magic != DAPL_MAGIC_EP || ep_ptr->param.ep_state != DAT_EP_STATE_COMPLETION_PENDING) { /* If someone pulled the plug on the EP or connection, * just exit */ dapl_os_unlock(&ep_ptr->header.lock); dat_status = DAT_SUCCESS; /* Set evd_ptr = NULL so we don't generate an event below */ evd_ptr = NULL; break; } ep_ptr->param.ep_state = DAT_EP_STATE_CONNECTED; dapl_os_unlock(&ep_ptr->header.lock); break; } case DAT_CONNECTION_EVENT_DISCONNECTED: { /* * EP is now fully disconnected; initiate any post processing * to reset the underlying QP and get the EP ready for * another connection */ dapl_os_lock(&ep_ptr->header.lock); if (ep_ptr->param.ep_state == DAT_EP_STATE_DISCONNECTED) { /* The disconnect has already occurred, we are now * cleaned up and ready to exit */ dapl_os_unlock(&ep_ptr->header.lock); return; } ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_FALSE, ib_cm_event); dapl_os_unlock(&ep_ptr->header.lock); break; } case DAT_CONNECTION_EVENT_NON_PEER_REJECTED: case DAT_CONNECTION_EVENT_PEER_REJECTED: case DAT_CONNECTION_EVENT_UNREACHABLE: { /* * After posting an accept the requesting node has * stopped talking. */ dapl_os_lock(&ep_ptr->header.lock); ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_FALSE, ib_cm_event); dapl_os_unlock(&ep_ptr->header.lock); break; } case DAT_CONNECTION_EVENT_BROKEN: { dapl_os_lock(&ep_ptr->header.lock); ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dapls_ib_disconnect_clean(ep_ptr, DAT_FALSE, ib_cm_event); dapl_os_unlock(&ep_ptr->header.lock); break; } default: { evd_ptr = NULL; dapl_os_assert(0); /* shouldn't happen */ break; } } if (evd_ptr != NULL) { dat_status = dapls_evd_post_connection_event(evd_ptr, dat_event_num, (DAT_HANDLE) ep_ptr, 0, NULL); } if (dat_status != DAT_SUCCESS) { /* The event post failed; take appropriate action. */ (void)dapls_ib_reject_connection(ib_cm_handle, DAT_CONNECTION_EVENT_BROKEN, 0, NULL); return; } }
/* * dapl_ep_connect * * DAPL Requirements Version xxx, 6.5.7 * * Request a connection be established between the local Endpoint * and a remote Endpoint. This operation is used by the active/client * side of a connection * * Input: * ep_handle * remote_ia_address * remote_conn_qual * timeout * private_data_size * privaet_data * qos * connect_flags * * Output: * None * * Returns: * DAT_SUCCESS * DAT_INSUFFICIENT_RESOUCRES * DAT_INVALID_PARAMETER * DAT_MODLE_NOT_SUPPORTED */ DAT_RETURN dapl_ep_connect( IN DAT_EP_HANDLE ep_handle, IN DAT_IA_ADDRESS_PTR remote_ia_address, IN DAT_CONN_QUAL remote_conn_qual, IN DAT_TIMEOUT timeout, IN DAT_COUNT private_data_size, IN const DAT_PVOID private_data, IN DAT_QOS qos, IN DAT_CONNECT_FLAGS connect_flags) { DAPL_EP *ep_ptr; DAPL_PRIVATE prd; DAPL_EP alloc_ep; DAT_RETURN dat_status; dapl_dbg_log(DAPL_DBG_TYPE_API | DAPL_DBG_TYPE_CM, "dapl_ep_connect (%p, {%u.%u.%u.%u}, %X, %d, %d, %p, %x, %x)\n", ep_handle, remote_ia_address->sa_data[2], remote_ia_address->sa_data[3], remote_ia_address->sa_data[4], remote_ia_address->sa_data[5], remote_conn_qual, timeout, private_data_size, private_data, qos, connect_flags); dat_status = DAT_SUCCESS; ep_ptr = (DAPL_EP *) ep_handle; /* * Verify parameter & state. The connection handle must be good * at this point. */ if (DAPL_BAD_HANDLE(ep_ptr, DAPL_MAGIC_EP)) { dat_status = DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_EP); goto bail; } if (DAPL_BAD_HANDLE(ep_ptr->param.connect_evd_handle, DAPL_MAGIC_EVD)) { dat_status = DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_EVD_CONN); goto bail; } /* * If the endpoint needs a QP, associated the QP with it. * This needs to be done carefully, in order to: * * Avoid allocating under a lock. * * Not step on data structures being altered by * routines with which we are racing. * So we: * * Confirm that a new QP is needed and is not forbidden by the * current state. * * Allocate it into a separate EP. * * Take the EP lock. * * Reconfirm that the EP is in a state where it needs a QP. * * Assign the QP and release the lock. */ if (ep_ptr->qp_state == DAPL_QP_STATE_UNATTACHED) { if (ep_ptr->param.pz_handle == NULL || DAPL_BAD_HANDLE(ep_ptr->param.pz_handle, DAPL_MAGIC_PZ)) { dat_status = DAT_ERROR(DAT_INVALID_STATE, DAT_INVALID_STATE_EP_NOTREADY); goto bail; } alloc_ep = *ep_ptr; dat_status = dapls_ib_qp_alloc(ep_ptr->header.owner_ia, &alloc_ep, ep_ptr); if (dat_status != DAT_SUCCESS) { dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES, DAT_RESOURCE_MEMORY); goto bail; } dapl_os_lock(&ep_ptr->header.lock); /* * PZ shouldn't have changed since we're only racing with * dapl_cr_accept() */ if (ep_ptr->qp_state != DAPL_QP_STATE_UNATTACHED) { /* Bail, cleaning up. */ dapl_os_unlock(&ep_ptr->header.lock); dat_status = dapls_ib_qp_free(ep_ptr->header.owner_ia, &alloc_ep); if (dat_status != DAT_SUCCESS) { dapl_dbg_log(DAPL_DBG_TYPE_WARN, "ep_connect: ib_qp_free failed with %x\n", dat_status); } dat_status = DAT_ERROR(DAT_INVALID_STATE, dapls_ep_state_subtype(ep_ptr)); goto bail; } ep_ptr->qp_handle = alloc_ep.qp_handle; ep_ptr->qpn = alloc_ep.qpn; ep_ptr->qp_state = alloc_ep.qp_state; dapl_os_unlock(&ep_ptr->header.lock); } /* * We do state checks and transitions under lock. * The only code we're racing against is dapl_cr_accept. */ dapl_os_lock(&ep_ptr->header.lock); /* * Verify the attributes of the EP handle before we connect it. Test * all of the handles to make sure they are currently valid. * Specifically: * pz_handle required * recv_evd_handle optional, but must be valid * request_evd_handle optional, but must be valid * connect_evd_handle required */ if (ep_ptr->param.pz_handle == NULL || DAPL_BAD_HANDLE(ep_ptr->param.pz_handle, DAPL_MAGIC_PZ) || ep_ptr->param.connect_evd_handle == NULL || DAPL_BAD_HANDLE(ep_ptr->param.connect_evd_handle, DAPL_MAGIC_EVD) || !(((DAPL_EVD *)ep_ptr->param.connect_evd_handle)->evd_flags & DAT_EVD_CONNECTION_FLAG) || (ep_ptr->param.recv_evd_handle != DAT_HANDLE_NULL && (DAPL_BAD_HANDLE(ep_ptr->param.recv_evd_handle, DAPL_MAGIC_EVD))) || (ep_ptr->param.request_evd_handle != DAT_HANDLE_NULL && (DAPL_BAD_HANDLE(ep_ptr->param.request_evd_handle, DAPL_MAGIC_EVD)))) { dapl_os_unlock(&ep_ptr->header.lock); dat_status = DAT_ERROR(DAT_INVALID_STATE, DAT_INVALID_STATE_EP_NOTREADY); goto bail; } /* * Check both the EP state and the QP state: if we don't have a QP * we need to attach one now. */ if (ep_ptr->qp_state == DAPL_QP_STATE_UNATTACHED) { dat_status = dapls_ib_qp_alloc(ep_ptr->header.owner_ia, ep_ptr, ep_ptr); if (dat_status != DAT_SUCCESS) { dapl_os_unlock(&ep_ptr->header.lock); dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES, DAT_RESOURCE_TEP); goto bail; } } if (ep_ptr->param.ep_state != DAT_EP_STATE_UNCONNECTED) { dapl_os_unlock(&ep_ptr->header.lock); dat_status = DAT_ERROR(DAT_INVALID_STATE, dapls_ep_state_subtype(ep_ptr)); goto bail; } if (qos != DAT_QOS_BEST_EFFORT || connect_flags != DAT_CONNECT_DEFAULT_FLAG) { /* * At this point we only support one QOS level */ dapl_os_unlock(&ep_ptr->header.lock); dat_status = DAT_ERROR(DAT_MODEL_NOT_SUPPORTED, 0); goto bail; } /* * Verify the private data size doesn't exceed the max */ if (private_data_size > DAPL_CONSUMER_MAX_PRIVATE_DATA_SIZE) { dapl_os_unlock(&ep_ptr->header.lock); dat_status = DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG5); goto bail; } /* * transition the state before requesting a connection to avoid * race conditions */ ep_ptr->param.ep_state = DAT_EP_STATE_ACTIVE_CONNECTION_PENDING; /* * At this point we're committed, and done with the endpoint * except for the connect, so we can drop the lock. */ dapl_os_unlock(&ep_ptr->header.lock); /* * fill in the private data */ (void) dapl_os_memzero(&prd, sizeof (DAPL_PRIVATE)); if (private_data_size > 0) (void) dapl_os_memcpy(prd.private_data, private_data, private_data_size); /* Copy the connection qualifiers */ (void) dapl_os_memcpy(ep_ptr->param.remote_ia_address_ptr, remote_ia_address, sizeof (DAT_SOCK_ADDR6)); ep_ptr->param.remote_port_qual = remote_conn_qual; dat_status = dapls_ib_connect(ep_handle, remote_ia_address, remote_conn_qual, private_data_size, &prd, timeout); if (dat_status != DAT_SUCCESS) { DAPL_EVD *evd_ptr; if (dat_status == DAT_ERROR(DAT_INVALID_ADDRESS, DAT_INVALID_ADDRESS_UNREACHABLE)) { /* Unreachable IP address */ evd_ptr = (DAPL_EVD *)ep_ptr->param.connect_evd_handle; if (evd_ptr != NULL) { (void) dapls_evd_post_connection_event(evd_ptr, DAT_CONNECTION_EVENT_UNREACHABLE, (DAT_HANDLE) ep_ptr, 0, 0); } ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dat_status = DAT_SUCCESS; } else if (dat_status == DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ADDRESS_UNREACHABLE)) { /* Non-existant connection qualifier */ evd_ptr = (DAPL_EVD *)ep_ptr->param.connect_evd_handle; if (evd_ptr != NULL) { (void) dapls_evd_post_connection_event(evd_ptr, DAT_CONNECTION_EVENT_NON_PEER_REJECTED, (DAT_HANDLE) ep_ptr, 0, 0); } ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED; dat_status = DAT_SUCCESS; } else { ep_ptr->param.ep_state = DAT_EP_STATE_UNCONNECTED; } } bail: dapl_dbg_log(DAPL_DBG_TYPE_RTN | DAPL_DBG_TYPE_CM, "dapl_ep_connect () returns 0x%x\n", dat_status); return (dat_status); }