2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2002-2003, Network Appliance, Inc. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/*
2N/A *
2N/A * MODULE: dapls_cr_callback.c
2N/A *
2N/A * PURPOSE: implements passive side connection callbacks
2N/A *
2N/A * Description: Accepts asynchronous callbacks from the Communications Manager
2N/A * for EVDs that have been specified as the connection_evd.
2N/A *
2N/A * $Id: dapl_cr_callback.c,v 1.58 2003/08/20 14:55:39 sjs2 Exp $
2N/A */
2N/A
2N/A#include "dapl.h"
2N/A#include "dapl_evd_util.h"
2N/A#include "dapl_cr_util.h"
2N/A#include "dapl_ia_util.h"
2N/A#include "dapl_sp_util.h"
2N/A#include "dapl_ep_util.h"
2N/A#include "dapl_adapter_util.h"
2N/A
2N/A
2N/A/*
2N/A * Prototypes
2N/A */
2N/ADAT_RETURN dapli_connection_request(
2N/A IN ib_cm_handle_t ib_cm_handle,
2N/A IN DAPL_SP *sp_ptr,
2N/A IN DAPL_PRIVATE *prd_ptr,
2N/A IN DAPL_EVD *evd_ptr);
2N/A
2N/ADAPL_EP * dapli_get_sp_ep(
2N/A IN ib_cm_handle_t ib_cm_handle,
2N/A IN DAPL_SP *sp_ptr,
2N/A IN const ib_cm_events_t ib_cm_event);
2N/A
2N/A/*
2N/A * dapls_cr_callback
2N/A *
2N/A * The callback function registered with verbs for passive side of
2N/A * connection requests. The interface is specified by cm_api.h
2N/A *
2N/A *
2N/A * Input:
2N/A * ib_cm_handle, Handle to CM
2N/A * ib_cm_event Specific CM event
2N/A * instant_data Private data with DAT ADDRESS header
2N/A * context SP pointer
2N/A *
2N/A * Output:
2N/A * None
2N/A *
2N/A */
2N/Avoid
2N/Adapls_cr_callback(
2N/A IN ib_cm_handle_t ib_cm_handle,
2N/A IN const ib_cm_events_t ib_cm_event,
2N/A IN const void *private_data_ptr, /* event data */
2N/A IN const void *context)
2N/A{
2N/A DAPL_EP *ep_ptr;
2N/A DAPL_EVD *evd_ptr;
2N/A DAPL_SP *sp_ptr;
2N/A DAPL_PRIVATE *prd_ptr;
2N/A DAT_EVENT_NUMBER event_type;
2N/A DAT_RETURN dat_status;
2N/A
2N/A dapl_dbg_log(DAPL_DBG_TYPE_CM,
2N/A "--> dapls_cr_callback! context: 0x%p "
2N/A "event: %d cm_handle 0x%llx magic 0x%x\n",
2N/A context, ib_cm_event, ib_cm_handle,
2N/A ((DAPL_HEADER *)context)->magic);
2N/A
2N/A if (((DAPL_HEADER *)context)->magic == DAPL_MAGIC_INVALID) {
2N/A return;
2N/A }
2N/A /*
2N/A * Passive side of the connection, context is a SP and
2N/A * we need to look up the EP.
2N/A */
2N/A dapl_os_assert(((DAPL_HEADER *)context)->magic == DAPL_MAGIC_PSP ||
2N/A ((DAPL_HEADER *)context)->magic == DAPL_MAGIC_RSP);
2N/A sp_ptr = (DAPL_SP *) context;
2N/A
2N/A /*
2N/A * CONNECT_REQUEST events create an event on the PSP
2N/A * EVD, which will trigger connection processing. The
2N/A * sequence is:
2N/A * CONNECT_REQUEST Event to SP
2N/A * CONNECTED Event to EP
2N/A * DISCONNECT Event to EP
2N/A *
2N/A * Obtain the EP if required and set an event up on the correct EVD.
2N/A */
2N/A if (ib_cm_event == IB_CME_CONNECTION_REQUEST_PENDING ||
2N/A ib_cm_event == IB_CME_CONNECTION_REQUEST_PENDING_PRIVATE_DATA) {
2N/A ep_ptr = NULL;
2N/A evd_ptr = sp_ptr->evd_handle;
2N/A } else {
2N/A ep_ptr = dapli_get_sp_ep(ib_cm_handle, sp_ptr, ib_cm_event);
2N/A dapl_os_assert(ep_ptr != NULL);
2N/A evd_ptr = (DAPL_EVD *) ep_ptr->param.connect_evd_handle;
2N/A dapl_dbg_log(DAPL_DBG_TYPE_CM,
2N/A " dapls_cr_callback cont: ep 0x%p evd 0x%p\n",
2N/A ep_ptr, evd_ptr);
2N/A }
2N/A
2N/A prd_ptr = (DAPL_PRIVATE *)private_data_ptr;
2N/A dat_status = DAT_INTERNAL_ERROR; /* init to ERR */
2N/A
2N/A switch (ib_cm_event) {
2N/A case IB_CME_CONNECTION_REQUEST_PENDING:
2N/A case IB_CME_CONNECTION_REQUEST_PENDING_PRIVATE_DATA: {
2N/A /*
2N/A * Requests arriving on a disabled SP are immediatly rejected
2N/A */
2N/A
2N/A dapl_os_lock(&sp_ptr->header.lock);
2N/A if (sp_ptr->listening == DAT_FALSE) {
2N/A dapl_os_unlock(&sp_ptr->header.lock);
2N/A dapl_dbg_log(DAPL_DBG_TYPE_CM,
2N/A "---> dapls_cr_callback: conn event on down SP\n");
2N/A return;
2N/A }
2N/A
2N/A if (sp_ptr->header.handle_type == DAT_HANDLE_TYPE_RSP) {
2N/A /*
2N/A * RSP connections only allow a single connection. Close
2N/A * it down NOW so we reject any further connections.
2N/A */
2N/A sp_ptr->listening = DAT_FALSE;
2N/A }
2N/A dapl_os_unlock(&sp_ptr->header.lock);
2N/A
2N/A /*
2N/A * Only occurs on the passive side of a connection
2N/A * dapli_connection_request will post the connection
2N/A * event if appropriate.
2N/A */
2N/A dat_status = dapli_connection_request(ib_cm_handle,
2N/A sp_ptr, prd_ptr, evd_ptr);
2N/A break;
2N/A }
2N/A case IB_CME_CONNECTED: {
2N/A /*
2N/A * This is just a notification the connection is now
2N/A * established, there isn't any private data to deal with.
2N/A *
2N/A * Update the EP state and cache a copy of the cm handle,
2N/A * then let the user know we are ready to go.
2N/A */
2N/A dapl_os_lock(&ep_ptr->header.lock);
2N/A if (ep_ptr->param.ep_state == DAT_EP_STATE_DISCONNECT_PENDING) {
2N/A /*
2N/A * If someone pulled the plug on the connection, just
2N/A * exit
2N/A */
2N/A dapl_os_unlock(&ep_ptr->header.lock);
2N/A dat_status = DAT_SUCCESS;
2N/A break;
2N/A }
2N/A dapls_ib_connected(ep_ptr);
2N/A ep_ptr->param.ep_state = DAT_EP_STATE_CONNECTED;
2N/A ep_ptr->cm_handle = ib_cm_handle;
2N/A dapl_os_unlock(&ep_ptr->header.lock);
2N/A
2N/A dat_status = dapls_evd_post_connection_event(
2N/A evd_ptr,
2N/A DAT_CONNECTION_EVENT_ESTABLISHED,
2N/A (DAT_HANDLE) ep_ptr,
2N/A ((DAPL_CR *)ep_ptr->cr_ptr)->param.private_data_size,
2N/A ((DAPL_CR *)ep_ptr->cr_ptr)->param.private_data);
2N/A /*
2N/A * post them to the recv evd now.
2N/A * there is a race here - if events arrive after we change
2N/A * the ep state to connected and before we process premature
2N/A * events
2N/A */
2N/A dapls_evd_post_premature_events(ep_ptr);
2N/A break;
2N/A }
2N/A case IB_CME_DISCONNECTED:
2N/A case IB_CME_DISCONNECTED_ON_LINK_DOWN: {
2N/A /*
2N/A * EP is now fully disconnected; initiate any post processing
2N/A * to reset the underlying QP and get the EP ready for
2N/A * another connection
2N/A */
2N/A dapl_os_lock(&ep_ptr->header.lock);
2N/A if (ep_ptr->param.ep_state == DAT_EP_STATE_DISCONNECTED) {
2N/A /* DTO error caused this */
2N/A event_type = DAT_CONNECTION_EVENT_BROKEN;
2N/A } else {
2N/A ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED;
2N/A dapls_ib_disconnect_clean(ep_ptr, DAT_FALSE,
2N/A ib_cm_event);
2N/A event_type = DAT_CONNECTION_EVENT_DISCONNECTED;
2N/A }
2N/A dapls_evd_post_premature_events(ep_ptr);
2N/A
2N/A ep_ptr->cr_ptr = NULL;
2N/A dapl_os_unlock(&ep_ptr->header.lock);
2N/A
2N/A /*
2N/A * If the user has done an ep_free of the EP, we have been
2N/A * waiting for the disconnect event; just clean it up now.
2N/A */
2N/A if (ep_ptr->header.magic == DAPL_MAGIC_EP_EXIT) {
2N/A (void) dapl_ep_free(ep_ptr);
2N/A }
2N/A
2N/A /* If the EP has been freed, the evd_ptr will be NULL */
2N/A if (evd_ptr != NULL) {
2N/A dat_status = dapls_evd_post_connection_event(
2N/A evd_ptr, event_type, (DAT_HANDLE) ep_ptr, 0, 0);
2N/A }
2N/A
2N/A break;
2N/A }
2N/A case IB_CME_DESTINATION_REJECT:
2N/A case IB_CME_DESTINATION_REJECT_PRIVATE_DATA:
2N/A case IB_CME_DESTINATION_UNREACHABLE: {
2N/A /*
2N/A * After posting an accept the requesting node has
2N/A * stopped talking.
2N/A */
2N/A dapl_os_lock(&ep_ptr->header.lock);
2N/A ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED;
2N/A ep_ptr->cm_handle = IB_INVALID_HANDLE;
2N/A dapls_ib_disconnect_clean(ep_ptr, DAT_FALSE, ib_cm_event);
2N/A dapl_os_unlock(&ep_ptr->header.lock);
2N/A dat_status = dapls_evd_post_connection_event(
2N/A evd_ptr,
2N/A DAT_CONNECTION_EVENT_ACCEPT_COMPLETION_ERROR,
2N/A (DAT_HANDLE) ep_ptr, 0, 0);
2N/A
2N/A break;
2N/A }
2N/A case IB_CME_TOO_MANY_CONNECTION_REQUESTS: {
2N/A /*
2N/A * DAPL does not deal with this IB error. There is a
2N/A * separate OVERFLOW event error if we try to post too many
2N/A * events, but we don't propagate this provider error. Not
2N/A * all providers generate this error.
2N/A */
2N/A break;
2N/A }
2N/A case IB_CME_LOCAL_FAILURE: {
2N/A ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED;
2N/A dapls_ib_disconnect_clean(ep_ptr, DAT_FALSE, ib_cm_event);
2N/A dat_status = dapls_evd_post_connection_event(
2N/A evd_ptr,
2N/A DAT_CONNECTION_EVENT_BROKEN,
2N/A (DAT_HANDLE) ep_ptr, 0, 0);
2N/A
2N/A break;
2N/A }
2N/A case IB_CME_TIMED_OUT: {
2N/A ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED;
2N/A dapls_ib_disconnect_clean(ep_ptr, DAT_FALSE, ib_cm_event);
2N/A dat_status = dapls_evd_post_connection_event(
2N/A evd_ptr,
2N/A DAT_CONNECTION_EVENT_TIMED_OUT,
2N/A (DAT_HANDLE) ep_ptr, 0, 0);
2N/A
2N/A break;
2N/A }
2N/A default:
2N/A dapl_os_assert(0); /* shouldn't happen */
2N/A break;
2N/A }
2N/A
2N/A if (dat_status != DAT_SUCCESS) {
2N/A /* The event post failed; take appropriate action. */
2N/A (void) dapls_ib_reject_connection(ib_cm_handle,
2N/A IB_CME_LOCAL_FAILURE, sp_ptr);
2N/A return;
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * dapli_connection_request
2N/A *
2N/A * Process a connection request on the Passive side of a connection.
2N/A * Create a CR record and link it on to the SP so we can update it
2N/A * and free it later. Create an EP if specified by the PSP flags.
2N/A *
2N/A * Input:
2N/A * ib_cm_handle,
2N/A * sp_ptr
2N/A * event_ptr
2N/A * prd_ptr
2N/A *
2N/A * Output:
2N/A * None
2N/A *
2N/A * Returns
2N/A * DAT_INSUFFICIENT_RESOURCES
2N/A * DAT_SUCCESS
2N/A *
2N/A */
2N/ADAT_RETURN
2N/Adapli_connection_request(
2N/A IN ib_cm_handle_t ib_cm_handle,
2N/A IN DAPL_SP *sp_ptr,
2N/A IN DAPL_PRIVATE *prd_ptr,
2N/A IN DAPL_EVD *evd_ptr)
2N/A{
2N/A DAT_RETURN dat_status;
2N/A DAPL_CR *cr_ptr;
2N/A DAPL_EP *ep_ptr;
2N/A DAPL_IA *ia_ptr;
2N/A DAT_SP_HANDLE sp_handle;
2N/A struct sockaddr_in *sv4;
2N/A struct sockaddr_in6 *sv6;
2N/A uint8_t *sadata;
2N/A DAT_COUNT length;
2N/A
2N/A cr_ptr = dapls_cr_alloc(sp_ptr->header.owner_ia);
2N/A if (cr_ptr == NULL) {
2N/A /* Invoking function will call dapls_ib_cm_reject() */
2N/A return (DAT_INSUFFICIENT_RESOURCES);
2N/A }
2N/A
2N/A /*
2N/A * Set up the CR
2N/A */
2N/A cr_ptr->sp_ptr = sp_ptr; /* maintain sp_ptr in case of reject */
2N/A cr_ptr->ib_cm_handle = ib_cm_handle;
2N/A /*
2N/A * Copy the remote address and private data out of the private_data
2N/A * payload and put them in a local structure
2N/A */
2N/A cr_ptr->param.private_data = cr_ptr->private_data;
2N/A cr_ptr->param.remote_ia_address_ptr =
2N/A (DAT_IA_ADDRESS_PTR)&cr_ptr->remote_ia_address;
2N/A cr_ptr->param.remote_port_qual =
2N/A (DAT_PORT_QUAL) prd_ptr->hello_msg.hi_port;
2N/A length = (DAT_COUNT) prd_ptr->hello_msg.hi_clen;
2N/A cr_ptr->param.private_data_size = length;
2N/A (void) dapl_os_memcpy(cr_ptr->private_data,
2N/A prd_ptr->private_data, length);
2N/A switch (prd_ptr->hello_msg.hi_ipv) {
2N/A case AF_INET:
2N/A sv4 = (struct sockaddr_in *)&cr_ptr->remote_ia_address;
2N/A sv4->sin_family = AF_INET;
2N/A sv4->sin_port = prd_ptr->hello_msg.hi_port;
2N/A sv4->sin_addr = prd_ptr->hello_msg.hi_v4ipaddr;
2N/A break;
2N/A case AF_INET6:
2N/A sv6 = (struct sockaddr_in6 *)&cr_ptr->remote_ia_address;
2N/A sv6->sin6_family = AF_INET6;
2N/A sv6->sin6_port = prd_ptr->hello_msg.hi_port;
2N/A sv6->sin6_addr = prd_ptr->hello_msg.hi_v6ipaddr;
2N/A break;
2N/A default:
2N/A sadata = (uint8_t *)&cr_ptr->remote_ia_address;
2N/A (void) dapl_os_memcpy(sadata, prd_ptr->hello_msg.hi_saaddr,
2N/A DAPL_ATS_NBYTES);
2N/A break;
2N/A }
2N/A
2N/A /* EP will be NULL unless RSP service point */
2N/A ep_ptr = (DAPL_EP *) sp_ptr->ep_handle;
2N/A
2N/A if (sp_ptr->psp_flags == DAT_PSP_PROVIDER_FLAG) {
2N/A /*
2N/A * Never true for RSP connections
2N/A *
2N/A * Create an EP for the user. If we can't allocate an
2N/A * EP we are out of resources and need to tell the
2N/A * requestor that we cant help them.
2N/A */
2N/A ia_ptr = sp_ptr->header.owner_ia;
2N/A ep_ptr = dapl_ep_alloc(ia_ptr, NULL, DAT_FALSE);
2N/A if (ep_ptr == NULL) {
2N/A dapls_cr_free(cr_ptr);
2N/A /* Invoking function will call dapls_ib_cm_reject() */
2N/A return (DAT_INSUFFICIENT_RESOURCES);
2N/A }
2N/A /* Link the EP onto the IA */
2N/A dapl_ia_link_ep(ia_ptr, ep_ptr);
2N/A }
2N/A
2N/A cr_ptr->param.local_ep_handle = ep_ptr;
2N/A
2N/A if (ep_ptr != NULL) {
2N/A /* Assign valid EP fields: RSP and PSP_PROVIDER_FLAG only */
2N/A if (sp_ptr->psp_flags == DAT_PSP_PROVIDER_FLAG) {
2N/A ep_ptr->param.ep_state =
2N/A DAT_EP_STATE_TENTATIVE_CONNECTION_PENDING;
2N/A } else { /* RSP */
2N/A dapl_os_assert(sp_ptr->header.handle_type ==
2N/A DAT_HANDLE_TYPE_RSP);
2N/A ep_ptr->param.ep_state =
2N/A DAT_EP_STATE_PASSIVE_CONNECTION_PENDING;
2N/A }
2N/A ep_ptr->cm_handle = ib_cm_handle;
2N/A }
2N/A
2N/A /* Post the event. */
2N/A /* assign sp_ptr to union to avoid typecast errors from compilers */
2N/A sp_handle.psp_handle = (DAT_PSP_HANDLE)sp_ptr;
2N/A dat_status = dapls_evd_post_cr_arrival_event(
2N/A evd_ptr,
2N/A DAT_CONNECTION_REQUEST_EVENT,
2N/A sp_handle,
2N/A (DAT_IA_ADDRESS_PTR)&sp_ptr->header.owner_ia->hca_ptr->hca_address,
2N/A sp_ptr->conn_qual,
2N/A (DAT_CR_HANDLE)cr_ptr);
2N/A if (dat_status != DAT_SUCCESS) {
2N/A dapls_cr_free(cr_ptr);
2N/A (void) dapls_ib_reject_connection(ib_cm_handle,
2N/A IB_CME_LOCAL_FAILURE, sp_ptr);
2N/A return (DAT_INSUFFICIENT_RESOURCES);
2N/A }
2N/A
2N/A /* link the CR onto the SP so we can pick it up later */
2N/A dapl_sp_link_cr(sp_ptr, cr_ptr);
2N/A
2N/A return (DAT_SUCCESS);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * dapli_get_sp_ep
2N/A *
2N/A * Passive side of a connection is now fully established. Clean
2N/A * up resources and obtain the EP pointer associated with a CR in
2N/A * the SP
2N/A *
2N/A * Input:
2N/A * ib_cm_handle,
2N/A * sp_ptr
2N/A *
2N/A * Output:
2N/A * none
2N/A *
2N/A * Returns
2N/A * ep_ptr
2N/A *
2N/A */
2N/ADAPL_EP *
2N/Adapli_get_sp_ep(
2N/A IN ib_cm_handle_t ib_cm_handle,
2N/A IN DAPL_SP *sp_ptr,
2N/A IN const ib_cm_events_t ib_cm_event)
2N/A{
2N/A DAPL_CR *cr_ptr;
2N/A DAPL_EP *ep_ptr;
2N/A
2N/A /*
2N/A * There are potentially multiple connections in progress. Need to
2N/A * go through the list and find the one we are interested
2N/A * in. There is no guarantee of order. dapl_sp_search_cr
2N/A * leaves the CR on the SP queue.
2N/A */
2N/A cr_ptr = dapl_sp_search_cr(sp_ptr, ib_cm_handle);
2N/A if (cr_ptr == NULL) {
2N/A dapl_os_assert(0);
2N/A return (NULL);
2N/A }
2N/A
2N/A ep_ptr = (DAPL_EP *)cr_ptr->param.local_ep_handle;
2N/A
2N/A dapl_os_assert(!(DAPL_BAD_HANDLE(ep_ptr, DAPL_MAGIC_EP)) ||
2N/A ep_ptr->header.magic == DAPL_MAGIC_EP_EXIT);
2N/A
2N/A if (ib_cm_event == IB_CME_DISCONNECTED ||
2N/A ib_cm_event == IB_CME_DISCONNECTED_ON_LINK_DOWN) {
2N/A /* Remove the CR from the queue */
2N/A dapl_sp_remove_cr(sp_ptr, cr_ptr);
2N/A /*
2N/A * Last event, time to clean up and dispose of the resource
2N/A */
2N/A dapls_cr_free(cr_ptr);
2N/A
2N/A /*
2N/A * If this SP has been removed from service, free it
2N/A * up after the last CR is removed
2N/A */
2N/A dapl_os_lock(&sp_ptr->header.lock);
2N/A if (sp_ptr->listening != DAT_TRUE &&
2N/A sp_ptr->cr_list_count == 0 &&
2N/A sp_ptr->state != DAPL_SP_STATE_FREE) {
2N/A dapl_dbg_log(DAPL_DBG_TYPE_CM,
2N/A "--> dapli_get_sp_ep! disconnect dump sp: %p \n",
2N/A sp_ptr);
2N/A sp_ptr->state = DAPL_SP_STATE_FREE;
2N/A dapl_os_unlock(&sp_ptr->header.lock);
2N/A (void) dapls_ib_remove_conn_listener(sp_ptr->
2N/A header.owner_ia, sp_ptr);
2N/A dapls_ia_unlink_sp((DAPL_IA *)sp_ptr->header.owner_ia,
2N/A sp_ptr);
2N/A dapls_sp_free_sp(sp_ptr);
2N/A } else {
2N/A dapl_os_unlock(&sp_ptr->header.lock);
2N/A }
2N/A }
2N/A return (ep_ptr);
2N/A}