+++ /dev/null
-/*\r
- * Copyright (c) 2005 SilverStorm Technologies. All rights reserved.\r
- * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. \r
- *\r
- * This software is available to you under the OpenIB.org BSD license\r
- * below:\r
- *\r
- * Redistribution and use in source and binary forms, with or\r
- * without modification, are permitted provided that the following\r
- * conditions are met:\r
- *\r
- * - Redistributions of source code must retain the above\r
- * copyright notice, this list of conditions and the following\r
- * disclaimer.\r
- *\r
- * - Redistributions in binary form must reproduce the above\r
- * copyright notice, this list of conditions and the following\r
- * disclaimer in the documentation and/or other materials\r
- * provided with the distribution.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- *\r
- * $Id$\r
- */\r
-\r
-#include <iba/ib_al.h>\r
-\r
-#include "al.h"\r
-#include "al_av.h"\r
-#include "al_ca.h"\r
-#include "al_cm.h"\r
-#include "al_cm_shared.h"\r
-#include "al_debug.h"\r
-\r
-#if defined(EVENT_TRACING)\r
-#ifdef offsetof\r
-#undef offsetof\r
-#endif\r
-#include "al_cm.tmh"\r
-#endif\r
-\r
-#include "al_mgr.h"\r
-#include "al_qp.h"\r
-\r
-#include "ib_common.h"\r
-\r
-\r
-\r
-\r
-/*\r
- * Comm id\r
- * Note: Running Comm id is a global number in this implementation.\r
- */\r
-atomic32_t local_comm_id = 1;\r
-\r
-/* NOTES:\r
- * The global cm object has a lock, as do connection objects. It is OK\r
- * to acquire the global cm object's lock when already holding a connection\r
- * object's lock. This means that it is not possible to acquire a connection's\r
- * lock after acquiring the global cm's lock. Connection objects provide\r
- * reference counting to allow releasing the global cm's lock and acquiring\r
- * the connection's lock.\r
- *\r
- * Callbacks where users are expected to call a CM function hold a reference\r
- * on the connection object until the expected call is made. These include:\r
- * REQ callback - expect REP or REJ\r
- * REP callback - expect RTU or REJ\r
- * LAP callback - expect APR\r
- * DREQ callback - expect DREP\r
- * The following callbacks do not expect a user call.\r
- * RTU callback - connection is established.\r
- * DREP callback - connection is released.\r
- * APR callback - LAP transaction is complete.\r
- * MRA callback - notification only.\r
- *\r
- * Connections are stored in the connection map once established using a 64-bit\r
- * key that is generated as (local_comm_id << 32 | remote_comm_id) if using the\r
- * connection as data source, or (remote_comm_id <<32 | local_comm_id) if using\r
- * a received MAD as source.\r
- *\r
- */\r
-\r
-\r
-/* Global instance of the CM agent. */\r
-al_cm_agent_t *gp_cm = NULL;\r
-\r
-\r
-\r
-static void\r
-__parse_cm_info(void);\r
-\r
-\r
-static void\r
-__process_cm_send_comp(\r
- IN cl_async_proc_item_t *p_item );\r
-\r
-\r
-/* debug functions */\r
-static void\r
-__parse_cm_info()\r
-{\r
-#if defined( _DEBUG_ )\r
- cl_list_item_t *p_item;\r
- al_listen_t *p_listen;\r
- al_conn_t *p_conn;\r
- uint32_t i;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("CM info:\n"\r
- "\tactive_listen_list..: %"PRIdSIZE_T"\n"\r
- "\tinactive_listen_list: %"PRIdSIZE_T"\n"\r
- "\tpending_list........: %"PRIdSIZE_T"\n"\r
- "\ttime_wait_list......: %"PRIdSIZE_T"\n"\r
- "\tconn_pool...........: %"PRIdSIZE_T"\n"\r
- "\treq_pool............: %"PRIdSIZE_T"\n"\r
- "\tconn_map............: %"PRIdSIZE_T"\n",\r
- cl_qlist_count( &gp_cm->active_listen_list ),\r
- cl_qlist_count( &gp_cm->inactive_listen_list ),\r
- cl_qlist_count( &gp_cm->pending_list ),\r
- cl_qlist_count( &gp_cm->time_wait_list ),\r
- cl_qpool_count( &gp_cm->conn_pool ),\r
- cl_pool_count( &gp_cm->req_pool ),\r
- cl_qmap_count( &gp_cm->conn_map ) ) );\r
- /* walk lists for connection info */\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Active listens:\n") );\r
- i = 1;\r
-\r
- for( p_item = cl_qlist_head( &gp_cm->active_listen_list );\r
- p_item != cl_qlist_end( &gp_cm->active_listen_list );\r
- p_item = cl_qlist_next( p_item ) )\r
- {\r
- p_listen = PARENT_STRUCT( p_item, al_listen_t, list_item );\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("\n%d)\n"\r
- "\tservice_id...:0x%I64x\n"\r
- "\tlid..........:0x%x\n"\r
- "\tport_guid....:0x%I64x\n"\r
- "\tqp_type......:%d\n",\r
- i++,\r
- p_listen->info.svc_id, p_listen->info.lid,\r
- p_listen->info.port_guid, p_listen->info.qp_type ) );\r
- }\r
-\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Inactive listens:\n") );\r
- i = 1;\r
-\r
- for( p_item = cl_qlist_head( &gp_cm->inactive_listen_list );\r
- p_item != cl_qlist_end( &gp_cm->inactive_listen_list );\r
- p_item = cl_qlist_next( p_item ) )\r
- {\r
- p_listen = PARENT_STRUCT( p_item, al_listen_t, list_item );\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("\n%d)\n"\r
- "\tservice_id...:0x%I64x\n"\r
- "\tlid..........:0x%x\n"\r
- "\tport_guid....:0x%I64x\n"\r
- "\tqp_type......:%d\n",\r
- i++,\r
- p_listen->info.svc_id, p_listen->info.lid,\r
- p_listen->info.port_guid, p_listen->info.qp_type ) );\r
- }\r
-\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("In Timewait:\n") );\r
- i = 1;\r
-\r
- for( p_item = cl_qlist_head( &gp_cm->time_wait_list );\r
- p_item != cl_qlist_end( &gp_cm->time_wait_list );\r
- p_item = cl_qlist_next( p_item ) )\r
- {\r
- p_conn = PARENT_STRUCT( p_item, al_conn_t, map_item );\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("\n%d)\n"\r
- "\tstate........:%d\n"\r
- "\tref_cnt......:%d\n"\r
- "\tlocal_id.....:0x%x\n"\r
- "\tremote_id....:0x%x\n"\r
- "\tlocal_qpn....:0x%x\n"\r
- "\tremote_qpn...:0x%x\n",\r
- i++,\r
- p_conn->state, p_conn->ref_cnt, p_conn->local_comm_id,\r
- p_conn->remote_comm_id,\r
- cl_ntoh32(p_conn->local_qpn),\r
- cl_ntoh32(p_conn->remote_qpn) ) );\r
- }\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-#endif\r
-}\r
-\r
-\r
-/*\r
- * PnP callback invoked when deregistration is complete.\r
- */\r
-/***static***/ void\r
-__cm_pnp_dereg_cb(\r
- IN void *context )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- UNUSED_PARAM( context );\r
-\r
- deref_al_obj( &gp_cm->obj );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Starts immediate cleanup of the CM. Invoked during al_obj destruction.\r
- */\r
-/***static***/ void\r
-__destroying_cm(\r
- IN al_obj_t* p_obj )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( &gp_cm->obj == p_obj );\r
- UNUSED_PARAM( p_obj );\r
-\r
- /* Deregister from PnP notifications. */\r
- if( gp_cm->h_pnp )\r
- {\r
- status = ib_dereg_pnp( gp_cm->h_pnp, __cm_pnp_dereg_cb );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_dereg_pnp failed with status %s.\n",\r
- ib_get_err_str(status)) );\r
- deref_al_obj( &gp_cm->obj );\r
- }\r
- }\r
-\r
- /* Cancel all timewait timers. */\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Frees the global CM agent. Invoked during al_obj destruction.\r
- */\r
-void\r
-__free_cm(\r
- IN al_obj_t* p_obj )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( &gp_cm->obj == p_obj );\r
- /* All listen request should have been cleaned up by this point. */\r
- CL_ASSERT( cl_is_qlist_empty( &gp_cm->active_listen_list ) );\r
- CL_ASSERT( cl_is_qlist_empty( &gp_cm->inactive_listen_list ) );\r
- /* All connections should have been cancelled/disconnected by now. */\r
- CL_ASSERT( cl_is_qlist_empty( &gp_cm->pending_list ) );\r
- CL_ASSERT( cl_is_qmap_empty( &gp_cm->conn_map ) );\r
-\r
- __parse_cm_info();\r
-\r
- //***TODO: Figure out the right handling of the time wait state.\r
- //***TODO: Must cancel all timewait timers.\r
- /* Return all connection objects in the time-wait state to their pool. */\r
- cl_qpool_put_list( &gp_cm->conn_pool, &gp_cm->time_wait_list );\r
-\r
- /*\r
- * All CM port agents should have been destroyed by now via the\r
- * standard child object destruction provided by the al_obj.\r
- */\r
- cl_qpool_destroy( &gp_cm->conn_pool );\r
- cl_pool_destroy( &gp_cm->req_pool );\r
- destroy_al_obj( p_obj );\r
-\r
- cl_free( gp_cm );\r
- gp_cm = NULL;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Performs immediate cleanup of resources.\r
- */\r
-/***static***/ void\r
-__destroying_port_cm(\r
- IN al_obj_t *p_obj )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_port_cm = PARENT_STRUCT( p_obj, cm_port_agent_t, obj );\r
-\r
- if( p_port_cm->h_qp )\r
- {\r
- ib_destroy_qp( p_port_cm->h_qp, (ib_pfn_destroy_cb_t)deref_al_obj );\r
- p_port_cm->h_qp = NULL;\r
- }\r
-\r
- if( p_port_cm->h_pd )\r
- {\r
- ib_dealloc_pd( p_port_cm->h_pd, (ib_pfn_destroy_cb_t)deref_al_obj );\r
- p_port_cm->h_pd = NULL;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Release all resources allocated by a port CM agent. Finishes any cleanup\r
- * for a port agent.\r
- */\r
-/***static***/ void\r
-__free_port_cm(\r
- IN al_obj_t *p_obj )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_port_cm = PARENT_STRUCT( p_obj, cm_port_agent_t, obj );\r
-\r
- CL_ASSERT( cl_is_qlist_empty( &p_port_cm->av_list ) );\r
-\r
- if( p_port_cm->h_ca )\r
- deref_al_obj( &p_port_cm->h_ca->obj );\r
-\r
- cl_spinlock_destroy( &p_port_cm->lock );\r
- destroy_al_obj( &p_port_cm->obj );\r
- cl_free( p_port_cm );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-ib_api_status_t\r
-__get_av(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN ib_mad_element_t *p_mad )\r
-{\r
- ib_api_status_t status = IB_SUCCESS;\r
- ib_av_handle_t h_av;\r
- ib_av_attr_t av_attr;\r
- cl_list_item_t *p_list_item;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_spinlock_acquire( &p_port_cm->lock );\r
-\r
- /* Search for an existing AV that can be used. */\r
- for( p_list_item = cl_qlist_head( &p_port_cm->av_list );\r
- p_list_item != cl_qlist_end( &p_port_cm->av_list );\r
- p_list_item = cl_qlist_next( p_list_item ) )\r
- {\r
- h_av = PARENT_STRUCT( p_list_item, ib_av_t, list_item );\r
-\r
- /* verify sl level */\r
- if( p_mad->remote_sl != h_av->av_attr.sl )\r
- continue;\r
-\r
- if( p_mad->grh_valid == FALSE )\r
- {\r
- /* verify source */\r
- if( p_mad->path_bits != h_av->av_attr.path_bits )\r
- continue;\r
-\r
- /* verify destination */\r
- if( p_mad->remote_lid != h_av->av_attr.dlid )\r
- continue;\r
- }\r
- else\r
- {\r
- /* verify source */\r
- if( cl_memcmp( &p_mad->p_grh->src_gid.raw,\r
- &h_av->av_attr.grh.src_gid.raw,\r
- sizeof(ib_gid_t) ) )\r
- continue;\r
-\r
- /* verify destination */\r
- if( cl_memcmp( &p_mad->p_grh->dest_gid.raw,\r
- &h_av->av_attr.grh.dest_gid.raw,\r
- sizeof(ib_gid_t) ) )\r
- continue;\r
- }\r
-\r
- ref_al_obj( &h_av->obj );\r
- p_mad->h_av = h_av;\r
- break;\r
- }\r
-\r
- cl_spinlock_release( &p_port_cm->lock );\r
-\r
- if( p_list_item == cl_qlist_end( &p_port_cm->av_list ) )\r
- {\r
- /* Create a new AV. */\r
- /* format av info */\r
- cl_memclr( &av_attr, sizeof(ib_av_attr_t) );\r
- av_attr.sl = p_mad->remote_sl;\r
- av_attr.static_rate = IB_PATH_RECORD_RATE_10_GBS;\r
- av_attr.path_bits = p_mad->path_bits;\r
- av_attr.dlid = p_mad->remote_lid;\r
- av_attr.port_num = (uint8_t)( p_port_cm->port_idx + 1 );\r
- av_attr.grh_valid = p_mad->grh_valid;\r
- if( p_mad->grh_valid == TRUE )\r
- av_attr.grh = *p_mad->p_grh;\r
-\r
- status = ib_create_av( p_port_cm->h_pd, &av_attr, &h_av );\r
- if( status == IB_SUCCESS )\r
- {\r
- /* take a reference on the object and add to av list */\r
- ref_al_obj( &h_av->obj );\r
- p_mad->h_av = h_av;\r
- cl_spinlock_acquire( &p_port_cm->lock );\r
- cl_qlist_insert_tail( &p_port_cm->av_list, &h_av->list_item );\r
- cl_spinlock_release( &p_port_cm->lock );\r
- }\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-ib_api_status_t\r
-__put_av(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN ib_mad_element_t *p_mad )\r
-{\r
- ib_av_t *h_av;\r
- uint32_t ref_cnt;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- h_av = p_mad->h_av;\r
-\r
- CL_ASSERT( h_av );\r
-\r
- /* Remove the reference on the AL object. */\r
- cl_spinlock_acquire( &p_port_cm->lock );\r
- ref_cnt = deref_al_obj( &h_av->obj );\r
- CL_ASSERT( ref_cnt >= 1 );\r
- if( ref_cnt == 1 )\r
- {\r
- cl_qlist_remove_item( &p_port_cm->av_list, &h_av->list_item );\r
- cl_spinlock_release( &p_port_cm->lock );\r
-\r
- ib_destroy_av( h_av );\r
- }\r
- else\r
- {\r
- cl_spinlock_release( &p_port_cm->lock );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-al_conn_t*\r
-__get_conn(\r
- IN const ib_al_handle_t h_al,\r
- IN const ib_qp_type_t qp_type )\r
-{\r
- cl_pool_item_t *p_item;\r
- al_conn_t *p_conn;\r
- uint8_t counter;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
- CL_ASSERT( h_al && h_al->obj.type == AL_OBJ_TYPE_H_AL );\r
-\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_item = cl_qpool_get( &gp_cm->conn_pool );\r
- if( !p_item )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cl_qpool_get failed for al_conn_t.\n") );\r
- return NULL;\r
- }\r
-\r
- p_conn = PARENT_STRUCT( p_item, al_conn_t, map_item );\r
-\r
- p_conn->p_req_info = cl_pool_get( &gp_cm->req_pool );\r
- if( !p_conn->p_req_info )\r
- {\r
- cl_qpool_put( &gp_cm->conn_pool, (cl_pool_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cl_pool_get failed for conn_req_t.\n") );\r
- return NULL;\r
- }\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- /*\r
- * Increment the counter in the comm ID to meet REJ retry requirements\r
- * from IB spec section 12.9.8.7\r
- */\r
- counter = (uint8_t)(p_conn->local_comm_id >> 24) + 1;\r
- p_conn->local_comm_id &= CM_CONN_POOL_MAX_MASK;\r
- p_conn->local_comm_id |= (((net32_t)counter) << 24);\r
-\r
- /* Clear out the request information. */\r
- cl_memclr( p_conn->p_req_info, sizeof(conn_req_t) );\r
- p_conn->p_req_info->qp_mod_rtr.req_state = IB_QPS_RTR;\r
- p_conn->p_req_info->qp_mod_rts.req_state = IB_QPS_RTS;\r
- p_conn->p_req_info->port_idx = (uint8_t)-1;\r
- p_conn->p_req_info->alt_port_idx = (uint8_t)-1;\r
-\r
- /* Initialize the connection parameters. */\r
- p_conn->ref_cnt = 1;\r
-\r
- /* set version to highest known version */\r
- p_conn->p_req_info->class_ver = IB_MCLASS_CM_VER_2;\r
-\r
- p_conn->qp_type = qp_type;\r
- p_conn->p_req_info->xport_type = qp_type;\r
-\r
- p_conn->h_qp = NULL;\r
-\r
- p_conn->local_qpn = 0;\r
-\r
- p_conn->remote_qpn = 0;\r
-\r
- /* init event based logic for synchronous operations */\r
- p_conn->p_sync_event = NULL;\r
-\r
- p_conn->pfn_cm_apr_cb = NULL;\r
- p_conn->pfn_cm_drep_cb = NULL;\r
- p_conn->pfn_cm_dreq_cb = NULL;\r
- p_conn->pfn_cm_lap_cb = NULL;\r
-\r
- p_conn->remote_ca_guid = 0;\r
- p_conn->remote_comm_id = 0;\r
-\r
- cl_memclr( &p_conn->path, sizeof( ib_path_rec_t ) * 2 );\r
-\r
- p_conn->state = CM_CONN_RESET;\r
-\r
- p_conn->was_active = TRUE;\r
- p_conn->target_ack_delay = 0;\r
-\r
- /*\r
- * Initialize the MAD header. The only field that needs to be set\r
- * is the attribute ID.\r
- */\r
- p_conn->mads.hdr.attr_mod = 0;\r
- p_conn->mads.hdr.base_ver = 1;\r
- p_conn->mads.hdr.class_spec = 0;\r
- p_conn->mads.hdr.method = IB_MAD_METHOD_SEND;\r
- p_conn->mads.hdr.mgmt_class = IB_MCLASS_COMM_MGMT;\r
- p_conn->mads.hdr.resv = 0;\r
- p_conn->mads.hdr.status = 0;\r
- p_conn->mads.hdr.trans_id = (ib_net64_t)cl_hton32( p_conn->local_comm_id );\r
-\r
- /* class version is set to the user's request */\r
- p_conn->mads.hdr.class_ver = IB_MCLASS_CM_VER_2;\r
-\r
- al_insert_conn( h_al, p_conn );\r
- if( p_conn->hdl == AL_INVALID_HANDLE )\r
- {\r
- al_remove_conn( p_conn );\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_pool_put( &gp_cm->req_pool, p_conn->p_req_info );\r
- p_conn->p_req_info = NULL;\r
- cl_qpool_put( &gp_cm->conn_pool, (cl_pool_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("al_insert_conn failed for conn_req_t.\n") );\r
- return NULL;\r
- }\r
-\r
- ref_al_obj( &gp_cm->obj );\r
- AL_EXIT( AL_DBG_CM );\r
- return p_conn;\r
-}\r
-\r
-void\r
-__ref_conn(\r
- IN al_conn_t* const p_conn )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_atomic_inc( &p_conn->ref_cnt );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-void\r
-cm_cleanup_conn(\r
- IN const ib_cm_handle_t h_conn )\r
-{\r
- /*\r
- * Set the reference count to 1, then decrement it to force cleanup.\r
- * Note that if we are in this function, the connection object has not\r
- * been cleaned up properly. We are simply trying to cleanup the best\r
- * that we can now.\r
- */\r
- h_conn->ref_cnt = 1;\r
- __deref_conn( h_conn );\r
-}\r
-\r
-\r
-\r
-void\r
-__deref_conn(\r
- IN al_conn_t* const p_conn )\r
-{\r
- ib_qp_handle_t h_qp;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /*\r
- * Destruction of the connection object and unbinding it from the QP\r
- * must be atomic. The QP will try to destroy the connection object\r
- * when the QP is destroyed. We use the global CM lock to synchronize\r
- * changes to the connection's QP handle.\r
- */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- if( !cl_atomic_dec( &p_conn->ref_cnt ) )\r
- {\r
- /* Free the connection request information. */\r
- __release_req_info( p_conn );\r
-\r
- /*\r
- * Unbind the QP while holding the CM lock -- see above.\r
- * Note that we should not have a QP if we are at this point. The\r
- * only way this can occur is if we did not properly cleanup the\r
- * QP in some other code path (such as disconnection).\r
- */\r
- h_qp = p_conn->h_qp;\r
- cm_unbind_qp( p_conn );\r
- \r
- /* Remove the connection from AL, if not already done. */\r
- if( p_conn->h_al )\r
- al_remove_conn( p_conn );\r
-\r
- /*\r
- * The connection should be in the RESET state. If it is not, then\r
- * we have most likely timed out trying to cleanup the connection.\r
- * Attempting to recover from such poor behavior now.\r
- */\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_RCVD:\r
- case CM_CONN_REQ_SENT:\r
- case CM_CONN_REQ_MRA_RCVD:\r
- case CM_CONN_REQ_MRA_SENT:\r
- case CM_CONN_REP_RCVD:\r
- case CM_CONN_REP_SENT:\r
- case CM_CONN_REP_MRA_RCVD:\r
- case CM_CONN_REP_MRA_SENT:\r
- /* We're a pending connection. */\r
- cl_qlist_remove_item( &gp_cm->pending_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
- break;\r
-\r
- case CM_CONN_ESTABLISHED:\r
- case CM_CONN_LAP_RCVD:\r
- case CM_CONN_LAP_SENT:\r
- case CM_CONN_LAP_MRA_RCVD:\r
- case CM_CONN_LAP_MRA_SENT:\r
- case CM_CONN_DREQ_SENT:\r
- case CM_CONN_DREQ_RCVD:\r
- case CM_CONN_DREP_SENT:\r
- /* We're still in the connection map. */\r
- cl_qmap_remove_item( &gp_cm->conn_map, &p_conn->map_item );\r
- break;\r
-\r
- case CM_CONN_TIMEWAIT:\r
- /* We're still in the time wait list. */\r
- cl_qlist_remove_item( &gp_cm->time_wait_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
- break;\r
-\r
- default:\r
- /* We shouldn't be in any lists or maps. */\r
- break;\r
- }\r
-\r
- /* Return the connection object to the free pool. */\r
- cl_qpool_put( &gp_cm->conn_pool,\r
- (cl_pool_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- \r
- if( h_qp )\r
- {\r
- /* We shouldn't have a QP at this point. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("QP (%016I64x) still referenced by connection object\n", (LONG_PTR)h_qp) );\r
- cm_reset_qp( h_qp, 0 );\r
- deref_al_obj( &h_qp->obj );\r
- }\r
- deref_al_obj( &gp_cm->obj );\r
- }\r
- else\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-boolean_t\r
-__is_lid_valid(\r
- IN ib_net16_t lid,\r
- IN ib_net16_t port_lid,\r
- IN uint8_t lmc )\r
-{\r
- uint16_t lid1;\r
- uint16_t lid2;\r
- uint16_t path_bits;\r
-\r
- if(lmc)\r
- {\r
- lid1 = CL_NTOH16(lid);\r
- lid2 = CL_NTOH16(port_lid);\r
- path_bits = 0;\r
-\r
- if( lid1 < lid2 )\r
- return FALSE;\r
-\r
- while( lmc-- )\r
- path_bits = (uint16_t)( (path_bits << 1) | 1 );\r
-\r
- lid2 |= path_bits;\r
-\r
- if( lid1 > lid2)\r
- return FALSE;\r
- }\r
- else\r
- {\r
- if (lid != port_lid)\r
- return FALSE;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__cm_send(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN al_conn_t* const p_conn )\r
-{\r
- ib_mad_element_t *p_mad;\r
- ib_api_status_t status;\r
- ib_grh_t grh;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_conn );\r
-\r
- /* Get a MAD from the pool. */\r
- status = ib_get_mad( p_port_cm->pool_key, MAD_BLOCK_SIZE, &p_mad );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_get_mad failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Set the addressing information in the MAD. */\r
- if( p_conn->path[p_conn->idx_primary].hop_flow_raw.val )\r
- {\r
- cl_memclr( &grh, sizeof( ib_grh_t ) );\r
- p_mad->p_grh = &grh;\r
- grh.ver_class_flow = ib_grh_set_ver_class_flow(\r
- 1, p_conn->path[p_conn->idx_primary].tclass,\r
- ib_path_rec_flow_lbl( &p_conn->path[p_conn->idx_primary] ) );\r
- grh.hop_limit =\r
- ib_path_rec_hop_limit( &p_conn->path[p_conn->idx_primary] );\r
- grh.src_gid = p_conn->path[p_conn->idx_primary].sgid;\r
- grh.dest_gid = p_conn->path[p_conn->idx_primary].dgid;\r
- }\r
- else\r
- {\r
- p_mad->p_grh = NULL;\r
- }\r
- p_mad->remote_sl =\r
- ib_path_rec_sl( &p_conn->path[p_conn->idx_primary] );\r
- p_mad->remote_lid = p_conn->path[p_conn->idx_primary].dlid;\r
- p_mad->path_bits = 0;\r
- p_mad->remote_qp = IB_QP1;\r
- p_mad->send_opt = IB_SEND_OPT_SIGNALED;\r
- p_mad->remote_qkey = IB_QP1_WELL_KNOWN_Q_KEY;\r
- switch( p_conn->mads.hdr.attr_id )\r
- {\r
- case CM_REQ_ATTR_ID:\r
- case CM_REP_ATTR_ID:\r
- case CM_LAP_ATTR_ID:\r
- case CM_DREQ_ATTR_ID:\r
- /*\r
- * REQ, REP, LAP, and DREQ are retried until either a response is\r
- * received or the operation times out.\r
- */\r
- p_mad->resp_expected = TRUE;\r
- p_mad->retry_cnt = p_conn->max_cm_retries;\r
- p_mad->timeout_ms = p_conn->retry_timeout;\r
- break;\r
-\r
- default:\r
- /*\r
- * All other CM MADs are sent once, and repeated if the previous MAD\r
- * is received again.\r
- */\r
- p_mad->resp_expected = FALSE;\r
- p_mad->retry_cnt = 0;\r
- p_mad->timeout_ms = 0;\r
- break;\r
- }\r
-\r
- /* Copy the mad contents. */\r
- cl_memcpy( p_mad->p_mad_buf, &p_conn->mads, MAD_BLOCK_SIZE );\r
-\r
- /* Set the contexts. */\r
- p_mad->context1 = p_conn;\r
- p_mad->context2 = NULL;\r
- /* reference the connection for which we are sending the MAD. */\r
- __ref_conn( p_conn );\r
-\r
- /* Store the mad service handle in the connection for cancelling. */\r
- p_conn->h_mad_svc = p_port_cm->h_mad_svc;\r
-\r
- /* Get AV for the send */\r
- p_mad->h_av = NULL;\r
- status = __get_av( p_port_cm, p_mad );\r
- if( status != IB_SUCCESS )\r
- {\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_mad );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_av failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Fire in the hole! */\r
- p_conn->p_send_mad = p_mad;\r
- status = ib_send_mad( p_port_cm->h_mad_svc, p_mad, NULL );\r
- if( status != IB_SUCCESS )\r
- {\r
- p_conn->p_send_mad = NULL;\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_mad );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__cm_send_mad(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN ib_mad_element_t* const p_mad )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_port_cm );\r
- CL_ASSERT( p_mad );\r
- CL_ASSERT( !p_mad->resp_expected );\r
-\r
- /* Use the mad's attributes already present */\r
-\r
- /* Set the contexts. */\r
- p_mad->context1 = NULL;\r
- p_mad->context2 = NULL;\r
-\r
- /* Get AV for the send */\r
- if( !p_mad->h_av )\r
- {\r
- status = __get_av( p_port_cm, p_mad );\r
- if( status != IB_SUCCESS )\r
- {\r
- ib_put_mad( p_mad );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_av failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
- }\r
-\r
- status = ib_send_mad( p_port_cm->h_mad_svc, p_mad, NULL );\r
- if( status != IB_SUCCESS )\r
- {\r
- ib_put_mad( p_mad );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-void\r
-__repeated_mad(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN al_conn_t* const p_conn,\r
- IN ib_mad_element_t* const p_mad )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_port_cm );\r
- CL_ASSERT( p_conn );\r
- CL_ASSERT( p_mad );\r
-\r
- /* Repeat the last mad sent for the connection. */\r
- cm_res_acquire( p_conn );\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_SENT:\r
- /*\r
- * CM_CONN_REQ_SENT is only valid for peer requests that win\r
- * the peer comparisson.\r
- */\r
- CL_ASSERT( p_conn->p_req_info->pfn_cm_req_cb );\r
- case CM_CONN_REP_SENT:\r
- case CM_CONN_ESTABLISHED:\r
- case CM_CONN_DREQ_SENT:\r
- case CM_CONN_DREP_SENT:\r
- case CM_CONN_TIMEWAIT:\r
- cl_memcpy( p_mad->p_mad_buf, &p_conn->mads, sizeof(mad_cm_req_t) );\r
- p_mad->send_context1 = NULL;\r
- p_mad->send_context2 = NULL;\r
- status = __cm_send_mad( p_port_cm, p_mad );\r
- if( status == IB_SUCCESS )\r
- break;\r
-\r
- /* Failure. Fall through. */\r
-\r
- default:\r
- /* Return the MAD to the mad pool */\r
- ib_put_mad( p_mad );\r
- break;\r
- }\r
- cm_res_release( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__cm_mad_recv_cb(\r
- IN ib_mad_svc_handle_t h_mad_svc,\r
- IN void *context,\r
- IN ib_mad_element_t *p_mad )\r
-{\r
- ib_mad_t *p_hdr;\r
- cm_async_mad_t *p_async_mad;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- UNUSED_PARAM( h_mad_svc );\r
-\r
- CL_ASSERT( p_mad->p_next == NULL );\r
-\r
- p_hdr = (ib_mad_t*)p_mad->p_mad_buf;\r
-\r
- p_async_mad = (cm_async_mad_t*)cl_zalloc( sizeof(cm_async_mad_t) );\r
- if( !p_async_mad )\r
- {\r
- ib_put_mad( p_mad );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("failed to cl_zalloc cm_async_mad_t (%d bytes)\n",\r
- sizeof(cm_async_mad_t)) );\r
- return;\r
- }\r
-\r
- p_async_mad->p_port_cm = (cm_port_agent_t*)context;\r
- p_async_mad->p_mad = p_mad;\r
-\r
- switch( p_hdr->attr_id )\r
- {\r
- case CM_REQ_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_req;\r
- break;\r
-\r
- case CM_MRA_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_mra;\r
- break;\r
-\r
- case CM_REJ_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_rej;\r
- break;\r
-\r
- case CM_REP_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_rep;\r
- break;\r
-\r
- case CM_RTU_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_rtu;\r
- break;\r
-\r
- case CM_DREQ_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_dreq;\r
- break;\r
-\r
- case CM_DREP_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_drep;\r
- break;\r
-\r
- case CM_LAP_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_lap;\r
- break;\r
-\r
- case CM_APR_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_apr;\r
- break;\r
-\r
- case CM_SIDR_REQ_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_sidr_req;\r
- break;\r
-\r
- case CM_SIDR_REP_ATTR_ID:\r
- p_async_mad->item.pfn_callback = __process_cm_sidr_rep;\r
- break;\r
-\r
- default:\r
- cl_free( p_async_mad );\r
- ib_put_mad( p_mad );\r
- AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid CM MAD attribute ID.\n") );\r
- return;\r
- }\r
-\r
- /* Queue the MAD for asynchronous processing. */\r
- cl_async_proc_queue( gp_async_proc_mgr, &p_async_mad->item );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__cm_mad_send_cb(\r
- IN ib_mad_svc_handle_t h_mad_svc,\r
- IN void *context,\r
- IN ib_mad_element_t *p_mad )\r
-{\r
- cm_async_mad_t *p_async_mad;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- UNUSED_PARAM( h_mad_svc );\r
- CL_ASSERT( p_mad->p_next == NULL );\r
-\r
- p_async_mad = (cm_async_mad_t*)cl_zalloc( sizeof(cm_async_mad_t) );\r
- if( !p_async_mad )\r
- {\r
- ib_put_mad( p_mad );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("failed to cl_zalloc cm_async_mad_t (%d bytes)\n",\r
- sizeof(cm_async_mad_t)) );\r
- return;\r
- }\r
-\r
- p_async_mad->p_port_cm = (cm_port_agent_t*)context;\r
- p_async_mad->p_mad = p_mad;\r
- p_async_mad->item.pfn_callback = __process_cm_send_comp;\r
-\r
- /* Queue the MAD for asynchronous processing. */\r
- cl_async_proc_queue( gp_async_proc_mgr, &p_async_mad->item );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__process_cm_send_comp(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- cm_async_mad_t *p_async_mad;\r
- cm_port_agent_t *p_port_cm;\r
- al_conn_t *p_conn;\r
- ib_mad_element_t *p_mad;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_mad = p_async_mad->p_mad;\r
- p_port_cm = p_async_mad->p_port_cm;\r
- cl_free( p_async_mad );\r
-\r
- p_conn = (ib_cm_handle_t)p_mad->context1;\r
-\r
- /*\r
- * The connection context is not set when performing immediate responses,\r
- * such as repeating MADS.\r
- */\r
- if( !p_conn )\r
- {\r
- __put_av( p_port_cm, p_mad );\r
- ib_put_mad( p_mad );\r
- AL_EXIT( AL_DBG_CM );\r
- return;\r
- }\r
-\r
- cm_res_acquire( p_conn );\r
- switch( p_mad->status )\r
- {\r
- case IB_WCS_SUCCESS:\r
- case IB_WCS_CANCELED:\r
- if( p_conn->state == CM_CONN_REP_SENT )\r
- {\r
- /*\r
- * Free connection for UD types (SIDR)\r
- */\r
- //***TODO: Do something similar with UD in IB_TIMEOUT case.\r
- if( p_conn->qp_type == IB_QPT_UNRELIABLE_DGRM )\r
- {\r
- /* Move the connection from the pending list to the connection map. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_remove_item( &gp_cm->pending_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- __deref_conn( p_conn );\r
- }\r
- }\r
-\r
- /* Release the reference taken when sending. */\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- break;\r
-\r
- case IB_WCS_TIMEOUT_RETRY_ERR:\r
- /*\r
- * Timeout. No response received within allowable time. Queue\r
- * an async item to reject and call the user back. We continue to\r
- * hold the reference on p_conn from the send.\r
- */\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_SENT:\r
- case CM_CONN_REP_SENT:\r
- p_conn->timeout_item.pfn_callback = __proc_conn_timeout;\r
- break;\r
- case CM_CONN_LAP_SENT:\r
- p_conn->timeout_item.pfn_callback = __proc_lap_timeout;\r
- break;\r
- case CM_CONN_DREQ_SENT:\r
- p_conn->timeout_item.pfn_callback = __proc_dconn_timeout;\r
- break;\r
- default:\r
- p_conn->timeout_item.pfn_callback = NULL;\r
- break;\r
- }\r
-\r
- /* Process timeouts asynchronously - queue the callback. */\r
- if( p_conn->timeout_item.pfn_callback )\r
- cl_async_proc_queue( gp_async_proc_mgr, &p_conn->timeout_item );\r
-\r
- cm_res_release( p_conn );\r
-\r
- /* Continue to hold the reference if we're processing a timeout. */\r
- if( !p_conn->timeout_item.pfn_callback )\r
- __deref_conn( p_conn );\r
- break;\r
-\r
- default:\r
- /* Some sort of error. */\r
- //***TODO: What kind of errors can we get, and how do we handle them?\r
-\r
- /* Release the reference taken when sending. */\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- }\r
-\r
- __put_av( p_port_cm, p_mad );\r
- ib_put_mad( p_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Called when the HCA generates the communication established event.\r
- * This happens when a receive happens when in the RTR state. For now, we\r
- * continue to wait until the RTU is received before transitioning the QP.\r
- * Doing this is not ideal, but should work in almost all cases, since\r
- * the REP and RTU will be retried, and avoids having to deal with unpleasant\r
- * race conditions.\r
- */\r
-void\r
-cm_conn_established(\r
- IN al_conn_t* const p_conn )\r
-{\r
- /* We ignore the callback since we use RTU as the trigger. */\r
- UNUSED_PARAM( p_conn );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Called when the HCA migrates to the alternate path.\r
- */\r
-void\r
-cm_conn_migrated(\r
- IN al_conn_t* const p_conn )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Update the index to the primary path. */\r
- cm_res_acquire( p_conn );\r
-\r
- /* Increment the index. */\r
- p_conn->idx_primary++;\r
- /* Only the lowest bit is valid (0 or 1). */\r
- p_conn->idx_primary&=0x1;\r
- cm_res_release( p_conn );\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-/*\r
- * Called when the HCA generates a communication established or APM event.\r
- */\r
-void\r
-cm_async_event_cb(\r
- IN const ib_async_event_rec_t* const p_event_rec )\r
-{\r
- al_conn_qp_t* p_qp;\r
-\r
- CL_ASSERT( p_event_rec );\r
-\r
- p_qp = (al_conn_qp_t* __ptr64)p_event_rec->context;\r
-\r
- /*\r
- * Make sure to check that the QP is still connected by verifying\r
- * that we still reference a p_conn structure. The QP may be being\r
- * destroyed.\r
- */\r
-\r
- switch( p_event_rec->code )\r
- {\r
- case IB_AE_QP_COMM:\r
- if( !p_qp->p_conn )\r
- break;\r
- cm_conn_established( p_qp->p_conn );\r
- break;\r
-\r
- case IB_AE_QP_APM:\r
- if( !p_qp->p_conn )\r
- break;\r
- cm_conn_migrated( p_qp->p_conn );\r
- break;\r
-\r
- case IB_AE_QP_APM_ERROR:\r
- //***TODO: Figure out how to handle these errors.\r
- break;\r
-\r
- default:\r
- break;\r
- }\r
-}\r
-\r
-\r
-void\r
-__cm_qp_event_cb(\r
- IN ib_async_event_rec_t *p_event_rec )\r
-{\r
- UNUSED_PARAM( p_event_rec );\r
-\r
- /*\r
- * Most of the QP events are trapped by the real owner of the QP.\r
- * For real events, the CM may not be able to do much anyways!\r
- */\r
-}\r
-\r
-\r
-/***static***/ ib_api_status_t\r
-__init_data_svc(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN const ib_port_attr_t* const p_port_attr )\r
-{\r
- ib_api_status_t status;\r
- cl_status_t cl_status;\r
- ib_qp_create_t qp_create;\r
- ib_mad_svc_t mad_svc;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_status = cl_spinlock_init( &p_port_cm->lock );\r
- if( cl_status != CL_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cl_spinlock_init failed\n") );\r
- return ib_convert_cl_status( cl_status );\r
- }\r
-\r
- /*\r
- * Create the PD alias. We use the port CM's al_obj_t as the context\r
- * to allow using deref_al_obj as the destroy callback.\r
- */\r
- status = ib_alloc_pd( p_port_cm->h_ca, IB_PDT_ALIAS, &p_port_cm->obj,\r
- &p_port_cm->h_pd );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_alloc_pd failed with status %s\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
- /* Reference the port object on behalf of the PD. */\r
- ref_al_obj( &p_port_cm->obj );\r
-\r
- /* Create the MAD QP. */\r
- cl_memclr( &qp_create, sizeof( ib_qp_create_t ) );\r
- qp_create.qp_type = IB_QPT_QP1_ALIAS;\r
- qp_create.rq_depth = CM_MAD_RQ_DEPTH;\r
- qp_create.sq_depth = CM_MAD_SQ_DEPTH;\r
- qp_create.rq_sge = CM_MAD_RQ_SGE;\r
- qp_create.sq_sge = CM_MAD_SQ_SGE;\r
- qp_create.sq_signaled = TRUE;\r
- /*\r
- * We use the port CM's al_obj_t as the context to allow using\r
- * deref_al_obj as the destroy callback.\r
- */\r
- status = ib_get_spl_qp( p_port_cm->h_pd, p_port_attr->port_guid,\r
- &qp_create, &p_port_cm->obj, __cm_qp_event_cb, &p_port_cm->pool_key,\r
- &p_port_cm->h_qp );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_get_spl_qp failed with status %s\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
- /* Reference the port object on behalf of the QP. */\r
- ref_al_obj( &p_port_cm->obj );\r
-\r
- /* Create the MAD service. */\r
- cl_memclr( &mad_svc, sizeof(mad_svc) );\r
- mad_svc.mad_svc_context = p_port_cm;\r
- mad_svc.pfn_mad_recv_cb = __cm_mad_recv_cb;\r
- mad_svc.pfn_mad_send_cb = __cm_mad_send_cb;\r
- mad_svc.support_unsol = TRUE;\r
- mad_svc.mgmt_class = IB_MCLASS_COMM_MGMT;\r
- mad_svc.mgmt_version = IB_MCLASS_CM_VER_2;\r
- mad_svc.method_array[IB_MAD_METHOD_SEND] = TRUE;\r
- status =\r
- ib_reg_mad_svc( p_port_cm->h_qp, &mad_svc, &p_port_cm->h_mad_svc );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_reg_mad_svc failed with status %s\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-/*\r
- * Create a port agent for a given port.\r
- */\r
-/***static***/ ib_api_status_t\r
-__create_port_cm(\r
- IN ib_pnp_port_rec_t *p_pnp_rec )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- ib_api_status_t status;\r
- ib_port_attr_mod_t port_attr_mod;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* calculate size of port_cm struct */\r
- p_port_cm = (cm_port_agent_t*)cl_zalloc( sizeof(cm_port_agent_t) +\r
- p_pnp_rec->p_ca_attr->size );\r
- if( !p_port_cm )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Failed to cl_zalloc port CM agent.\n") );\r
- return IB_INSUFFICIENT_MEMORY;\r
- }\r
-\r
- construct_al_obj( &p_port_cm->obj, AL_OBJ_TYPE_CM );\r
- cl_qlist_init( &p_port_cm->av_list );\r
- cl_spinlock_construct( &p_port_cm->lock );\r
-\r
- status = init_al_obj( &p_port_cm->obj, p_port_cm, TRUE,\r
- __destroying_port_cm, NULL, __free_port_cm );\r
- if( status != IB_SUCCESS )\r
- {\r
- __free_port_cm( &p_port_cm->obj );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Attach to the global CM object. */\r
- status = attach_al_obj( &gp_cm->obj, &p_port_cm->obj );\r
- if( status != IB_SUCCESS )\r
- {\r
- p_port_cm->obj.pfn_destroy( &p_port_cm->obj, NULL );\r
- AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- p_port_cm->port_idx =\r
- (uint8_t)(p_pnp_rec->p_port_attr->port_num - 1);\r
-\r
- /* cache required port attributes */\r
- p_port_cm->p_ca_attr = (ib_ca_attr_t*)\r
- (((uint8_t*)p_port_cm) + sizeof(cm_port_agent_t));\r
-\r
- ib_copy_ca_attr( p_port_cm->p_ca_attr, p_pnp_rec->p_ca_attr );\r
-\r
- /* Get a reference to the CA on which we are loading. */\r
- p_port_cm->h_ca = acquire_ca( p_pnp_rec->p_ca_attr->ca_guid );\r
- if( !p_port_cm->h_ca )\r
- {\r
- p_port_cm->obj.pfn_destroy( &p_port_cm->obj, NULL );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("acquire_ca failed.\n") );\r
- return IB_INVALID_GUID;\r
- }\r
-\r
- status = __init_data_svc( p_port_cm, p_pnp_rec->p_port_attr );\r
- if( status != IB_SUCCESS )\r
- {\r
- p_port_cm->obj.pfn_destroy( &p_port_cm->obj, NULL );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__init_data_svc failed with status %s.\n",\r
- ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Update local port attributes */\r
- cl_memclr( &port_attr_mod, sizeof(ib_port_attr_mod_t) );\r
- port_attr_mod.cap.cm = TRUE;\r
- status = ib_modify_ca( p_port_cm->h_ca, p_pnp_rec->p_port_attr->port_num,\r
- IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );\r
-\r
- /* Update the PNP context to reference this port. */\r
- p_pnp_rec->pnp_rec.context = p_port_cm;\r
-\r
- /* Release the reference taken in init_al_obj. */\r
- deref_al_obj( &p_port_cm->obj );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-void\r
-__refresh_port_cm(\r
- IN ib_pnp_port_rec_t *p_pnp_rec )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
-\r
- CL_ASSERT( p_pnp_rec->pnp_rec.context );\r
-\r
- p_port_cm = (cm_port_agent_t* __ptr64)p_pnp_rec->pnp_rec.context;\r
- if( !p_port_cm )\r
- return;\r
-\r
- if( p_pnp_rec->p_ca_attr )\r
- {\r
- CL_ASSERT( p_port_cm->p_ca_attr->size == p_pnp_rec->p_ca_attr->size );\r
- ib_copy_ca_attr( p_port_cm->p_ca_attr, p_pnp_rec->p_ca_attr );\r
- }\r
-}\r
-\r
-/*\r
- * PnP callback for port event notifications.\r
- */\r
-/***static***/ ib_api_status_t\r
-__cm_pnp_cb(\r
- IN ib_pnp_rec_t *p_pnp_rec )\r
-{\r
- ib_api_status_t status = IB_SUCCESS;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- switch( p_pnp_rec->pnp_event )\r
- {\r
- case IB_PNP_PORT_ADD:\r
- /* Create the port agent. */\r
- CL_ASSERT( !p_pnp_rec->context );\r
- status = __create_port_cm( (ib_pnp_port_rec_t*)p_pnp_rec );\r
- break;\r
-\r
- case IB_PNP_PORT_ACTIVE:\r
- /* refresh cache. */\r
- __refresh_port_cm( (ib_pnp_port_rec_t*)p_pnp_rec );\r
- break;\r
-\r
- case IB_PNP_PORT_REMOVE:\r
- CL_ASSERT( p_pnp_rec->context );\r
-\r
- /* Destroy the port agent. */\r
- ref_al_obj( &((cm_port_agent_t* __ptr64)p_pnp_rec->context)->obj );\r
- ((cm_port_agent_t* __ptr64)p_pnp_rec->context)->obj.pfn_destroy(\r
- &((cm_port_agent_t* __ptr64)p_pnp_rec->context)->obj, NULL );\r
-\r
- /* Fall through to validate the listen requests. */\r
- break;\r
- case IB_PNP_PORT_DOWN:\r
- case IB_PNP_PKEY_CHANGE:\r
- case IB_PNP_GID_CHANGE:\r
- case IB_PNP_LID_CHANGE:\r
- /*\r
- * Validate listen requests. Due to the potential lack of port\r
- * attributes (when a port is removed), it is not possible to\r
- * invalidate requests. Instead, we validate all requests and\r
- * fail any that are not valid on any exitsting ports.\r
- */\r
- __refresh_port_cm( (ib_pnp_port_rec_t*)p_pnp_rec );\r
- __validate_listens();\r
- break;\r
-\r
- default:\r
- break; /* Ignore other PNP events. */\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-/*\r
- * Timer async proc routine that locks and does the job\r
- */\r
-void\r
-__process_cm_timer(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- //*** TODO: Timer callback must handle retries, timeouts, and time wait.\r
- al_conn_t *p_conn;\r
- cm_async_timer_t *p_async_timer;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_timer = PARENT_STRUCT( p_item, cm_async_timer_t, item );\r
- p_conn = p_async_timer->p_conn;\r
-\r
- cm_res_acquire( p_conn );\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_MRA_RCVD:\r
- case CM_CONN_REP_MRA_RCVD:\r
- {\r
- /*\r
- * Format the reject record before aborting the connection since\r
- * we need the QP context.\r
- */\r
- ib_cm_rej_rec_t rej_rec;\r
- cl_memclr( &rej_rec, sizeof( ib_cm_rej_rec_t ) );\r
- rej_rec.h_qp = p_conn->h_qp;\r
- rej_rec.qp_context = p_conn->h_qp->obj.context;\r
- rej_rec.rej_status = IB_REJ_TIMEOUT;\r
-\r
- /* Report the timeout and send the REJ. */\r
- __conn_reject( p_conn, IB_REJ_TIMEOUT, NULL, 0, NULL );\r
- cm_res_release( p_conn );\r
-\r
- p_conn->pfn_cm_rej_cb( &rej_rec );\r
- }\r
- break;\r
-\r
- case CM_CONN_LAP_MRA_RCVD:\r
- {\r
- /* Report the timeout. */\r
- ib_cm_apr_rec_t apr_rec;\r
- cl_memclr( &apr_rec, sizeof( ib_cm_apr_rec_t ) );\r
- apr_rec.h_qp = p_conn->h_qp;\r
- apr_rec.qp_context = p_conn->h_qp->obj.context;\r
- apr_rec.cm_status = IB_TIMEOUT;\r
- apr_rec.apr_status = IB_AP_REJECT;\r
-\r
- /* Return to the established state. */\r
- p_conn->state = CM_CONN_ESTABLISHED;\r
- cm_res_release( p_conn );\r
-\r
- /* Notify the user that the LAP failed. */\r
- p_conn->pfn_cm_apr_cb( &apr_rec );\r
- }\r
- break;\r
-\r
- //***TODO: Check synchronization issues relating to the timer being part\r
- //***TODO: of the connection object. Only timewait has potential issues.\r
- //***TODO: If when destroying all connections in the timewait state the\r
- //***TODO: timers are trimmed to zero, the destruction should be able to\r
- //***TODO: wait until all timers expire.\r
- case CM_CONN_TIMEWAIT:\r
- p_conn->state = CM_CONN_RESET;\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_remove_item( &gp_cm->time_wait_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- cm_res_release( p_conn );\r
- break;\r
-\r
- default:\r
- /*\r
- * No assert here, since the state could change just after the timer\r
- * expires but before the callback is invoked.\r
- */\r
- cm_res_release( p_conn );\r
- break;\r
- }\r
-\r
- /* Release the connection. */\r
- __deref_conn( p_conn );\r
- cl_free( p_async_timer );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Timer callback for taking connections out of the time wait state.\r
- */\r
-/***static***/ void\r
-__conn_timer_cb(\r
- IN void *context )\r
-{\r
- cm_async_timer_t *p_async_timer;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_timer = (cm_async_timer_t*)cl_zalloc( sizeof(cm_async_timer_t) );\r
- if( !p_async_timer )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("failed to cl_zalloc cm_async_timer_t (%d bytes). System unstable!\n",\r
- sizeof(cm_async_timer_t)) );\r
- /* Bah - just release the connection now. */\r
- __deref_conn( (al_conn_t*)context );\r
- return;\r
- }\r
-\r
- /*\r
- * Queue an async item for further processing. We are still holding a\r
- * reference on the gp_cm.\r
- */\r
- p_async_timer->p_conn = (al_conn_t*)context;\r
- p_async_timer->item.pfn_callback = __process_cm_timer;\r
- cl_async_proc_queue( gp_async_proc_mgr, &p_async_timer->item );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Constructor for connection objects, called by the qpool when allocating new\r
- * connection objects.\r
- */\r
-/***static***/ cl_status_t\r
-__conn_ctor(\r
- IN void* const p_object,\r
- IN void* context,\r
- OUT cl_pool_item_t** const pp_pool_item )\r
-{\r
- cl_status_t cl_status;\r
- al_conn_t* p_conn = (al_conn_t*)p_object;\r
-\r
- UNUSED_PARAM( context );\r
-\r
- cl_status = cm_res_init( p_conn );\r
- if( cl_status != CL_SUCCESS )\r
- {\r
- /*AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cm_res_init failed with status %s.\n",\r
- CL_STATUS_MSG( cl_status )) );*/\r
- return CL_ERROR;\r
- }\r
-\r
- cl_status = cl_timer_init( &p_conn->timer, __conn_timer_cb, p_conn );\r
- if( cl_status != CL_SUCCESS )\r
- {\r
- /*AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cl_timer_init failed with status %s.\n",\r
- CL_STATUS_MSG( cl_status )) );*/\r
- cm_res_destroy( p_conn );\r
- return cl_status;\r
- }\r
-\r
- p_conn->state = CM_CONN_RESET;\r
- p_conn->local_comm_id = (net32_t)cl_atomic_inc( &local_comm_id );\r
- /* Preset the top byte of the communication ID. */\r
- p_conn->local_comm_id |= (net32_t)(cl_get_time_stamp() << 24);\r
-\r
- *pp_pool_item = (cl_pool_item_t*)&p_conn->map_item;\r
-\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Destructor for connection objects, called by the qpool when freeing memory\r
- * allocated for a connection object.\r
- */\r
-/***static***/ void\r
-__conn_dtor(\r
- IN const cl_pool_item_t* const p_pool_item,\r
- IN void* context )\r
-{\r
- al_conn_t *p_conn;\r
-\r
- UNUSED_PARAM( context );\r
-\r
- p_conn = PARENT_STRUCT( p_pool_item, al_conn_t, map_item );\r
-\r
- /* Destroy the timer. */\r
- cl_timer_destroy( &p_conn->timer );\r
-\r
- /* Destroy the lock/mutex. */\r
- cm_res_destroy( p_conn );\r
-}\r
-\r
-\r
-/*\r
- * Allocates and initialized the global CM agent.\r
- */\r
-ib_api_status_t\r
-create_cm(\r
- IN al_obj_t* const p_parent_obj )\r
-{\r
- ib_api_status_t status;\r
- cl_status_t cl_status;\r
- ib_pnp_req_t pnp_req;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( gp_cm == NULL );\r
-\r
- /* Allocate the global CM agent. */\r
- gp_cm = (al_cm_agent_t*)cl_zalloc( sizeof(al_cm_agent_t) );\r
- if( !gp_cm )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Failed allocation of global CM agent.\n") );\r
- return IB_INSUFFICIENT_MEMORY;\r
- }\r
-\r
- construct_al_obj( &gp_cm->obj, AL_OBJ_TYPE_CM );\r
- cl_qpool_construct( &gp_cm->conn_pool );\r
- cl_pool_construct( &gp_cm->req_pool );\r
- cl_qlist_init( &gp_cm->active_listen_list );\r
- cl_qlist_init( &gp_cm->inactive_listen_list );\r
- cl_qlist_init( &gp_cm->pending_list );\r
- cl_qmap_init( &gp_cm->conn_map );\r
- cl_qlist_init( &gp_cm->time_wait_list );\r
-\r
- status = init_al_obj( &gp_cm->obj, NULL, TRUE, __destroying_cm, NULL,\r
- __free_cm );\r
- if( status != IB_SUCCESS )\r
- {\r
- __free_cm( &gp_cm->obj );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
- /* Attach to the parent object. */\r
- status = attach_al_obj( p_parent_obj, &gp_cm->obj );\r
- if( status != IB_SUCCESS )\r
- {\r
- gp_cm->obj.pfn_destroy( &gp_cm->obj, NULL );\r
- AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- cl_status = cl_qpool_init( &gp_cm->conn_pool,\r
- CM_CONN_POOL_MIN, CM_CONN_POOL_MAX, CM_CONN_POOL_GROW,\r
- sizeof(al_conn_t), __conn_ctor, __conn_dtor, NULL );\r
- if( cl_status != CL_SUCCESS )\r
- {\r
- gp_cm->obj.pfn_destroy( &gp_cm->obj, NULL );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cl_qpool_init failed with status %s.\n",\r
- CL_STATUS_MSG(cl_status)) );\r
- return ib_convert_cl_status( cl_status );\r
- }\r
-\r
- cl_status = cl_pool_init( &gp_cm->req_pool, CM_REQ_POOL_MIN, 0,\r
- CM_REQ_POOL_GROW, sizeof(conn_req_t), NULL, NULL, NULL );\r
- if( cl_status != CL_SUCCESS )\r
- {\r
- gp_cm->obj.pfn_destroy( &gp_cm->obj, NULL );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cl_pool_init failed with status %s.\n",\r
- CL_STATUS_MSG(cl_status)) );\r
- return ib_convert_cl_status( cl_status );\r
- }\r
-\r
- /* Register for port PnP notifications. */\r
- cl_memclr( &pnp_req, sizeof(pnp_req) );\r
- pnp_req.pnp_class = IB_PNP_PORT;\r
- pnp_req.pfn_pnp_cb = __cm_pnp_cb;\r
- status = ib_reg_pnp( gh_al, &pnp_req, &gp_cm->h_pnp );\r
- if( status != IB_SUCCESS )\r
- {\r
- gp_cm->obj.pfn_destroy( &gp_cm->obj, NULL );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_reg_pnp failed with status %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /*\r
- * Leave the reference taken in init_al_obj oustanding since PnP\r
- * deregistration is asynchronous. This replaces a call to ref and\r
- * deref the object.\r
- */\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__get_port_attr(\r
- IN const ib_gid_t* const p_gid,\r
- IN const ib_net16_t lid,\r
- OUT cm_port_agent_t** const pp_port_cm OPTIONAL,\r
- OUT ib_ca_attr_t** const pp_ca_attr OPTIONAL )\r
-{\r
- cl_list_item_t *p_item;\r
- cm_port_agent_t *p_port_cm;\r
- ib_api_status_t status = IB_INVALID_SETTING;\r
- ib_ca_attr_t *p_ca_attr;\r
- ib_port_attr_t *p_port_attr;\r
- uint16_t gid_idx;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- for( p_item = cl_qlist_head( &gp_cm->obj.obj_list );\r
- p_item != cl_qlist_end( &gp_cm->obj.obj_list );\r
- p_item = cl_qlist_next( p_item ) )\r
- {\r
- p_port_cm = PARENT_STRUCT(\r
- PARENT_STRUCT( p_item, al_obj_t, pool_item ),\r
- cm_port_agent_t, obj );\r
-\r
- p_ca_attr = p_port_cm->p_ca_attr;\r
-\r
- /* Shortcut to the port attributes for cleaner code. */\r
- p_port_attr = &p_ca_attr->p_port_attr[p_port_cm->port_idx];\r
-\r
- for( gid_idx = 0; gid_idx < p_port_attr->num_gids; gid_idx++ )\r
- {\r
- if( cl_memcmp( &p_port_attr->p_gid_table[gid_idx],\r
- p_gid, sizeof(ib_gid_t) ) )\r
- {\r
- continue;\r
- }\r
-\r
- /* Found a GID match. Look for LID match. */\r
- if( __is_lid_valid( lid, p_port_attr->lid,\r
- p_port_attr->lmc ) != TRUE )\r
- {\r
- continue;\r
- }\r
-\r
- /* Chaa-ching! We have a winner! */\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- if( pp_ca_attr )\r
- *pp_ca_attr = p_ca_attr;\r
-\r
- if( pp_port_cm )\r
- *pp_port_cm = p_port_cm;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
- }\r
-\r
- /* No match. Reset the port cm pointer. */\r
- status = IB_INVALID_SETTING;\r
- }\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("No match found.\n") );\r
- return status;\r
-}\r
-\r
+++ /dev/null
-/*\r
- * Copyright (c) 2005 SilverStorm Technologies. All rights reserved.\r
- * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. \r
- *\r
- * This software is available to you under the OpenIB.org BSD license\r
- * below:\r
- *\r
- * Redistribution and use in source and binary forms, with or\r
- * without modification, are permitted provided that the following\r
- * conditions are met:\r
- *\r
- * - Redistributions of source code must retain the above\r
- * copyright notice, this list of conditions and the following\r
- * disclaimer.\r
- *\r
- * - Redistributions in binary form must reproduce the above\r
- * copyright notice, this list of conditions and the following\r
- * disclaimer in the documentation and/or other materials\r
- * provided with the distribution.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- *\r
- * $Id$\r
- */\r
-\r
-#include <iba/ib_al.h>\r
-#include "al.h"\r
-#include "al_av.h"\r
-#include "al_ca.h"\r
-#include "al_cm.h"\r
-#include "al_cm_shared.h"\r
-#include "al_debug.h"\r
-#include "al_mgr.h"\r
-#include "al_qp.h"\r
-#include "al_verbs.h"\r
-#include "ib_common.h"\r
-\r
-\r
-extern al_cm_agent_t *gp_cm;\r
-\r
-\r
-/*\r
- * See if a REQ is a duplicate.\r
- */\r
-cl_status_t\r
-__req_match_pending(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- mad_cm_req_t *p_req;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_list_item );\r
- CL_ASSERT( context );\r
-\r
- p_req = (mad_cm_req_t*)context;\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
-\r
- if( p_req->local_comm_id != p_conn->remote_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Remote comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_req->local_ca_guid != p_conn->remote_ca_guid )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Remote CA GUID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /* Reference the connection since we're working on it... */\r
- __ref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-/*\r
- * See if message is for a connection that has already entered the timewait\r
- * state.\r
- */\r
-cl_status_t\r
-__match_timewait(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- mad_cm_req_t *p_req;\r
- al_conn_t *p_conn;\r
- conn_req_t *p_info;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_list_item );\r
- CL_ASSERT( context );\r
-\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- p_req = (mad_cm_req_t*)context;\r
- p_info = PARENT_STRUCT( p_list_item, al_conn_t, map_item )->p_req_info;\r
-\r
- if( p_conn->remote_ca_guid != p_req->local_ca_guid ||\r
- conn_req_get_lcl_qpn( p_req ) != p_conn->remote_qpn )\r
- {\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REQ received for stale connection.\n") );\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-/*\r
- * Search for a listening server.\r
- */\r
-cl_status_t\r
-__match_conn_listen(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- mad_cm_req_t *p_req;\r
- al_listen_t *p_listen;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_list_item );\r
- CL_ASSERT( context );\r
-\r
- p_req = (mad_cm_req_t*)context;\r
- p_listen = PARENT_STRUCT( p_list_item, al_listen_t, list_item );\r
-\r
- if( p_listen->info.qp_type == IB_QPT_UNRELIABLE_DGRM )\r
- return CL_NOT_FOUND;\r
-\r
- if( p_req->sid != p_listen->info.svc_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM, ("Svc ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_listen->info.p_compare_buffer )\r
- {\r
- if( cl_memcmp( &p_req->pdata[p_listen->info.compare_offset],\r
- p_listen->info.p_compare_buffer, p_listen->info.compare_length ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Svc ID match but compare buffer mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- }\r
-\r
- /* Reference the listen so that it doesn't go away. */\r
- ref_al_obj( &p_listen->obj );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-/*\r
- * Try to find a matching peer-to-peer request.\r
- */\r
-cl_status_t\r
-__match_peer(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- mad_cm_req_t *p_req;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_list_item );\r
- CL_ASSERT( context );\r
-\r
- p_req = (mad_cm_req_t*)context;\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
-\r
- /* Only match against peer-to-peer requests, not client requests. */\r
- if( !p_conn->p_req_info->pfn_cm_req_cb )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM, ("Client request.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /* Validate the transport type against the connection's QP type. */\r
- if( conn_req_get_qp_type( p_req ) != p_conn->h_qp->type )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Transport type mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /* Compare SID and compare data. */\r
- if( p_req->sid != p_conn->p_req_info->svc_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM, ("Svc ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_conn->state != CM_CONN_REQ_SENT )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Peer connection already matched.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /*\r
- * Make sure the CA GUID and local comm ID are not the same\r
- * so that a peer request doesn't match itself.\r
- */\r
- if( p_conn->p_req_info->p_ca_attr->ca_guid == p_req->local_ca_guid )\r
- {\r
- /* Trying to connect to the same CA as the request. */\r
- if( p_conn->local_comm_id == p_req->local_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Same peer request.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- }\r
-\r
- /* do a local lid and gid match */\r
- if( p_conn->path->slid != p_req->primary_path.remote_lid )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("slid mismatch. May be a different req in list\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- if( cl_memcmp( &p_conn->path->sgid, &p_req->primary_path.remote_gid,\r
- sizeof(ib_gid_t) ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("sgid mismatch. May be a different req in list\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_conn->path->dlid != p_req->primary_path.local_lid )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("dlid mismatch. May be a different req in list\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- if( cl_memcmp( &p_conn->path->dgid, &p_req->primary_path.local_gid,\r
- sizeof(ib_gid_t) ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("dgid mismatch. May be a different req in list\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /* compare private data */\r
- if( p_conn->p_req_info->p_compare_buffer )\r
- {\r
- if( cl_memcmp( &p_req->pdata[p_conn->p_req_info->compare_offset],\r
- p_conn->p_req_info->p_compare_buffer,\r
- p_conn->p_req_info->compare_length ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Svc ID match but compare buffer mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- }\r
-\r
- /* Reference the connection object so it doesn't go away. */\r
- __ref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-cl_status_t\r
-__match_conn_handoff(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- al_conn_t *p_conn;\r
- al_listen_t *p_listen;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_list_item );\r
- CL_ASSERT( context );\r
-\r
- p_conn = (al_conn_t*)context;\r
- p_listen = PARENT_STRUCT( p_list_item, al_listen_t, list_item );\r
-\r
- if( p_listen->info.qp_type == IB_QPT_UNRELIABLE_DGRM )\r
- return CL_NOT_FOUND;\r
-\r
- if( p_conn->p_req_info->svc_id != p_listen->info.svc_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM, ("Svc ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_listen->info.p_compare_buffer )\r
- {\r
- if( !p_conn->p_req_info->p_compare_buffer )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Svc ID match but compare buffer mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- if( ( p_conn->p_req_info->compare_length !=\r
- p_listen->info.compare_length ) ||\r
- ( p_conn->p_req_info->compare_offset !=\r
- p_listen->info.compare_offset ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Svc ID match but compare buffer mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- if( cl_memcmp( p_conn->p_req_info->p_compare_buffer,\r
- p_listen->info.p_compare_buffer, p_listen->info.compare_length ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Svc ID match but compare buffer mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- }\r
-\r
- /* Reference the listen so that it doesn't go away. */\r
- ref_al_obj( &p_listen->obj );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Migrate a connection request from the pending list to the\r
- * connection map.\r
- */\r
-static void\r
-__migrate_conn_to_map(\r
- IN al_conn_t *p_conn )\r
-{\r
- uint64_t key;\r
-\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- __release_req_info( p_conn );\r
-\r
- cl_qlist_remove_item( &gp_cm->pending_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
-\r
- key = ((uint64_t)p_conn->local_comm_id << 32) |\r
- ((uint64_t)p_conn->remote_comm_id);\r
- cl_qmap_insert( &gp_cm->conn_map, key, &p_conn->map_item );\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-}\r
-\r
-\r
-\r
-void\r
-__format_req_path_rec(\r
- IN mad_cm_req_t *p_req,\r
- IN req_path_info_t *p_path,\r
- OUT ib_path_rec_t *p_path_rec )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_req );\r
- CL_ASSERT( p_path );\r
- CL_ASSERT( p_path_rec );\r
-\r
- /*\r
- * Format a local path record. The local ack timeout specified in the\r
- * REQ is twice the packet life plus the sender's CA ACK delay. When\r
- * reporting the packet life, we divide the local ack timeout by 2 to\r
- * approach the path's packet lifetime. Since local ack timeout is\r
- * expressed as 4.096 * 2^x, subtracting 1 is equivalent to dividing the\r
- * time in half.\r
- */\r
- ib_path_rec_init_local( p_path_rec,\r
- &p_path->local_gid,\r
- &p_path->remote_gid,\r
- p_path->local_lid,\r
- p_path->remote_lid,\r
- 1, p_req->pkey,\r
- conn_req_path_get_svc_lvl( p_path ),\r
- IB_PATH_SELECTOR_EXACTLY, conn_req_get_mtu( p_req ),\r
- IB_PATH_SELECTOR_EXACTLY,\r
- conn_req_path_get_pkt_rate( p_path ),\r
- IB_PATH_SELECTOR_EXACTLY,\r
- (uint8_t)( conn_req_path_get_lcl_ack_timeout( p_path ) - 1 ),\r
- 0 );\r
-\r
- p_path_rec->hop_flow_raw.val = 0;\r
- /* Add global routing info as necessary. */\r
- if( !conn_req_path_get_subn_lcl( p_path ) )\r
- {\r
- ib_path_rec_set_hop_flow_raw( p_path_rec, p_path->hop_limit,\r
- conn_req_path_get_flow_lbl( p_path ), FALSE );\r
- p_path_rec->tclass = p_path->traffic_class;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-ib_rej_status_t\r
-__validate_req_2_listen_info(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN al_listen_t* const p_listen,\r
- IN ib_path_rec_t* const p_path_rec )\r
-{\r
- ib_port_attr_t *p_port_attr;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_port_attr = &p_port_cm->p_ca_attr->p_port_attr[p_port_cm->port_idx];\r
-\r
- /* match options set in the listen attributes */\r
- if( ( p_listen->info.ca_guid != IB_ALL_CAS ) &&\r
- ( p_listen->info.ca_guid != p_port_cm->p_ca_attr->ca_guid ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("CaGuid mismatch on listen.\n") );\r
- return IB_REJ_INVALID_SID;\r
- }\r
-\r
- if( ( p_listen->info.lid != IB_ALL_LIDS ) &&\r
- ( ( p_listen->info.lid != p_path_rec->slid ) ||\r
- ( p_listen->info.lid != p_port_attr->lid ) ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("lid mismatch on listen.\n") );\r
- return IB_REJ_INVALID_LID;\r
- }\r
-\r
- if( ( p_listen->info.port_guid != IB_ALL_PORTS ) &&\r
- ( p_listen->info.port_guid != p_port_attr->port_guid ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("PortGuid mismatch on listen.\n") );\r
- return IB_REJ_INVALID_SID;\r
- }\r
-\r
- if( ( p_listen->info.pkey != IB_ALL_PKEYS ) &&\r
- ( p_listen->info.pkey != p_path_rec->pkey ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("pkey mismatch on listen.\n") );\r
- return IB_REJ_INVALID_SID;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return 0;\r
-}\r
-\r
-\r
-ib_rej_status_t\r
-__format_req_rec(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN mad_cm_req_t* const p_req,\r
- IN al_listen_t* const p_listen OPTIONAL,\r
- OUT ib_cm_req_rec_t *p_req_rec )\r
-{\r
- ib_rej_status_t rej_status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_port_cm );\r
- CL_ASSERT( p_req );\r
- CL_ASSERT( p_req_rec );\r
-\r
- cl_memclr( p_req_rec, sizeof( ib_cm_req_rec_t ) );\r
-\r
- /* validate version and transport type info */\r
- switch( p_req->hdr.class_ver )\r
- {\r
- default:\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid class version type received.\n") );\r
- return IB_REJ_UNSUPPORTED;\r
-\r
- case IB_MCLASS_CM_VER_2:\r
- break;\r
- }\r
- if( conn_req_get_qp_type( p_req ) > IB_QPT_UNRELIABLE_CONN )\r
- {\r
- /* Reserved value. Reject. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid transport type received.\n") );\r
- return IB_REJ_INVALID_XPORT;\r
- }\r
-\r
- /* format version specific data */\r
- p_req_rec->p_req_pdata = p_req->pdata;\r
-\r
- p_req_rec->qp_type = conn_req_get_qp_type( p_req );\r
-\r
- p_req_rec->resp_res = conn_req_get_resp_res( p_req );\r
- p_req_rec->flow_ctrl = conn_req_get_flow_ctrl( p_req );\r
- p_req_rec->rnr_retry_cnt = conn_req_get_rnr_retry_cnt( p_req );\r
-\r
- __format_req_path_rec( p_req, &p_req->primary_path,\r
- &p_req_rec->primary_path );\r
- __format_req_path_rec( p_req, &p_req->alternate_path,\r
- &p_req_rec->alt_path );\r
-\r
- /* validate a listen's inputs for ca, port or lid info */\r
- if( p_listen )\r
- {\r
- rej_status = __validate_req_2_listen_info( p_port_cm, p_listen,\r
- &p_req_rec->primary_path );\r
-\r
- if( rej_status )\r
- return rej_status;\r
- }\r
-\r
- /* These values are filled in later based on listen or peer connections\r
- p_req_rec->context = ;\r
- p_req_rec->h_cm_req = ;\r
- p_req_rec->h_cm_listen = ;\r
- */\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return 0;\r
-}\r
-\r
-\r
-void\r
-__reject_req(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN ib_mad_element_t* const p_mad,\r
- IN const ib_rej_status_t reason )\r
-{\r
- mad_cm_req_t *p_req;\r
- mad_cm_rej_t *p_rej;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_port_cm );\r
- CL_ASSERT( p_mad );\r
- CL_ASSERT( reason != 0 );\r
-\r
- p_req = (mad_cm_req_t*)p_mad->p_mad_buf;\r
- p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;\r
-\r
- /*\r
- * Format the reject information, overwriting the REQ data and send\r
- * the response.\r
- */\r
- p_rej->hdr.attr_id = CM_REJ_ATTR_ID;\r
- p_rej->remote_comm_id = p_req->local_comm_id;\r
- p_rej->local_comm_id = 0;\r
- conn_rej_set_msg_rejected( 0, p_rej );\r
- p_rej->reason = reason;\r
- conn_rej_set_ari( NULL, 0, p_rej );\r
- conn_rej_set_pdata( NULL, 0, p_rej );\r
- conn_rej_clr_rsvd_fields( p_rej );\r
-\r
- /* Assumption: The retry count, send options, etc are zeroed on a recv. */\r
- __cm_send_mad( p_port_cm, p_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Validates the primary path specified for a connection, and stores\r
- * the assocated CA attributes and port index.\r
- */\r
-ib_api_status_t\r
-__validate_primary_path(\r
- IN OUT al_conn_t* const p_conn,\r
- OUT cm_port_agent_t** const pp_port_cm )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_conn );\r
- CL_ASSERT( pp_port_cm );\r
- CL_ASSERT( p_conn->p_req_info );\r
- CL_ASSERT( p_conn->h_qp );\r
-\r
- /* Get the CA attributes for the paths requested. */\r
- status = __get_port_attr( &p_conn->path[0].sgid, p_conn->path[0].slid,\r
- &p_port_cm, &p_conn->p_req_info->p_ca_attr );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_port_attr failed for primary path.\n") );\r
- return status;\r
- }\r
- /* Check that the primary path is on the same CA as the QP. */\r
- if( p_conn->p_req_info->p_ca_attr->ca_guid !=\r
- p_conn->h_qp->obj.p_ci_ca->verbs.guid )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Primary path is not on the same CA as QP.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- p_conn->p_req_info->port_idx = p_port_cm->port_idx;\r
-\r
- *pp_port_cm = p_port_cm;\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-/*\r
- * Validates the paths specified for a connection are valid, and stores\r
- * the assocated CA attributes and port indeces.\r
- */\r
-ib_api_status_t\r
-__validate_alt_path(\r
- IN OUT al_conn_t* const p_conn )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
- ib_ca_attr_t *p_ca_attr;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Get the alternate path port attributes. */\r
- status = __get_port_attr( &p_conn->path[1].sgid, p_conn->path[1].slid,\r
- &p_port_cm, &p_ca_attr );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_port_attr failed for alternate path.\n") );\r
- return status;\r
- }\r
- if( p_ca_attr->ca_guid != p_conn->p_req_info->p_ca_attr->ca_guid )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Primary and alternate paths on different CAs.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
- p_conn->p_req_info->alt_port_idx = p_port_cm->port_idx;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-/*\r
- * How the QP modify structures are populated for the RTR and RTS transitions:\r
- *\r
- *\r
- * The actual state transition to RTR and RTS is performed as per the IB spec.\r
- *\r
- * Matrix of the settings that are set for each MAD.\r
- *\r
- * | REQ | REP | RTU | grp\r
- * | send | recv | send | recv | send |\r
- * ----------------------------------------------------------------------------\r
- * RTR rq_psn | x | | x | | | 2\r
- * dest_qp | | x | | x | | 3\r
- * primary_av | | | | | |\r
- * port_num | x | | x | | | 2\r
- * sl | x | x | | | | 1\r
- * dlid | x | x | | | | 1\r
- * grh | x | x | | | | 1\r
- * static_rate | x | x | | | | 1\r
- * path_bits | x | | x | | | 2\r
- * path_mtu | x | x | | | | 1\r
- * local_ack_timeout | | x | | x | | 3\r
- * seq_err_retry_cnt | x | x | | | | 1\r
- * rnr_retry_cnt | | x | | x | | 3\r
- * resp_res | | x(1) | x | x | | 3,4\r
- * alt_av | | | | | |\r
- * port_num | x | | x | | | 2\r
- * sl | x | x | | | | 1\r
- * dlid | x | x | | | | 1\r
- * grh | x | x | | | | 1\r
- * static_rate | x | x | | | | 1\r
- * path_bits | x | | x | | | 2\r
- * path_mtu | x | x | | | | 1\r
- * local_ack_timeout | | x | | x | | 3\r
- * seq_err_retry_cnt | x | x | | | | 1\r
- * rnr_retry_cnt | | x | | x | | 3\r
- * q_key | | x(2) | | x(2) | | 3\r
- * pkey_index | x | | x | | | 2\r
- * access_ctrl | *(5) | | x | | | 2\r
- * sq_depth | | | x | | x | 5\r
- * rq_depth | | | x | | x | 5\r
- * rnr_nak_timeout | *(5) | | x | | | 2\r
- * ----------------------------------------------------------------------\r
- * RTS sq_psn | | x | | x | | 3\r
- * retry_cnt | x | x | | | | 1\r
- * rnr_retry_cnt | | x | | x | | 3\r
- * local_ack_timeout | | x | | x | | 3\r
- * init_depth | | | x | x | | 4\r
- * qkey | (3) | (3) | (3) | (3) | (3) |\r
- * access_ctrl | | | | | x | 5\r
- * resp_res | (3) | (3) | (3) | (3) | (3) |\r
- * alt_av | (3) | (3) | (3) | (3) | (3) |\r
- * sq_depth | | | x | | x | 5\r
- * rq_depth | | | x | | x | 5\r
- * apm_state | | | x | x | | 4\r
- * primary_port | (4) | (4) | (4) | (4) | (4) |\r
- * pkey_index | (3) | (3) | (3) | (3) | (3) |\r
- * ----------------------------------------------------------------------------\r
- * Notes:\r
- * (1) the responder resources are initialized to REQ.init_depth and then\r
- * scaled down when sending the REP if the local CA cannot support the\r
- * requested value.\r
- * (2) q_key is only used for RD and is not yet supported.\r
- * (3) handled in the RTR transition.\r
- * (4) handled in the INIT transition.\r
- * (5) set by CM to allow all operations. Real value set at RTU time.\r
- */\r
-\r
-\r
-\r
-/*\r
- * + Validates the path information provided in the REQ and stores the\r
- * associated CA attributes and port indeces.\r
- * + Transitions a connection object from active to passive in the peer case.\r
- * + Sets the path information in the connection and sets the CA GUID\r
- * in the REQ callback record.\r
- */\r
-void\r
-__conn_save_wire_req(\r
- IN const mad_cm_req_t* const p_req,\r
- OUT al_conn_t* const p_conn,\r
- IN OUT ib_cm_req_rec_t* const p_req_rec )\r
-{\r
- ib_qp_handle_t h_qp;\r
- struct _qp_rtr *p_rtr;\r
- struct _qp_rts *p_rts;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn->state = CM_CONN_REQ_RCVD;\r
- p_conn->was_active = FALSE;\r
-\r
- /* Store pertinent information in the connection. */\r
- p_conn->remote_comm_id = p_req->local_comm_id;\r
- p_conn->remote_ca_guid = p_req->local_ca_guid;\r
-\r
- p_conn->remote_qpn = conn_req_get_lcl_qpn( p_req );\r
-\r
- /*\r
- * Release the QP, if any, since the user can change the QP handles\r
- * when replying. This prevents the connection from being\r
- * aborted if the user destroys a QP that was used to send the REQ for\r
- * a peer-to-peer request that lost the peer comparisson.\r
- */\r
- h_qp = p_conn->h_qp;\r
- if( h_qp )\r
- {\r
- cm_unbind_qp( p_conn );\r
- deref_al_obj( &h_qp->obj );\r
- }\r
-\r
- /*\r
- * Calculate the retry timeout.\r
- * All timeout values in micro seconds are expressed as 4.096 * 2^x,\r
- * where x is the timeout. This approximates to 2^(x+2).\r
- * Since we want milliseconds, we can further approximate to 2^(x-8).\r
- * This results in a timeout that is roughly 5% on the low side, but\r
- * good enough since OS timer resolutions are ~10ms.\r
- */\r
- if( conn_req_get_lcl_resp_timeout( p_req ) > 8 )\r
- {\r
- p_conn->retry_timeout =\r
- 1 << (conn_req_get_lcl_resp_timeout( p_req ) - 8);\r
-\r
- /* Minimum 10 ms timeout - picked to match typical OS timer resolution. */\r
- if( p_conn->retry_timeout < 10 )\r
- p_conn->retry_timeout = 10;\r
- }\r
- else\r
- {\r
- p_conn->retry_timeout = 10;\r
- }\r
-\r
- /* Store the retry count. */\r
- p_conn->max_cm_retries = conn_req_get_max_cm_retries( p_req );\r
-\r
- /*\r
- * Copy the paths from the req_rec into the connection for\r
- * future use.\r
- */\r
- cl_memcpy( &p_conn->path[0], &p_req_rec->primary_path,\r
- sizeof(ib_path_rec_t) );\r
- cl_memcpy( &p_conn->path[1], &p_req_rec->alt_path, sizeof(ib_path_rec_t) );\r
- p_conn->idx_primary = 0;\r
-\r
- /* Setup the QP modify structures for the RTR and RTS transitions */\r
- p_rtr = &p_conn->p_req_info->qp_mod_rtr.state.rtr;\r
- p_rts = &p_conn->p_req_info->qp_mod_rts.state.rts;\r
-\r
- /* Update RTR info */\r
- p_rtr->dest_qp = conn_req_get_lcl_qpn( p_req );\r
-\r
- /* The responder resources may be scaled down when sending the REP. */\r
- p_rtr->resp_res = conn_req_get_init_depth( p_req );\r
-\r
- /* updated in the REP\r
- p_rtr->qkey;\r
- p_rtr->access_ctrl;\r
- p_rtr->sq_depth;\r
- p_rtr->rq_depth;\r
- p_rtr->rnr_nak_timeout;\r
- p_rtr->primary_av;\r
- p_rtr->alternate_av;\r
- p_rtr->rq_psn;\r
- */\r
-\r
- /* Update RTS info */\r
- p_rts->retry_cnt = conn_req_get_retry_cnt( p_req );\r
- p_rts->rnr_retry_cnt = conn_req_get_rnr_retry_cnt( p_req );\r
- p_rts->local_ack_timeout = conn_req_path_get_lcl_ack_timeout(\r
- &p_req->primary_path );\r
-\r
- p_rts->init_depth = conn_req_get_resp_res( p_req );\r
-\r
- p_rts->opts = 0;\r
- p_rts->resp_res = p_rtr->resp_res;\r
- p_rts->sq_psn = conn_req_get_starting_psn( p_req );\r
-\r
- /* Set in the REP\r
- p_rts->rnr_nak_timeout;\r
- p_rts->qkey;\r
- p_rts->access_ctrl;\r
- p_rts->sq_depth;\r
- p_rts->rq_depth;\r
- p_rts->apm_state;\r
- p_rts->primary_port;\r
- */\r
-\r
- /* copy pdata for cm handoffs\r
- cl_memcpy( p_conn->mads.req.pdata,\r
- p_req->pdata, IB_REQ_PDATA_SIZE );*/\r
-\r
- /* copy mad info for cm handoff */\r
- p_conn->mads.req = *p_req;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-void\r
-__listen_req(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN al_listen_t* const p_listen,\r
- IN ib_mad_element_t* const p_mad )\r
-{\r
- al_conn_t *p_conn;\r
- mad_cm_req_t *p_req;\r
- ib_cm_req_rec_t req_rec;\r
- ib_rej_status_t rej_status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_port_cm );\r
- CL_ASSERT( p_listen );\r
- CL_ASSERT( p_mad );\r
-\r
- p_req = (mad_cm_req_t*)p_mad->p_mad_buf;\r
-\r
- /* Format the callback record. */\r
- rej_status = __format_req_rec( p_port_cm, p_req, p_listen, &req_rec );\r
- if( rej_status )\r
- {\r
- /* The request is malformed. Reject it. */\r
- __reject_req( p_port_cm, p_mad, rej_status );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("REJ sent for malformed REQ.\n") );\r
- return;\r
- }\r
-\r
- /* Get a new connection object. */\r
- p_conn = __get_conn( (ib_al_handle_t)p_listen->obj.p_parent_obj,\r
- req_rec.qp_type );\r
- if( !p_conn )\r
- {\r
- /* Reject the request for insufficient resources. */\r
- __reject_req( p_port_cm, p_mad, IB_REJ_INSUF_RESOURCES );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("REJ sent for insufficient resources.\n") );\r
- return;\r
- }\r
-\r
- /* update p_conn from listen and req values. */\r
- p_conn->p_req_info->pfn_cm_req_cb = NULL;\r
- p_conn->pfn_cm_rej_cb = p_listen->info.pfn_cm_rej_cb;\r
- p_conn->p_req_info->pfn_cm_rep_cb = NULL;\r
- p_conn->pfn_cm_mra_cb = p_listen->info.pfn_cm_mra_cb;\r
-\r
- p_conn->p_port_cm = p_port_cm;\r
-\r
- /* update listen based rec */\r
- req_rec.context = p_listen->obj.context;\r
- req_rec.h_cm_req = p_conn;\r
- req_rec.h_cm_listen = p_listen;\r
-\r
- /* save req info received */\r
- __conn_save_wire_req( p_req, p_conn, &req_rec );\r
-\r
- /* Add the connection to the list of pending connections. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_insert_tail( &gp_cm->pending_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- /* Reference the connection object until the user calls REP or REJ. */\r
- __ref_conn( p_conn );\r
-\r
- /* Invoke the user's callback. */\r
- p_listen->info.pfn_cm_req_cb( &req_rec );\r
-\r
- /* Return the REQ to the mad pool */\r
- ib_put_mad( p_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__peer_req(\r
- IN cm_port_agent_t* const p_port_cm,\r
- IN al_conn_t* const p_conn,\r
- IN ib_mad_element_t* const p_mad )\r
-{\r
- mad_cm_req_t *p_req;\r
- ib_cm_req_rec_t req_rec;\r
- ib_rej_status_t rej_status;\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_port_cm );\r
- CL_ASSERT( p_conn );\r
- CL_ASSERT( p_conn->p_req_info );\r
- /* Must be peer-to-peer. */\r
- CL_ASSERT( p_conn->p_req_info->pfn_cm_req_cb );\r
- CL_ASSERT( p_mad );\r
- CL_ASSERT( p_conn->h_qp );\r
-\r
- p_req = ib_get_mad_buf( p_mad );\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- /* Perform peer comparison. */\r
- if( cl_ntoh64( p_conn->p_req_info->p_ca_attr->ca_guid ) >\r
- cl_ntoh64( p_req->local_ca_guid ) )\r
- {\r
- cm_res_release( p_conn );\r
- __repeated_mad( p_port_cm, p_conn, p_mad );\r
- /* Release reference on p_conn obtained from __match_peer. */\r
- __deref_conn( p_conn );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Won peer compare, repeating REQ.\n") );\r
- return;\r
- }\r
- if( cl_ntoh32( p_conn->p_req_info->local_qpn ) >\r
- cl_ntoh32( conn_req_get_lcl_qpn( p_req ) ) )\r
- {\r
- cm_res_release( p_conn );\r
- __repeated_mad( p_port_cm, p_conn, p_mad );\r
- /* Release reference on p_conn obtained from __match_peer. */\r
- __deref_conn( p_conn );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Won peer compare, repeating REQ.\n") );\r
- return;\r
- }\r
-\r
- /* Format the callback record. */\r
- rej_status = __format_req_rec( p_port_cm, p_req, NULL, &req_rec );\r
- if( rej_status )\r
- {\r
- cm_res_release( p_conn );\r
- /* Release reference on p_conn obtained from __match_peer. */\r
- __deref_conn( p_conn );\r
-\r
- /* The request is malformed. Reject it. */\r
- __reject_req( p_port_cm, p_mad, rej_status );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("REJ sent for malformed REQ.\n") );\r
- return;\r
- }\r
-\r
- /* Stop sending the REQ and change the state. */\r
- status = ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
- if( status == IB_NOT_FOUND )\r
- {\r
- cm_res_release( p_conn );\r
- /* Release reference on p_conn obtained from __match_peer. */\r
- __deref_conn( p_conn );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("REQ in invalid state.\n") );\r
- return;\r
- }\r
-\r
- /* update peer based rec handles and context values */\r
- req_rec.context = p_conn->h_qp->obj.context;\r
- req_rec.h_cm_req = p_conn;\r
- req_rec.h_cm_listen = NULL;\r
-\r
- __conn_save_wire_req(p_req, p_conn, &req_rec );\r
- cm_res_release( p_conn );\r
-\r
- /* Note that we're holding a reference on p_conn from __match_peer(). */\r
-\r
- /* Invoke the user's callback. User must call ib_cm_rep or ib_cm_rej. */\r
- p_conn->p_req_info->pfn_cm_req_cb( &req_rec );\r
-\r
- /* Return the REQ to the mad pool */\r
- ib_put_mad( p_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_handoff(\r
- IN al_conn_t* const p_conn,\r
- IN const net64_t svc_id )\r
-{\r
- cl_list_item_t *p_list_item;\r
- al_listen_t *p_listen;\r
- ib_cm_req_rec_t req_rec;\r
- ib_rej_status_t rej_status;\r
- al_conn_t *p_conn_handoff;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_conn );\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- /*\r
- * To do a successful handoff the current connection's attribs should be\r
- * validated against the service_id to be handed over to.\r
- * We change the service_id of the current connection to the one we want\r
- * to hand off to. The match routine will validate the rest of the handoff\r
- * process.\r
- */\r
- p_conn->p_req_info->svc_id = svc_id;\r
-\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_list_item = cl_qlist_find_from_head( &gp_cm->active_listen_list,\r
- __match_conn_handoff, p_conn );\r
- if( p_list_item == cl_qlist_end( &gp_cm->active_listen_list ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- __conn_reject( p_conn, IB_REJ_INVALID_SID, NULL, 0, NULL );\r
- cm_res_release( p_conn );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Handoff failed on svc_id!\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- /* A listen for handoff was found. Now transfer details from old conn */\r
- p_listen = PARENT_STRUCT( p_list_item, al_listen_t, list_item );\r
-\r
- /* Format the callback record. */\r
- rej_status = __format_req_rec( p_conn->p_port_cm, &p_conn->mads.req,\r
- p_listen, &req_rec );\r
- if( rej_status )\r
- {\r
- /* The request does not conform to the handoff process service. */\r
- __conn_reject( p_conn, rej_status, NULL, 0, NULL );\r
- cm_res_release( p_conn );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Handoff failed on svc_id info!\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- /* Get a new connection object. */\r
- p_conn_handoff = __get_conn( (ib_al_handle_t)p_listen->obj.p_parent_obj,\r
- req_rec.qp_type );\r
- if( !p_conn_handoff )\r
- {\r
- /* Reject the request for insufficient resources. */\r
- __conn_reject( p_conn, IB_REJ_INSUF_RESOURCES, NULL, 0, NULL );\r
- cm_res_release( p_conn );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Handoff failed on insufficient resources.\n") );\r
- return IB_INSUFFICIENT_MEMORY;\r
- }\r
-\r
- /* update p_conn from listen and req values. */\r
- p_conn_handoff->p_req_info->pfn_cm_req_cb = NULL;\r
- p_conn_handoff->pfn_cm_rej_cb = p_listen->info.pfn_cm_rej_cb;\r
- p_conn_handoff->p_req_info->pfn_cm_rep_cb = NULL;\r
- p_conn_handoff->pfn_cm_mra_cb = p_listen->info.pfn_cm_mra_cb;\r
-\r
- p_conn_handoff->p_port_cm = p_conn->p_port_cm;\r
-\r
- /* update listen based rec */\r
- req_rec.context = p_listen->obj.context;\r
- req_rec.h_cm_req = p_conn_handoff;\r
- req_rec.h_cm_listen = p_listen;\r
-\r
- /* save req info received */\r
- __conn_save_wire_req( &p_conn->mads.req, p_conn_handoff, &req_rec );\r
-\r
- /* Add the connection to the list of pending connections. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_insert_tail( &gp_cm->pending_list,\r
- (cl_list_item_t*)&p_conn_handoff->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- /* Reference the connection object until the user calls REP or REJ. */\r
- __ref_conn( p_conn_handoff );\r
-\r
- /* Abort the original connection created when the REQ was received. */\r
- __conn_abort( p_conn );\r
- cm_res_release( p_conn );\r
-\r
- /* Invoke the user's callback. */\r
- p_listen->info.pfn_cm_req_cb( &req_rec );\r
-\r
- /* deref the listen object */\r
- deref_al_obj( &p_listen->obj );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-void\r
-__process_cm_req(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- mad_cm_req_t *p_req;\r
- cm_async_mad_t *p_async_mad;\r
- cl_list_item_t *p_list_item;\r
- al_conn_t *p_conn;\r
- al_listen_t *p_listen;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_req = (mad_cm_req_t*)p_async_mad->p_mad->p_mad_buf;\r
- p_port_cm = p_async_mad->p_port_cm;\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REQ: comm_id (x%x) received\n",\r
- p_req->local_comm_id ) );\r
-\r
- /* Match against pending connections using remote comm ID and CA GUID. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_list_item = cl_qlist_find_from_head( &gp_cm->pending_list,\r
- __req_match_pending, p_req );\r
- if( p_list_item != cl_qlist_end( &gp_cm->pending_list ) )\r
- {\r
- /* Already received the REQ. */\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- __repeated_mad( p_port_cm, p_conn, p_async_mad->p_mad );\r
- __deref_conn( p_conn );\r
- cl_free( p_async_mad );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Duplicate REQ received.\n") );\r
- return;\r
- }\r
-\r
- /* Match against stale connections using remote comm ID and CA GUID. */\r
- p_list_item = cl_qlist_find_from_tail( &gp_cm->time_wait_list,\r
- __match_timewait, p_req );\r
- if( p_list_item != cl_qlist_end( &gp_cm->time_wait_list ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- __reject_req( p_port_cm, p_async_mad->p_mad, IB_REJ_STALE_CONN );\r
- cl_free( p_async_mad );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REQ received for connection in TIME_WAIT state.\n") );\r
- return;\r
- }\r
-\r
- /* Match against listens using SID and compare data. */\r
- p_list_item = cl_qlist_find_from_head( &gp_cm->active_listen_list,\r
- __match_conn_listen, p_req );\r
- if( p_list_item != cl_qlist_end( &gp_cm->active_listen_list ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- p_listen = PARENT_STRUCT( p_list_item, al_listen_t, list_item );\r
- __listen_req( p_port_cm, p_listen, p_async_mad->p_mad );\r
- deref_al_obj( &p_listen->obj );\r
- cl_free( p_async_mad );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REQ matched a listen.\n") );\r
- return;\r
- }\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("No listens active!\n") );\r
-\r
- /* Match against peer-to-peer requests using SID and compare data. */\r
- p_list_item = cl_qlist_find_from_head( &gp_cm->pending_list,\r
- __match_peer, p_req );\r
- if( p_list_item != cl_qlist_end( &gp_cm->pending_list ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- __peer_req( p_port_cm, p_conn, p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REQ matched a peer-to-peer request.\n") );\r
- return;\r
- }\r
-\r
- /* No match found. Reject. */\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- __reject_req( p_port_cm, p_async_mad->p_mad, IB_REJ_INVALID_SID );\r
- cl_free( p_async_mad );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REQ received but no match found.\n") );\r
-}\r
-\r
-\r
-/*\r
- * Matches a connection by local (and remote if available) communication ID.\r
- */\r
-cl_status_t\r
-__mra_match(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- al_conn_t *p_conn;\r
- mad_cm_mra_t *p_mra;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- p_mra = (mad_cm_mra_t*)context;\r
-\r
- if( p_conn->local_comm_id != p_mra->remote_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Local conn ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_conn->remote_comm_id &&\r
- p_conn->remote_comm_id == p_mra->local_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Remote conn ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-void\r
-__start_mra_timer(\r
- IN al_conn_t* const p_conn,\r
- IN const mad_cm_mra_t* const p_mra )\r
-{\r
- uint32_t timeout;\r
- uint8_t pkt_life;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Cancel the send for the REQ, REP, or LAP. */\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
-\r
- /*\r
- * Set the retry timer to the interval specified in the MRA.\r
- * All timeout values in micro seconds are expressed as 4.096 * 2^x,\r
- * where x is the timeout. This approximates to 2^(x+2).\r
- * Since we want milliseconds, we can further approximate to 2^(x-8).\r
- * This results in a timeout that is roughly 5% on the low side, but\r
- * good enough.\r
- */\r
- if( conn_mra_get_svc_timeout( p_mra ) > 8 )\r
- timeout = 1 << (conn_mra_get_svc_timeout( p_mra ) - 8);\r
- else\r
- timeout = 0;\r
-\r
- pkt_life = ib_path_rec_pkt_life( &p_conn->path[p_conn->idx_primary] );\r
- if( pkt_life > 8 )\r
- timeout += 1 << (pkt_life - 8);\r
-\r
- /*\r
- * Minimum 10 ms timeout - picked to match typical OS timer resolution.\r
- */\r
- if( timeout < 10 )\r
- timeout = 10;\r
- if( p_conn->state != CM_CONN_REQ_MRA_RCVD &&\r
- p_conn->state != CM_CONN_REP_MRA_RCVD &&\r
- p_conn->state != CM_CONN_LAP_MRA_RCVD )\r
- {\r
- /* Only reference the connection the first time we set the timer. */\r
- __ref_conn( p_conn );\r
- }\r
-\r
- if( cl_timer_start( &p_conn->timer, timeout ) != CL_SUCCESS )\r
- {\r
- __deref_conn( p_conn );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__do_mra_callback(\r
- IN al_conn_t* const p_conn,\r
- IN const mad_cm_mra_t* const p_mra )\r
-{\r
- ib_cm_mra_rec_t mra_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_conn->h_qp );\r
-\r
- /* Format the MRA callback record. */\r
- cl_memclr( &mra_rec, sizeof( ib_cm_mra_rec_t ) );\r
-\r
- mra_rec.h_qp = p_conn->h_qp;\r
- mra_rec.qp_context = p_conn->h_qp->obj.context;\r
- mra_rec.p_mra_pdata = p_mra->pdata;\r
-\r
- /*\r
- * Call the user back. Note that users will get a callback only\r
- * for the first MRA received in response to a REQ, REP, or LAP.\r
- */\r
- p_conn->pfn_cm_mra_cb( &mra_rec );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__process_cm_mra(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- mad_cm_mra_t *p_mra;\r
- cm_async_mad_t *p_async_mad;\r
- cl_map_item_t *p_map_item;\r
- al_conn_t *p_conn;\r
- boolean_t do_callback = TRUE;\r
- uint64_t key;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_port_cm = p_async_mad->p_port_cm;\r
- p_mra = (mad_cm_mra_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- if( conn_mra_get_msg_mraed( p_mra ) == 2 )\r
- {\r
- key = ((uint64_t)p_mra->remote_comm_id << 32) |\r
- ((uint64_t)p_mra->local_comm_id );\r
-\r
- p_map_item = cl_qmap_get( &gp_cm->conn_map, key );\r
- if( p_map_item == cl_qmap_end( &gp_cm->conn_map ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("MRA received for LAP that could not be matched.\n") );\r
- return;\r
- }\r
- }\r
- else\r
- {\r
- p_map_item = (cl_map_item_t*)cl_qlist_find_from_head(\r
- &gp_cm->pending_list, __mra_match, p_mra );\r
- if( p_map_item ==\r
- (cl_map_item_t*)cl_qlist_end( &gp_cm->pending_list ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("MRA received that could not be matched.\n") );\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- return;\r
- }\r
- }\r
-\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
-\r
- __ref_conn( p_conn );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_MRA_RCVD:\r
- do_callback = FALSE;\r
- case CM_CONN_REQ_SENT:\r
- /* MRA is not for the REQ. Drop it. */\r
- if( conn_mra_get_msg_mraed( p_mra ) != 0 )\r
- {\r
- do_callback = FALSE;\r
- break;\r
- }\r
- /* Store the remote communication ID. */\r
- p_conn->remote_comm_id = p_mra->local_comm_id;\r
- __start_mra_timer( p_conn, p_mra );\r
- p_conn->state = CM_CONN_REQ_MRA_RCVD;\r
- break;\r
-\r
- case CM_CONN_REP_MRA_RCVD:\r
- do_callback = FALSE;\r
- case CM_CONN_REP_SENT:\r
- /* MRA is not for the REP. Drop it. */\r
- if( conn_mra_get_msg_mraed( p_mra ) != 1 )\r
- {\r
- do_callback = FALSE;\r
- break;\r
- }\r
- __start_mra_timer( p_conn, p_mra );\r
- p_conn->state = CM_CONN_REP_MRA_RCVD;\r
- break;\r
-\r
- case CM_CONN_LAP_MRA_RCVD:\r
- do_callback = FALSE;\r
- case CM_CONN_LAP_SENT:\r
- /* MRA is not for the LAP. Drop it. */\r
- if( conn_mra_get_msg_mraed( p_mra ) != 2 )\r
- {\r
- do_callback = FALSE;\r
- break;\r
- }\r
- __start_mra_timer( p_conn, p_mra );\r
- p_conn->state = CM_CONN_LAP_MRA_RCVD;\r
- break;\r
-\r
- default:\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("MRA received in invalid state.\n") );\r
- do_callback = FALSE;\r
- break;\r
- }\r
-\r
- cm_res_release( p_conn );\r
- if( do_callback )\r
- __do_mra_callback( p_conn, p_mra );\r
- __deref_conn( p_conn );\r
-\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__do_rej_callback(\r
- IN al_conn_t* const p_conn,\r
- IN const mad_cm_rej_t* const p_rej )\r
-{\r
- ib_cm_rej_rec_t rej_rec;\r
-\r
- /* Format the REJ callback record. */\r
- cl_memclr( &rej_rec, sizeof( ib_cm_rej_rec_t ) );\r
-\r
- CL_ASSERT( p_conn->h_qp );\r
-\r
- rej_rec.h_qp = p_conn->h_qp;\r
- rej_rec.qp_context = p_conn->h_qp->obj.context;\r
-\r
- rej_rec.p_rej_pdata = p_rej->pdata;\r
- rej_rec.p_ari = p_rej->ari;\r
- rej_rec.ari_length = conn_rej_get_ari_len( p_rej );\r
- rej_rec.rej_status = p_rej->reason;\r
-\r
- /*\r
- * Unbind the QP from the connection object. This allows the QP to\r
- * be immediately reused in another connection request.\r
- */\r
- __cm_conn_unbind( p_conn, 0 );\r
-\r
- /* Call the user back. */\r
- p_conn->pfn_cm_rej_cb( &rej_rec );\r
-}\r
-\r
-\r
-/*\r
- * Matches a connection by CA GUID and communication ID.\r
- */\r
-cl_status_t\r
-__rej_match(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- al_conn_t *p_conn;\r
- mad_cm_rej_t *p_rej;\r
- const ib_net64_t UNALIGNED *p_ca_guid;\r
- uint8_t ari_len;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- p_rej = (mad_cm_rej_t*)context;\r
-\r
- /* Either one of the communication IDs must be set. */\r
- CL_ASSERT( p_rej->remote_comm_id || p_rej->local_comm_id );\r
-\r
- /* REJ remote comm ID can be zero if rejecting due to REP timeout. */\r
- if( p_rej->remote_comm_id &&\r
- p_conn->local_comm_id != p_rej->remote_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Local comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /* Only match on remote comm ID if set. */\r
- if( p_conn->remote_comm_id &&\r
- p_conn->remote_comm_id != p_rej->local_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Remote comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /* If rejection is due to a REP timeout, compare the CA GUID. */\r
- p_ca_guid = (const net64_t*)p_rej->ari;\r
- ari_len = conn_rej_get_ari_len( p_rej );\r
- if( p_rej->reason == IB_REJ_TIMEOUT &&\r
- ari_len == sizeof(ib_net64_t) )\r
- {\r
- /* Only check if a connection req is started*/\r
- if( ( p_conn->p_req_info->p_ca_attr ) &&\r
- ( p_conn->p_req_info->p_ca_attr->ca_guid != *p_ca_guid ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Local CA GUID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
- }\r
-\r
- /* We reached here with a comm id match which is good enough for me */\r
-\r
- __ref_conn( p_conn );\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-void\r
-__process_cm_rej(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- mad_cm_rej_t *p_rej;\r
- cm_async_mad_t *p_async_mad;\r
- cl_map_item_t *p_map_item;\r
- al_conn_t *p_conn;\r
- uint64_t key;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_port_cm = p_async_mad->p_port_cm;\r
- p_rej = (mad_cm_rej_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- /* Check the pending list by the remote CA GUID and connection ID. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_map_item = (cl_map_item_t*)cl_qlist_find_from_head( &gp_cm->pending_list,\r
- __rej_match, p_rej );\r
- if( p_map_item == (cl_map_item_t*)cl_qlist_end( &gp_cm->pending_list ) )\r
- {\r
- /* Not in the pending list, check the connection map. */\r
- key = ((uint64_t)p_rej->remote_comm_id << 32) |\r
- ((uint64_t)p_rej->local_comm_id );\r
-\r
- p_map_item = cl_qmap_get( &gp_cm->conn_map, key );\r
- if( p_map_item == cl_qmap_end( &gp_cm->conn_map ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REJ received that could not be matched.\n") );\r
- return;\r
- }\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
- __ref_conn( p_conn );\r
- }\r
- else\r
- {\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
- }\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_SENT:\r
- /*\r
- * Ignore rejects with the status set to IB_REJ_INVALID_SID. We will\r
- * continue to retry (up to max_cm_retries) to connect to the remote\r
- * side. This is required to support peer-to-peer connections and\r
- * clients that try to connect before the server comes up.\r
- */\r
- if( (p_rej->reason == IB_REJ_INVALID_SID) &&\r
- p_conn->max_cm_retries )\r
- {\r
- p_conn->max_cm_retries--;\r
- /* Deref the connection to match the reference taken when matching. */\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Request rejected (invalid SID) - retrying.\n") );\r
- return;\r
- }\r
-\r
- /* Fall through */\r
- case CM_CONN_REP_SENT:\r
- /* Cancel any outstanding MAD. */\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
-\r
- /* Fall through */\r
-\r
- case CM_CONN_REQ_RCVD:\r
- case CM_CONN_REP_RCVD:\r
- case CM_CONN_REQ_MRA_RCVD:\r
- case CM_CONN_REQ_MRA_SENT:\r
- case CM_CONN_REP_MRA_RCVD:\r
- case CM_CONN_REP_MRA_SENT:\r
- case CM_CONN_ESTABLISHED:\r
- /* Notify the user only for a valid CEP. */\r
- /* TODO: Can this IF go away? */\r
- if( p_conn->h_qp )\r
- __do_rej_callback( p_conn, p_rej );\r
-\r
- /* Abort connection establishment. No transition to timewait. */\r
- __conn_abort( p_conn );\r
-\r
- cm_res_release( p_conn );\r
- break;\r
-\r
- default:\r
- /* Ignore the REJ. */\r
- cm_res_release( p_conn );\r
-\r
- /* Deref the connection to match the reference taken when matching. */\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REJ received in invalid state.\n") );\r
- return;\r
- }\r
-\r
- /* Deref the connection to match the reference taken when matching. */\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Function invoked to send a REJ.\r
- */\r
-void\r
-__conn_reject(\r
- IN al_conn_t* const p_conn,\r
- IN const ib_rej_status_t reason,\r
- IN const ib_ari_t* const p_ari OPTIONAL,\r
- IN const uint8_t ari_length,\r
- IN const ib_rej_pdata_t* const p_rej_pdata OPTIONAL )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
- UNUSED_PARAM( p_rej_pdata );\r
-\r
- /*\r
- * Format the reject information.\r
- */\r
- p_conn->mads.rej.hdr.attr_id = CM_REJ_ATTR_ID;\r
- p_conn->mads.rej.remote_comm_id = p_conn->remote_comm_id;\r
- p_conn->mads.rej.local_comm_id = p_conn->local_comm_id;\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_RCVD:\r
- conn_rej_set_msg_rejected( 0, &p_conn->mads.rej );\r
- break;\r
-\r
- case CM_CONN_REP_RCVD:\r
- conn_rej_set_msg_rejected( 1, &p_conn->mads.rej );\r
- break;\r
-\r
- default:\r
- conn_rej_set_msg_rejected( 2, &p_conn->mads.rej );\r
- break;\r
- }\r
-\r
- p_conn->mads.rej.reason = reason;\r
-\r
- /* Copy ARI data, if any. */\r
- if( reason == IB_REJ_TIMEOUT )\r
- {\r
- /* ARI contains CA GUID. */\r
- /* Copy the local CA GUID into the ARI. */\r
- conn_rej_set_ari( (uint8_t*)&p_conn->p_req_info->p_ca_attr->ca_guid,\r
- sizeof(ib_net64_t), &p_conn->mads.rej );\r
- }\r
- else if( p_ari )\r
- {\r
- conn_rej_set_ari( p_ari->data, ari_length, &p_conn->mads.rej );\r
- }\r
- else\r
- {\r
- conn_rej_set_ari( NULL, 0, &p_conn->mads.rej );\r
- }\r
-\r
- /* Copy private data, if any. */\r
- conn_rej_set_pdata( NULL, 0, &p_conn->mads.rej );\r
-\r
- conn_rej_clr_rsvd_fields( &p_conn->mads.rej );\r
-\r
- status = __get_port_attr( &p_conn->path[0].sgid, p_conn->path[0].slid,\r
- &p_port_cm, NULL );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_port_attr returned %s\n", ib_get_err_str(status)) );\r
- return;\r
- }\r
-\r
- /* Cancel any outstanding sends. */\r
- if( p_conn->p_send_mad )\r
- {\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
- }\r
-\r
- __cm_send( p_port_cm, p_conn );\r
- __conn_abort( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Matches a connection given a REP.\r
- */\r
-cl_status_t\r
-__rep_match(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- al_conn_t *p_conn;\r
- mad_cm_rep_t *p_rep;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- p_rep = (mad_cm_rep_t*)context;\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("p_conn:: local_comm_id (x%x) remote_comm_id (x%x)\n"\r
- "p_rep:: local_comm_id (x%x) remote_comm_id (x%x)\n",\r
- p_conn->local_comm_id,\r
- p_conn->remote_comm_id,\r
- p_rep->local_comm_id,\r
- p_rep->remote_comm_id ) );\r
-\r
- if( p_conn->local_comm_id != p_rep->remote_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Local comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- __ref_conn( p_conn );\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-\r
-\r
-void\r
-__format_rep_rec(\r
- IN const mad_cm_rep_t* const p_rep,\r
- IN const al_conn_t* const p_conn,\r
- OUT ib_cm_rep_rec_t* const p_rep_rec )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_memclr( p_rep_rec, sizeof(ib_cm_rep_rec_t) );\r
-\r
- /* fill the rec callback data */\r
- p_rep_rec->p_rep_pdata = p_rep->pdata;\r
- p_rep_rec->qp_type = p_conn->qp_type;\r
-\r
- p_rep_rec->h_cm_rep = (ib_cm_handle_t)p_conn;\r
- p_rep_rec->qp_context = p_conn->h_qp->obj.context;\r
- p_rep_rec->resp_res = p_rep->resp_resources;\r
- p_rep_rec->flow_ctrl = conn_rep_get_e2e_flow_ctl( p_rep );\r
- p_rep_rec->apr_status = conn_rep_get_failover( p_rep );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-void\r
-__conn_save_wire_rep(\r
- IN const mad_cm_rep_t* const p_rep,\r
- OUT al_conn_t* const p_conn )\r
-{\r
- struct _qp_rtr *p_rtr;\r
- struct _qp_rts *p_rts;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* The send should have been cancelled during MRA processing. */\r
- p_conn->state = CM_CONN_REP_RCVD;\r
-\r
- /* Store pertinent information in the connection. */\r
- p_conn->remote_comm_id = p_rep->local_comm_id;\r
- p_conn->remote_ca_guid = p_rep->remote_comm_id;\r
-\r
- p_conn->remote_qpn = conn_rep_get_lcl_qpn( p_rep );\r
-\r
- /*\r
- * Store the target ack delay. This is used to calculate the\r
- * ack timeouts when setting up address vectors for alternate paths.\r
- */\r
- p_conn->target_ack_delay = conn_rep_get_target_ack_delay( p_rep );\r
-\r
- /* Setup the QP modify structures for the RTR and RTS transitions */\r
- p_rtr = &p_conn->p_req_info->qp_mod_rtr.state.rtr;\r
- p_rts = &p_conn->p_req_info->qp_mod_rts.state.rts;\r
-\r
- /* Save RTR info */\r
- p_rtr->dest_qp = conn_rep_get_lcl_qpn( p_rep );\r
-\r
- p_rtr->primary_av.conn.rnr_retry_cnt = conn_rep_get_rnr_retry_cnt( p_rep );\r
-\r
- p_rtr->primary_av.conn.local_ack_timeout = cm_local_ack_timeout(\r
- ib_path_rec_pkt_life( &p_conn->path[0] ), p_conn->target_ack_delay );\r
- if( p_conn->path[1].slid )\r
- {\r
- p_rtr->alternate_av.conn.rnr_retry_cnt =\r
- conn_rep_get_rnr_retry_cnt( p_rep );\r
- p_rtr->alternate_av.conn.local_ack_timeout = cm_local_ack_timeout(\r
- ib_path_rec_pkt_life( &p_conn->path[1] ),\r
- p_conn->target_ack_delay );\r
- }\r
-\r
- /* Saved in a REQ and/or passed back to user in rep_rec callback\r
- p_rtr->resp_res;\r
- p_rtr->qkey;\r
- p_rtr->sq_depth;\r
- p_rtr->rq_depth;\r
- p_rtr->rq_psn;\r
- */\r
-\r
- /* Save RTS info */\r
- p_rts->rnr_retry_cnt = conn_rep_get_rnr_retry_cnt( p_rep );\r
-\r
- /* Responder resources and initiator depth are only for RC QPs. */\r
- if( p_conn->p_req_info->xport_type == IB_QPT_RELIABLE_CONN )\r
- {\r
- p_rtr->resp_res = p_rep->initiator_depth;\r
- p_rts->init_depth = p_rep->resp_resources;\r
- }\r
-\r
- /* Set the APM state. */\r
- if( conn_rep_get_failover( p_rep ) == IB_AP_SUCCESS )\r
- {\r
- p_rts->opts |= IB_MOD_QP_APM_STATE;\r
- p_rts->apm_state = IB_APM_REARM;\r
- }\r
-\r
- p_rts->sq_psn = conn_rep_get_starting_psn( p_rep );\r
-\r
- /* Saved in REQ and/or passed back in rep_rec callback\r
- p_rts->retry_cnt;\r
- p_rts->rnr_nak_timeout;\r
- p_rts->local_ack_timeout;\r
- p_rts->access_ctrl;\r
- p_rts->sq_depth;\r
- p_rts->rq_depth;\r
- p_rts->primary_port;\r
- p_rts->opts;\r
- p_rts->qkey = p_rep->local_qkey;\r
- */\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-void\r
-__process_cm_rep(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- mad_cm_rep_t *p_rep;\r
- cm_async_mad_t *p_async_mad;\r
- cl_map_item_t *p_map_item;\r
- al_conn_t *p_conn;\r
- ib_cm_rep_rec_t rep_rec;\r
- uint64_t key;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_rep = (mad_cm_rep_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REP: comm_id (x%x) received\n",\r
- p_rep->local_comm_id ) );\r
-\r
- /* Check the pending list by the local connection ID. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_map_item = (cl_map_item_t*)cl_qlist_find_from_head( &gp_cm->pending_list,\r
- __rep_match, p_rep );\r
- if( p_map_item == (cl_map_item_t*)cl_qlist_end( &gp_cm->pending_list ) )\r
- {\r
- /* Not in the pending list, check the connection map. */\r
- key = ((uint64_t)p_rep->remote_comm_id << 32) |\r
- ((uint64_t)p_rep->local_comm_id );\r
-\r
- p_map_item = cl_qmap_get( &gp_cm->conn_map, key );\r
- if( p_map_item == cl_qmap_end( &gp_cm->conn_map ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("REP received that could not be matched.\n") );\r
- return;\r
- }\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
- __ref_conn( p_conn );\r
- }\r
- else\r
- {\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
- }\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_SENT:\r
- /* Cancel any outstanding send. */\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
-\r
- /* Fall through. */\r
- case CM_CONN_REQ_MRA_RCVD:\r
- if( p_conn->state == CM_CONN_REQ_MRA_RCVD )\r
- {\r
- /* Cancel the MRA timer and release its reference. */\r
- cl_timer_stop( &p_conn->timer );\r
- __deref_conn( p_conn );\r
- }\r
-\r
- __conn_save_wire_rep( p_rep, p_conn );\r
- __format_rep_rec( p_rep, p_conn, &rep_rec );\r
-\r
- cm_res_release( p_conn );\r
- /* Reference the connection until the user calls REJ or RTU. */\r
- __ref_conn( p_conn );\r
-\r
- /* Notify the user of the reply. */\r
- p_conn->p_req_info->pfn_cm_rep_cb( &rep_rec );\r
-\r
- ib_put_mad( p_async_mad->p_mad );\r
- break;\r
-\r
- case CM_CONN_ESTABLISHED:\r
- cm_res_release( p_conn );\r
- /* Repeate the RTU. */\r
- __repeated_mad( p_async_mad->p_port_cm, p_conn, p_async_mad->p_mad );\r
- break;\r
-\r
- default:\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("REP received in invalid state.\n") );\r
- cm_res_release( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- break;\r
- }\r
- /* Release the reference acquired when matching the connection. */\r
- __deref_conn( p_conn );\r
- cl_free( p_async_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Matches a connection given an RTU.\r
- */\r
-cl_status_t\r
-__rtu_match(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- al_conn_t *p_conn;\r
- mad_cm_rtu_t *p_rtu;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- p_rtu = (mad_cm_rtu_t*)context;\r
-\r
- if( p_conn->local_comm_id != p_rtu->remote_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Local comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_conn->remote_comm_id != p_rtu->local_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Remote comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- __ref_conn( p_conn );\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-void\r
-__format_rtu_rec(\r
- IN const mad_cm_rtu_t* const p_rtu,\r
- IN const al_conn_t* const p_conn,\r
- OUT ib_cm_rtu_rec_t* const p_rtu_rec )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_memclr( p_rtu_rec, sizeof(ib_cm_rtu_rec_t) );\r
-\r
- p_rtu_rec->p_rtu_pdata = p_rtu->pdata;\r
- p_rtu_rec->qp_type = p_conn->qp_type;\r
- p_rtu_rec->h_qp = p_conn->h_qp;\r
- p_rtu_rec->qp_context = p_conn->h_qp->obj.context;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-void\r
-__process_cm_rtu(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- mad_cm_rtu_t *p_rtu;\r
- cm_async_mad_t *p_async_mad;\r
- cl_list_item_t *p_list_item;\r
- al_conn_t *p_conn;\r
- ib_pfn_cm_rtu_cb_t pfn_rtu;\r
- ib_cm_rtu_rec_t rtu_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_rtu = (mad_cm_rtu_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU: comm_id (x%x) received\n",\r
- p_rtu->local_comm_id ) );\r
-\r
- /* Find the connection by local connection ID. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_list_item = cl_qlist_find_from_head( &gp_cm->pending_list,\r
- __rtu_match, p_rtu );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- if( p_list_item == cl_qlist_end( &gp_cm->pending_list ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("RTU received that could not be matched.\n") );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- return;\r
- }\r
-\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REP_SENT:\r
- /* Cancel any outstanding send. */\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
-\r
- /* Fall through. */\r
- case CM_CONN_REP_MRA_RCVD:\r
- if( p_conn->state == CM_CONN_REP_MRA_RCVD )\r
- {\r
- /* Cancel the MRA timer and release its reference. */\r
- cl_timer_stop( &p_conn->timer );\r
- __deref_conn( p_conn );\r
- }\r
-\r
- /* The send should have been cancelled during MRA processing. */\r
- p_conn->state = CM_CONN_ESTABLISHED;\r
-\r
- /* Store the callback pointers. */\r
- pfn_rtu = p_conn->p_req_info->pfn_cm_rtu_cb;\r
-\r
- /* Move the connection from the pending list to the connection map. */\r
- __migrate_conn_to_map( p_conn );\r
-\r
- /* Release the request info. */\r
- cm_res_release( p_conn );\r
-\r
- /* callback user */\r
- __format_rtu_rec( p_rtu, p_conn, &rtu_rec );\r
- pfn_rtu( &rtu_rec );\r
-\r
- break;\r
-\r
- default:\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("RTU received in invalid state.\n") );\r
- cm_res_release( p_conn );\r
- break;\r
- }\r
-\r
- /* Release the reference acquired when matching the connection. */\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Matches a connection given a DREQ.\r
- */\r
-cl_status_t\r
-__dreq_match(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- al_conn_t *p_conn;\r
- mad_cm_dreq_t *p_dreq;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_list_item, al_conn_t, map_item );\r
- p_dreq = (mad_cm_dreq_t*)context;\r
-\r
- if( p_conn->local_comm_id != p_dreq->remote_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Local comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_conn->remote_comm_id != p_dreq->local_comm_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Remote comm ID mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- if( p_conn->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("local QPN mismatch.\n") );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_SUCCESS;\r
-}\r
-\r
-void\r
-__format_dreq_rec(\r
- IN const mad_cm_dreq_t* const p_dreq,\r
- IN const al_conn_t* const p_conn,\r
- OUT ib_cm_dreq_rec_t* const p_dreq_rec )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_memclr( p_dreq_rec, sizeof(ib_cm_dreq_rec_t) );\r
-\r
- p_dreq_rec->h_cm_dreq = (ib_cm_handle_t)p_conn;\r
- p_dreq_rec->p_dreq_pdata = p_dreq->pdata;\r
-\r
- p_dreq_rec->qp_type = p_conn->qp_type;\r
- p_dreq_rec->qp_context = p_conn->h_qp->obj.context;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-void\r
-__process_cm_dreq(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- mad_cm_dreq_t *p_dreq;\r
- cm_async_mad_t *p_async_mad;\r
- cl_map_item_t *p_map_item;\r
- al_conn_t *p_conn;\r
- uint64_t key;\r
- ib_cm_dreq_rec_t dreq_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_dreq = (mad_cm_dreq_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- /* Find the connection by connection IDs. */\r
- key = ( (uint64_t)p_dreq->remote_comm_id ) << 32 |\r
- ( (uint64_t)p_dreq->local_comm_id );\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_map_item = cl_qmap_get( &gp_cm->conn_map, key );\r
- if( p_map_item == cl_qmap_end( &gp_cm->conn_map ) )\r
- {\r
- /* Look in the timewait list in order to repeat the final DREP. */\r
- p_map_item = (cl_map_item_t*)cl_qlist_find_from_head(\r
- &gp_cm->time_wait_list, __dreq_match, p_dreq );\r
- if( p_map_item ==\r
- (cl_map_item_t*)cl_qlist_end( &gp_cm->time_wait_list ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("DREQ received that could not be matched.\n") );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- return;\r
- }\r
- }\r
-\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
-\r
- /*\r
- * Reference the connection to sync with a destroy QP and prevent the\r
- * connection object from being destroyed while in a callback.\r
- */\r
- __ref_conn( p_conn );\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- cm_res_acquire( p_conn );\r
- /* Do the additional check as per spec. */\r
- if( p_conn->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )\r
- {\r
- cm_res_release( p_conn );\r
-\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- AL_EXIT( AL_DBG_CM );\r
- return;\r
- }\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REP_SENT:\r
- case CM_CONN_DREQ_SENT:\r
- /* Cancel the outstanding MAD. */\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
-\r
- /* Fall through and process as DREQ received case. */\r
- case CM_CONN_ESTABLISHED:\r
- /*\r
- * The user must call ib_cm_drep. Continue to hold the initial\r
- * reference on p_conn until the user makes the call (or we give\r
- * up waiting on them).\r
- */\r
- p_conn->state = CM_CONN_DREQ_RCVD;\r
-\r
- __format_dreq_rec( p_dreq, p_conn, &dreq_rec );\r
-\r
- cm_res_release( p_conn );\r
- p_conn->pfn_cm_dreq_cb( &dreq_rec );\r
- ib_put_mad( p_async_mad->p_mad );\r
- break;\r
-\r
- case CM_CONN_DREP_SENT:\r
- case CM_CONN_TIMEWAIT:\r
- cm_res_release( p_conn );\r
- /* Repeat the DREP. */\r
- __repeated_mad( p_async_mad->p_port_cm, p_conn, p_async_mad->p_mad );\r
- break;\r
-\r
- default:\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("DREQ received in invalid state.\n") );\r
- cm_res_release( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- break;\r
- }\r
-\r
- /* Release the reference taken above. */\r
- __deref_conn( p_conn );\r
- cl_free( p_async_mad );\r
-}\r
-\r
-\r
-\r
-void\r
-__format_drep_rec(\r
- IN const mad_cm_drep_t* const p_drep,\r
- IN const al_conn_t* const p_conn,\r
- OUT ib_cm_drep_rec_t* const p_drep_rec )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_memclr( p_drep_rec, sizeof(ib_cm_drep_rec_t) );\r
-\r
- /* Copy qp context before the connection is released */\r
- p_drep_rec->cm_status = IB_SUCCESS;\r
- p_drep_rec->p_drep_pdata = p_drep->pdata;\r
- p_drep_rec->qp_type = p_conn->qp_type;\r
- p_drep_rec->h_qp = p_conn->h_qp;\r
- p_drep_rec->qp_context = p_conn->h_qp->obj.context;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__process_cm_drep(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- mad_cm_drep_t *p_drep;\r
- cm_async_mad_t *p_async_mad;\r
- cl_map_item_t *p_map_item;\r
- uint64_t key;\r
- al_conn_t *p_conn;\r
- ib_cm_drep_rec_t drep_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_drep = (mad_cm_drep_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- /* Find the connection by local connection ID. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- key = ( (uint64_t)p_drep->remote_comm_id << 32) |\r
- ( (uint64_t)p_drep->local_comm_id );\r
- p_map_item = cl_qmap_get( &gp_cm->conn_map, key );\r
- if( p_map_item == cl_qmap_end( &gp_cm->conn_map ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("DREP received that could not be matched.\n") );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- return;\r
- }\r
-\r
- /*\r
- * Reference the connection to sync with a destroy QP and prevent the\r
- * connection object from being destroyed while in a callback.\r
- */\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
-\r
- __ref_conn( p_conn );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_DREQ_SENT:\r
- /* Cancel the DREQ. */\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
-\r
- __format_drep_rec( p_drep, p_conn, &drep_rec );\r
-\r
- /*\r
- * Release the connection. This puts the QP and connection object\r
- * into the timewait state.\r
- */\r
- __conn_release( p_conn );\r
- cm_res_release( p_conn );\r
-\r
- p_conn->pfn_cm_drep_cb( &drep_rec );\r
- break;\r
-\r
- default:\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("DREP received in invalid state.\n") );\r
- cm_res_release( p_conn );\r
- }\r
-\r
- /* Release the reference taken above. */\r
- __deref_conn( p_conn );\r
-\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__format_lap_path_rec(\r
- IN al_conn_t* const p_conn,\r
- IN const mad_cm_lap_t* const p_lap,\r
- OUT ib_path_rec_t* const p_path_rec )\r
-{\r
- const lap_path_info_t* const p_path = &p_lap->alternate_path;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_lap );\r
- CL_ASSERT( p_path_rec );\r
-\r
- /*\r
- * Format a local path record. The local ack timeout specified in the\r
- * REQ is twice the packet life plus the sender's CA ACK delay. When\r
- * reporting the packet life, we divide the local ack timeout by 2 to\r
- * approach the path's packet lifetime. Since local ack timeout is\r
- * expressed as 4.096 * 2^x, subtracting 1 is equivalent to dividing the\r
- * time in half.\r
- */\r
- ib_path_rec_init_local( p_path_rec,\r
- &p_lap->alternate_path.local_gid,\r
- &p_lap->alternate_path.remote_gid,\r
- p_lap->alternate_path.local_lid,\r
- p_lap->alternate_path.remote_lid,\r
- 1, p_conn->path[p_conn->idx_primary].pkey,\r
- conn_lap_path_get_svc_lvl( p_path ),\r
- IB_PATH_SELECTOR_EXACTLY,\r
- ib_path_rec_mtu( &p_conn->path[p_conn->idx_primary] ),\r
- IB_PATH_SELECTOR_EXACTLY,\r
- conn_lap_path_get_pkt_rate( p_path ),\r
- IB_PATH_SELECTOR_EXACTLY,\r
- (uint8_t)( conn_lap_path_get_lcl_ack_timeout( p_path ) - 1 ),\r
- 0 );\r
-\r
- p_path_rec->hop_flow_raw.val = 0;\r
- /* Add global routing info as necessary. */\r
- if( !conn_lap_path_get_subn_lcl( p_path ) )\r
- {\r
- ib_path_rec_set_hop_flow_raw( p_path_rec,\r
- p_lap->alternate_path.hop_limit,\r
- conn_lap_path_get_flow_lbl( p_path ),\r
- FALSE );\r
- p_path_rec->tclass = conn_lap_path_get_tclass( p_path );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__process_cm_lap(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- mad_cm_lap_t *p_lap;\r
- cm_async_mad_t *p_async_mad;\r
- cl_map_item_t *p_map_item;\r
- uint64_t key;\r
- al_conn_t *p_conn;\r
- ib_cm_lap_rec_t lap_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_port_cm = p_async_mad->p_port_cm;\r
- p_lap = (mad_cm_lap_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- key = ((uint64_t)p_lap->remote_comm_id << 32) |\r
- ((uint64_t)p_lap->local_comm_id );\r
- /* Find the connection by local connection ID. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_map_item = cl_qmap_get( &gp_cm->conn_map, key );\r
- if( p_map_item == cl_qmap_end( &gp_cm->conn_map ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("LAP received that could not be matched.\n") );\r
- return;\r
- }\r
-\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_ESTABLISHED:\r
- p_conn->state = CM_CONN_LAP_RCVD;\r
- cl_memclr( &lap_rec, sizeof( ib_cm_lap_rec_t ) );\r
- lap_rec.qp_context = p_conn->h_qp->obj.context;\r
- lap_rec.h_cm_lap = p_conn;\r
- /* Format the path record. */\r
- __format_lap_path_rec( p_conn, p_lap, &lap_rec.alt_path );\r
-\r
- lap_rec.p_lap_pdata = p_lap->pdata;\r
-\r
- /*\r
- * Copy the path record into the connection for use when\r
- * sending the APR and loading the path.\r
- */\r
- cl_memcpy( &p_conn->new_alt_path, &lap_rec.alt_path,\r
- sizeof(ib_path_rec_t) );\r
-\r
- /* Hold on to the connection reference until the user responds. */\r
- __ref_conn( p_conn );\r
- cm_res_release( p_conn );\r
- p_conn->pfn_cm_lap_cb( &lap_rec );\r
- break;\r
-\r
- default:\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("LAP received in invalid state.\n") );\r
- cm_res_release( p_conn );\r
- break;\r
- }\r
-\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-ib_api_status_t\r
-__cm_lap_qp(\r
- IN al_conn_t* const p_conn )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
- ib_ca_attr_t *p_ca_attr;\r
- ib_qp_mod_t qp_mod;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- status = __get_port_attr( &p_conn->new_alt_path.sgid,\r
- p_conn->new_alt_path.slid, &p_port_cm, &p_ca_attr );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_port_attr returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Setup the alt address vector */\r
- cl_memclr( &qp_mod, sizeof( ib_qp_mod_t ) );\r
- qp_mod.state.rts.opts = IB_MOD_QP_ALTERNATE_AV | IB_MOD_QP_APM_STATE;\r
- cm_save_path_av( &p_ca_attr->p_port_attr[p_port_cm->port_idx],\r
- &p_conn->new_alt_path, p_conn->p_req_info->qp_mod_rts.state.rts.retry_cnt,\r
- p_conn->p_req_info->qp_mod_rts.state.rts.rnr_retry_cnt,\r
- &qp_mod.state.rts.alternate_av );\r
-\r
- qp_mod.state.rts.apm_state = IB_APM_REARM;\r
- qp_mod.req_state = IB_QPS_RTS;\r
- status = ib_modify_qp( p_conn->h_qp, &qp_mod );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("ib_modify_qp for LAP returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- cl_memcpy( &p_conn->path[(p_conn->idx_primary + 1) & 0x1],\r
- &p_conn->new_alt_path, sizeof(ib_path_rec_t) );\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-void\r
-__process_cm_apr(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- mad_cm_apr_t *p_apr;\r
- cm_async_mad_t *p_async_mad;\r
- cl_map_item_t *p_map_item;\r
- al_conn_t *p_conn;\r
- uint64_t key;\r
- ib_cm_apr_rec_t apr_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_async_mad = PARENT_STRUCT( p_item, cm_async_mad_t, item );\r
- p_port_cm = p_async_mad->p_port_cm;\r
- p_apr = (mad_cm_apr_t*)p_async_mad->p_mad->p_mad_buf;\r
-\r
- key = ((uint64_t)p_apr->remote_comm_id << 32) |\r
- ((uint64_t)p_apr->local_comm_id );\r
- /* Find the connection by local connection ID. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- p_map_item = cl_qmap_get( &gp_cm->conn_map, key );\r
- if( p_map_item == cl_qmap_end( &gp_cm->conn_map ) )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("APR received that could not be matched.\n") );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
- return;\r
- }\r
-\r
- p_conn = PARENT_STRUCT( p_map_item, al_conn_t, map_item );\r
-\r
- __ref_conn( p_conn );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_LAP_SENT:\r
- /* Cancel sending the LAP. */\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
-\r
- /* Fall through to handle the callback. */\r
- case CM_CONN_LAP_MRA_RCVD:\r
- cl_memclr( &apr_rec, sizeof( ib_cm_apr_rec_t ) );\r
- apr_rec.h_qp = p_conn->h_qp;\r
- apr_rec.qp_context = p_conn->h_qp->obj.context;\r
- apr_rec.p_info = (const uint8_t*)&p_apr->info;\r
- apr_rec.info_length = p_apr->info_len;\r
- apr_rec.p_apr_pdata = p_apr->pdata;\r
- apr_rec.apr_status = p_apr->status;\r
-\r
- if( apr_rec.apr_status == IB_AP_SUCCESS )\r
- {\r
- apr_rec.cm_status = __cm_lap_qp( p_conn );\r
- }\r
- else\r
- {\r
- apr_rec.cm_status = IB_ERROR;\r
- }\r
- cm_res_release( p_conn );\r
- p_conn->pfn_cm_apr_cb( &apr_rec );\r
- break;\r
-\r
- default:\r
- cm_res_release( p_conn );\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("APR received in invalid state.\n") );\r
- break;\r
- }\r
- __deref_conn( p_conn );\r
- ib_put_mad( p_async_mad->p_mad );\r
- cl_free( p_async_mad );\r
-}\r
-\r
-\r
-/*\r
- * Callback to process a disconnection timeout due to not receiving the DREP\r
- * within allowable time.\r
- */\r
-void\r
-__proc_dconn_timeout(\r
- IN cl_async_proc_item_t* p_item )\r
-{\r
- al_conn_t *p_conn;\r
- ib_cm_drep_rec_t drep_rec;\r
- ib_cm_drep_t cm_drep;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_item, al_conn_t, timeout_item );\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_DREQ_SENT:\r
- /* No response. We're done. Deliver a DREP callback. */\r
- cl_memclr( &drep_rec, sizeof( ib_cm_drep_rec_t ) );\r
- drep_rec.h_qp = p_conn->h_qp;\r
- drep_rec.qp_context = p_conn->h_qp->obj.context;\r
- drep_rec.cm_status = IB_TIMEOUT;\r
-\r
- /*\r
- * Format a DREP message in case a DREQ is received while\r
- * the connection is in the timewait state.\r
- */\r
- cl_memclr( &cm_drep, sizeof( ib_cm_drep_t ) );\r
- __format_mad_drep( &cm_drep, p_conn );\r
-\r
- /*\r
- * Release the connection now. Note that we still hold a reference\r
- * on p_conn from trying to send the DREQ.\r
- */\r
- __conn_release( p_conn );\r
- cm_res_release( p_conn );\r
-\r
- /* Call the user back. */\r
- p_conn->pfn_cm_drep_cb( &drep_rec );\r
- break;\r
-\r
- default:\r
- /*\r
- * Something changed the state. The DREP was likely received\r
- * after timing out, but before we could process the timeout.\r
- */\r
- cm_res_release( p_conn );\r
- break;\r
- }\r
-\r
- /* Release the reference taken when sending. */\r
- __deref_conn( p_conn );\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * Formats a REQ mad's path information given a path record.\r
- */\r
-ib_api_status_t\r
-__format_mad_req_path(\r
- IN const ib_path_rec_t* const p_path_rec,\r
- IN const uint8_t ack_delay,\r
- OUT req_path_info_t* const p_req_path )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_req_path->local_lid = p_path_rec->slid;\r
- p_req_path->remote_lid = p_path_rec->dlid;\r
- p_req_path->local_gid = p_path_rec->sgid;\r
- p_req_path->remote_gid = p_path_rec->dgid;\r
-\r
- conn_req_path_set_flow_lbl( ib_path_rec_flow_lbl( p_path_rec ),\r
- p_req_path );\r
- conn_req_path_set_pkt_rate( ib_path_rec_rate( p_path_rec ),\r
- p_req_path );\r
-\r
- /* Traffic class & hop limit */\r
- p_req_path->traffic_class = p_path_rec->tclass;\r
- p_req_path->hop_limit = ib_path_rec_hop_limit( p_path_rec );\r
-\r
- /* SL & Subnet Local fields */\r
- conn_req_path_set_svc_lvl( ib_path_rec_sl( p_path_rec ),\r
- p_req_path );\r
- conn_req_path_set_subn_lcl(\r
- ib_gid_is_link_local( &p_path_rec->dgid ), p_req_path );\r
-\r
- conn_req_path_set_lcl_ack_timeout( cm_local_ack_timeout(\r
- ib_path_rec_pkt_life( p_path_rec ), ack_delay ), p_req_path );\r
-\r
- conn_req_path_clr_rsvd_fields( p_req_path );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-ib_api_status_t\r
-__format_mad_req(\r
- IN const ib_cm_req_t* const p_cm_req,\r
- IN OUT al_conn_t* const p_conn )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( p_cm_req->p_alt_path )\r
- {\r
- /* MTUs must match since they are specified only once. */\r
- if( ib_path_rec_mtu( p_cm_req->p_primary_path ) !=\r
- ib_path_rec_mtu( p_cm_req->p_alt_path ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Mismatched primary and alternate path MTUs.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- /* Format the alternate path. */\r
- status = __format_mad_req_path( p_cm_req->p_alt_path,\r
- p_conn->p_req_info->p_ca_attr->local_ack_delay,\r
- &p_conn->mads.req.alternate_path );\r
-\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_EXIT( AL_DBG_ERROR );\r
- return status;\r
- }\r
- }\r
- else\r
- {\r
- cl_memclr( &p_conn->mads.req.alternate_path,\r
- sizeof( req_path_info_t ) );\r
- }\r
-\r
- /* Format the primary path. */\r
- status = __format_mad_req_path( p_cm_req->p_primary_path,\r
- p_conn->p_req_info->p_ca_attr->local_ack_delay,\r
- &p_conn->mads.req.primary_path );\r
-\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_EXIT( AL_DBG_ERROR );\r
- return status;\r
- }\r
-\r
- /* Format the rest of the REQ. */\r
- p_conn->mads.req.hdr.attr_id = CM_REQ_ATTR_ID;\r
- p_conn->mads.req.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.req.sid = p_cm_req->svc_id;\r
- p_conn->mads.req.local_ca_guid = p_cm_req->h_qp->obj.p_ci_ca->verbs.guid;\r
-\r
- conn_req_set_lcl_qpn( p_conn->p_req_info->local_qpn, &p_conn->mads.req );\r
- conn_req_set_resp_res( p_cm_req->resp_res, &p_conn->mads.req );\r
- conn_req_set_init_depth( p_cm_req->init_depth, &p_conn->mads.req );\r
- conn_req_set_remote_resp_timeout( p_cm_req->remote_resp_timeout,\r
- &p_conn->mads.req );\r
- conn_req_set_qp_type( p_cm_req->h_qp->type, &p_conn->mads.req );\r
- conn_req_set_flow_ctrl( p_cm_req->flow_ctrl, &p_conn->mads.req );\r
- conn_req_set_starting_psn( p_conn->p_req_info->local_qpn,\r
- &p_conn->mads.req );\r
-\r
- conn_req_set_lcl_resp_timeout( p_cm_req->local_resp_timeout,\r
- &p_conn->mads.req );\r
- conn_req_set_retry_cnt( p_cm_req->retry_cnt, &p_conn->mads.req );\r
-\r
- p_conn->mads.req.pkey = p_cm_req->p_primary_path->pkey;\r
-\r
- conn_req_set_mtu( ib_path_rec_mtu( p_cm_req->p_primary_path ),\r
- &p_conn->mads.req );\r
- conn_req_set_rnr_retry_cnt( p_cm_req->rnr_retry_cnt,\r
- &p_conn->mads.req );\r
-\r
- conn_req_set_max_cm_retries( p_cm_req->max_cm_retries, &p_conn->mads.req );\r
- conn_req_set_pdata(p_cm_req->p_req_pdata, p_cm_req->req_length,\r
- &p_conn->mads.req );\r
-\r
- conn_req_clr_rsvd_fields( &p_conn->mads.req );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-ib_api_status_t\r
-__format_mad_rep(\r
- IN const ib_cm_rep_t* const p_cm_rep,\r
- IN OUT al_conn_t* const p_conn )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn->mads.rep.hdr.attr_id = CM_REP_ATTR_ID;\r
- p_conn->mads.rep.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.rep.remote_comm_id = p_conn->remote_comm_id;\r
- conn_rep_set_lcl_qpn( p_conn->p_req_info->local_qpn, &p_conn->mads.rep );\r
- conn_rep_set_starting_psn( p_conn->p_req_info->local_qpn,\r
- &p_conn->mads.rep );\r
-\r
- /* Check the CA's responder resource max and trim if necessary. */\r
- if( (p_conn->p_req_info->p_ca_attr->max_qp_resp_res <\r
- p_conn->p_req_info->qp_mod_rtr.state.rtr.resp_res) )\r
- {\r
- /*\r
- * The CA cannot handle the requested responder resources.\r
- * Set the response to the CA's maximum.\r
- */\r
- p_conn->mads.rep.resp_resources = \r
- p_conn->p_req_info->p_ca_attr->max_qp_resp_res;\r
- }\r
- else\r
- {\r
- /* The CA supports the requested responder resources. */\r
- p_conn->mads.rep.resp_resources = \r
- p_conn->p_req_info->qp_mod_rtr.state.rtr.resp_res;\r
- }\r
-\r
- p_conn->mads.rep.initiator_depth = p_cm_rep->init_depth;\r
-\r
- conn_rep_set_target_ack_delay( p_cm_rep->target_ack_delay,\r
- &p_conn->mads.rep );\r
- conn_rep_set_failover( p_cm_rep->failover_accepted, &p_conn->mads.rep );\r
- conn_rep_set_e2e_flow_ctl( p_cm_rep->flow_ctrl, &p_conn->mads.rep );\r
-\r
- conn_rep_set_rnr_retry_cnt( (uint8_t)(p_cm_rep->rnr_retry_cnt & 0x07),\r
- &p_conn->mads.rep );\r
-\r
- p_conn->mads.rep.local_ca_guid = p_conn->p_req_info->p_ca_attr->ca_guid;\r
-\r
- conn_rep_set_pdata( p_cm_rep->p_rep_pdata, p_cm_rep->rep_length,\r
- &p_conn->mads.rep );\r
-\r
- conn_rep_clr_rsvd_fields( &p_conn->mads.rep );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-void\r
-__format_mad_rtu(\r
- IN const ib_cm_rtu_t* const p_cm_rtu,\r
- IN OUT al_conn_t* const p_conn )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn->mads.rtu.hdr.attr_id = CM_RTU_ATTR_ID;\r
-\r
- p_conn->mads.rtu.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.rtu.remote_comm_id = p_conn->remote_comm_id;\r
-\r
- /* copy optional data */\r
- conn_rtu_set_pdata( p_cm_rtu->p_rtu_pdata, p_cm_rtu->rtu_length,\r
- &p_conn->mads.rtu );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-ib_rej_status_t\r
-__conn_save_user_rep(\r
- IN const ib_cm_rep_t* const p_cm_rep,\r
- IN OUT al_conn_t* const p_conn,\r
- OUT cm_port_agent_t** const pp_port_cm )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Cache the local QPN. */\r
- p_conn->p_req_info->local_qpn = p_conn->h_qp->num;\r
-\r
- /* Validate the primary path. This must be done after binding. */\r
- status = __validate_primary_path( p_conn, pp_port_cm );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Primary path validation failed: %s.\n", ib_get_err_str(status)) );\r
-\r
- /* Reject and abort the connection. */\r
- return IB_REJ_INVALID_GID;\r
- }\r
-\r
- if( p_cm_rep->failover_accepted == IB_FAILOVER_ACCEPT_SUCCESS )\r
- {\r
- status = __validate_alt_path( p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Alternate path validation failed: %s.\n",\r
- ib_get_err_str(status)) );\r
-\r
- return IB_REJ_INVALID_ALT_GID;\r
- }\r
- }\r
-\r
- /* Update conn info */\r
- p_conn->p_req_info->pfn_cm_rtu_cb = p_cm_rep->pfn_cm_rtu_cb;\r
- p_conn->pfn_cm_lap_cb = p_cm_rep->pfn_cm_lap_cb;\r
- p_conn->pfn_cm_dreq_cb = p_cm_rep->pfn_cm_dreq_cb;\r
-\r
- if( p_cm_rep->qp_type != p_conn->p_req_info->xport_type )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("qp_type mistach!\n") );\r
- return IB_REJ_INVALID_XPORT;\r
- }\r
-\r
- cm_save_rep_qp_attr( p_cm_rep,\r
- &p_conn->p_req_info->p_ca_attr->p_port_attr[\r
- p_conn->p_req_info->port_idx], &p_conn->path[0],\r
- &p_conn->path[1], &p_conn->p_req_info->qp_mod_rtr,\r
- &p_conn->p_req_info->qp_mod_rts );\r
-\r
- /* Update the QP destruction timeout = timeout x retries + 2 seconds. */\r
- set_al_obj_timeout( &p_cm_rep->h_qp->obj,\r
- p_conn->retry_timeout * p_conn->max_cm_retries + 2000 );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return 0;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__conn_save_user_rtu(\r
- IN const ib_cm_rtu_t* const p_cm_rtu,\r
- IN OUT al_conn_t* const p_conn,\r
- OUT cm_port_agent_t** const pp_port_cm )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- status = __get_port_attr( &p_conn->path[0].sgid, p_conn->path[0].slid,\r
- pp_port_cm, NULL );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_port_attr returned %s.\n", ib_get_err_str(status)) );\r
- /*\r
- * Don't fail the connection since the remote side will repeat\r
- * the REP perhaps with better luck.\r
- */\r
- return status;\r
- }\r
-\r
- p_conn->pfn_cm_apr_cb = p_cm_rtu->pfn_cm_apr_cb;\r
- p_conn->pfn_cm_dreq_cb = p_cm_rtu->pfn_cm_dreq_cb;\r
-\r
- cm_save_rtu_qp_attr( p_cm_rtu, &p_conn->p_req_info->qp_mod_rtr );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Formats a LAP mad's path information given a path record.\r
- */\r
-ib_api_status_t\r
-__format_mad_lap_path(\r
- IN const ib_path_rec_t* const p_path_rec,\r
- IN const uint8_t ack_delay,\r
- OUT lap_path_info_t* const p_lap_path )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_lap_path->local_lid = p_path_rec->slid;\r
- p_lap_path->remote_lid = p_path_rec->dlid;\r
- p_lap_path->local_gid = p_path_rec->sgid;\r
- p_lap_path->remote_gid = p_path_rec->dgid;\r
-\r
- /* Set Flow Label and Packet Rate */\r
- conn_lap_path_set_flow_lbl( ib_path_rec_flow_lbl( p_path_rec ),\r
- p_lap_path );\r
- conn_lap_path_set_tclass( p_path_rec->tclass, p_lap_path );\r
-\r
- p_lap_path->hop_limit = ib_path_rec_hop_limit( p_path_rec );\r
- conn_lap_path_set_pkt_rate( ib_path_rec_rate( p_path_rec ),\r
- p_lap_path );\r
-\r
- /* Set SL and Subnet Local */\r
- conn_lap_path_set_svc_lvl( ib_path_rec_sl( p_path_rec ),\r
- p_lap_path );\r
- conn_lap_path_set_subn_lcl(\r
- ib_gid_is_link_local( &p_path_rec->dgid ), p_lap_path );\r
-\r
- conn_lap_path_set_lcl_ack_timeout(\r
- cm_local_ack_timeout( ib_path_rec_pkt_life( p_path_rec ),\r
- ack_delay ), p_lap_path );\r
-\r
- conn_lap_path_clr_rsvd_fields( p_lap_path );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Validates the primary path specified for a connection, and stores\r
- * the assocated CA attributes and port index.\r
- */\r
-ib_api_status_t\r
-__validate_lap_path(\r
- IN al_conn_t* const p_conn,\r
- OUT cm_port_agent_t** const pp_port_cm,\r
- OUT uint8_t* const p_ack_delay )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
- ib_ca_attr_t *p_ca_attr, *p_alt_ca_attr;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- CL_ASSERT( p_conn );\r
- CL_ASSERT( pp_port_cm );\r
- CL_ASSERT( p_ack_delay );\r
-\r
- status = __get_port_attr( &p_conn->path[p_conn->idx_primary].sgid,\r
- p_conn->path[p_conn->idx_primary].slid, &p_port_cm, &p_ca_attr );\r
- if( status != IB_SUCCESS )\r
- {\r
- /* Primary path invalid. Should have received a path migration. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Primary connection path invalid!\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- status = __get_port_attr( &p_conn->new_alt_path.sgid,\r
- p_conn->new_alt_path.slid, NULL, &p_alt_ca_attr );\r
- if( status != IB_SUCCESS )\r
- {\r
- /* Alternate path invalid. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Alternate path invalid!\n") );\r
- return status;\r
- }\r
-\r
- /*\r
- * The primary path and alternate path are both valid individually.\r
- * Check that they are valid as a pair.\r
- */\r
- if( p_ca_attr->ca_guid != p_alt_ca_attr->ca_guid )\r
- {\r
- /* Paths are not on the same CA. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Primary and alternate paths must be on the same CA.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- *p_ack_delay = p_ca_attr->local_ack_delay;\r
- *pp_port_cm = p_port_cm;\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-void\r
-__format_mad_dreq(\r
- IN const ib_cm_dreq_t* const p_cm_dreq,\r
- OUT al_conn_t* const p_conn )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn->mads.dreq.hdr.attr_id = CM_DREQ_ATTR_ID;\r
- p_conn->mads.dreq.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.dreq.remote_comm_id = p_conn->remote_comm_id;\r
- conn_dreq_set_remote_qpn( p_conn->remote_qpn, &p_conn->mads.dreq );\r
- conn_dreq_set_pdata( p_cm_dreq->p_dreq_pdata, p_cm_dreq->dreq_length,\r
- &p_conn->mads.dreq );\r
- conn_dreq_clr_rsvd_fields( &p_conn->mads.dreq );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-void\r
-__format_mad_drep(\r
- IN const ib_cm_drep_t* const p_cm_drep,\r
- OUT al_conn_t* const p_conn )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn->mads.drep.hdr.attr_id = CM_DREP_ATTR_ID;\r
- p_conn->mads.drep.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.drep.remote_comm_id = p_conn->remote_comm_id;\r
- conn_drep_set_pdata( p_cm_drep->p_drep_pdata, p_cm_drep->drep_length,\r
- &p_conn->mads.drep );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_mra(\r
- IN al_conn_t* const p_conn,\r
- IN const ib_cm_mra_t* const p_cm_mra )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_RCVD:\r
- p_conn->state = CM_CONN_REQ_MRA_SENT;\r
- case CM_CONN_REQ_MRA_SENT:\r
- conn_mra_set_msg_mraed( 0, &p_conn->mads.mra );\r
- break;\r
-\r
- case CM_CONN_REP_RCVD:\r
- p_conn->state = CM_CONN_REP_MRA_SENT;\r
- case CM_CONN_REP_MRA_SENT:\r
- conn_mra_set_msg_mraed( 1, &p_conn->mads.mra );\r
- break;\r
-\r
- case CM_CONN_LAP_RCVD:\r
- p_conn->state = CM_CONN_LAP_MRA_SENT;\r
- case CM_CONN_LAP_MRA_SENT:\r
- conn_mra_set_msg_mraed( 2, &p_conn->mads.mra );\r
- break;\r
-\r
- default:\r
- /* Invalid state. Fail the request. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid state for sending MRA.\n") );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- p_conn->mads.mra.hdr.attr_id = CM_MRA_ATTR_ID;\r
-\r
- p_conn->mads.mra.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.mra.remote_comm_id = p_conn->remote_comm_id;\r
- conn_mra_set_svc_timeout( p_cm_mra->svc_timeout, &p_conn->mads.mra );\r
- status = conn_mra_set_pdata( p_cm_mra->p_mra_pdata, p_cm_mra->mra_length,\r
- &p_conn->mads.mra );\r
- if( status != IB_SUCCESS )\r
- return status;\r
- conn_mra_clr_rsvd_fields( &p_conn->mads.mra );\r
-\r
- status = __get_port_attr( &p_conn->path[p_conn->idx_primary].sgid,\r
- p_conn->path[p_conn->idx_primary].slid, &p_port_cm, NULL );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_port_attr for MRA returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- status = __cm_send( p_port_cm, p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Abort a connection.\r
- */\r
-void\r
-__conn_abort(\r
- IN al_conn_t* const p_conn )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_SENT:\r
- case CM_CONN_REP_SENT:\r
- case CM_CONN_REQ_MRA_SENT:\r
- case CM_CONN_REP_MRA_SENT:\r
- case CM_CONN_REQ_RCVD:\r
- case CM_CONN_REQ_MRA_RCVD:\r
- case CM_CONN_REP_RCVD:\r
- case CM_CONN_REP_MRA_RCVD:\r
- /* Remove from the pending list. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_remove_item( &gp_cm->pending_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- __cm_conn_unbind( p_conn, 0 );\r
-\r
- p_conn->state = CM_CONN_RESET;\r
- /*\r
- * Dereference the connection so that it will make its way back\r
- * to the connection pool.\r
- */\r
- __deref_conn( p_conn );\r
- break;\r
-\r
- case CM_CONN_ESTABLISHED:\r
- /* Remove from the connection map. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qmap_remove_item( &gp_cm->conn_map, &p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- __cm_conn_unbind( p_conn, 0 );\r
-\r
- p_conn->state = CM_CONN_RESET;\r
- /*\r
- * Dereference the connection so that it will make its way back\r
- * to the connection pool.\r
- */\r
- __deref_conn( p_conn );\r
- break;\r
-\r
- default:\r
- /* Should never get here. */\r
- break;\r
- }\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Release the connection request information. This function must be called\r
- * while holding the global CM lock.\r
- */\r
-void\r
-__release_req_info(\r
- IN al_conn_t* const p_conn )\r
-{\r
- if( !p_conn->p_req_info )\r
- return;\r
-\r
- if( p_conn->p_req_info->p_compare_buffer )\r
- cl_free( p_conn->p_req_info->p_compare_buffer );\r
-\r
- cl_pool_put( &gp_cm->req_pool, p_conn->p_req_info );\r
- p_conn->p_req_info = NULL;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__conn_save_user_req(\r
- IN const ib_cm_req_t* const p_cm_req,\r
- OUT al_conn_t* const p_conn,\r
- OUT cm_port_agent_t** const pp_port_cm )\r
-{\r
- ib_api_status_t status;\r
- struct _qp_rtr *p_rtr;\r
- struct _qp_rts *p_rts;\r
- uint8_t pkt_life;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Format the request info. */\r
- p_conn->p_req_info->svc_id = p_cm_req->svc_id;\r
- p_conn->p_req_info->pfn_cm_req_cb = p_cm_req->pfn_cm_req_cb;\r
- p_conn->p_req_info->pfn_cm_rep_cb = p_cm_req->pfn_cm_rep_cb;\r
- p_conn->pfn_cm_rej_cb = p_cm_req->pfn_cm_rej_cb;\r
-\r
- if( p_cm_req->p_compare_buffer )\r
- {\r
- /* Allocate a compare buffer. */\r
- p_conn->p_req_info->p_compare_buffer =\r
- (uint8_t*)cl_malloc( p_cm_req->compare_length );\r
- if( !p_conn->p_req_info->p_compare_buffer )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cl_malloc failed for compare buffer [%d bytes].\n",\r
- p_cm_req->compare_length) );\r
- return IB_INSUFFICIENT_MEMORY;\r
- }\r
-\r
- /* Copy the compare buffer contents. */\r
- cl_memcpy( p_conn->p_req_info->p_compare_buffer,\r
- p_cm_req->p_compare_buffer, p_cm_req->compare_length );\r
-\r
- /* Store the size and offset of the compare data. */\r
- p_conn->p_req_info->compare_length = p_cm_req->compare_length;\r
- p_conn->p_req_info->compare_offset = p_cm_req->compare_offset;\r
- }\r
- else\r
- {\r
- p_conn->p_req_info->p_compare_buffer = NULL;\r
- p_conn->p_req_info->compare_length = 0;\r
- p_conn->p_req_info->compare_offset = 0;\r
- }\r
-\r
- /* Store the transport type and local CA guid. */\r
- p_conn->p_req_info->xport_type = p_cm_req->qp_type;\r
-\r
- /* Copy the paths. */\r
- p_conn->idx_primary = 0;\r
- cl_memcpy( &p_conn->path[0], p_cm_req->p_primary_path,\r
- sizeof(ib_path_rec_t) );\r
- if( p_cm_req->p_alt_path )\r
- {\r
- cl_memcpy( &p_conn->path[1], p_cm_req->p_alt_path,\r
- sizeof(ib_path_rec_t) );\r
- }\r
-\r
- /* Store the pointer to the MRA callback. */\r
- p_conn->pfn_cm_mra_cb = p_cm_req->pfn_cm_mra_cb;\r
-\r
- /*\r
- * Calculate the retry timeout.\r
- * All timeout values in micro seconds are expressed as 4.096 * 2^x,\r
- * where x is the timeout. This approximates to 2^(x+2).\r
- * Since we want milliseconds, we can further approximate to 2^(x-8).\r
- * This results in a timeout that is roughly 5% on the low side, but\r
- * good enough since OS timer resolutions are ~10ms.\r
- */\r
- if( p_cm_req->remote_resp_timeout > 8 )\r
- p_conn->retry_timeout = 1 << (p_cm_req->remote_resp_timeout - 8);\r
- else\r
- p_conn->retry_timeout = 0;\r
-\r
- /* Add twice the packet lifetime. */\r
- pkt_life = ib_path_rec_pkt_life( &p_conn->path[p_conn->idx_primary] );\r
- if( pkt_life > 8 )\r
- p_conn->retry_timeout += 2 << (pkt_life - 8);\r
-\r
- /* Minimum 10 ms timeout - picked to match typical OS timer resolution. */\r
- if( p_conn->retry_timeout < 10 )\r
- p_conn->retry_timeout = 10;\r
-\r
- /* Store the retry count. */\r
- p_conn->max_cm_retries = p_cm_req->max_cm_retries;\r
- p_conn->p_req_info->max_cm_retries = p_cm_req->max_cm_retries;\r
-\r
- /* Update the QP destruction timeout = timeout x retries + 2 seconds. */\r
- set_al_obj_timeout( &p_cm_req->h_qp->obj,\r
- p_conn->retry_timeout * p_conn->max_cm_retries + 2000 );\r
-\r
- /* Validate the primary path. The QP must be bound by now. */\r
- status = __validate_primary_path( p_conn, pp_port_cm );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Primary path validation failure: %s\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* validate alt path */\r
- if( p_cm_req->p_alt_path )\r
- {\r
- status = __validate_alt_path( p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Alternate path validation failure: %s\n",\r
- ib_get_err_str(status)) );\r
- return status;\r
- }\r
- }\r
-\r
- /* Cache the local QPN. */\r
- p_conn->p_req_info->local_qpn = p_conn->h_qp->num;\r
-\r
- /* format qp state information */\r
- p_rtr = &p_conn->p_req_info->qp_mod_rtr.state.rtr;\r
- p_rts = &p_conn->p_req_info->qp_mod_rts.state.rts;\r
-\r
- /* Format RTR info */\r
-\r
- /* these values are set with an incoming REP and successive RTU\r
- p_rtr->dest_qp = 0;\r
- p_rtr->sq_depth = 0;\r
- p_rtr->rq_depth = 0;\r
- */\r
-\r
- p_rtr->resp_res = p_cm_req->resp_res;\r
- p_rtr->qkey = 0;\r
- p_rtr->rq_psn = p_conn->p_req_info->local_qpn;\r
-\r
- p_rtr->access_ctrl = 0;\r
- p_rtr->rnr_nak_timeout = p_cm_req->rnr_nak_timeout;\r
-\r
- /* format RTS info */\r
- p_rts->retry_cnt = p_cm_req->retry_cnt;\r
- p_rts->rnr_retry_cnt = p_cm_req->rnr_retry_cnt;\r
- p_rts->local_ack_timeout = cm_local_ack_timeout(\r
- ib_path_rec_pkt_life( p_cm_req->p_primary_path ), 0 );\r
- p_rts->init_depth = p_cm_req->init_depth;\r
-\r
- p_rts->qkey = 0;\r
- p_rts->access_ctrl = 0;\r
- p_rts->resp_res = p_cm_req->resp_res;\r
- p_rts->primary_port = (uint8_t)(p_conn->p_req_info->port_idx + 1);\r
-\r
- /* set in the wire rep\r
- p_rts->sq_psn = 0;\r
- */\r
-\r
- /* Setup primary and alternate AV's for RTR */\r
- cm_save_path_av( &p_conn->p_req_info->p_ca_attr->p_port_attr[\r
- p_conn->p_req_info->port_idx], &p_conn->path[0],\r
- p_conn->p_req_info->qp_mod_rts.state.rts.retry_cnt,\r
- p_conn->p_req_info->qp_mod_rts.state.rts.rnr_retry_cnt,\r
- &p_rtr->primary_av );\r
-\r
- if( p_conn->path[1].slid )\r
- {\r
- p_rtr->opts |= IB_MOD_QP_ALTERNATE_AV;\r
- cm_save_path_av( &p_conn->p_req_info->p_ca_attr->p_port_attr[\r
- p_conn->p_req_info->port_idx], &p_conn->path[1],\r
- p_conn->p_req_info->qp_mod_rts.state.rts.retry_cnt,\r
- p_conn->p_req_info->qp_mod_rts.state.rts.rnr_retry_cnt,\r
- &p_rtr->alternate_av );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Associate the connection object and the QP with each other.\r
- */\r
-void\r
-__bind_qp(\r
- IN al_conn_t* const p_conn,\r
- IN const ib_qp_handle_t h_qp )\r
-{\r
- CL_ASSERT( p_conn && h_qp );\r
-\r
- /* Setup the mutual references between the connection object and the QP. */\r
- p_conn->h_qp = h_qp;\r
- ref_al_obj( &h_qp->obj );\r
- ((al_conn_qp_t*)h_qp)->p_conn = p_conn;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__cm_pre_req(\r
- IN OUT al_conn_t* const p_conn )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Exported QPs have done this in user-mode already. */\r
- if( AL_OBJ_IS_SUBTYPE( p_conn->h_qp, AL_OBJ_SUBTYPE_UM_EXPORT ) )\r
- {\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
- }\r
-\r
- /*\r
- * Warning! Using all access rights. We need to modify\r
- * the ib_cm_req_t to include this.\r
- */\r
- status = cm_init_qp( p_conn->h_qp, &p_conn->path[0].sgid,\r
- p_conn->path[0].pkey,\r
- (IB_AC_RDMA_READ | IB_AC_RDMA_WRITE | IB_AC_LOCAL_WRITE | IB_AC_MW_BIND) );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cm_init_qp returned %s\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__cm_pre_rep(\r
- IN OUT al_conn_t* const p_conn,\r
- IN OUT const ib_cm_rep_t* p_cm_rep )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Exported QPs have done this in user-mode already. */\r
- if( AL_OBJ_IS_SUBTYPE( p_conn->h_qp, AL_OBJ_SUBTYPE_UM_EXPORT ) )\r
- {\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
- }\r
-\r
- /* Transition the QP to the INIT state. */\r
- status = cm_init_qp( p_conn->h_qp, &p_conn->path[0].sgid,\r
- p_conn->path[0].pkey, p_cm_rep->access_ctrl );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cm_init_qp returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Prepost receives. */\r
- if( p_cm_rep->p_recv_wr )\r
- {\r
- status = ib_post_recv( p_cm_rep->h_qp, p_cm_rep->p_recv_wr,\r
- p_cm_rep->pp_recv_failure );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("ib_post_recv returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
- }\r
-\r
- /* Transition the QP to the RTR and RTS states. */\r
- status = cm_rts_qp( p_conn->h_qp, &p_conn->p_req_info->qp_mod_rtr,\r
- &p_conn->p_req_info->qp_mod_rts );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cm_rts_qp returned %s.\n", ib_get_err_str(status)) );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-__cm_pre_rtu(\r
- IN OUT al_conn_t* const p_conn )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Exported QPs have done this in user-mode already. */\r
- if( AL_OBJ_IS_SUBTYPE( p_conn->h_qp, AL_OBJ_SUBTYPE_UM_EXPORT ) )\r
- {\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
- }\r
-\r
- status = cm_rts_qp( p_conn->h_qp, &p_conn->p_req_info->qp_mod_rtr,\r
- &p_conn->p_req_info->qp_mod_rts );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cm_rts_qp returned %s.\n", ib_get_err_str(status)) );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_listen(\r
- IN const ib_al_handle_t h_al,\r
- IN const ib_cm_listen_t* const p_cm_listen,\r
- IN const ib_pfn_listen_err_cb_t pfn_listen_err_cb,\r
- IN const void* const listen_context,\r
- OUT ib_listen_handle_t* const ph_cm_listen )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( AL_OBJ_INVALID_HANDLE( h_al, AL_OBJ_TYPE_H_AL ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_AL_HANDLE\n") );\r
- return IB_INVALID_AL_HANDLE;\r
- }\r
- if( !p_cm_listen || !pfn_listen_err_cb || !ph_cm_listen )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- status = cm_listen(h_al, p_cm_listen, pfn_listen_err_cb, listen_context,\r
- ph_cm_listen );\r
-\r
- /* Release the reference taken in init_al_obj. */\r
- if( status == IB_SUCCESS )\r
- deref_al_obj( &(*ph_cm_listen)->obj );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-ib_api_status_t\r
-ib_cm_rej(\r
- IN const ib_cm_handle_t h_cm,\r
- IN const ib_cm_rej_t* const p_cm_rej )\r
-{\r
- ib_api_status_t status;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !h_cm )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
- if( !p_cm_rej )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- p_conn = h_cm;\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- status = cm_conn_rej( h_cm, p_cm_rej );\r
-\r
- cm_res_release( p_conn );\r
-\r
- /* Release the reference taken for the callback. */\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_mra(\r
- IN const ib_cm_handle_t h_cm,\r
- IN const ib_cm_mra_t* const p_cm_mra )\r
-{\r
- ib_api_status_t status;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !h_cm )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
- if( !p_cm_mra )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- p_conn = h_cm;\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- status = cm_conn_mra( p_conn, p_cm_mra );\r
-\r
- cm_res_release( p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__cm_send for MRA returned %s.\n", ib_get_err_str(status)) );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_req(\r
- IN const ib_cm_req_t* const p_cm_req )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !p_cm_req )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- /* Validate inputs */\r
- if( ( p_cm_req->p_req_pdata ) &&\r
- ( p_cm_req->req_length > IB_REQ_PDATA_SIZE ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid pdata length.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- /* Make sure there's a primary path. Both SIDR and CM require it. */\r
- if( !p_cm_req->p_primary_path )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid primary path record.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- /* Only supported qp types allowed */\r
- switch( p_cm_req->qp_type )\r
- {\r
- default:\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid qp_type.\n") );\r
- return IB_INVALID_SETTING;\r
-\r
- case IB_QPT_RELIABLE_CONN:\r
- case IB_QPT_UNRELIABLE_CONN:\r
- if( AL_OBJ_INVALID_HANDLE( p_cm_req->h_qp, AL_OBJ_TYPE_H_QP ) ||\r
- (p_cm_req->h_qp->type != p_cm_req->qp_type) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_QP_HANDLE\n") );\r
- return IB_INVALID_QP_HANDLE;\r
- }\r
-\r
- status = cm_conn_req( qp_get_al( p_cm_req->h_qp ), p_cm_req );\r
- break;\r
-\r
- case IB_QPT_UNRELIABLE_DGRM:\r
- if( AL_OBJ_INVALID_HANDLE( p_cm_req->h_al, AL_OBJ_TYPE_H_AL ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_AL_HANDLE\n") );\r
- return IB_INVALID_AL_HANDLE;\r
- }\r
- status = cm_sidr_req( p_cm_req->h_al, p_cm_req );\r
- break;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_rep(\r
- IN const ib_cm_handle_t h_cm_req,\r
- IN const ib_cm_rep_t* const p_cm_rep )\r
-{\r
- ib_api_status_t status;\r
- ib_al_handle_t h_al;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !h_cm_req )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
- if( !p_cm_rep )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- p_conn = h_cm_req;\r
-\r
- /*\r
- * NOTE: The connection object has a reference taken on it before\r
- * the REQ callback that is released when the user calls either REP\r
- * or REJ. We must release this reference count before returning.\r
- */\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- /* Validate inputs */\r
- if( ( p_cm_rep->p_rep_pdata ) &&\r
- ( p_cm_rep->rep_length > IB_REP_PDATA_SIZE ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid pdata length.\n") );\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- /* Make sure we are in the correct state. */\r
- if( p_conn->state != CM_CONN_REQ_RCVD &&\r
- p_conn->state != CM_CONN_REQ_MRA_SENT )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Connection in invalid state (%d).\n", p_conn->state) );\r
- /*\r
- * The connection will get cleaned up by whatever put it in the\r
- * invalid state.\r
- */\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- /* Only supported qp types allowed */\r
- status = IB_SUCCESS;\r
- switch( p_cm_rep->qp_type )\r
- {\r
- default:\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid qp_type.\n") );\r
- status = IB_INVALID_SETTING;\r
- break;\r
-\r
- case IB_QPT_RELIABLE_CONN:\r
- case IB_QPT_UNRELIABLE_CONN:\r
- if( AL_OBJ_INVALID_HANDLE( p_cm_rep->h_qp, AL_OBJ_TYPE_H_QP ) ||\r
- (p_cm_rep->h_qp->type != p_cm_rep->qp_type) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_QP_HANDLE\n") );\r
- status = IB_INVALID_QP_HANDLE;\r
- }\r
- break;\r
-\r
- case IB_QPT_UNRELIABLE_DGRM:\r
- if( ( p_cm_rep->status == IB_SIDR_SUCCESS ) &&\r
- (AL_OBJ_INVALID_HANDLE( p_cm_rep->h_qp, AL_OBJ_TYPE_H_QP ) ||\r
- (p_cm_rep->h_qp->type != p_cm_rep->qp_type) ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_QP_HANDLE\n") );\r
- status = IB_INVALID_QP_HANDLE;\r
- }\r
- break;\r
- }\r
-\r
- if( status != IB_SUCCESS )\r
- {\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
- }\r
-\r
- /*\r
- * Migrating connection object to AL instance associated with the QP.\r
- * This is required for SDP, which may want to listen on one instance\r
- * and connect on another.\r
- */\r
- h_al = qp_get_al( p_cm_rep->h_qp );\r
- if( p_conn->h_al != h_al )\r
- {\r
- al_remove_conn( p_conn );\r
- al_insert_conn( h_al, p_conn );\r
- }\r
-\r
- if( p_conn->qp_type == IB_QPT_UNRELIABLE_DGRM )\r
- status = cm_sidr_rep( p_conn, p_cm_rep );\r
- else\r
- status = cm_conn_rep( p_conn, p_cm_rep );\r
-\r
- /*\r
- * The lock is held until the MAD contents are set to avoid a retransmit\r
- * of garbage data.\r
- */\r
- cm_res_release( p_conn );\r
-\r
- /* Release the reference taken for the callback. */\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_rtu(\r
- IN const ib_cm_handle_t h_cm_rep,\r
- IN const ib_cm_rtu_t* const p_cm_rtu )\r
-{\r
- al_conn_t *p_conn;\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !h_cm_rep )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
- if( !p_cm_rtu )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- p_conn = h_cm_rep;\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- /* Validate inputs */\r
- if( ( p_cm_rtu->p_rtu_pdata ) &&\r
- ( p_cm_rtu->rtu_length > IB_RTU_PDATA_SIZE ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid pdata length.\n") );\r
-\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- if( p_conn->state != CM_CONN_REP_RCVD &&\r
- p_conn->state != CM_CONN_REP_MRA_SENT )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Connection in invalid state (%d) for RTU.\n", p_conn->state) );\r
-\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- /*\r
- * Call invalid if event is still processed.\r
- * User may have called rtu in rep callback.\r
- */\r
- if( p_conn->p_sync_event )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Connection in invalid state. Sync call in progress.\n" ) );\r
-\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- status = cm_conn_rtu( p_conn, p_cm_rtu );\r
-\r
- cm_res_release( p_conn );\r
-\r
- /* Release the reference taken for the callback. */\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_dreq(\r
- IN const ib_cm_dreq_t* const p_cm_dreq )\r
-{\r
- ib_api_status_t status;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !p_cm_dreq )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- /* Only supported qp types allowed */\r
- switch( p_cm_dreq->qp_type )\r
- {\r
- default:\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid qp_type.\n") );\r
- return IB_INVALID_SETTING;\r
-\r
- case IB_QPT_RELIABLE_CONN:\r
- case IB_QPT_UNRELIABLE_CONN:\r
- if( AL_OBJ_INVALID_HANDLE( p_cm_dreq->h_qp, AL_OBJ_TYPE_H_QP ) ||\r
- (p_cm_dreq->h_qp->type != p_cm_dreq->qp_type) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_QP_HANDLE\n") );\r
- return IB_INVALID_QP_HANDLE;\r
- }\r
- break;\r
- }\r
-\r
- if( !((al_conn_qp_t*)p_cm_dreq->h_qp)->p_conn )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("No connection info!\n") );\r
-\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- p_conn = ((al_conn_qp_t*)p_cm_dreq->h_qp)->p_conn;\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- /*\r
- * Note that the user could be disconnecting at the same time as the remote\r
- * endpoint. In this case, a DREQ could have been received, and maybe even\r
- * responded to by the local user calling ib_cm_drep. In this case, the\r
- * caller's p_conn pointer may be invalid.\r
- */\r
- if( p_conn->state != CM_CONN_ESTABLISHED )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Invalid state (%d) for sending DREQ.\n", p_conn->state) );\r
-\r
- cm_res_release( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- if( ( p_cm_dreq->p_dreq_pdata ) &&\r
- ( p_cm_dreq->dreq_length > IB_DREQ_PDATA_SIZE ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid pdata length.\n") );\r
-\r
- cm_res_release( p_conn );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- /*\r
- * Keep the connection object around until we're done trying to send\r
- * the DREQ.\r
- */\r
- __ref_conn( p_conn );\r
- status = cm_conn_dreq( p_cm_dreq );\r
-\r
- /*\r
- * If we failed to send the DREQ, just release the connection. It's\r
- * unreliable anyway. The local port may be down. Note that we could\r
- * not send the DREQ, but we still could have received one. The DREQ\r
- * will have a reference on the connection until the user calls ib_cm_drep.\r
- */\r
- if( status != IB_SUCCESS )\r
- __conn_release( p_conn );\r
-\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_drep(\r
- IN const ib_cm_handle_t h_cm_dreq,\r
- IN const ib_cm_drep_t* const p_cm_drep )\r
-{\r
- al_conn_t *p_conn;\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !h_cm_dreq )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
- if( !p_cm_drep )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- p_conn = h_cm_dreq;\r
-\r
- cm_res_acquire( p_conn );\r
- if( p_conn->state != CM_CONN_DREQ_RCVD )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Invalid state (%d) for DREP.\n", p_conn->state) );\r
-\r
- cm_res_release( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- /* Hold a reference while we are processing sending the DREP. */\r
- __ref_conn( p_conn );\r
- status = cm_conn_drep( p_cm_drep, p_conn );\r
- cm_res_release( p_conn );\r
-\r
- /* Release the reference taken while processing. */\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_lap(\r
- IN const ib_cm_lap_t* const p_cm_lap )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !p_cm_lap )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- /* Only supported qp types allowed */\r
- switch( p_cm_lap->qp_type )\r
- {\r
- default:\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid qp_type.\n") );\r
- return IB_INVALID_SETTING;\r
-\r
- case IB_QPT_RELIABLE_CONN:\r
- case IB_QPT_UNRELIABLE_CONN:\r
- if( AL_OBJ_INVALID_HANDLE( p_cm_lap->h_qp, AL_OBJ_TYPE_H_QP ) ||\r
- (p_cm_lap->h_qp->type != p_cm_lap->qp_type) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_QP_HANDLE\n") );\r
- return IB_INVALID_QP_HANDLE;\r
- }\r
- break;\r
- }\r
-\r
- /* Make sure a path was provided. */\r
- if( !p_cm_lap->p_alt_path )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Path missing.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- status = cm_conn_lap( p_cm_lap );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_apr(\r
- IN const ib_cm_handle_t h_cm_lap,\r
- IN const ib_cm_apr_t* const p_cm_apr )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !h_cm_lap )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
- if( !p_cm_apr )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_PARAMETER\n") );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- /* Only supported qp types allowed */\r
- switch( p_cm_apr->qp_type )\r
- {\r
- default:\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid qp_type.\n") );\r
- return IB_INVALID_SETTING;\r
-\r
- case IB_QPT_RELIABLE_CONN:\r
- case IB_QPT_UNRELIABLE_CONN:\r
- if( AL_OBJ_INVALID_HANDLE( p_cm_apr->h_qp, AL_OBJ_TYPE_H_QP ) ||\r
- (p_cm_apr->h_qp->type != p_cm_apr->qp_type) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_QP_HANDLE\n") );\r
- return IB_INVALID_QP_HANDLE;\r
- }\r
- break;\r
- }\r
-\r
- status = cm_conn_apr( h_cm_lap, p_cm_apr );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-ib_api_status_t\r
-ib_force_apm(\r
- IN const ib_qp_handle_t h_qp )\r
-{\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( AL_OBJ_INVALID_HANDLE( h_qp, AL_OBJ_TYPE_H_QP ) )\r
- {\r
- AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_QP_HANDLE\n") );\r
- return IB_INVALID_QP_HANDLE;\r
- }\r
-\r
- status = cm_force_apm( h_qp );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-/*\r
- * TBD: revisit this call when the implementation handles the handoff in\r
- * a different ioctl path\r
- */\r
-ib_api_status_t\r
-ib_cm_handoff(\r
- IN const ib_cm_handle_t h_cm_req,\r
- IN const ib_net64_t svc_id )\r
-{\r
- ib_api_status_t status;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( !h_cm_req )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
- if( !svc_id )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("IB_INVALID_PARAMETER\n" ) );\r
- return IB_INVALID_PARAMETER;\r
- }\r
-\r
- p_conn = (al_conn_t*)h_cm_req;\r
-\r
- if( p_conn->state != CM_CONN_REQ_RCVD )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("State not valid for handoff\n" ) );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- /* only the passive side or the server can handoff */\r
- if( p_conn->was_active == TRUE )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("State not valid for handoff\n" ) );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- if( p_conn->qp_type != IB_QPT_RELIABLE_CONN )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid conn handle for handoff\n" ) );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- /* cm_conn_handoff will call cm_res_acquire and cm_res_release. */\r
- status = cm_conn_handoff( p_conn, svc_id );\r
-\r
- /* Release the reference taken for the callback. */\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r
-\r
-\r
-\r
-static void\r
-__cm_internal_dreq_cb(\r
- IN ib_cm_dreq_rec_t *p_dreq_rec )\r
-{\r
- ib_cm_drep_t cm_drep;\r
-\r
- /* Call ib_cm_drep to complete the connection protocol. */\r
- cl_memclr( &cm_drep, sizeof( ib_cm_drep_t ) );\r
- ib_cm_drep( p_dreq_rec->h_cm_dreq, &cm_drep );\r
-}\r
-\r
-\r
-\r
-static void\r
-__cm_internal_drep_cb(\r
- IN ib_cm_drep_rec_t *p_drep_rec )\r
-{\r
- /* Suppress the callback. */\r
- UNUSED_PARAM( p_drep_rec );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Function invoked when an QP is being destroyed, or the connection\r
- * is being aborted. Depending on the state of the connection, sends either\r
- * a REJ or DREQ and cleans up.\r
- */\r
-void\r
-cm_conn_destroy(\r
- IN al_conn_qp_t * const p_conn_qp )\r
-{\r
- ib_cm_dreq_t cm_dreq;\r
- ib_cm_drep_t cm_drep;\r
- ib_api_status_t status;\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /*\r
- * We need to serialize with the destruction of the connection object.\r
- * The connection can also be destroyed if a DREQ is received, and the\r
- * user calls DREP. To ensure that the connection object is valid, we\r
- * lock the global CM when unbinding the QP from the connection.\r
- */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- if( !p_conn_qp->p_conn )\r
- {\r
- /* The connection object has already been unbound. */\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- AL_EXIT( AL_DBG_CM );\r
- return;\r
- }\r
-\r
- /* Reference the connection to prevent it from going away. */\r
- p_conn = p_conn_qp->p_conn;\r
- __ref_conn( p_conn );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- cm_res_acquire( p_conn );\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_LAP_SENT:\r
- ib_cancel_mad( p_conn->h_mad_svc, p_conn->p_send_mad );\r
- p_conn->p_send_mad = NULL;\r
- /* Fall through. */\r
-\r
- case CM_CONN_ESTABLISHED:\r
- case CM_CONN_LAP_RCVD:\r
- case CM_CONN_LAP_MRA_RCVD:\r
- case CM_CONN_LAP_MRA_SENT:\r
- cl_memclr( &cm_dreq, sizeof( ib_cm_dreq_t ) );\r
- cm_dreq.h_qp = p_conn->h_qp;\r
- cm_dreq.pfn_cm_drep_cb = __cm_internal_drep_cb;\r
-\r
- /*\r
- * Override the DREQ callback to prevent calling the user back with\r
- * a received DREQ.\r
- */\r
- p_conn->pfn_cm_dreq_cb = __cm_internal_dreq_cb;\r
-\r
- status = cm_conn_dreq( &cm_dreq );\r
-\r
- /*\r
- * If we failed to send the DREQ, just release the connection. It's\r
- * unreliable anyway. The local port may be down. Note that we could\r
- * not send the DREQ, but we still could have received one. The DREQ\r
- * will have a reference on the connection until the CM calls ib_cm_drep.\r
- */\r
- if( status != IB_SUCCESS )\r
- __conn_release( p_conn );\r
- cm_res_release( p_conn );\r
- break;\r
-\r
- case CM_CONN_DREQ_RCVD:\r
- cl_memclr( &cm_drep, sizeof( ib_cm_drep_t ) );\r
- cm_conn_drep( &cm_drep, p_conn );\r
- cm_res_release( p_conn );\r
- break;\r
-\r
- case CM_CONN_REQ_RCVD:\r
- case CM_CONN_REQ_SENT:\r
- case CM_CONN_REQ_MRA_RCVD:\r
- case CM_CONN_REQ_MRA_SENT:\r
- case CM_CONN_REP_RCVD:\r
- case CM_CONN_REP_SENT:\r
- case CM_CONN_REP_MRA_RCVD:\r
- case CM_CONN_REP_MRA_SENT:\r
- __conn_reject( p_conn, IB_REJ_TIMEOUT, NULL, 0, NULL );\r
- cm_res_release( p_conn );\r
- break;\r
-\r
- default:\r
- /* handled elsewhere. */\r
- cm_res_release( p_conn );\r
- break;\r
- }\r
-\r
- __deref_conn( p_conn );\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-/*\r
- * callback to process a connection establishment timeout due to reply not\r
- * being received. The connection object has a reference\r
- * taken when the timer is set or when the send is sent.\r
- */\r
-void\r
-__proc_conn_timeout(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- al_conn_t *p_conn;\r
- ib_cm_rej_rec_t rej_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_item, al_conn_t, timeout_item );\r
-\r
- cm_res_acquire( p_conn );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_SENT:\r
- case CM_CONN_REP_SENT:\r
- /*\r
- * Format the reject record before aborting the connection since\r
- * we need the QP context.\r
- */\r
- cl_memclr( &rej_rec, sizeof( ib_cm_rej_rec_t ) );\r
- rej_rec.h_qp = p_conn->h_qp;\r
- rej_rec.qp_context = p_conn->h_qp->obj.context;\r
- rej_rec.rej_status = IB_REJ_TIMEOUT;\r
-\r
- /* Reject and abort the connection. */\r
- __conn_reject( p_conn, IB_REJ_TIMEOUT, NULL, 0, NULL );\r
- cm_res_release( p_conn );\r
-\r
- /* Invoke the callback. */\r
- p_conn->pfn_cm_rej_cb( &rej_rec );\r
- break;\r
-\r
- default:\r
- /* Something changed our state, so ignore the timeout. */\r
- cm_res_release( p_conn );\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("Connection in invalid state for send timeout.\n") );\r
- break;\r
- }\r
- /* Release the reference taken for the send. */\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-/*\r
- * callback to process a LAP timeout due to APR not being received.\r
- */\r
-void\r
-__proc_lap_timeout(\r
- IN cl_async_proc_item_t* p_item )\r
-{\r
- al_conn_t *p_conn;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = PARENT_STRUCT( p_item, al_conn_t, timeout_item );\r
-\r
- cm_res_acquire( p_conn );\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_LAP_SENT:\r
- {\r
- /* Report the timeout. */\r
- ib_cm_apr_rec_t apr_rec;\r
- cl_memclr( &apr_rec, sizeof( ib_cm_apr_rec_t ) );\r
- apr_rec.h_qp = p_conn->h_qp;\r
- apr_rec.qp_context = p_conn->h_qp->obj.context;\r
- apr_rec.cm_status = IB_TIMEOUT;\r
- apr_rec.apr_status = IB_AP_REJECT;\r
-\r
- /* Return to the established state. */\r
- p_conn->state = CM_CONN_ESTABLISHED;\r
- cm_res_release( p_conn );\r
-\r
- /* Notify the user that the LAP failed. */\r
- p_conn->pfn_cm_apr_cb( &apr_rec );\r
- }\r
- break;\r
-\r
- default:\r
- cm_res_release( p_conn );\r
- /* Something changed our state, so ignore the timeout. */\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("Connection in invalid state for send timeout.\n") );\r
- break;\r
- }\r
- /* Release the reference taken for the send. */\r
- __deref_conn( p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Reset the QP associated with a connection. Place the QP into the timewait\r
- * state. Unbind the QP and connection object.\r
- */\r
-void\r
-__cm_conn_unbind(\r
- IN al_conn_t* const p_conn,\r
- IN const uint32_t timewait )\r
-{\r
- ib_qp_handle_t h_qp;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /*\r
- * See if we've already been unbound. Note that if we haven't, then we\r
- * already have a reference on the QP, so we don't need a new one. We\r
- * synchronize access to the QP handle stored by the connection object\r
- * using the global CM lock.\r
- */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
-\r
- h_qp = p_conn->h_qp;\r
- cm_unbind_qp( p_conn );\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- /*\r
- * The CM should have set the proper timewait time-out value. Reset\r
- * the QP and let it enter the timewait state.\r
- */\r
- if( h_qp )\r
- {\r
- cm_reset_qp( h_qp, timewait );\r
- deref_al_obj( &h_qp->obj );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-/*\r
- * Function invoked when a connection is being disconnected.\r
- */\r
-void\r
-__conn_release(\r
- IN al_conn_t* const p_conn )\r
-{\r
- uint32_t timewait = 0;\r
- uint8_t pkt_life;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_ESTABLISHED:\r
- case CM_CONN_LAP_RCVD:\r
- case CM_CONN_LAP_SENT:\r
- case CM_CONN_LAP_MRA_RCVD:\r
- case CM_CONN_LAP_MRA_SENT:\r
- case CM_CONN_DREQ_SENT:\r
- case CM_CONN_DREQ_RCVD:\r
- case CM_CONN_DREP_SENT:\r
- /* Remove from the connection map. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qmap_remove_item( &gp_cm->conn_map, &p_conn->map_item );\r
-\r
- /*\r
- * Calculate the timewait time.\r
- * All timeout values in micro seconds are expressed as 4.096 * 2^x,\r
- * where x is the timeout. This approximates to 2^(x+2).\r
- * Since we want milliseconds, we can further approximate to 2^(x-8).\r
- * This results in a timeout that is roughly 5% on the low side, but\r
- * good enough since OS timer resolutions are ~10ms.\r
- */\r
- pkt_life = ib_path_rec_pkt_life( &p_conn->path[p_conn->idx_primary] );\r
- if( pkt_life > 8 )\r
- timewait = 2 << (pkt_life - 8);\r
- else\r
- timewait = 0;\r
-\r
- if( p_conn->was_active && p_conn->target_ack_delay > 8 )\r
- {\r
- /*\r
- * The active side's path packet life does not include the remote\r
- * CA's ACK delay.\r
- */\r
- timewait += 1 << (p_conn->target_ack_delay - 8);\r
- }\r
- /* Minimum 10 ms timeout - to match typical OS timer resolution. */\r
- if( timewait < 10 )\r
- timewait = 10;\r
-\r
- /* watch out for over the top timeouts which may run into hours!!!*/\r
- if( timewait > 1*1000 )\r
- timewait = 1*1000;\r
-\r
- p_conn->state = CM_CONN_TIMEWAIT;\r
-\r
- /* Insert in the timewait list. */\r
- cl_qlist_insert_tail( &gp_cm->time_wait_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- /* Unbind the connection and enter the QP into timewait. */\r
- __cm_conn_unbind( p_conn, timewait );\r
-\r
- /*\r
- * Remove the connection from AL, to allow AL's destruction.\r
- * The connection object will be tracked by the CM until its\r
- * timewait expires.\r
- */\r
- if( p_conn->h_al )\r
- al_remove_conn( p_conn );\r
-\r
- /* Set the timer. */\r
- if( cl_timer_start( &p_conn->timer, timewait ) != CL_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Timer failed to start for comm_id( x%x )!\n",\r
- p_conn->local_comm_id) );\r
- /* Never mind the timer then, just release the connection now. */\r
- __conn_timer_cb( p_conn );\r
- }\r
- break;\r
-\r
- default:\r
- /* Should never get here. */\r
- break;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-\r
-/* exported functions */\r
-ib_api_status_t\r
-__is_listen_valid(\r
- IN const cm_port_agent_t* const p_port_cm,\r
- IN const ib_cm_listen_t* const p_info )\r
-{\r
- ib_ca_attr_t *p_ca_attr;\r
- ib_port_attr_t *p_port_attr;\r
- uint16_t i_pkey;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* A full wildcard listen should have been trapped elsewhere. */\r
- CL_ASSERT( !(p_info->ca_guid == IB_ALL_CAS &&\r
- p_info->port_guid == IB_ALL_PORTS &&\r
- p_info->lid == IB_ALL_LIDS &&\r
- p_info->pkey == IB_ALL_PKEYS) );\r
-\r
- p_ca_attr = p_port_cm->p_ca_attr;\r
-\r
- if( p_info->ca_guid != IB_ALL_CAS &&\r
- p_info->ca_guid != p_ca_attr->ca_guid )\r
- {\r
- /* CA GUID is not wildcard and does not match. */\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_INVALID_GUID;\r
- }\r
- /* CA GUID is either wildcard or matches. */\r
-\r
- /* Handy pointer to make code cleaner. */\r
- p_port_attr = &p_ca_attr->p_port_attr[p_port_cm->port_idx];\r
-\r
- if( p_info->port_guid != IB_ALL_PORTS &&\r
- p_info->port_guid != p_port_attr->port_guid )\r
- {\r
- /* Port GUID is not wildcard and does not match. */\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_INVALID_GUID;\r
- }\r
- /* Port GUID is either wildcard or matches. */\r
-\r
- if( (p_info->lid != IB_ALL_LIDS) &&\r
- (p_info->lid != p_port_attr->lid) &&\r
- (__is_lid_valid(p_info->lid, p_port_attr->lid,\r
- p_port_attr->lmc) == FALSE) )\r
- {\r
- /*\r
- * LID is not wildcard and does not match the LID range for\r
- * the port.\r
- */\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_INVALID_LID;\r
- }\r
- /* LID is either wildcard or is within range. */\r
-\r
- /* Look for a match on the pkey. */\r
- if( p_info->pkey == IB_ALL_PKEYS )\r
- {\r
- /* PKey is wildcard. */\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
- }\r
-\r
- /* PKey is not wildcard. */\r
- for( i_pkey = 0; i_pkey < p_port_attr->num_pkeys; i_pkey++ )\r
- {\r
- if( p_info->pkey == p_port_attr->p_pkey_table[i_pkey] )\r
- {\r
- /* PKey is valid for the port. */\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
- }\r
- }\r
- /* No PKey match. */\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_INVALID_PKEY;\r
-}\r
-\r
-/*\r
- * Callback that validates a listen on a specific port cm. Returns\r
- * CL_SUCCESS if the listen is valid for the port cm.\r
- */\r
-cl_status_t\r
-__port_cm_validate_listen(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- cm_port_agent_t *p_port_cm;\r
- al_listen_t *p_listen;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_port_cm = PARENT_STRUCT(\r
- PARENT_STRUCT( p_list_item, al_obj_t, pool_item ),\r
- cm_port_agent_t, obj );\r
- p_listen = (al_listen_t*)context;\r
-\r
- p_listen->err_code = __is_listen_valid( p_port_cm, &p_listen->info );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-\r
- return( p_listen->err_code == IB_SUCCESS ? CL_SUCCESS : CL_NOT_FOUND );\r
-}\r
-\r
-\r
-/*\r
- * Callback that verifies that listen attributes are valid.\r
- * Returns CL_NOT_FOUND if the listen is valid, and CL_SUCCESS\r
- * if the listen is invalid.\r
- */\r
-/***static***/ cl_status_t\r
-__is_listen_bad(\r
- IN const cl_list_item_t* const p_list_item,\r
- IN void *context )\r
-{\r
- cl_list_item_t *p_port_item;\r
- al_listen_t *p_listen;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- UNUSED_PARAM( context );\r
- p_listen = PARENT_STRUCT( p_list_item, al_listen_t, list_item );\r
-\r
- /* If the listen is not directed, it cannot fail. */\r
- if( p_listen->info.ca_guid == IB_ALL_CAS &&\r
- p_listen->info.port_guid == IB_ALL_PORTS &&\r
- p_listen->info.lid == IB_ALL_LIDS &&\r
- p_listen->info.pkey == IB_ALL_PKEYS )\r
- {\r
- AL_EXIT( AL_DBG_CM );\r
- return CL_NOT_FOUND;\r
- }\r
-\r
- /*\r
- * Check that the listen matches the parameters of at least one\r
- * of the port CM agents.\r
- */\r
- p_port_item = cl_qlist_find_from_head( &gp_cm->obj.obj_list,\r
- __port_cm_validate_listen, p_listen );\r
- if( p_port_item == cl_qlist_end( &gp_cm->obj.obj_list ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Invalid listen found.\n") );\r
- /* Reference the listen and queue it for a callback. */\r
- ref_al_obj( &p_listen->obj );\r
- cl_async_proc_queue( gp_async_proc_mgr, &p_listen->async_item );\r
- return CL_SUCCESS;\r
- }\r
- AL_EXIT( AL_DBG_CM );\r
-\r
- return CL_NOT_FOUND;\r
-}\r
-\r
-\r
-void\r
-__validate_listens( void )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Move all invalid listens to a temporary list. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_move_items( &gp_cm->active_listen_list,\r
- &gp_cm->inactive_listen_list, __is_listen_bad, NULL );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__listen_err_cb(\r
- IN cl_async_proc_item_t *p_item )\r
-{\r
- al_listen_t *p_listen;\r
- ib_listen_err_rec_t err_rec;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_listen = PARENT_STRUCT( p_item, al_listen_t, async_item );\r
-\r
- /* Format the error record and invoke the callback to the user. */\r
- cl_memclr( &err_rec, sizeof( ib_listen_err_rec_t ) );\r
- err_rec.reason = p_listen->err_code;\r
- err_rec.listen_context = (void*)p_listen->obj.context;\r
- err_rec.h_cm_listen = p_listen;\r
-\r
- p_listen->pfn_err_cb( &err_rec );\r
-\r
- /* Release the reference to the listen to allow destruction to complete. */\r
- deref_al_obj( &p_listen->obj );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
-}\r
-\r
-\r
-void\r
-__destroying_listen(\r
- IN al_obj_t* p_obj )\r
-{\r
- al_listen_t *p_listen;\r
-\r
- p_listen = PARENT_STRUCT( p_obj, al_listen_t, obj );\r
-\r
- /*\r
- * Move the listen from the active list to the inactive list to\r
- * prevent any further connection requests from matching with this\r
- * listen. Requests in progress will be allowed to complete, since\r
- * they already hold a reference on the listen object.\r
- */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
-\r
- /* Move the listen to the inactive list. */\r
- if( p_listen->err_code == IB_SUCCESS )\r
- {\r
- ASSERT( p_listen->list_item.p_list == &gp_cm->active_listen_list );\r
- cl_qlist_remove_item( &gp_cm->active_listen_list,\r
- &p_listen->list_item );\r
- cl_qlist_insert_head( &gp_cm->inactive_listen_list,\r
- &p_listen->list_item );\r
- }\r
- else\r
- {\r
- CL_ASSERT( p_listen->list_item.p_list ==\r
- &gp_cm->inactive_listen_list );\r
- }\r
-\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-}\r
-\r
-\r
-\r
-void\r
-__free_listen(\r
- IN al_obj_t* p_obj )\r
-{\r
- al_listen_t *p_listen;\r
-\r
- p_listen = PARENT_STRUCT( p_obj, al_listen_t, obj );\r
-\r
- /* Remove from the inactive listen list. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_remove_item( &gp_cm->inactive_listen_list,\r
- &p_listen->list_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- destroy_al_obj( p_obj );\r
- cl_free( PARENT_STRUCT( p_obj, al_listen_t, obj ) );\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-ib_cm_cancel(\r
- IN const ib_listen_handle_t h_cm_listen,\r
- IN const ib_pfn_destroy_cb_t pfn_destroy_cb OPTIONAL )\r
-{\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- if( AL_OBJ_INVALID_HANDLE( h_cm_listen, AL_OBJ_TYPE_H_LISTEN ) )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("IB_INVALID_HANDLE\n") );\r
- return IB_INVALID_HANDLE;\r
- }\r
-\r
- ref_al_obj( &h_cm_listen->obj );\r
- h_cm_listen->obj.pfn_destroy( &h_cm_listen->obj, pfn_destroy_cb );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Validates that the listen does not conflict with an existing one.\r
- */\r
-ib_api_status_t\r
-__is_listen_unique(\r
- IN const ib_cm_listen_t* const p_new_listen )\r
-{\r
- cl_list_item_t *p_list_item;\r
- al_listen_t *p_listen;\r
-\r
- for( p_list_item = cl_qlist_head( &gp_cm->active_listen_list );\r
- p_list_item != cl_qlist_end( &gp_cm->active_listen_list );\r
- p_list_item = cl_qlist_next( p_list_item ) )\r
- {\r
- p_listen = PARENT_STRUCT( p_list_item, al_listen_t, list_item );\r
-\r
- if( p_listen->info.svc_id != p_new_listen->svc_id )\r
- continue;\r
-\r
- /*\r
- * Service ID matches. Compare CA GUIDs. If the GUIDS\r
- * don't match and neither is a wildcard, the listens differ.\r
- */\r
- if( p_listen->info.ca_guid != p_new_listen->ca_guid &&\r
- p_listen->info.ca_guid != IB_ALL_CAS &&\r
- p_new_listen->ca_guid != IB_ALL_CAS )\r
- {\r
- continue;\r
- }\r
-\r
- /*\r
- * CA GUID matches. Compare port GUID. If the GUIDs don't\r
- * match and neither is a wildcard, the listens differ.\r
- */\r
- if( p_listen->info.port_guid != p_new_listen->port_guid &&\r
- p_listen->info.port_guid != IB_ALL_PORTS &&\r
- p_new_listen->port_guid != IB_ALL_PORTS )\r
- {\r
- continue;\r
- }\r
-\r
- /*\r
- * Port GUID matches. Compare LIDs. If the LIDs don't match and\r
- * neither is a wailcard, the listens differ.\r
- */\r
- if( p_listen->info.lid != p_new_listen->lid &&\r
- p_listen->info.lid != IB_ALL_LIDS &&\r
- p_new_listen->lid != IB_ALL_LIDS )\r
- {\r
- continue;\r
- }\r
-\r
- /*\r
- * LID matches. Compare PKey. If the PKeys don't match and\r
- * neither is a wailcard, the listens differ.\r
- */\r
- if( p_listen->info.pkey != p_new_listen->pkey &&\r
- p_listen->info.pkey != IB_ALL_PKEYS &&\r
- p_new_listen->pkey != IB_ALL_PKEYS )\r
- {\r
- continue;\r
- }\r
-\r
- /*\r
- * Make sure compare data is specified and compatible with existing\r
- * requests.\r
- */\r
- if( !p_listen->info.p_compare_buffer || !p_new_listen->p_compare_buffer )\r
- return IB_INVALID_PARAMETER;\r
-\r
- if( p_listen->info.compare_length != p_new_listen->compare_length )\r
- return IB_INVALID_PARAMETER;\r
-\r
- if( p_listen->info.compare_offset != p_new_listen->compare_offset )\r
- return IB_INVALID_PARAMETER;\r
-\r
- if( !cl_memcmp( p_listen->info.p_compare_buffer,\r
- p_new_listen->p_compare_buffer, p_new_listen->compare_length ) )\r
- {\r
- return IB_INVALID_PARAMETER;\r
- }\r
- }\r
-\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-/*\r
- * Validates the parameters of a listen request to make sure that\r
- * it does not conflict with an existing listen.\r
- */\r
-ib_api_status_t\r
-__validate_listen(\r
- IN const ib_cm_listen_t* const p_new_listen )\r
-{\r
- cl_list_item_t *p_port_item;\r
- al_listen_t listen;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /*\r
- * Check validity of CA GUID, Port GUID, LID, and PKey if any\r
- * are specified.\r
- */\r
- if( p_new_listen->ca_guid != IB_ALL_CAS &&\r
- p_new_listen->port_guid != IB_ALL_PORTS &&\r
- p_new_listen->lid != IB_ALL_LIDS &&\r
- p_new_listen->pkey != IB_ALL_PKEYS )\r
- {\r
- /*\r
- * Copy the listen information so we can process the request\r
- * as if it was already queued. This also allows detailed\r
- * status to be returned.\r
- */\r
- listen.info = *p_new_listen;\r
-\r
- /*\r
- * Loop through all port CM agents to match the directed parameters.\r
- */\r
- p_port_item = cl_qlist_find_from_head( &gp_cm->obj.obj_list,\r
- __port_cm_validate_listen, &listen );\r
-\r
- if( p_port_item == cl_qlist_end( &gp_cm->obj.obj_list ) )\r
- {\r
- /* No port could accomodate the listen. */\r
- AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
- ("Invalid listen request.\n") );\r
- return listen.err_code;\r
- }\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return __is_listen_unique( p_new_listen );\r
-}\r
-\r
-ib_api_status_t\r
-cm_listen(\r
- IN const ib_al_handle_t h_al,\r
- IN const ib_cm_listen_t* const p_cm_listen,\r
- IN const ib_pfn_listen_err_cb_t pfn_listen_err_cb,\r
- IN const void* const listen_context,\r
- OUT ib_listen_handle_t* const ph_cm_listen )\r
-{\r
- ib_api_status_t status;\r
- al_listen_t *p_listen;\r
- size_t alloc_size;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- /* Validate the listen paramters to make sure they are unique. */\r
- status = __validate_listen( p_cm_listen );\r
- if( status != IB_SUCCESS )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- return status;\r
- }\r
-\r
- /* Calculate the total size of the listen request, including compare data. */\r
- alloc_size = sizeof(al_listen_t);\r
- if( p_cm_listen->p_compare_buffer )\r
- alloc_size += p_cm_listen->compare_length;\r
- /* Allocate the listen object. */\r
- p_listen = (al_listen_t*)cl_zalloc( alloc_size );\r
- if( !p_listen )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- return IB_INSUFFICIENT_MEMORY;\r
- }\r
-\r
- /* Copy the listen request information for matching incoming requests. */\r
- p_listen->info = *p_cm_listen;\r
-\r
- if( p_cm_listen->p_compare_buffer )\r
- {\r
- /* Set the compare buffer pointer. */\r
- p_listen->info.p_compare_buffer = (uint8_t*)(p_listen + 1);\r
- /* Copy the compare buffer. */\r
- cl_memcpy( p_listen->info.p_compare_buffer,\r
- p_cm_listen->p_compare_buffer, p_cm_listen->compare_length );\r
- }\r
-\r
- p_listen->pfn_err_cb = pfn_listen_err_cb;\r
- p_listen->async_item.pfn_callback = __listen_err_cb;\r
-\r
- /*\r
- * Cast of ib_cm_cancel to type al_pfn_destroy_t required for first\r
- * paramter type mismatch.\r
- */\r
- construct_al_obj( &p_listen->obj, AL_OBJ_TYPE_H_LISTEN );\r
- status = init_al_obj( &p_listen->obj, listen_context, TRUE,\r
- __destroying_listen, NULL, __free_listen );\r
- if( status != IB_SUCCESS )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- __free_listen( &p_listen->obj );\r
- return status;\r
- }\r
-\r
- /* Add the listen to the CM agent's list of listens. */\r
- cl_qlist_insert_tail( &gp_cm->active_listen_list,\r
- &p_listen->list_item );\r
-\r
- /* Add the listen to the AL instance's object list. */\r
- status = attach_al_obj( &h_al->obj, &p_listen->obj );\r
- if( status != IB_SUCCESS )\r
- {\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
- p_listen->obj.pfn_destroy( &p_listen->obj, NULL );\r
- AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- *ph_cm_listen = p_listen;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_rej(\r
- IN al_conn_t* const p_conn,\r
- IN const ib_cm_rej_t* const p_cm_rej )\r
-{\r
- ib_api_status_t ib_status = IB_SUCCESS;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- switch( p_conn->state )\r
- {\r
- case CM_CONN_REQ_RCVD:\r
- case CM_CONN_REQ_MRA_SENT:\r
- case CM_CONN_REP_RCVD:\r
- case CM_CONN_REP_MRA_SENT:\r
- __conn_reject( p_conn, p_cm_rej->rej_status, p_cm_rej->p_ari,\r
- p_cm_rej->ari_length,\r
- (ib_rej_pdata_t* __ptr64)p_cm_rej->p_rej_pdata );\r
- break;\r
-\r
- default:\r
- break;\r
- }\r
- AL_EXIT( AL_DBG_CM );\r
- return ib_status;\r
-}\r
-\r
-\r
-ib_api_status_t\r
-cm_force_apm(\r
- IN const ib_qp_handle_t h_qp )\r
-{\r
- ib_api_status_t status;\r
- al_conn_qp_t *p_conn_qp;\r
- struct _qp_rts *p_rts;\r
-\r
- CL_ASSERT( h_qp );\r
-\r
- p_conn_qp = PARENT_STRUCT( h_qp, al_conn_qp_t, qp );\r
- p_rts = &p_conn_qp->p_conn->p_req_info->qp_mod_rts.state.rts;\r
-\r
- p_conn_qp->p_conn->p_req_info->qp_mod_rts.req_state = IB_QPS_RTS;\r
- p_rts->apm_state = IB_APM_MIGRATED;\r
- p_rts->opts = IB_MOD_QP_APM_STATE;\r
-\r
- /* Set the QP to RTS. */\r
- status = ib_modify_qp( h_qp,\r
- &p_conn_qp->p_conn->p_req_info->qp_mod_rts );\r
- if( status == IB_SUCCESS )\r
- {\r
- //***TODO: When forcing a migration, does the hardware generate a\r
- //***TODO: migration event? If so, the following code should be\r
- //***TODO: eliminated.\r
- /* Update the primary path index. */\r
- if( h_qp && p_conn_qp->p_conn )\r
- {\r
- cm_conn_migrated( p_conn_qp->p_conn );\r
- }\r
- }\r
-\r
- return status;\r
-\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_req(\r
- IN const ib_al_handle_t h_al,\r
- IN const ib_cm_req_t* const p_cm_req )\r
-{\r
- ib_api_status_t status;\r
- al_conn_t *p_conn;\r
- cm_port_agent_t *p_port_cm;\r
- cl_status_t cl_status;\r
- cl_event_t sync_event;\r
- cl_event_t *p_sync_event = NULL;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Get a connection object from the pool. */\r
- p_conn = __get_conn( h_al, p_cm_req->qp_type );\r
- if( !p_conn )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("__get_conn failed.\n") );\r
- return IB_INSUFFICIENT_MEMORY;\r
- }\r
-\r
- /* See if this QP has already been connected. */\r
- cl_spinlock_acquire( &p_cm_req->h_qp->obj.lock );\r
- if( ((al_conn_qp_t*)p_cm_req->h_qp)->p_conn )\r
- {\r
- cl_spinlock_release( &p_cm_req->h_qp->obj.lock );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("QP already connected.\n") );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- /* Bind the connection and the QP. */\r
- __bind_qp( p_conn, p_cm_req->h_qp );\r
- cl_spinlock_release( &p_cm_req->h_qp->obj.lock );\r
-\r
- /* Save REQ and connection info and setup the QP modify structures. */\r
- status = __conn_save_user_req( p_cm_req, p_conn, &p_port_cm );\r
- if( status != IB_SUCCESS )\r
- {\r
- __deref_conn( p_conn );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__conn_save_user_req failed.\n") );\r
- return status;\r
- }\r
-\r
- /* Format the REQ MAD. */\r
- status = __format_mad_req( p_cm_req, p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- __deref_conn( p_conn );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__format_mad_req failed.\n") );\r
- return status;\r
- }\r
-\r
- /* Transition QP through state machine */\r
- status = __cm_pre_req( p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- __deref_conn( p_conn );\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__cm_pre_req returned %s\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* event based mechanism */\r
- if( p_cm_req->flags & IB_FLAGS_SYNC )\r
- {\r
- cl_event_construct( &sync_event );\r
- cl_status = cl_event_init( &sync_event, FALSE );\r
- if( cl_status != CL_SUCCESS )\r
- {\r
- __deref_conn( p_conn );\r
- return ib_convert_cl_status( cl_status );\r
- }\r
- p_conn->p_sync_event = p_sync_event = &sync_event;\r
- }\r
-\r
- /* Add conn to list and send REQ */\r
- cm_res_acquire( p_conn );\r
- p_conn->state = CM_CONN_REQ_SENT;\r
- p_conn->local_qpn = p_conn->p_req_info->local_qpn;\r
-\r
- /* Insert the connection to the pending list. */\r
- cl_spinlock_acquire( &gp_cm->obj.lock );\r
- cl_qlist_insert_head( &gp_cm->pending_list,\r
- (cl_list_item_t*)&p_conn->map_item );\r
- cl_spinlock_release( &gp_cm->obj.lock );\r
-\r
- /* Send the MAD. */\r
- status = __cm_send( p_port_cm, p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- cm_res_release( p_conn );\r
- __deref_conn( p_conn );\r
- if( p_sync_event )\r
- cl_event_destroy( p_sync_event );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__cm_send returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
- cm_res_release( p_conn );\r
-\r
- /* wait on event if synchronous operation */\r
- if( p_sync_event )\r
- {\r
- AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
- ("event blocked on REQ...\n") );\r
- cl_event_wait_on( p_sync_event, EVENT_NO_TIMEOUT, FALSE );\r
-\r
- cl_event_destroy( p_sync_event );\r
- }\r
-\r
- /* We will set the retry timer upon completion. */\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_rep(\r
- IN al_conn_t* const p_conn,\r
- IN const ib_cm_rep_t* const p_cm_rep )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
- ib_rej_status_t rej_status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- cl_spinlock_acquire( &p_cm_rep->h_qp->obj.lock );\r
- if( ((al_conn_qp_t*)p_cm_rep->h_qp)->p_conn )\r
- {\r
- cl_spinlock_release( &p_cm_rep->h_qp->obj.lock );\r
- __conn_reject( p_conn, IB_REJ_INSUF_QP, NULL, 0, NULL );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("QP already connected.\n") );\r
- return IB_INVALID_QP_HANDLE;\r
- }\r
-\r
- /* Bind the connection and the QP. */\r
- __bind_qp( p_conn, p_cm_rep->h_qp );\r
- cl_spinlock_release( &p_cm_rep->h_qp->obj.lock );\r
-\r
- /* Save REP and connection info and setup the QP modify structures. */\r
- rej_status = __conn_save_user_rep( p_cm_rep, p_conn, &p_port_cm );\r
- if( rej_status )\r
- {\r
- /* Reject and abort the connection. */\r
- __conn_reject( p_conn, rej_status, NULL, 0, NULL );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__conn_save_user_rep failed.\n") );\r
- return IB_INVALID_SETTING;\r
- }\r
-\r
- /* format the REP mad. */\r
- status = __format_mad_rep( p_cm_rep, p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- /* Reject and abort the connection. */\r
- __conn_reject( p_conn, IB_REJ_INSUF_QP, NULL, 0, NULL );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__format_mad_rep failed.\n") );\r
- return status;\r
- }\r
-\r
- /* Transition QP through state machine */\r
- status = __cm_pre_rep( p_conn, p_cm_rep );\r
- if( status != IB_SUCCESS )\r
- {\r
- /* Reject and abort the connection. */\r
- __conn_reject( p_conn, IB_REJ_INSUF_QP, NULL, 0, NULL );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__cm_pre_req returned %s\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- p_conn->state = CM_CONN_REP_SENT;\r
- p_conn->local_qpn = p_conn->p_req_info->local_qpn;\r
-\r
- status = __cm_send( p_port_cm, p_conn );\r
- /*\r
- * If the send fails, the REQ will be retried,\r
- * and hopefully the REP will get sent.\r
- */\r
- /* TODO: comment doesn't match code. */\r
- if( status != IB_SUCCESS )\r
- {\r
- /* Reject and abort the connection. */\r
- __conn_reject( p_conn, IB_REJ_INSUF_QP, NULL, 0, NULL );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__cm_send returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_rtu(\r
- IN al_conn_t* const p_conn,\r
- IN const ib_cm_rtu_t* const p_cm_rtu )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* save user info */\r
- status = __conn_save_user_rtu( p_cm_rtu, p_conn, &p_port_cm );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__conn_save_user_rtu returned %s.\n", ib_get_err_str(status)) );\r
- /*\r
- * Don't fail the connection since the remote side will repeat\r
- * the REP perhaps with better luck.\r
- */\r
- return status;\r
- }\r
-\r
- /* format outgoing mad */\r
- __format_mad_rtu( p_cm_rtu, p_conn );\r
-\r
- /* Transition QP through state machine */\r
- status = __cm_pre_rtu( p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- /* Reject and abort the connection. */\r
- __conn_reject( p_conn, IB_REJ_INSUF_QP, NULL, 0, NULL );\r
-\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("cm_pre_rtu to RTS returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- p_conn->state = CM_CONN_ESTABLISHED;\r
-\r
- /* Move the connection from the pending list to the connection map. */\r
- __migrate_conn_to_map( p_conn );\r
-\r
- /* No need to check the send status since the RTU is unreliable. */\r
- __cm_send( p_port_cm, p_conn );\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_lap(\r
- IN const ib_cm_lap_t* const p_cm_lap )\r
-{\r
- al_conn_t *p_conn;\r
- cm_port_agent_t *p_port_cm;\r
- uint8_t ack_delay;\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = ((al_conn_qp_t*)p_cm_lap->h_qp)->p_conn;\r
-\r
- if( !p_conn )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid connection info.\n") );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- cm_res_acquire( p_conn );\r
- if( !p_conn->was_active )\r
- {\r
- /* Only the side that took the active role can initialte a LAP. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Only the active side of a connection can initiate a LAP.\n") );\r
-\r
- cm_res_release( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- if( p_conn->state != CM_CONN_ESTABLISHED )\r
- {\r
- /* Only allow sending LAP if in the established state. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid connection state (%d) for sending LAP.\n",\r
- p_conn->state) );\r
-\r
- cm_res_release( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- /*\r
- * TODO:Should we check for APM support available flag from REP?\r
- */\r
-\r
- /*\r
- * Copy the new alternate path into our the connection so that we can\r
- * use it when we get a successful APR.\r
- */\r
- cl_memcpy( &p_conn->new_alt_path, p_cm_lap->p_alt_path,\r
- sizeof(ib_path_rec_t) );\r
-\r
- status = __validate_lap_path( p_conn, &p_port_cm, &ack_delay );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__validate_lap_path returned %s.\n", ib_get_err_str(status)) );\r
-\r
- cm_res_release( p_conn );\r
- return status;\r
- }\r
-\r
- /* Format the LAP path info. */\r
- status = __format_mad_lap_path( p_cm_lap->p_alt_path, ack_delay,\r
- &p_conn->mads.lap.alternate_path );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__format_mad_lap_path returned %s.\n", ib_get_err_str(status)) );\r
-\r
- cm_res_release( p_conn );\r
- return status;\r
- }\r
-\r
- p_conn->mads.lap.hdr.attr_id = CM_LAP_ATTR_ID;\r
- p_conn->mads.lap.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.lap.remote_comm_id = p_conn->remote_comm_id;\r
- conn_lap_set_remote_qpn( p_conn->remote_qpn, &p_conn->mads.lap );\r
- conn_lap_set_resp_timeout( p_cm_lap->remote_resp_timeout,\r
- &p_conn->mads.lap );\r
-\r
- status = conn_lap_set_pdata( p_cm_lap->p_lap_pdata, p_cm_lap->lap_length,\r
- &p_conn->mads.lap );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("lap pdata invalid.\n") );\r
-\r
- cm_res_release( p_conn );\r
- return status;\r
- }\r
-\r
- conn_lap_clr_rsvd_fields( &p_conn->mads.lap );\r
-\r
- status = __cm_send( p_port_cm, p_conn );\r
- cm_res_release( p_conn );\r
- return status;\r
-}\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_apr(\r
- IN const ib_cm_handle_t h_cm_lap,\r
- IN const ib_cm_apr_t* const p_cm_apr )\r
-{\r
- al_conn_t *p_conn;\r
- cm_port_agent_t *p_port_cm;\r
- uint8_t ack_delay;\r
- ib_api_status_t status;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = h_cm_lap;\r
-\r
- cm_res_acquire( p_conn );\r
- if( p_conn->was_active )\r
- {\r
- /* Only the side that took the active role can initiate a LAP. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Only the inactive side of a connection can initiate a APR.\n") );\r
-\r
- cm_res_release( p_conn );\r
- return IB_UNSUPPORTED;\r
- }\r
-\r
- /* There is no MRA that follows a LAP */\r
- if( p_conn->state != CM_CONN_LAP_RCVD )\r
- {\r
- /* Only allow sending LAP if in the established state. */\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("Invalid connection state (%d) for sending APR.\n",\r
- p_conn->state) );\r
-\r
- cm_res_release( p_conn );\r
- return IB_INVALID_STATE;\r
- }\r
-\r
- status = __validate_lap_path( p_conn, &p_port_cm, &ack_delay );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__validate_lap_path returned %s.\n", ib_get_err_str(status)) );\r
-\r
- cm_res_release( p_conn );\r
- return status;\r
- }\r
-\r
- /* Load alt path into QP */\r
- status = __cm_lap_qp( p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("lap failed on qp with %s.\n", ib_get_err_str(status)) );\r
-\r
- cm_res_release( p_conn );\r
- return status;\r
- }\r
-\r
- /* format APR MAD */\r
- p_conn->mads.apr.hdr.attr_id = CM_APR_ATTR_ID;\r
- p_conn->mads.apr.local_comm_id = p_conn->local_comm_id;\r
- p_conn->mads.apr.remote_comm_id = p_conn->remote_comm_id;\r
- p_conn->mads.apr.status = p_cm_apr->apr_status;\r
- status = conn_apr_set_apr_info( p_cm_apr->p_info->data,\r
- p_cm_apr->info_length, &p_conn->mads.apr );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("apr_info invalid\n") );\r
-\r
- cm_res_release( p_conn );\r
- return status;\r
- }\r
- status = conn_apr_set_pdata( p_cm_apr->p_apr_pdata,\r
- p_cm_apr->apr_length, &p_conn->mads.apr );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("apr pdata invalid\n") );\r
-\r
- cm_res_release( p_conn );\r
- return status;\r
- }\r
-\r
- conn_apr_clr_rsvd_fields( &p_conn->mads.apr );\r
- \r
- status = __cm_send( p_port_cm, p_conn );\r
- cm_res_release( p_conn );\r
- return status;\r
-}\r
-\r
-\r
-\r
-/*\r
- * Send a DREQ to disconnection a connection. This call must be called while\r
- * holding the lock to the corresponding connection object.\r
- */\r
-ib_api_status_t\r
-cm_conn_dreq(\r
- IN const ib_cm_dreq_t* const p_cm_dreq )\r
-{\r
- al_conn_t *p_conn;\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- p_conn = ((al_conn_qp_t*)p_cm_dreq->h_qp)->p_conn;\r
-\r
- /* Save the callback to invoke for when the DREP is received. */\r
- p_conn->pfn_cm_drep_cb = p_cm_dreq->pfn_cm_drep_cb;\r
-\r
- /* Format the DREQ. */\r
- __format_mad_dreq( p_cm_dreq, p_conn );\r
-\r
- /* Find the port CM on which to send the mad. */\r
- status = __get_port_attr( &p_conn->path[p_conn->idx_primary].sgid,\r
- p_conn->path[p_conn->idx_primary].slid, &p_port_cm, NULL );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__get_port_attr for DREQ returned %s.\n",\r
- ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Send the DREQ. */\r
- status = __cm_send( p_port_cm, p_conn );\r
- if( status != IB_SUCCESS )\r
- {\r
- AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
- ("__cm_send for DREQ returned %s.\n", ib_get_err_str(status)) );\r
- return status;\r
- }\r
-\r
- /* Set the state. */\r
- p_conn->state = CM_CONN_DREQ_SENT;\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return IB_SUCCESS;\r
-}\r
-\r
-\r
-\r
-ib_api_status_t\r
-cm_conn_drep(\r
- IN const ib_cm_drep_t* const p_cm_drep,\r
- OUT al_conn_t* const p_conn )\r
-{\r
- ib_api_status_t status;\r
- cm_port_agent_t *p_port_cm;\r
-\r
- AL_ENTER( AL_DBG_CM );\r
-\r
- /* Format the DREP. */\r
- __format_mad_drep( p_cm_drep, p_conn );\r
-\r
- /*\r
- * Release the QP. If sending the DREP fails, the other side will\r
- * either repeat the DREQ, which will give the DREP another chance.\r
- */\r
- __conn_release( p_conn );\r
-\r
- /* Get the port CM on which to send. */\r
- status = __get_port_attr( &p_conn->path[p_conn->idx_primary].sgid,\r
- p_conn->path[p_conn->idx_primary].slid, &p_port_cm, NULL );\r
- if( status == IB_SUCCESS )\r
- {\r
- /*\r
- * Send the DREP. We don't check status because the protocol\r
- * handles transmission failures (like Aamco).\r
- */\r
- __cm_send( p_port_cm, p_conn );\r
- }\r
-\r
- AL_EXIT( AL_DBG_CM );\r
- return status;\r
-}\r