2 * Copyright (c) 2005 SilverStorm Technologies. All rights reserved.
\r
3 * Portions Copyright (c) 2008 Microsoft Corporation. All rights reserved.
\r
5 * This software is available to you under the OpenIB.org BSD license
\r
8 * Redistribution and use in source and binary forms, with or
\r
9 * without modification, are permitted provided that the following
\r
10 * conditions are met:
\r
12 * - Redistributions of source code must retain the above
\r
13 * copyright notice, this list of conditions and the following
\r
16 * - Redistributions in binary form must reproduce the above
\r
17 * copyright notice, this list of conditions and the following
\r
18 * disclaimer in the documentation and/or other materials
\r
19 * provided with the distribution.
\r
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
\r
25 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
\r
26 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
34 #include <iba/ib_al.h>
\r
35 #include <complib/cl_vector.h>
\r
36 #include <complib/cl_rbmap.h>
\r
37 #include <complib/cl_qmap.h>
\r
38 #include <complib/cl_spinlock.h>
\r
39 #include <iba/ib_cm_ifc.h>
\r
40 #include "al_common.h"
\r
41 #include "al_cm_cep.h"
\r
42 #include "al_cm_conn.h"
\r
43 #include "al_cm_sidr.h"
\r
44 #include "al_debug.h"
\r
45 #if defined(EVENT_TRACING)
\r
49 #include "al_cm_cep.tmh"
\r
51 #include "ib_common.h"
\r
60 * The vector object uses a list item at the front of the buffers
\r
61 * it allocates. Take the list item into account so that allocations
\r
62 * are for full page sizes.
\r
64 #define CEP_CID_MIN \
\r
65 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
66 #define CEP_CID_GROW \
\r
67 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
70 * We reserve the upper byte of the connection ID as a revolving counter so
\r
71 * that connections that are retried by the client change connection ID.
\r
72 * This counter is never zero, so it is OK to use all CIDs since we will never
\r
73 * have a full CID (base + counter) that is zero.
\r
74 * See the IB spec, section 12.9.8.7 for details about REJ retry.
\r
76 #define CEP_MAX_CID (0x00FFFFFF)
\r
77 #define CEP_MAX_CID_MASK (0x00FFFFFF)
\r
79 #define CEP_MAD_SQ_DEPTH (128)
\r
80 #define CEP_MAD_RQ_DEPTH (1) /* ignored. */
\r
81 #define CEP_MAD_SQ_SGE (1)
\r
82 #define CEP_MAD_RQ_SGE (1) /* ignored. */
\r
85 /* Global connection manager object. */
\r
86 typedef struct _al_cep_mgr
\r
94 /* Bitmap of CEPs, indexed by CID. */
\r
95 cl_vector_t cid_vector;
\r
98 /* List of active listens. */
\r
99 cl_rbmap_t listen_map;
\r
101 /* Map of CEP by remote CID and CA GUID. */
\r
102 cl_rbmap_t conn_id_map;
\r
103 /* Map of CEP by remote QPN, used for stale connection matching. */
\r
104 cl_rbmap_t conn_qp_map;
\r
106 NPAGED_LOOKASIDE_LIST cep_pool;
\r
107 NPAGED_LOOKASIDE_LIST req_pool;
\r
110 * Periodically walk the list of connections in the time wait state
\r
111 * and flush them as appropriate.
\r
113 cl_timer_t timewait_timer;
\r
114 cl_qlist_t timewait_list;
\r
116 ib_pnp_handle_t h_pnp;
\r
121 /* Per-port CM object. */
\r
122 typedef struct _cep_port_agent
\r
126 cl_map_item_t item;
\r
128 ib_ca_handle_t h_ca;
\r
129 ib_pd_handle_t h_pd;
\r
130 ib_qp_handle_t h_qp;
\r
131 ib_pool_key_t pool_key;
\r
132 ib_mad_svc_handle_t h_mad_svc;
\r
142 * Note: the REQ, REP, and LAP values must be 1, 2, and 4 respectively.
\r
143 * This allows shifting 1 << msg_mraed from an MRA to figure out for what
\r
144 * message the MRA was sent for.
\r
146 #define CEP_STATE_RCVD 0x10000000
\r
147 #define CEP_STATE_SENT 0x20000000
\r
148 #define CEP_STATE_MRA 0x01000000
\r
149 #define CEP_STATE_REQ 0x00000001
\r
150 #define CEP_STATE_REP 0x00000002
\r
151 #define CEP_STATE_LAP 0x00000004
\r
152 #define CEP_STATE_RTU 0x00000008
\r
153 #define CEP_STATE_DREQ 0x00000010
\r
154 #define CEP_STATE_DREP 0x00000020
\r
155 #define CEP_STATE_DESTROYING 0x00010000
\r
156 #define CEP_STATE_USER 0x00020000
\r
158 #define CEP_MSG_MASK 0x000000FF
\r
159 #define CEP_OP_MASK 0xF0000000
\r
161 #define CEP_STATE_PREP 0x00100000
\r
163 /* States match CM state transition diagrams from spec. */
\r
164 typedef enum _cep_state
\r
168 CEP_STATE_ESTABLISHED,
\r
169 CEP_STATE_TIMEWAIT,
\r
170 CEP_STATE_SREQ_SENT,
\r
171 CEP_STATE_SREQ_RCVD,
\r
173 CEP_STATE_DESTROY = CEP_STATE_DESTROYING,
\r
174 CEP_STATE_PRE_REQ = CEP_STATE_IDLE | CEP_STATE_PREP,
\r
175 CEP_STATE_REQ_RCVD = CEP_STATE_REQ | CEP_STATE_RCVD,
\r
176 CEP_STATE_PRE_REP = CEP_STATE_REQ_RCVD | CEP_STATE_PREP,
\r
177 CEP_STATE_REQ_SENT = CEP_STATE_REQ | CEP_STATE_SENT,
\r
178 CEP_STATE_REQ_MRA_RCVD = CEP_STATE_REQ_SENT | CEP_STATE_MRA,
\r
179 CEP_STATE_REQ_MRA_SENT = CEP_STATE_REQ_RCVD | CEP_STATE_MRA,
\r
180 CEP_STATE_PRE_REP_MRA_SENT = CEP_STATE_REQ_MRA_SENT | CEP_STATE_PREP,
\r
181 CEP_STATE_REP_RCVD = CEP_STATE_REP | CEP_STATE_RCVD,
\r
182 CEP_STATE_REP_SENT = CEP_STATE_REP | CEP_STATE_SENT,
\r
183 CEP_STATE_REP_MRA_RCVD = CEP_STATE_REP_SENT | CEP_STATE_MRA,
\r
184 CEP_STATE_REP_MRA_SENT = CEP_STATE_REP_RCVD | CEP_STATE_MRA,
\r
185 CEP_STATE_LAP_RCVD = CEP_STATE_LAP | CEP_STATE_RCVD,
\r
186 CEP_STATE_PRE_APR = CEP_STATE_LAP_RCVD | CEP_STATE_PREP,
\r
187 CEP_STATE_LAP_SENT = CEP_STATE_LAP | CEP_STATE_SENT,
\r
188 CEP_STATE_LAP_MRA_RCVD = CEP_STATE_LAP_SENT | CEP_STATE_MRA,
\r
189 CEP_STATE_LAP_MRA_SENT = CEP_STATE_LAP_RCVD | CEP_STATE_MRA,
\r
190 CEP_STATE_PRE_APR_MRA_SENT = CEP_STATE_LAP_MRA_SENT | CEP_STATE_PREP,
\r
191 CEP_STATE_DREQ_SENT = CEP_STATE_DREQ | CEP_STATE_SENT,
\r
192 CEP_STATE_DREQ_RCVD = CEP_STATE_DREQ | CEP_STATE_RCVD,
\r
193 CEP_STATE_DREQ_DESTROY = CEP_STATE_DREQ_SENT | CEP_STATE_DESTROYING
\r
198 /* Active side CEP state transitions:
\r
199 * al_create_cep -> IDLE
\r
200 * al_cep_pre_req -> PRE_REQ
\r
201 * al_cep_send_req -> REQ_SENT
\r
202 * Recv REQ MRA -> REQ_MRA_RCVD
\r
203 * Recv REP -> REP_RCVD
\r
204 * al_cep_mra -> REP_MRA_SENT
\r
205 * al_cep_rtu -> ESTABLISHED
\r
207 * Passive side CEP state transitions:
\r
208 * al_create_cep -> IDLE
\r
209 * Recv REQ -> REQ_RCVD
\r
210 * al_cep_mra* -> REQ_MRA_SENT
\r
211 * al_cep_pre_rep -> PRE_REP
\r
212 * al_cep_mra* -> PRE_REP_MRA_SENT
\r
213 * al_cep_send_rep -> REP_SENT
\r
214 * Recv RTU -> ESTABLISHED
\r
216 * *al_cep_mra can only be called once - either before or after PRE_REP.
\r
219 typedef struct _al_kcep_av
\r
223 uint16_t pkey_index;
\r
228 typedef struct _al_kcep
\r
233 struct _cep_cid *p_cid;
\r
237 /* Port guid for filtering incoming requests. */
\r
240 uint8_t* p_cmp_buf;
\r
241 uint8_t cmp_offset;
\r
246 /* Used to store connection structure with owning AL instance. */
\r
247 cl_list_item_t al_item;
\r
249 /* Flag to indicate whether a user is processing events. */
\r
250 boolean_t signalled;
\r
252 /* Destroy callback. */
\r
253 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
255 ib_mad_element_t *p_mad_head;
\r
256 ib_mad_element_t *p_mad_tail;
\r
257 al_pfn_cep_cb_t pfn_cb;
\r
261 /* MAP item for finding listen CEPs. */
\r
262 cl_rbmap_item_t listen_item;
\r
264 /* Map item for finding CEPs based on remote comm ID & CA GUID. */
\r
265 cl_rbmap_item_t rem_id_item;
\r
267 /* Map item for finding CEPs based on remote QP number. */
\r
268 cl_rbmap_item_t rem_qp_item;
\r
270 /* Communication ID's for the connection. */
\r
271 net32_t local_comm_id;
\r
272 net32_t remote_comm_id;
\r
274 net64_t local_ca_guid;
\r
275 net64_t remote_ca_guid;
\r
277 /* Remote QP, used for stale connection checking. */
\r
278 net32_t remote_qpn;
\r
280 /* Parameters to format QP modification structure. */
\r
284 * Note that we store the requested initiator depth as received in the REQ
\r
285 * and cap it when sending the REP to the actual capabilities of the HCA.
\r
288 uint8_t init_depth;
\r
289 uint8_t rnr_nak_timeout;
\r
292 * Local QP number, used for the "additional check" required
\r
297 /* PKEY to make sure a LAP is on the same partition. */
\r
301 * Primary and alternate path info, used to create the address vectors for
\r
302 * sending MADs, to locate the port CM agent to use for outgoing sends,
\r
303 * and for creating the address vectors for transitioning QPs.
\r
306 uint8_t idx_primary;
\r
308 /* Temporary AV and CEP port GUID used when processing LAP. */
\r
310 uint8_t alt_2pkt_life;
\r
312 /* maxium packet lifetime * 2 of any path used on a connection. */
\r
313 uint8_t max_2pkt_life;
\r
314 /* Given by the REP, used for alternate path setup. */
\r
315 uint8_t target_ack_delay;
\r
316 /* Stored to help calculate the local ACK delay in the LAP. */
\r
317 uint8_t local_ack_delay;
\r
319 /* Volatile to allow using atomic operations for state checks. */
\r
323 * Flag that indicates whether a connection took the active role during
\r
326 boolean_t was_active;
\r
329 * Handle to the sent MAD, used for cancelling. We store the handle to
\r
330 * the mad service so that we can properly cancel. This should not be a
\r
331 * problem since all outstanding sends should be completed before the
\r
332 * mad service completes its destruction and the handle becomes invalid.
\r
334 ib_mad_svc_handle_t h_mad_svc;
\r
335 ib_mad_element_t *p_send_mad;
\r
337 atomic32_t ref_cnt;
\r
339 /* MAD transaction ID to use when sending MADs. */
\r
342 /* Maximum retries per MAD. Set at REQ time, stored to retry LAP. */
\r
343 uint8_t max_cm_retries;
\r
344 /* Timeout value, in milliseconds. Set at REQ time, stored to retry LAP. */
\r
345 uint32_t retry_timeout;
\r
347 /* Timer that will be signalled when the CEP exits timewait. */
\r
348 KTIMER timewait_timer;
\r
349 LARGE_INTEGER timewait_time;
\r
350 cl_list_item_t timewait_item;
\r
353 * Pointer to a formatted MAD. The pre_req, pre_rep and pre_apr calls
\r
354 * allocate and format the MAD, and the send_req, send_rep and send_apr
\r
357 ib_mad_element_t *p_mad;
\r
359 /* Cache the last MAD sent for retransmission. */
\r
365 mad_cm_drep_t drep;
\r
370 * NDI stuff - TODO: manage above core kernel CM code
\r
373 /* IRP list head */
\r
374 LIST_ENTRY irp_que;
\r
376 /* private data of REQ, REP, REJ CM requests */
\r
378 uint8_t pdata[IB_REP_PDATA_SIZE];
\r
383 /* Structures stored in the CID vector. */
\r
384 typedef struct _cep_cid
\r
386 /* Owning AL handle. NULL if invalid. */
\r
387 ib_al_handle_t h_al;
\r
388 /* Pointer to CEP, or index of next free entry if h_al is NULL. */
\r
390 /* For REJ Retry support */
\r
396 /* Global instance of the CM agent. */
\r
397 al_cep_mgr_t *gp_cep_mgr = NULL;
\r
400 static ib_api_status_t
\r
402 IN kcep_t* const p_cep,
\r
403 IN const uint8_t* p_pdata OPTIONAL,
\r
404 IN uint8_t pdata_len,
\r
405 IN OUT mad_cm_drep_t* const p_drep );
\r
407 static ib_api_status_t
\r
409 IN kcep_t* const p_cep,
\r
410 IN ib_mad_element_t* p_mad );
\r
414 IN kcep_t* const p_cep );
\r
416 static inline uint32_t
\r
417 __calc_mad_timeout(
\r
418 IN const uint8_t pkt_life );
\r
422 IN kcep_t* const p_cep );
\r
425 __create_cep( void );
\r
429 IN kcep_t* const p_cep );
\r
433 IN kcep_t* const p_cep );
\r
437 IN kcep_t* const p_cep,
\r
438 IN ib_al_handle_t h_al,
\r
439 IN al_pfn_cep_cb_t pfn_cb,
\r
440 IN void* context );
\r
444 IN kcep_t* const p_cep );
\r
448 IN kcep_t* const p_cep );
\r
452 IN net32_t remote_comm_id,
\r
453 IN net64_t remote_ca_guid );
\r
458 IN net64_t port_guid,
\r
459 IN void *p_pdata );
\r
461 static inline kcep_t*
\r
463 IN ib_al_handle_t h_al OPTIONAL,
\r
466 static inline kcep_t*
\r
468 IN kcep_t* const p_new_cep );
\r
472 IN kcep_t* const p_cep );
\r
476 IN kcep_t* const p_cep );
\r
478 static ib_api_status_t
\r
480 IN kcep_t* const p_cep,
\r
481 IN net16_t attr_id,
\r
482 OUT cep_agent_t** const pp_port_cep,
\r
483 OUT ib_mad_element_t** const pp_mad );
\r
485 static ib_api_status_t
\r
487 IN cep_agent_t* const p_port_cep,
\r
488 IN ib_mad_element_t* const p_mad );
\r
490 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
491 static cep_agent_t*
\r
493 IN const ib_gid_t* const p_gid,
\r
494 IN const net16_t lid,
\r
495 IN const net16_t pkey,
\r
496 OUT uint16_t* const p_pkey_index );
\r
500 OUT net32_t* const p_cid );
\r
503 __process_cep_send_comp(
\r
504 IN cl_async_proc_item_t *p_item );
\r
507 /******************************************************************************
\r
508 * Per-port CEP agent
\r
509 ******************************************************************************/
\r
514 IN ib_mad_t* const p_mad,
\r
515 IN const kcep_t* const p_cep,
\r
516 IN net16_t attr_id )
\r
518 p_mad->base_ver = 1;
\r
519 p_mad->mgmt_class = IB_MCLASS_COMM_MGMT;
\r
520 p_mad->class_ver = IB_MCLASS_CM_VER_2;
\r
521 p_mad->method = IB_MAD_METHOD_SEND;
\r
523 p_mad->class_spec = 0;
\r
524 p_mad->trans_id = p_cep->tid;
\r
525 p_mad->attr_id = attr_id;
\r
527 p_mad->attr_mod = 0;
\r
531 /* Consumes the input MAD. */
\r
534 IN cep_agent_t* const p_port_cep,
\r
535 IN kcep_t* const p_cep,
\r
536 IN ib_mad_element_t* const p_mad,
\r
537 IN ib_rej_status_t reason )
\r
539 mad_cm_rej_t *p_rej;
\r
541 AL_ENTER( AL_DBG_CM );
\r
543 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
545 __format_mad_hdr( p_mad->p_mad_buf, p_cep, CM_REJ_ATTR_ID );
\r
547 p_rej->local_comm_id = p_cep->local_comm_id;
\r
548 p_rej->remote_comm_id = p_cep->remote_comm_id;
\r
549 p_rej->reason = reason;
\r
551 switch( p_cep->state )
\r
553 case CEP_STATE_REQ_RCVD:
\r
554 case CEP_STATE_REQ_MRA_SENT:
\r
555 case CEP_STATE_PRE_REP:
\r
556 case CEP_STATE_PRE_REP_MRA_SENT:
\r
557 conn_rej_set_msg_rejected( 0, p_rej );
\r
560 case CEP_STATE_REP_RCVD:
\r
561 case CEP_STATE_REP_MRA_SENT:
\r
562 conn_rej_set_msg_rejected( 1, p_rej );
\r
566 CL_ASSERT( reason == IB_REJ_TIMEOUT );
\r
567 conn_rej_set_msg_rejected( 2, p_rej );
\r
571 conn_rej_clr_rsvd_fields( p_rej );
\r
572 __cep_send_mad( p_port_cep, p_mad );
\r
574 AL_EXIT( AL_DBG_CM );
\r
580 IN cep_agent_t* const p_port_cep,
\r
581 IN kcep_t* const p_cep,
\r
582 IN const ib_mad_element_t* const p_mad )
\r
584 ib_api_status_t status;
\r
585 ib_mad_element_t *p_rej_mad;
\r
586 ib_mad_t *p_mad_buf;
\r
589 AL_ENTER( AL_DBG_CM );
\r
591 status = ib_get_mad( p_port_cep->pool_key, MAD_BLOCK_SIZE, &p_rej_mad );
\r
592 if( status != IB_SUCCESS )
\r
594 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
595 ("ib_get_mad returned %s\n", ib_get_err_str( status )) );
\r
599 /* Save the buffer pointers from the new element. */
\r
600 p_mad_buf = p_rej_mad->p_mad_buf;
\r
601 p_grh = p_rej_mad->p_grh;
\r
604 * Copy the input MAD element to the reject - this gives us
\r
605 * all appropriate addressing information.
\r
607 cl_memcpy( p_rej_mad, p_mad, sizeof(ib_mad_element_t) );
\r
608 cl_memcpy( p_grh, p_mad->p_grh, sizeof(ib_grh_t) );
\r
610 /* Restore the buffer pointers now that the copy is complete. */
\r
611 p_rej_mad->p_mad_buf = p_mad_buf;
\r
612 p_rej_mad->p_grh = p_grh;
\r
614 status = conn_rej_set_pdata( NULL, 0, (mad_cm_rej_t*)p_mad_buf );
\r
615 CL_ASSERT( status == IB_SUCCESS );
\r
617 /* Copy the local CA GUID into the ARI. */
\r
618 switch( p_mad->p_mad_buf->attr_id )
\r
620 case CM_REQ_ATTR_ID:
\r
621 status = conn_rej_set_ari(
\r
622 (uint8_t*)&p_cep->local_ca_guid,
\r
623 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
624 CL_ASSERT( status == IB_SUCCESS );
\r
625 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
628 case CM_REP_ATTR_ID:
\r
629 status = conn_rej_set_ari(
\r
630 (uint8_t*)&p_cep->local_ca_guid,
\r
631 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
632 CL_ASSERT( status == IB_SUCCESS );
\r
633 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
637 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
638 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID );
\r
639 ib_put_mad( p_rej_mad );
\r
643 AL_EXIT( AL_DBG_CM );
\r
649 IN cep_agent_t* const p_port_cep,
\r
650 IN ib_mad_element_t* const p_mad,
\r
651 IN const ib_rej_status_t reason )
\r
653 mad_cm_req_t *p_req;
\r
654 mad_cm_rej_t *p_rej;
\r
656 AL_ENTER( AL_DBG_CM );
\r
658 CL_ASSERT( p_port_cep );
\r
659 CL_ASSERT( p_mad );
\r
660 CL_ASSERT( reason != 0 );
\r
662 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
663 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
666 * Format the reject information, overwriting the REQ data and send
\r
669 p_rej->hdr.attr_id = CM_REJ_ATTR_ID;
\r
670 p_rej->remote_comm_id = p_req->local_comm_id;
\r
671 p_rej->local_comm_id = 0;
\r
672 conn_rej_set_msg_rejected( 0, p_rej );
\r
673 p_rej->reason = reason;
\r
674 conn_rej_set_ari( NULL, 0, p_rej );
\r
675 conn_rej_set_pdata( NULL, 0, p_rej );
\r
676 conn_rej_clr_rsvd_fields( p_rej );
\r
678 p_mad->retry_cnt = 0;
\r
679 p_mad->send_opt = 0;
\r
680 p_mad->timeout_ms = 0;
\r
681 p_mad->resp_expected = FALSE;
\r
683 __cep_send_mad( p_port_cep, p_mad );
\r
685 AL_EXIT( AL_DBG_CM );
\r
691 IN kcep_t* const p_cep,
\r
692 IN const mad_cm_req_t* const p_req,
\r
693 IN const uint8_t idx )
\r
695 cep_agent_t *p_port_cep;
\r
696 const req_path_info_t *p_path;
\r
698 AL_ENTER( AL_DBG_CM );
\r
700 CL_ASSERT( p_cep );
\r
701 CL_ASSERT( p_req );
\r
703 cl_memclr( &p_cep->av[idx], sizeof(kcep_av_t) );
\r
705 p_path = &((&p_req->primary_path)[idx]);
\r
707 p_port_cep = __find_port_cep( &p_path->remote_gid,
\r
708 p_path->remote_lid, p_req->pkey, &p_cep->av[idx].pkey_index );
\r
712 p_cep->local_ca_guid = 0;
\r
713 AL_EXIT( AL_DBG_CM );
\r
718 p_cep->local_ca_guid = p_port_cep->h_ca->obj.p_ci_ca->verbs.guid;
\r
720 /* Check that CA GUIDs match if formatting the alternate path. */
\r
722 p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
724 AL_EXIT( AL_DBG_CM );
\r
729 * Pkey indeces must match if formating the alternat path - the QP
\r
730 * modify structure only allows for a single PKEY index to be specified.
\r
733 p_cep->av[0].pkey_index != p_cep->av[1].pkey_index )
\r
735 AL_EXIT( AL_DBG_CM );
\r
739 p_cep->av[idx].port_guid = p_port_cep->port_guid;
\r
740 p_cep->av[idx].attr.port_num = p_port_cep->port_num;
\r
742 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Received REQ SL %d", conn_req_path_get_svc_lvl( p_path )) );
\r
743 p_cep->av[idx].attr.sl = conn_req_path_get_svc_lvl( p_path );
\r
744 p_cep->av[idx].attr.dlid = p_path->local_lid;
\r
746 if( !conn_req_path_get_subn_lcl( p_path ) )
\r
748 p_cep->av[idx].attr.grh_valid = TRUE;
\r
749 p_cep->av[idx].attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
750 1, p_path->traffic_class, conn_req_path_get_flow_lbl( p_path ) );
\r
751 p_cep->av[idx].attr.grh.hop_limit = p_path->hop_limit;
\r
752 p_cep->av[idx].attr.grh.dest_gid = p_path->local_gid;
\r
753 p_cep->av[idx].attr.grh.src_gid = p_path->remote_gid;
\r
757 p_cep->av[idx].attr.grh_valid = FALSE;
\r
759 p_cep->av[idx].attr.static_rate = conn_req_path_get_pkt_rate( p_path );
\r
760 p_cep->av[idx].attr.path_bits =
\r
761 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
764 * Note that while we never use the connected AV attributes internally,
\r
765 * we store them so we can pass them back to users.
\r
767 p_cep->av[idx].attr.conn.path_mtu = conn_req_get_mtu( p_req );
\r
768 p_cep->av[idx].attr.conn.local_ack_timeout =
\r
769 conn_req_path_get_lcl_ack_timeout( p_path );
\r
770 p_cep->av[idx].attr.conn.seq_err_retry_cnt =
\r
771 conn_req_get_retry_cnt( p_req );
\r
772 p_cep->av[idx].attr.conn.rnr_retry_cnt =
\r
773 conn_req_get_rnr_retry_cnt( p_req );
\r
775 AL_EXIT( AL_DBG_CM );
\r
780 * + Validates the path information provided in the REQ and stores the
\r
781 * associated CA attributes and port indeces.
\r
782 * + Transitions a connection object from active to passive in the peer case.
\r
783 * + Sets the path information in the connection and sets the CA GUID
\r
784 * in the REQ callback record.
\r
788 IN OUT kcep_t* const p_cep,
\r
789 IN OUT mad_cm_req_t* const p_req )
\r
791 AL_ENTER( AL_DBG_CM );
\r
793 p_cep->state = CEP_STATE_REQ_RCVD;
\r
794 p_cep->was_active = FALSE;
\r
796 p_cep->sid = p_req->sid;
\r
798 /* Store pertinent information in the connection. */
\r
799 p_cep->remote_comm_id = p_req->local_comm_id;
\r
800 p_cep->remote_ca_guid = p_req->local_ca_guid;
\r
802 p_cep->remote_qpn = conn_req_get_lcl_qpn( p_req );
\r
803 p_cep->local_qpn = 0;
\r
805 p_cep->retry_timeout =
\r
806 __calc_mad_timeout( conn_req_get_lcl_resp_timeout( p_req ) );
\r
808 /* Store the retry count. */
\r
809 p_cep->max_cm_retries = conn_req_get_max_cm_retries( p_req );
\r
812 * Copy the paths from the req_rec into the connection for
\r
813 * future use. Note that if the primary path is invalid,
\r
814 * the REP will fail.
\r
816 __format_req_av( p_cep, p_req, 0 );
\r
818 if( p_req->alternate_path.local_lid )
\r
819 __format_req_av( p_cep, p_req, 1 );
\r
821 cl_memclr( &p_cep->av[1], sizeof(kcep_av_t) );
\r
823 p_cep->idx_primary = 0;
\r
825 /* Store the maximum packet lifetime, used to calculate timewait. */
\r
826 p_cep->max_2pkt_life = conn_req_path_get_lcl_ack_timeout( &p_req->primary_path );
\r
827 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life,
\r
828 conn_req_path_get_lcl_ack_timeout( &p_req->alternate_path ) );
\r
831 * Make sure the target ack delay is cleared - the above
\r
832 * "packet life" includes it.
\r
834 p_cep->target_ack_delay = 0;
\r
836 /* Store the requested initiator depth. */
\r
837 p_cep->resp_res = conn_req_get_init_depth( p_req );
\r
840 * Store the provided responder resources. These turn into the local
\r
841 * QP's initiator depth.
\r
843 p_cep->init_depth = conn_req_get_resp_res( p_req );
\r
845 p_cep->sq_psn = conn_req_get_starting_psn( p_req );
\r
847 p_cep->tid = p_req->hdr.trans_id;
\r
848 /* copy mad info for cm handoff */
\r
849 /* TODO: Do need to support CM handoff? */
\r
850 //p_cep->mads.req = *p_req;
\r
852 /* Cache the private data. */
\r
853 p_cep->psize = IB_REQ_PDATA_SIZE;
\r
854 memcpy( p_cep->pdata, p_req->pdata, IB_REQ_PDATA_SIZE );
\r
856 AL_EXIT( AL_DBG_CM );
\r
860 /* Must be called with the CEP lock held. */
\r
863 IN cep_agent_t* const p_port_cep,
\r
864 IN kcep_t* const p_cep,
\r
865 IN ib_mad_element_t* const p_mad )
\r
867 AL_ENTER( AL_DBG_CM );
\r
869 CL_ASSERT( p_port_cep );
\r
870 CL_ASSERT( p_cep );
\r
871 CL_ASSERT( p_mad );
\r
873 /* Repeat the last mad sent for the connection. */
\r
874 switch( p_cep->state )
\r
876 case CEP_STATE_REQ_MRA_SENT: /* resend MRA(REQ) */
\r
877 case CEP_STATE_REP_MRA_SENT: /* resend MRA(REP) */
\r
878 case CEP_STATE_LAP_MRA_SENT: /* resend MRA(LAP) */
\r
879 case CEP_STATE_ESTABLISHED: /* resend RTU */
\r
880 case CEP_STATE_TIMEWAIT: /* resend the DREP */
\r
881 cl_memcpy( p_mad->p_mad_buf, &p_cep->mads, MAD_BLOCK_SIZE );
\r
882 p_mad->send_context1 = NULL;
\r
883 p_mad->send_context2 = NULL;
\r
884 __cep_send_mad( p_port_cep, p_mad );
\r
888 /* Return the MAD to the mad pool */
\r
889 ib_put_mad( p_mad );
\r
893 AL_EXIT( AL_DBG_CM );
\r
897 static ib_api_status_t
\r
899 IN kcep_t* const p_cep,
\r
900 IN ib_mad_element_t* const p_mad )
\r
902 ib_api_status_t status;
\r
903 mad_cm_rej_t *p_rej;
\r
905 AL_ENTER( AL_DBG_CM );
\r
909 ASSERT( p_mad->p_mad_buf );
\r
911 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
913 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
914 ("Request rejected p_rej %p, reason - %d.\n",
\r
915 p_rej, cl_ntoh16(p_rej->reason) ) );
\r
917 switch( p_cep->state )
\r
919 case CEP_STATE_REQ_SENT:
\r
921 * Ignore rejects with the status set to IB_REJ_INVALID_SID. We will
\r
922 * continue to retry (up to max_cm_retries) to connect to the remote
\r
923 * side. This is required to support peer-to-peer connections.
\r
925 if( p_cep->p2p && p_rej->reason == IB_REJ_INVALID_SID )
\r
927 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
928 ("Request rejected (invalid SID) - retrying.\n") );
\r
933 case CEP_STATE_REP_SENT:
\r
934 case CEP_STATE_REQ_MRA_RCVD:
\r
935 case CEP_STATE_REP_MRA_RCVD:
\r
936 /* Cancel any outstanding MAD. */
\r
937 if( p_cep->p_send_mad )
\r
939 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
940 p_cep->p_send_mad = NULL;
\r
944 case CEP_STATE_REQ_RCVD:
\r
945 case CEP_STATE_REP_RCVD:
\r
946 case CEP_STATE_REQ_MRA_SENT:
\r
947 case CEP_STATE_REP_MRA_SENT:
\r
948 case CEP_STATE_PRE_REP:
\r
949 case CEP_STATE_PRE_REP_MRA_SENT:
\r
950 if( p_cep->state & CEP_STATE_PREP )
\r
952 CL_ASSERT( p_cep->p_mad );
\r
953 ib_put_mad( p_cep->p_mad );
\r
954 p_cep->p_mad = NULL;
\r
956 /* Abort connection establishment. No transition to timewait. */
\r
957 __remove_cep( p_cep );
\r
958 p_cep->state = CEP_STATE_IDLE;
\r
961 case CEP_STATE_ESTABLISHED:
\r
962 case CEP_STATE_LAP_RCVD:
\r
963 case CEP_STATE_LAP_SENT:
\r
964 case CEP_STATE_LAP_MRA_RCVD:
\r
965 case CEP_STATE_LAP_MRA_SENT:
\r
966 case CEP_STATE_PRE_APR:
\r
967 case CEP_STATE_PRE_APR_MRA_SENT:
\r
968 if( p_cep->state & CEP_STATE_PREP )
\r
970 CL_ASSERT( p_cep->p_mad );
\r
971 ib_put_mad( p_cep->p_mad );
\r
972 p_cep->p_mad = NULL;
\r
974 p_cep->state = CEP_STATE_TIMEWAIT;
\r
975 __insert_timewait( p_cep );
\r
979 /* Ignore the REJ. */
\r
980 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REJ received in invalid state.\n") );
\r
982 ib_put_mad( p_mad );
\r
983 AL_EXIT( AL_DBG_CM );
\r
984 return IB_NO_MATCH;
\r
987 /* Cache the private data. */
\r
988 p_cep->psize = IB_REJ_PDATA_SIZE;
\r
989 memcpy( p_cep->pdata, p_rej->pdata, IB_REJ_PDATA_SIZE );
\r
991 status = __cep_queue_mad( p_cep, p_mad );
\r
993 AL_EXIT( AL_DBG_CM );
\r
998 static ib_api_status_t
\r
1000 IN kcep_t* const p_cep )
\r
1002 ib_api_status_t status;
\r
1003 cep_agent_t *p_port_cep;
\r
1004 ib_mad_element_t *p_mad;
\r
1005 mad_cm_rej_t *p_rej;
\r
1007 status = __cep_get_mad( p_cep, CM_REJ_ATTR_ID, &p_port_cep, &p_mad );
\r
1008 if( status != IB_SUCCESS )
\r
1011 p_rej = ib_get_mad_buf( p_mad );
\r
1013 conn_rej_set_ari( NULL, 0, p_rej );
\r
1014 conn_rej_set_pdata( NULL, 0, p_rej );
\r
1016 p_rej->local_comm_id = p_cep->remote_comm_id;
\r
1017 p_rej->remote_comm_id = p_cep->local_comm_id;
\r
1018 p_rej->reason = IB_REJ_STALE_CONN;
\r
1020 switch( p_cep->state )
\r
1022 case CEP_STATE_REQ_RCVD:
\r
1023 case CEP_STATE_REQ_MRA_SENT:
\r
1024 case CEP_STATE_PRE_REP:
\r
1025 case CEP_STATE_PRE_REP_MRA_SENT:
\r
1026 conn_rej_set_msg_rejected( 0, p_rej );
\r
1029 case CEP_STATE_REQ_SENT:
\r
1030 case CEP_STATE_REP_RCVD:
\r
1031 case CEP_STATE_REP_MRA_SENT:
\r
1032 conn_rej_set_msg_rejected( 1, p_rej );
\r
1036 conn_rej_set_msg_rejected( 2, p_rej );
\r
1039 conn_rej_clr_rsvd_fields( p_rej );
\r
1041 return __process_rej( p_cep, p_mad );
\r
1047 IN cep_agent_t* const p_port_cep,
\r
1048 IN ib_mad_element_t* const p_mad )
\r
1050 ib_api_status_t status = IB_SUCCESS;
\r
1051 mad_cm_req_t *p_req;
\r
1052 kcep_t *p_cep, *p_new_cep, *p_stale_cep = NULL;
\r
1053 KLOCK_QUEUE_HANDLE hdl;
\r
1054 ib_rej_status_t reason;
\r
1056 AL_ENTER( AL_DBG_CM );
\r
1058 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1060 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
1062 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1063 ("REQ: comm_id (x%x) qpn (x%x) received\n",
\r
1064 p_req->local_comm_id, conn_req_get_lcl_qpn( p_req )) );
\r
1066 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1068 if( conn_req_get_qp_type( p_req ) > IB_QPT_UNRELIABLE_CONN ||
\r
1069 conn_req_get_lcl_qpn( p_req ) == 0 )
\r
1071 /* Reserved value. Reject. */
\r
1072 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid transport type received.\n") );
\r
1073 reason = IB_REJ_INVALID_XPORT;
\r
1077 /* Match against pending connections using remote comm ID and CA GUID. */
\r
1078 p_cep = __lookup_by_id( p_req->local_comm_id, p_req->local_ca_guid );
\r
1081 /* Already received the REQ. */
\r
1082 switch( p_cep->state )
\r
1084 case CEP_STATE_REQ_MRA_SENT:
\r
1085 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1088 case CEP_STATE_TIMEWAIT:
\r
1089 case CEP_STATE_DESTROY:
\r
1090 /* Send a reject. */
\r
1091 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1092 ("REQ received for connection in TIME_WAIT state.\n") );
\r
1093 __reject_req( p_port_cep, p_mad, IB_REJ_STALE_CONN );
\r
1098 * Let regular retries repeat the MAD. If our last message was
\r
1099 * dropped, resending only adds to the congestion. If it wasn't
\r
1100 * dropped, then the remote CM will eventually process it, and
\r
1101 * we'd just be adding traffic.
\r
1103 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Duplicate REQ received.\n") );
\r
1104 ib_put_mad( p_mad );
\r
1106 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1107 AL_EXIT( AL_DBG_CM );
\r
1112 * Allocate a new CEP for the new request. This will
\r
1113 * prevent multiple identical REQs from queueing up for processing.
\r
1115 p_new_cep = __create_cep();
\r
1118 /* Reject the request for insufficient resources. */
\r
1119 reason = IB_REJ_INSUF_RESOURCES;
\r
1120 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
1121 ("al_create_cep failed\nREJ sent for insufficient resources.\n") );
\r
1125 __save_wire_req( p_new_cep, p_req );
\r
1128 * Match against listens using SID and compare data, also provide the receiving
\r
1129 * MAD service's port GUID so we can properly filter.
\r
1131 p_cep = __lookup_listen( p_req->sid, p_port_cep->port_guid, p_req->pdata );
\r
1134 __bind_cep( p_new_cep, p_cep->p_cid->h_al, p_cep->pfn_cb, NULL );
\r
1136 /* Add the new CEP to the map so that repeated REQs match up. */
\r
1137 p_stale_cep = __insert_cep( p_new_cep );
\r
1138 if( p_stale_cep != p_new_cep )
\r
1140 /* Duplicate - must be a stale connection. */
\r
1141 reason = IB_REJ_STALE_CONN;
\r
1142 /* Fail the local stale CEP. */
\r
1143 status = __process_stale( p_stale_cep );
\r
1147 /* __cep_queue_mad may complete a pending IRP */
\r
1148 p_mad->send_context1 = p_new_cep;
\r
1151 * Queue the mad - the return value indicates whether we should
\r
1152 * invoke the callback.
\r
1154 status = __cep_queue_mad( p_cep, p_mad );
\r
1161 case IB_UNSUPPORTED:
\r
1162 p_mad->send_context1 = NULL;
\r
1163 reason = IB_REJ_USER_DEFINED;
\r
1167 p_mad->send_context1 = NULL;
\r
1168 reason = IB_REJ_INSUF_RESOURCES;
\r
1174 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("No listens active!\n") );
\r
1176 /* Match against peer-to-peer requests using SID and compare data. */
\r
1177 //p_cep = __lookup_peer();
\r
1180 // p_mad->send_context2 = NULL;
\r
1181 // p_list_item = cl_qlist_find_from_head( &gp_cep_mgr->pending_list,
\r
1182 // __match_peer, p_req );
\r
1183 // if( p_list_item != cl_qlist_end( &gp_cep_mgr->pending_list ) )
\r
1185 // KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1186 // p_conn = PARENT_STRUCT( p_list_item, kcep_t, map_item );
\r
1187 // __peer_req( p_port_cep, p_conn, p_async_mad->p_mad );
\r
1188 // cl_free( p_async_mad );
\r
1189 // AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
1190 // ("REQ matched a peer-to-peer request.\n") );
\r
1193 // reason = IB_REJ_INVALID_SID;
\r
1198 /* No match found. Reject. */
\r
1199 reason = IB_REJ_INVALID_SID;
\r
1200 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REQ received but no match found.\n") );
\r
1205 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1207 /* Process any queued MADs for the CEP. */
\r
1208 if( status == IB_SUCCESS )
\r
1209 __process_cep( p_cep );
\r
1211 AL_EXIT( AL_DBG_CM );
\r
1215 __unbind_cep( p_new_cep );
\r
1219 * Move the CEP in the idle state so that we don't send a reject
\r
1220 * for it when cleaning up. Also clear the RQPN and RCID so that
\r
1221 * we don't try to remove it from our maps (since it isn't inserted).
\r
1223 p_new_cep->state = CEP_STATE_IDLE;
\r
1224 p_new_cep->remote_comm_id = 0;
\r
1225 p_new_cep->remote_qpn = 0;
\r
1226 __cleanup_cep( p_new_cep );
\r
1229 __reject_req( p_port_cep, p_mad, reason );
\r
1231 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1233 if( reason == IB_REJ_STALE_CONN && status == IB_SUCCESS )
\r
1234 __process_cep( p_stale_cep );
\r
1236 AL_EXIT( AL_DBG_CM );
\r
1242 IN OUT kcep_t* const p_cep,
\r
1243 IN const mad_cm_rep_t* const p_rep )
\r
1245 AL_ENTER( AL_DBG_CM );
\r
1247 /* The send should have been cancelled during MRA processing. */
\r
1248 p_cep->state = CEP_STATE_REP_RCVD;
\r
1250 /* Store pertinent information in the connection. */
\r
1251 p_cep->remote_comm_id = p_rep->local_comm_id;
\r
1252 p_cep->remote_ca_guid = p_rep->local_ca_guid;
\r
1254 p_cep->remote_qpn = conn_rep_get_lcl_qpn( p_rep );
\r
1256 /* Store the remote endpoint's target ACK delay. */
\r
1257 p_cep->target_ack_delay = conn_rep_get_target_ack_delay( p_rep );
\r
1259 /* Update the local ACK delay stored in the AV's. */
\r
1260 p_cep->av[0].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1261 p_cep->av[0].attr.conn.local_ack_timeout, p_cep->target_ack_delay );
\r
1262 p_cep->av[0].attr.conn.rnr_retry_cnt = conn_rep_get_rnr_retry_cnt( p_rep );
\r
1264 if( p_cep->av[1].port_guid )
\r
1266 p_cep->av[1].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1267 p_cep->av[1].attr.conn.local_ack_timeout,
\r
1268 p_cep->target_ack_delay );
\r
1269 p_cep->av[1].attr.conn.rnr_retry_cnt =
\r
1270 p_cep->av[0].attr.conn.rnr_retry_cnt;
\r
1273 p_cep->init_depth = p_rep->resp_resources;
\r
1274 p_cep->resp_res = p_rep->initiator_depth;
\r
1276 p_cep->sq_psn = conn_rep_get_starting_psn( p_rep );
\r
1278 /* Cache the private data. */
\r
1279 p_cep->psize = IB_REP_PDATA_SIZE;
\r
1280 memcpy( p_cep->pdata, p_rep->pdata, IB_REP_PDATA_SIZE );
\r
1282 AL_EXIT( AL_DBG_CM );
\r
1288 IN ib_mad_element_t* const p_mad )
\r
1290 ib_api_status_t status;
\r
1291 mad_cm_mra_t *p_mra;
\r
1293 KLOCK_QUEUE_HANDLE hdl;
\r
1295 AL_ENTER( AL_DBG_CM );
\r
1297 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1299 p_mra = (mad_cm_mra_t*)p_mad->p_mad_buf;
\r
1301 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1302 p_cep = __lookup_cep( NULL, p_mra->remote_comm_id );
\r
1305 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1306 ("MRA received that could not be matched.\n") );
\r
1310 if( p_cep->remote_comm_id )
\r
1312 if( p_cep->remote_comm_id != p_mra->local_comm_id )
\r
1314 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1315 ("MRA received that could not be matched.\n") );
\r
1321 * Note that we don't update the CEP's remote comm ID - it messes up REP
\r
1322 * processing since a non-zero RCID implies the connection is in the RCID
\r
1323 * map. Adding it here requires checking there and conditionally adding
\r
1324 * it. Ignoring it is a valid thing to do.
\r
1326 if( !(p_cep->state & CEP_STATE_SENT) ||
\r
1327 (1 << conn_mra_get_msg_mraed( p_mra ) !=
\r
1328 (p_cep->state & CEP_MSG_MASK)) )
\r
1330 /* Invalid state. */
\r
1331 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("MRA received in invalid state.\n") );
\r
1335 /* Delay the current send. */
\r
1336 CL_ASSERT( p_cep->p_send_mad );
\r
1337 ib_delay_mad( p_cep->h_mad_svc, p_cep->p_send_mad,
\r
1338 __calc_mad_timeout( conn_mra_get_svc_timeout( p_mra ) ) +
\r
1339 __calc_mad_timeout( p_cep->max_2pkt_life - 1 ) );
\r
1341 /* We only invoke a single callback for MRA. */
\r
1342 if( p_cep->state & CEP_STATE_MRA )
\r
1344 /* Invalid state. */
\r
1345 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Already received MRA.\n") );
\r
1349 p_cep->state |= CEP_STATE_MRA;
\r
1351 status = __cep_queue_mad( p_cep, p_mad );
\r
1353 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1355 if( status == IB_SUCCESS )
\r
1356 __process_cep( p_cep );
\r
1358 AL_EXIT( AL_DBG_CM );
\r
1362 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1363 ib_put_mad( p_mad );
\r
1364 AL_EXIT( AL_DBG_CM );
\r
1370 IN ib_mad_element_t* const p_mad )
\r
1372 ib_api_status_t status;
\r
1373 mad_cm_rej_t *p_rej;
\r
1374 kcep_t *p_cep = NULL;
\r
1375 KLOCK_QUEUE_HANDLE hdl;
\r
1378 AL_ENTER( AL_DBG_CM );
\r
1380 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1382 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
1384 /* Either one of the communication IDs must be set. */
\r
1385 if( !p_rej->remote_comm_id && !p_rej->local_comm_id )
\r
1388 /* Check the pending list by the remote CA GUID and connection ID. */
\r
1389 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1390 if( p_rej->remote_comm_id )
\r
1392 p_cep = __lookup_cep( NULL, p_rej->remote_comm_id );
\r
1394 else if( p_rej->reason == IB_REJ_TIMEOUT &&
\r
1395 conn_rej_get_ari_len( p_rej ) == sizeof(net64_t) )
\r
1397 cl_memcpy( &ca_guid, p_rej->ari, sizeof(net64_t) );
\r
1398 p_cep = __lookup_by_id( p_rej->local_comm_id, ca_guid );
\r
1406 if( p_cep->remote_comm_id &&
\r
1407 p_cep->remote_comm_id != p_rej->local_comm_id )
\r
1410 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1412 ib_put_mad( p_mad );
\r
1413 AL_EXIT( AL_DBG_CM );
\r
1417 status = __process_rej( p_cep, p_mad );
\r
1419 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1421 if( status == IB_SUCCESS )
\r
1422 __process_cep( p_cep );
\r
1424 AL_EXIT( AL_DBG_CM );
\r
1430 IN cep_agent_t* const p_port_cep,
\r
1431 IN ib_mad_element_t* const p_mad )
\r
1433 ib_api_status_t status;
\r
1434 mad_cm_rep_t *p_rep;
\r
1436 KLOCK_QUEUE_HANDLE hdl;
\r
1437 cep_state_t old_state;
\r
1439 AL_ENTER( AL_DBG_CM );
\r
1441 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1443 p_rep = (mad_cm_rep_t*)p_mad->p_mad_buf;
\r
1445 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1446 ("REP: comm_id (x%x) received\n", p_rep->local_comm_id ) );
\r
1448 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1449 p_cep = __lookup_cep( NULL, p_rep->remote_comm_id );
\r
1452 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1453 ib_put_mad( p_mad );
\r
1454 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1455 ("REP received that could not be matched.\n") );
\r
1459 switch( p_cep->state )
\r
1461 case CEP_STATE_REQ_MRA_RCVD:
\r
1462 case CEP_STATE_REQ_SENT:
\r
1463 old_state = p_cep->state;
\r
1464 /* Save pertinent information and change state. */
\r
1465 __save_wire_rep( p_cep, p_rep );
\r
1467 if( __insert_cep( p_cep ) != p_cep )
\r
1469 /* Roll back the state change. */
\r
1470 __reject_mad( p_port_cep, p_cep, p_mad, IB_REJ_STALE_CONN );
\r
1471 p_cep->state = old_state;
\r
1472 status = __process_stale( p_cep );
\r
1477 * Cancel any outstanding send. Note that we do this only after
\r
1478 * inserting the CEP - if we failed, then the send will timeout
\r
1479 * and we'll finish our way through the state machine.
\r
1481 if( p_cep->p_send_mad )
\r
1483 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1484 p_cep->p_send_mad = NULL;
\r
1487 status = __cep_queue_mad( p_cep, p_mad );
\r
1490 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1492 if( status == IB_SUCCESS )
\r
1493 __process_cep( p_cep );
\r
1495 AL_EXIT( AL_DBG_CM );
\r
1498 case CEP_STATE_ESTABLISHED:
\r
1499 case CEP_STATE_LAP_RCVD:
\r
1500 case CEP_STATE_LAP_SENT:
\r
1501 case CEP_STATE_LAP_MRA_RCVD:
\r
1502 case CEP_STATE_LAP_MRA_SENT:
\r
1503 case CEP_STATE_REP_MRA_SENT:
\r
1504 /* Repeate the MRA or RTU. */
\r
1505 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1509 ib_put_mad( p_mad );
\r
1510 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REP received in invalid state.\n") );
\r
1514 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1516 AL_EXIT( AL_DBG_CM );
\r
1522 IN ib_mad_element_t* const p_mad )
\r
1524 ib_api_status_t status;
\r
1525 mad_cm_rtu_t *p_rtu;
\r
1527 KLOCK_QUEUE_HANDLE hdl;
\r
1529 AL_ENTER( AL_DBG_CM );
\r
1531 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1533 p_rtu = (mad_cm_rtu_t*)p_mad->p_mad_buf;
\r
1535 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1536 ("RTU: comm_id (x%x) received\n", p_rtu->local_comm_id) );
\r
1538 /* Find the connection by local connection ID. */
\r
1539 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1540 p_cep = __lookup_cep( NULL, p_rtu->remote_comm_id );
\r
1541 if( !p_cep || p_cep->remote_comm_id != p_rtu->local_comm_id )
\r
1543 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received that could not be matched.\n") );
\r
1547 switch( p_cep->state )
\r
1549 case CEP_STATE_REP_SENT:
\r
1550 case CEP_STATE_REP_MRA_RCVD:
\r
1551 /* Cancel any outstanding send. */
\r
1552 if( p_cep->p_send_mad )
\r
1554 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1555 p_cep->p_send_mad = NULL;
\r
1558 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1560 status = __cep_queue_mad( p_cep, p_mad );
\r
1562 /* Update timewait time. */
\r
1563 __calc_timewait( p_cep );
\r
1565 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1567 if( status == IB_SUCCESS )
\r
1568 __process_cep( p_cep );
\r
1570 AL_EXIT( AL_DBG_CM );
\r
1574 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received in invalid state.\n") );
\r
1579 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1580 ib_put_mad( p_mad );
\r
1581 AL_EXIT( AL_DBG_CM );
\r
1587 IN cep_agent_t* const p_port_cep,
\r
1588 IN ib_mad_element_t* const p_mad )
\r
1590 ib_api_status_t status;
\r
1591 mad_cm_dreq_t *p_dreq;
\r
1593 KLOCK_QUEUE_HANDLE hdl;
\r
1595 AL_ENTER( AL_DBG_CM );
\r
1597 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1599 p_dreq = (mad_cm_dreq_t*)p_mad->p_mad_buf;
\r
1601 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1602 ("DREQ: comm_id (x%x) qpn (x%x) received\n",
\r
1603 p_dreq->local_comm_id, conn_dreq_get_remote_qpn( p_dreq )) );
\r
1605 /* Find the connection by connection IDs. */
\r
1606 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1607 p_cep = __lookup_cep( NULL, p_dreq->remote_comm_id );
\r
1609 p_cep->remote_comm_id != p_dreq->local_comm_id ||
\r
1610 p_cep->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )
\r
1612 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received that could not be matched.\n") );
\r
1613 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1614 ib_put_mad( p_mad );
\r
1615 AL_EXIT( AL_DBG_CM );
\r
1619 switch( p_cep->state )
\r
1621 case CEP_STATE_REP_SENT:
\r
1622 case CEP_STATE_REP_MRA_RCVD:
\r
1623 case CEP_STATE_DREQ_SENT:
\r
1624 /* Cancel the outstanding MAD. */
\r
1625 if( p_cep->p_send_mad )
\r
1627 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1628 p_cep->p_send_mad = NULL;
\r
1631 /* Fall through and process as DREQ received case. */
\r
1632 case CEP_STATE_ESTABLISHED:
\r
1633 case CEP_STATE_LAP_RCVD:
\r
1634 case CEP_STATE_LAP_SENT:
\r
1635 case CEP_STATE_LAP_MRA_RCVD:
\r
1636 case CEP_STATE_LAP_MRA_SENT:
\r
1637 p_cep->state = CEP_STATE_DREQ_RCVD;
\r
1639 status = __cep_queue_mad( p_cep, p_mad );
\r
1641 /* Store the TID for use in the reply DREP. */
\r
1642 p_cep->tid = p_dreq->hdr.trans_id;
\r
1644 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1646 if( status == IB_SUCCESS )
\r
1647 __process_cep( p_cep );
\r
1648 AL_EXIT( AL_DBG_CM );
\r
1651 case CEP_STATE_TIMEWAIT:
\r
1652 case CEP_STATE_DESTROY:
\r
1653 /* Repeat the DREP. */
\r
1654 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1657 case CEP_STATE_DREQ_DESTROY:
\r
1658 /* Send the DREP with no private data. */
\r
1660 ib_put_mad( p_mad ); /* release DREQ MAD */
\r
1662 status = __cep_get_mad( p_cep, CM_DREP_ATTR_ID, &(cep_agent_t*)p_port_cep,
\r
1663 &(ib_mad_element_t*)p_mad );
\r
1664 if( status != IB_SUCCESS )
\r
1667 p_mad->p_mad_buf->attr_id = CM_DREP_ATTR_ID;
\r
1668 /* __format_drep returns always SUCCESS while no private data */
\r
1669 __format_drep( p_cep, NULL, 0, (mad_cm_drep_t*)p_mad->p_mad_buf );
\r
1670 __cep_send_mad( p_port_cep, p_mad );
\r
1674 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received in invalid state.\n") );
\r
1675 case CEP_STATE_DREQ_RCVD:
\r
1676 ib_put_mad( p_mad );
\r
1680 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1681 AL_EXIT( AL_DBG_CM );
\r
1687 IN ib_mad_element_t* const p_mad )
\r
1689 ib_api_status_t status;
\r
1690 mad_cm_drep_t *p_drep;
\r
1692 KLOCK_QUEUE_HANDLE hdl;
\r
1694 AL_ENTER( AL_DBG_CM );
\r
1696 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1698 p_drep = (mad_cm_drep_t*)p_mad->p_mad_buf;
\r
1700 /* Find the connection by local connection ID. */
\r
1701 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1702 p_cep = __lookup_cep( NULL, p_drep->remote_comm_id );
\r
1703 if( !p_cep || p_cep->remote_comm_id != p_drep->local_comm_id )
\r
1705 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received that could not be matched.\n") );
\r
1706 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1707 ib_put_mad( p_mad );
\r
1708 AL_EXIT( AL_DBG_CM );
\r
1712 if( p_cep->state != CEP_STATE_DREQ_SENT &&
\r
1713 p_cep->state != CEP_STATE_DREQ_DESTROY )
\r
1715 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received in invalid state.\n") );
\r
1717 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1718 ib_put_mad( p_mad );
\r
1719 AL_EXIT( AL_DBG_CM );
\r
1723 /* Cancel the DREQ. */
\r
1724 if( p_cep->p_send_mad )
\r
1726 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1727 p_cep->p_send_mad = NULL;
\r
1730 if( p_cep->state == CEP_STATE_DREQ_SENT )
\r
1732 p_cep->state = CEP_STATE_TIMEWAIT;
\r
1734 status = __cep_queue_mad( p_cep, p_mad );
\r
1738 /* State is DREQ_DESTROY - move to DESTROY to allow cleanup. */
\r
1739 CL_ASSERT( p_cep->state == CEP_STATE_DREQ_DESTROY );
\r
1740 p_cep->state = CEP_STATE_DESTROY;
\r
1742 ib_put_mad( p_mad );
\r
1743 status = IB_INVALID_STATE;
\r
1746 __insert_timewait( p_cep );
\r
1748 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1750 if( status == IB_SUCCESS )
\r
1751 __process_cep( p_cep );
\r
1753 AL_EXIT( AL_DBG_CM );
\r
1759 IN kcep_t* const p_cep,
\r
1760 IN const lap_path_info_t* const p_path )
\r
1762 cep_agent_t *p_port_cep;
\r
1764 AL_ENTER( AL_DBG_CM );
\r
1766 CL_ASSERT( p_cep );
\r
1767 CL_ASSERT( p_path );
\r
1769 cl_memclr( &p_cep->alt_av, sizeof(kcep_av_t) );
\r
1771 p_port_cep = __find_port_cep( &p_path->remote_gid, p_path->remote_lid,
\r
1772 p_cep->pkey, &p_cep->alt_av.pkey_index );
\r
1775 AL_EXIT( AL_DBG_CM );
\r
1779 if( p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
1781 AL_EXIT( AL_DBG_CM );
\r
1785 p_cep->alt_av.port_guid = p_port_cep->port_guid;
\r
1786 p_cep->alt_av.attr.port_num = p_port_cep->port_num;
\r
1788 p_cep->alt_av.attr.sl = conn_lap_path_get_svc_lvl( p_path );
\r
1789 p_cep->alt_av.attr.dlid = p_path->local_lid;
\r
1791 if( !conn_lap_path_get_subn_lcl( p_path ) )
\r
1793 p_cep->alt_av.attr.grh_valid = TRUE;
\r
1794 p_cep->alt_av.attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
1795 1, conn_lap_path_get_tclass( p_path ),
\r
1796 conn_lap_path_get_flow_lbl( p_path ) );
\r
1797 p_cep->alt_av.attr.grh.hop_limit = p_path->hop_limit;
\r
1798 p_cep->alt_av.attr.grh.dest_gid = p_path->local_gid;
\r
1799 p_cep->alt_av.attr.grh.src_gid = p_path->remote_gid;
\r
1803 p_cep->alt_av.attr.grh_valid = FALSE;
\r
1805 p_cep->alt_av.attr.static_rate = conn_lap_path_get_pkt_rate( p_path );
\r
1806 p_cep->alt_av.attr.path_bits =
\r
1807 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
1810 * Note that while we never use the connected AV attributes internally,
\r
1811 * we store them so we can pass them back to users. For the LAP, we
\r
1812 * first copy the settings from the current primary - MTU and retry
\r
1813 * counts are only specified in the REQ.
\r
1815 p_cep->alt_av.attr.conn = p_cep->av[p_cep->idx_primary].attr.conn;
\r
1816 p_cep->alt_av.attr.conn.local_ack_timeout =
\r
1817 conn_lap_path_get_lcl_ack_timeout( p_path );
\r
1819 AL_EXIT( AL_DBG_CM );
\r
1826 IN cep_agent_t* const p_port_cep,
\r
1827 IN ib_mad_element_t* const p_mad )
\r
1829 ib_api_status_t status;
\r
1830 mad_cm_lap_t *p_lap;
\r
1832 KLOCK_QUEUE_HANDLE hdl;
\r
1834 AL_ENTER( AL_DBG_CM );
\r
1836 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1838 p_lap = (mad_cm_lap_t*)p_mad->p_mad_buf;
\r
1840 /* Find the connection by local connection ID. */
\r
1841 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1842 p_cep = __lookup_cep( NULL, p_lap->remote_comm_id );
\r
1843 if( !p_cep || p_cep->remote_comm_id != p_lap->local_comm_id )
\r
1845 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1846 ib_put_mad( p_mad );
\r
1847 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received that could not be matched.\n") );
\r
1851 switch( p_cep->state )
\r
1853 case CEP_STATE_REP_SENT:
\r
1854 case CEP_STATE_REP_MRA_RCVD:
\r
1856 * These two cases handle the RTU being dropped. Receipt of
\r
1857 * a LAP indicates that the connection is established.
\r
1859 case CEP_STATE_ESTABLISHED:
\r
1861 * We don't check for other "established" states related to
\r
1862 * alternate path management (CEP_STATE_LAP_RCVD, etc)
\r
1865 /* We only support receiving LAP if we took the passive role. */
\r
1866 if( p_cep->was_active )
\r
1868 ib_put_mad( p_mad );
\r
1872 /* Store the transaction ID for use during the LAP exchange. */
\r
1873 p_cep->tid = p_lap->hdr.trans_id;
\r
1876 * Copy the path record into the connection for use when
\r
1877 * sending the APR and loading the path.
\r
1879 if( !__format_lap_av( p_cep, &p_lap->alternate_path ) )
\r
1881 /* Trap an invalid path. */
\r
1882 ib_put_mad( p_mad );
\r
1886 p_cep->state = CEP_STATE_LAP_RCVD;
\r
1888 status = __cep_queue_mad( p_cep, p_mad );
\r
1890 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1892 if( status == IB_SUCCESS )
\r
1893 __process_cep( p_cep );
\r
1895 AL_EXIT( AL_DBG_CM );
\r
1898 case CEP_STATE_LAP_MRA_SENT:
\r
1899 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1903 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received in invalid state.\n") );
\r
1904 ib_put_mad( p_mad );
\r
1908 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1909 AL_EXIT( AL_DBG_CM );
\r
1915 IN ib_mad_element_t* const p_mad )
\r
1917 ib_api_status_t status;
\r
1918 mad_cm_apr_t *p_apr;
\r
1920 KLOCK_QUEUE_HANDLE hdl;
\r
1922 AL_ENTER( AL_DBG_CM );
\r
1924 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1926 p_apr = (mad_cm_apr_t*)p_mad->p_mad_buf;
\r
1928 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1929 p_cep = __lookup_cep( NULL, p_apr->remote_comm_id );
\r
1930 if( !p_cep || p_cep->remote_comm_id != p_apr->local_comm_id )
\r
1932 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received that could not be matched.\n") );
\r
1936 switch( p_cep->state )
\r
1938 case CEP_STATE_LAP_SENT:
\r
1939 case CEP_STATE_LAP_MRA_RCVD:
\r
1940 /* Cancel sending the LAP. */
\r
1941 if( p_cep->p_send_mad )
\r
1943 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1944 p_cep->p_send_mad = NULL;
\r
1947 /* Copy the temporary alternate AV. */
\r
1948 p_cep->av[(p_cep->idx_primary + 1) & 0x1] = p_cep->alt_av;
\r
1950 /* Update the maximum packet lifetime. */
\r
1951 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life, p_cep->alt_2pkt_life );
\r
1953 /* Update the timewait time. */
\r
1954 __calc_timewait( p_cep );
\r
1956 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1958 status = __cep_queue_mad( p_cep, p_mad );
\r
1960 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1962 if( status == IB_SUCCESS )
\r
1963 __process_cep( p_cep );
\r
1965 AL_EXIT( AL_DBG_CM );
\r
1969 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received in invalid state.\n") );
\r
1974 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1975 ib_put_mad( p_mad );
\r
1976 AL_EXIT( AL_DBG_CM );
\r
1981 __cep_mad_recv_cb(
\r
1982 IN ib_mad_svc_handle_t h_mad_svc,
\r
1984 IN ib_mad_element_t *p_mad )
\r
1986 cep_agent_t *p_port_cep;
\r
1989 AL_ENTER( AL_DBG_CM );
\r
1991 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1993 UNUSED_PARAM( h_mad_svc );
\r
1994 p_port_cep = (cep_agent_t*)context;
\r
1996 CL_ASSERT( p_mad->p_next == NULL );
\r
1998 p_hdr = (ib_mad_t*)p_mad->p_mad_buf;
\r
2001 * TODO: Add filtering in all the handlers for unsupported class version.
\r
2002 * See 12.6.7.2 Rejection Reason, code 31.
\r
2005 switch( p_hdr->attr_id )
\r
2007 case CM_REQ_ATTR_ID:
\r
2008 __req_handler( p_port_cep, p_mad );
\r
2011 case CM_MRA_ATTR_ID:
\r
2012 __mra_handler( p_mad );
\r
2015 case CM_REJ_ATTR_ID:
\r
2016 __rej_handler( p_mad );
\r
2019 case CM_REP_ATTR_ID:
\r
2020 __rep_handler( p_port_cep, p_mad );
\r
2023 case CM_RTU_ATTR_ID:
\r
2024 __rtu_handler( p_mad );
\r
2027 case CM_DREQ_ATTR_ID:
\r
2028 __dreq_handler( p_port_cep, p_mad );
\r
2031 case CM_DREP_ATTR_ID:
\r
2032 __drep_handler( p_mad );
\r
2035 case CM_LAP_ATTR_ID:
\r
2036 __lap_handler( p_port_cep, p_mad );
\r
2039 case CM_APR_ATTR_ID:
\r
2040 __apr_handler( p_mad );
\r
2043 case CM_SIDR_REQ_ATTR_ID:
\r
2044 // p_async_mad->item.pfn_callback = __process_cm_sidr_req;
\r
2047 case CM_SIDR_REP_ATTR_ID:
\r
2048 // p_async_mad->item.pfn_callback = __process_cm_sidr_rep;
\r
2052 ib_put_mad( p_mad );
\r
2053 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2054 ("Invalid CM MAD attribute ID.\n") );
\r
2058 AL_EXIT( AL_DBG_CM );
\r
2062 static inline cep_agent_t*
\r
2064 IN kcep_t* const p_cep )
\r
2066 cl_map_item_t *p_item;
\r
2068 CL_ASSERT( p_cep );
\r
2070 /* Look up the primary CEP port agent */
\r
2071 p_item = cl_qmap_get( &gp_cep_mgr->port_map,
\r
2072 p_cep->av[p_cep->idx_primary].port_guid );
\r
2073 if( p_item == cl_qmap_end( &gp_cep_mgr->port_map ) )
\r
2076 return PARENT_STRUCT( p_item, cep_agent_t, item );
\r
2080 static inline void
\r
2082 OUT ib_mad_element_t* const p_mad,
\r
2083 IN kcep_av_t* const p_av )
\r
2085 /* Set the addressing information in the MAD. */
\r
2086 p_mad->grh_valid = p_av->attr.grh_valid;
\r
2087 if( p_av->attr.grh_valid )
\r
2088 cl_memcpy( p_mad->p_grh, &p_av->attr.grh, sizeof(ib_grh_t) );
\r
2090 p_mad->remote_sl = p_av->attr.sl;
\r
2091 p_mad->remote_lid = p_av->attr.dlid;
\r
2092 p_mad->path_bits = p_av->attr.path_bits;
\r
2093 p_mad->pkey_index = p_av->pkey_index;
\r
2094 p_mad->remote_qp = IB_QP1;
\r
2095 p_mad->send_opt = IB_SEND_OPT_SIGNALED;
\r
2096 p_mad->remote_qkey = IB_QP1_WELL_KNOWN_Q_KEY;
\r
2097 /* Let the MAD service manage the AV for us. */
\r
2098 p_mad->h_av = NULL;
\r
2102 static ib_api_status_t
\r
2104 IN cep_agent_t* const p_port_cep,
\r
2105 IN ib_mad_element_t* const p_mad )
\r
2107 ib_api_status_t status;
\r
2109 AL_ENTER( AL_DBG_CM );
\r
2111 CL_ASSERT( p_port_cep );
\r
2112 CL_ASSERT( p_mad );
\r
2114 /* Use the mad's attributes already present */
\r
2115 p_mad->resp_expected = FALSE;
\r
2116 p_mad->retry_cnt = 0;
\r
2117 p_mad->timeout_ms = 0;
\r
2119 /* Clear the contexts since the send isn't associated with a CEP. */
\r
2120 p_mad->context1 = NULL;
\r
2121 p_mad->context2 = NULL;
\r
2123 status = ib_send_mad( p_port_cep->h_mad_svc, p_mad, NULL );
\r
2124 if( status != IB_SUCCESS )
\r
2126 ib_put_mad( p_mad );
\r
2127 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2128 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2131 AL_EXIT( AL_DBG_CM );
\r
2136 static ib_api_status_t
\r
2138 IN cep_agent_t* const p_port_cep,
\r
2139 IN kcep_t* const p_cep,
\r
2140 IN ib_mad_element_t* const p_mad )
\r
2142 ib_api_status_t status;
\r
2144 AL_ENTER( AL_DBG_CM );
\r
2146 CL_ASSERT( p_cep );
\r
2147 CL_ASSERT( p_mad );
\r
2148 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
2149 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID ||
\r
2150 p_mad->p_mad_buf->attr_id == CM_LAP_ATTR_ID ||
\r
2151 p_mad->p_mad_buf->attr_id == CM_DREQ_ATTR_ID );
\r
2154 * REQ, REP, and DREQ are retried until either a response is
\r
2155 * received or the operation times out.
\r
2157 p_mad->resp_expected = TRUE;
\r
2158 p_mad->retry_cnt = p_cep->max_cm_retries;
\r
2159 p_mad->timeout_ms = p_cep->retry_timeout;
\r
2161 CL_ASSERT( !p_cep->p_send_mad );
\r
2163 /* Store the mad & mad service handle in the CEP for cancelling. */
\r
2164 p_cep->h_mad_svc = p_port_cep->h_mad_svc;
\r
2165 p_cep->p_send_mad = p_mad;
\r
2167 /* reference the connection for which we are sending the MAD. */
\r
2168 cl_atomic_inc( &p_cep->ref_cnt );
\r
2170 /* Set the context. */
\r
2171 p_mad->context1 = p_cep;
\r
2172 p_mad->context2 = NULL;
\r
2174 /* Fire in the hole! */
\r
2175 status = ib_send_mad( p_cep->h_mad_svc, p_mad, NULL );
\r
2176 if( status != IB_SUCCESS )
\r
2179 * Note that we don't need to check for destruction here since
\r
2180 * we're holding the global lock.
\r
2182 cl_atomic_dec( &p_cep->ref_cnt );
\r
2183 p_cep->p_send_mad = NULL;
\r
2184 ib_put_mad( p_mad );
\r
2185 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2186 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2189 AL_EXIT( AL_DBG_CM );
\r
2195 __cep_mad_send_cb(
\r
2196 IN ib_mad_svc_handle_t h_mad_svc,
\r
2198 IN ib_mad_element_t *p_mad )
\r
2200 ib_api_status_t status;
\r
2201 cep_agent_t *p_port_cep;
\r
2203 KLOCK_QUEUE_HANDLE hdl;
\r
2204 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
2205 void *cep_context;
\r
2207 AL_ENTER( AL_DBG_CM );
\r
2209 UNUSED_PARAM( h_mad_svc );
\r
2210 CL_ASSERT( p_mad->p_next == NULL );
\r
2211 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
2213 p_port_cep = (cep_agent_t*)context;
\r
2215 p_cep = (kcep_t*)p_mad->context1;
\r
2218 * The connection context is not set when performing immediate responses,
\r
2219 * such as repeating MADS.
\r
2223 ib_put_mad( p_mad );
\r
2224 AL_EXIT( AL_DBG_CM );
\r
2228 p_mad->context1 = NULL;
\r
2230 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
2231 /* Clear the sent MAD pointer so that we don't try cancelling again. */
\r
2232 if( p_cep->p_send_mad == p_mad )
\r
2233 p_cep->p_send_mad = NULL;
\r
2235 switch( p_mad->status )
\r
2237 case IB_WCS_SUCCESS:
\r
2238 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2239 ib_put_mad( p_mad );
\r
2242 case IB_WCS_CANCELED:
\r
2243 if( p_cep->state != CEP_STATE_REQ_SENT &&
\r
2244 p_cep->state != CEP_STATE_REQ_MRA_RCVD &&
\r
2245 p_cep->state != CEP_STATE_REP_SENT &&
\r
2246 p_cep->state != CEP_STATE_REP_MRA_RCVD &&
\r
2247 p_cep->state != CEP_STATE_LAP_SENT &&
\r
2248 p_cep->state != CEP_STATE_LAP_MRA_RCVD &&
\r
2249 p_cep->state != CEP_STATE_DREQ_SENT &&
\r
2250 p_cep->state != CEP_STATE_SREQ_SENT )
\r
2252 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2253 ib_put_mad( p_mad );
\r
2256 /* Treat as a timeout so we don't stall the state machine. */
\r
2257 p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;
\r
2259 /* Fall through. */
\r
2260 case IB_WCS_TIMEOUT_RETRY_ERR:
\r
2262 /* Timeout. Reject the connection. */
\r
2263 switch( p_cep->state )
\r
2265 case CEP_STATE_REQ_SENT:
\r
2266 case CEP_STATE_REQ_MRA_RCVD:
\r
2267 case CEP_STATE_REP_SENT:
\r
2268 case CEP_STATE_REP_MRA_RCVD:
\r
2269 /* Send the REJ. */
\r
2270 __reject_timeout( p_port_cep, p_cep, p_mad );
\r
2271 __remove_cep( p_cep );
\r
2272 p_cep->state = CEP_STATE_IDLE;
\r
2275 case CEP_STATE_DREQ_DESTROY:
\r
2276 p_cep->state = CEP_STATE_DESTROY;
\r
2277 __insert_timewait( p_cep );
\r
2278 /* Fall through. */
\r
2280 case CEP_STATE_DESTROY:
\r
2281 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2282 ib_put_mad( p_mad );
\r
2285 case CEP_STATE_DREQ_SENT:
\r
2287 * Make up a DREP mad so we can respond if we receive
\r
2288 * a DREQ while in timewait.
\r
2290 __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );
\r
2291 __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );
\r
2292 p_cep->state = CEP_STATE_TIMEWAIT;
\r
2293 __insert_timewait( p_cep );
\r
2299 status = __cep_queue_mad( p_cep, p_mad );
\r
2300 CL_ASSERT( status != IB_INVALID_STATE );
\r
2301 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2303 if( status == IB_SUCCESS )
\r
2304 __process_cep( p_cep );
\r
2309 pfn_destroy_cb = p_cep->pfn_destroy_cb;
\r
2310 cep_context = p_cep->context;
\r
2312 if( !cl_atomic_dec( &p_cep->ref_cnt ) && pfn_destroy_cb )
\r
2313 pfn_destroy_cb( cep_context );
\r
2314 AL_EXIT( AL_DBG_CM );
\r
2319 __cep_qp_event_cb(
\r
2320 IN ib_async_event_rec_t *p_event_rec )
\r
2322 UNUSED_PARAM( p_event_rec );
\r
2325 * Most of the QP events are trapped by the real owner of the QP.
\r
2326 * For real events, the CM may not be able to do much anyways!
\r
2331 static ib_api_status_t
\r
2333 IN cep_agent_t* const p_port_cep,
\r
2334 IN const ib_port_attr_t* const p_port_attr )
\r
2336 ib_api_status_t status;
\r
2337 ib_qp_create_t qp_create;
\r
2338 ib_mad_svc_t mad_svc;
\r
2340 AL_ENTER( AL_DBG_CM );
\r
2343 * Create the PD alias. We use the port CM's al_obj_t as the context
\r
2344 * to allow using deref_al_obj as the destroy callback.
\r
2346 status = ib_alloc_pd( p_port_cep->h_ca, IB_PDT_ALIAS, &p_port_cep->obj,
\r
2347 &p_port_cep->h_pd );
\r
2348 if( status != IB_SUCCESS )
\r
2350 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2351 ("ib_alloc_pd failed with status %s\n", ib_get_err_str(status)) );
\r
2354 /* Reference the port object on behalf of the PD. */
\r
2355 ref_al_obj( &p_port_cep->obj );
\r
2357 /* Create the MAD QP. */
\r
2358 cl_memclr( &qp_create, sizeof( ib_qp_create_t ) );
\r
2359 qp_create.qp_type = IB_QPT_QP1_ALIAS;
\r
2360 qp_create.rq_depth = CEP_MAD_RQ_DEPTH;
\r
2361 qp_create.sq_depth = CEP_MAD_SQ_DEPTH;
\r
2362 qp_create.rq_sge = CEP_MAD_RQ_SGE;
\r
2363 qp_create.sq_sge = CEP_MAD_SQ_SGE;
\r
2364 qp_create.sq_signaled = TRUE;
\r
2366 * We use the port CM's al_obj_t as the context to allow using
\r
2367 * deref_al_obj as the destroy callback.
\r
2369 status = ib_get_spl_qp( p_port_cep->h_pd, p_port_attr->port_guid,
\r
2370 &qp_create, &p_port_cep->obj, __cep_qp_event_cb, &p_port_cep->pool_key,
\r
2371 &p_port_cep->h_qp );
\r
2372 if( status != IB_SUCCESS )
\r
2374 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2375 ("ib_get_spl_qp failed with status %s\n", ib_get_err_str(status)) );
\r
2378 /* Reference the port object on behalf of the QP. */
\r
2379 ref_al_obj( &p_port_cep->obj );
\r
2381 /* Create the MAD service. */
\r
2382 cl_memclr( &mad_svc, sizeof(mad_svc) );
\r
2383 mad_svc.mad_svc_context = p_port_cep;
\r
2384 mad_svc.pfn_mad_recv_cb = __cep_mad_recv_cb;
\r
2385 mad_svc.pfn_mad_send_cb = __cep_mad_send_cb;
\r
2386 mad_svc.support_unsol = TRUE;
\r
2387 mad_svc.mgmt_class = IB_MCLASS_COMM_MGMT;
\r
2388 mad_svc.mgmt_version = IB_MCLASS_CM_VER_2;
\r
2389 mad_svc.method_array[IB_MAD_METHOD_SEND] = TRUE;
\r
2391 ib_reg_mad_svc( p_port_cep->h_qp, &mad_svc, &p_port_cep->h_mad_svc );
\r
2392 if( status != IB_SUCCESS )
\r
2394 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2395 ("ib_reg_mad_svc failed with status %s\n", ib_get_err_str(status)) );
\r
2399 AL_EXIT( AL_DBG_CM );
\r
2400 return IB_SUCCESS;
\r
2405 * Performs immediate cleanup of resources.
\r
2408 __destroying_port_cep(
\r
2409 IN al_obj_t *p_obj )
\r
2411 cep_agent_t *p_port_cep;
\r
2412 KLOCK_QUEUE_HANDLE hdl;
\r
2414 AL_ENTER( AL_DBG_CM );
\r
2416 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2418 if( p_port_cep->port_guid )
\r
2420 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2421 cl_qmap_remove_item( &gp_cep_mgr->port_map, &p_port_cep->item );
\r
2422 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2425 if( p_port_cep->h_qp )
\r
2427 ib_destroy_qp( p_port_cep->h_qp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2428 p_port_cep->h_qp = NULL;
\r
2431 if( p_port_cep->h_pd )
\r
2433 ib_dealloc_pd( p_port_cep->h_pd, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2434 p_port_cep->h_pd = NULL;
\r
2437 AL_EXIT( AL_DBG_CM );
\r
2443 * Release all resources allocated by a port CM agent. Finishes any cleanup
\r
2444 * for a port agent.
\r
2448 IN al_obj_t *p_obj )
\r
2450 cep_agent_t *p_port_cep;
\r
2451 ib_port_attr_mod_t port_attr_mod;
\r
2453 AL_ENTER( AL_DBG_CM );
\r
2455 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2457 if( p_port_cep->h_ca )
\r
2459 /* Update local port attributes */
\r
2460 port_attr_mod.cap.cm = FALSE;
\r
2461 ib_modify_ca( p_port_cep->h_ca, p_port_cep->port_num,
\r
2462 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2464 deref_al_obj( &p_port_cep->h_ca->obj );
\r
2467 destroy_al_obj( &p_port_cep->obj );
\r
2468 cl_free( p_port_cep );
\r
2470 AL_EXIT( AL_DBG_CM );
\r
2475 * Create a port agent for a given port.
\r
2477 static ib_api_status_t
\r
2478 __create_port_cep(
\r
2479 IN ib_pnp_port_rec_t *p_pnp_rec )
\r
2481 cep_agent_t *p_port_cep;
\r
2482 ib_api_status_t status;
\r
2483 ib_port_attr_mod_t port_attr_mod;
\r
2484 KLOCK_QUEUE_HANDLE hdl;
\r
2486 AL_ENTER( AL_DBG_CM );
\r
2488 /* calculate size of port_cm struct */
\r
2489 p_port_cep = (cep_agent_t*)cl_zalloc( sizeof(cep_agent_t) );
\r
2492 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2493 ("Failed to cl_zalloc port CM agent.\n") );
\r
2494 return IB_INSUFFICIENT_MEMORY;
\r
2497 construct_al_obj( &p_port_cep->obj, AL_OBJ_TYPE_CM );
\r
2499 status = init_al_obj( &p_port_cep->obj, p_port_cep, TRUE,
\r
2500 __destroying_port_cep, NULL, __free_port_cep );
\r
2501 if( status != IB_SUCCESS )
\r
2503 __free_port_cep( &p_port_cep->obj );
\r
2504 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2505 ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );
\r
2509 /* Attach to the global CM object. */
\r
2510 status = attach_al_obj( &gp_cep_mgr->obj, &p_port_cep->obj );
\r
2511 if( status != IB_SUCCESS )
\r
2513 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2514 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2515 ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );
\r
2519 p_port_cep->port_guid = p_pnp_rec->p_port_attr->port_guid;
\r
2520 p_port_cep->port_num = p_pnp_rec->p_port_attr->port_num;
\r
2521 p_port_cep->base_lid = p_pnp_rec->p_port_attr->lid;
\r
2523 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2525 &gp_cep_mgr->port_map, p_port_cep->port_guid, &p_port_cep->item );
\r
2526 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2528 /* Get a reference to the CA on which we are loading. */
\r
2529 p_port_cep->h_ca = acquire_ca( p_pnp_rec->p_ca_attr->ca_guid );
\r
2530 if( !p_port_cep->h_ca )
\r
2532 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2533 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("acquire_ca failed.\n") );
\r
2534 return IB_INVALID_GUID; }
\r
2536 status = __init_data_svc( p_port_cep, p_pnp_rec->p_port_attr );
\r
2537 if( status != IB_SUCCESS )
\r
2539 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2540 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2541 ("__init_data_svc failed with status %s.\n",
\r
2542 ib_get_err_str(status)) );
\r
2546 /* Update local port attributes */
\r
2547 cl_memclr( &port_attr_mod, sizeof(ib_port_attr_mod_t) );
\r
2548 port_attr_mod.cap.cm = TRUE;
\r
2549 status = ib_modify_ca( p_port_cep->h_ca, p_pnp_rec->p_port_attr->port_num,
\r
2550 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2552 /* Update the PNP context to reference this port. */
\r
2553 p_pnp_rec->pnp_rec.context = p_port_cep;
\r
2555 /* Release the reference taken in init_al_obj. */
\r
2556 deref_al_obj( &p_port_cep->obj );
\r
2558 AL_EXIT( AL_DBG_CM );
\r
2559 return IB_SUCCESS;
\r
2563 /******************************************************************************
\r
2564 * Global CEP manager
\r
2565 ******************************************************************************/
\r
2569 OUT net32_t* const p_cid )
\r
2571 cl_status_t status;
\r
2572 uint32_t size, cid;
\r
2573 cep_cid_t *p_cep_cid;
\r
2575 AL_ENTER( AL_DBG_CM );
\r
2577 size = (uint32_t)cl_vector_get_size( &gp_cep_mgr->cid_vector );
\r
2578 cid = gp_cep_mgr->free_cid;
\r
2579 if( gp_cep_mgr->free_cid == size )
\r
2581 /* Grow the vector pool. */
\r
2583 cl_vector_set_size( &gp_cep_mgr->cid_vector, size + CEP_CID_GROW );
\r
2584 if( status != CL_SUCCESS )
\r
2586 AL_EXIT( AL_DBG_CM );
\r
2590 * Return the the start of the free list since the
\r
2591 * entry initializer incremented it.
\r
2593 gp_cep_mgr->free_cid = size;
\r
2596 /* Get the next free entry. */
\r
2597 p_cep_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, cid );
\r
2599 /* Update the next entry index. */
\r
2600 gp_cep_mgr->free_cid = (uint32_t)(uintn_t)p_cep_cid->p_cep;
\r
2604 AL_EXIT( AL_DBG_CM );
\r
2609 static inline kcep_t*
\r
2611 IN ib_al_handle_t h_al OPTIONAL,
\r
2617 /* Mask off the counter bits so we get the index in our vector. */
\r
2618 idx = cid & CEP_MAX_CID_MASK;
\r
2620 if( idx >= cl_vector_get_size( &gp_cep_mgr->cid_vector ) )
\r
2623 p_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, idx );
\r
2624 if( !p_cid->h_al )
\r
2628 * h_al is NULL when processing MADs, so we need to match on
\r
2629 * the actual local communication ID. If h_al is non-NULL, we
\r
2630 * are doing a lookup from a call to our API, and only need to match
\r
2631 * on the index in the vector (without the modifier).
\r
2635 if( p_cid->h_al != h_al )
\r
2638 else if( p_cid->p_cep->local_comm_id != cid )
\r
2643 return p_cid->p_cep;
\r
2648 * Lookup a CEP by remote comm ID and CA GUID.
\r
2652 IN net32_t remote_comm_id,
\r
2653 IN net64_t remote_ca_guid )
\r
2655 cl_rbmap_item_t *p_item;
\r
2658 AL_ENTER( AL_DBG_CM );
\r
2660 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2661 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2662 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2664 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2666 if( remote_comm_id < p_cep->remote_comm_id )
\r
2667 p_item = cl_rbmap_left( p_item );
\r
2668 else if( remote_comm_id > p_cep->remote_comm_id )
\r
2669 p_item = cl_rbmap_right( p_item );
\r
2670 else if( remote_ca_guid < p_cep->remote_ca_guid )
\r
2671 p_item = cl_rbmap_left( p_item );
\r
2672 else if( remote_ca_guid > p_cep->remote_ca_guid )
\r
2673 p_item = cl_rbmap_right( p_item );
\r
2678 AL_EXIT( AL_DBG_CM );
\r
2684 * Lookup a CEP by Service ID and private data.
\r
2689 IN net64_t port_guid,
\r
2690 IN uint8_t *p_pdata )
\r
2692 cl_rbmap_item_t *p_item;
\r
2696 AL_ENTER( AL_DBG_CM );
\r
2698 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2699 p_item = cl_rbmap_root( &gp_cep_mgr->listen_map );
\r
2700 while( p_item != cl_rbmap_end( &gp_cep_mgr->listen_map ) )
\r
2702 p_cep = PARENT_STRUCT( p_item, kcep_t, listen_item );
\r
2704 if( sid == p_cep->sid )
\r
2706 else if( sid < p_cep->sid )
\r
2707 p_item = cl_rbmap_left( p_item );
\r
2709 p_item = cl_rbmap_right( p_item );
\r
2714 if( p_cep->port_guid != IB_ALL_PORTS )
\r
2716 if( port_guid == p_cep->port_guid )
\r
2718 else if( port_guid < p_cep->port_guid )
\r
2719 p_item = cl_rbmap_left( p_item );
\r
2721 p_item = cl_rbmap_right( p_item );
\r
2727 if( p_cep->p_cmp_buf && p_pdata )
\r
2729 cmp = cl_memcmp( &p_pdata[p_cep->cmp_offset],
\r
2730 p_cep->p_cmp_buf, p_cep->cmp_len );
\r
2734 else if( cmp < 0 )
\r
2735 p_item = cl_rbmap_left( p_item );
\r
2737 p_item = cl_rbmap_right( p_item );
\r
2739 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
2740 ("Svc ID match but compare buffer mismatch.\n") );
\r
2745 /* Everything matched. */
\r
2746 AL_PRINT( TRACE_LEVEL_VERBOSE, AL_DBG_CM, ("] matched CID = %d\n", p_cep->cid) );
\r
2750 AL_PRINT( TRACE_LEVEL_VERBOSE, AL_DBG_CM, ("] not found\n") );
\r
2757 IN kcep_t* const p_new_cep )
\r
2760 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2761 boolean_t left = TRUE;
\r
2763 AL_ENTER( AL_DBG_CM );
\r
2765 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2766 p_insert_at = p_item;
\r
2767 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2769 p_insert_at = p_item;
\r
2770 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2772 if( p_new_cep->remote_comm_id < p_cep->remote_comm_id )
\r
2773 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2774 else if( p_new_cep->remote_comm_id > p_cep->remote_comm_id )
\r
2775 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2776 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2777 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2778 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2779 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2782 AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
2783 ("WARNING: Duplicate remote CID and CA GUID.\n") );
\r
2789 &gp_cep_mgr->conn_id_map, p_insert_at, &p_new_cep->rem_id_item, left );
\r
2790 p_cep = p_new_cep;
\r
2793 AL_EXIT( AL_DBG_CM );
\r
2800 IN kcep_t* const p_new_cep )
\r
2803 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2804 boolean_t left = TRUE;
\r
2806 AL_ENTER( AL_DBG_CM );
\r
2808 p_item = cl_rbmap_root( &gp_cep_mgr->conn_qp_map );
\r
2809 p_insert_at = p_item;
\r
2810 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_qp_map ) )
\r
2812 p_insert_at = p_item;
\r
2813 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2815 if( p_new_cep->remote_qpn < p_cep->remote_qpn )
\r
2816 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2817 else if( p_new_cep->remote_qpn > p_cep->remote_qpn )
\r
2818 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2819 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2820 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2821 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2822 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2825 AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
2826 ("WARNING: Duplicate remote QPN and CA GUID.\n") );
\r
2832 &gp_cep_mgr->conn_qp_map, p_insert_at, &p_new_cep->rem_qp_item, left );
\r
2833 p_cep = p_new_cep;
\r
2836 AL_EXIT( AL_DBG_CM );
\r
2841 static inline kcep_t*
\r
2843 IN kcep_t* const p_new_cep )
\r
2847 AL_ENTER( AL_DBG_CM );
\r
2849 p_cep = __insert_by_qpn( p_new_cep );
\r
2850 if( p_cep != p_new_cep )
\r
2853 p_cep = __insert_by_id( p_new_cep );
\r
2854 if( p_cep != p_new_cep )
\r
2856 cl_rbmap_remove_item(
\r
2857 &gp_cep_mgr->conn_qp_map, &p_new_cep->rem_qp_item );
\r
2860 * Clear the remote QPN and comm ID so that we don't try
\r
2861 * to remove the CEP from those maps.
\r
2863 p_new_cep->remote_qpn = 0;
\r
2864 p_new_cep->remote_comm_id = 0;
\r
2867 AL_EXIT( AL_DBG_CM );
\r
2872 static inline void
\r
2874 IN kcep_t* const p_cep )
\r
2876 AL_ENTER( AL_DBG_CM );
\r
2878 if( p_cep->remote_comm_id )
\r
2880 cl_rbmap_remove_item(
\r
2881 &gp_cep_mgr->conn_id_map, &p_cep->rem_id_item );
\r
2882 p_cep->remote_comm_id = 0;
\r
2884 if( p_cep->remote_qpn )
\r
2886 cl_rbmap_remove_item(
\r
2887 &gp_cep_mgr->conn_qp_map, &p_cep->rem_qp_item );
\r
2888 p_cep->remote_qpn = 0;
\r
2891 AL_EXIT( AL_DBG_CM );
\r
2897 IN ib_net16_t lid,
\r
2898 IN ib_net16_t port_lid,
\r
2903 uint16_t path_bits;
\r
2907 lid1 = CL_NTOH16(lid);
\r
2908 lid2 = CL_NTOH16(port_lid);
\r
2915 path_bits = (uint16_t)( (path_bits << 1) | 1 );
\r
2917 lid2 |= path_bits;
\r
2924 if (lid != port_lid)
\r
2932 static inline boolean_t
\r
2934 IN const ib_port_attr_t* const p_port_attr,
\r
2935 IN const ib_gid_t* const p_gid )
\r
2939 for( idx = 0; idx < p_port_attr->num_gids; idx++ )
\r
2942 p_gid, &p_port_attr->p_gid_table[idx], sizeof(ib_gid_t) ) )
\r
2951 static inline boolean_t
\r
2953 IN const ib_port_attr_t* const p_port_attr,
\r
2954 IN const net16_t pkey,
\r
2955 OUT uint16_t* const p_pkey_index )
\r
2959 for( idx = 0; idx < p_port_attr->num_pkeys; idx++ )
\r
2961 if( p_port_attr->p_pkey_table[idx] == pkey )
\r
2963 *p_pkey_index = idx;
\r
2972 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
2973 static cep_agent_t*
\r
2975 IN const ib_gid_t* const p_gid,
\r
2976 IN const net16_t lid,
\r
2977 IN const net16_t pkey,
\r
2978 OUT uint16_t* const p_pkey_index )
\r
2980 cep_agent_t *p_port_cep;
\r
2981 cl_list_item_t *p_item;
\r
2982 const ib_port_attr_t *p_port_attr;
\r
2984 AL_ENTER( AL_DBG_CM );
\r
2986 cl_spinlock_acquire( &gp_cep_mgr->obj.lock );
\r
2987 for( p_item = cl_qlist_head( &gp_cep_mgr->obj.obj_list );
\r
2988 p_item != cl_qlist_end( &gp_cep_mgr->obj.obj_list );
\r
2989 p_item = cl_qlist_next( p_item ) )
\r
2991 p_port_cep = PARENT_STRUCT( p_item, cep_agent_t, obj.pool_item );
\r
2993 CL_ASSERT( p_port_cep->port_num );
\r
2995 ci_ca_lock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2997 p_port_attr = p_port_cep->h_ca->obj.p_ci_ca->p_pnp_attr->p_port_attr;
\r
2998 p_port_attr += (p_port_cep->port_num - 1);
\r
3000 if( __is_lid_valid( lid, p_port_attr->lid, p_port_attr->lmc ) &&
\r
3001 __is_gid_valid( p_port_attr, p_gid ) &&
\r
3002 __get_pkey_index( p_port_attr, pkey, p_pkey_index ) )
\r
3004 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
3005 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
3006 AL_EXIT( AL_DBG_CM );
\r
3007 return p_port_cep;
\r
3010 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
3012 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
3013 AL_EXIT( AL_DBG_CM );
\r
3019 * PnP callback for port event notifications.
\r
3021 static ib_api_status_t
\r
3023 IN ib_pnp_rec_t *p_pnp_rec )
\r
3025 ib_api_status_t status = IB_SUCCESS;
\r
3027 AL_ENTER( AL_DBG_CM );
\r
3029 switch( p_pnp_rec->pnp_event )
\r
3031 case IB_PNP_PORT_ADD:
\r
3032 /* Create the port agent. */
\r
3033 CL_ASSERT( !p_pnp_rec->context );
\r
3034 status = __create_port_cep( (ib_pnp_port_rec_t*)p_pnp_rec );
\r
3037 case IB_PNP_PORT_REMOVE:
\r
3038 CL_ASSERT( p_pnp_rec->context );
\r
3040 /* Destroy the port agent. */
\r
3041 ref_al_obj( &((cep_agent_t*)p_pnp_rec->context)->obj );
\r
3042 ((cep_agent_t*)p_pnp_rec->context)->obj.pfn_destroy(
\r
3043 &((cep_agent_t*)p_pnp_rec->context)->obj, NULL );
\r
3047 break; /* Ignore other PNP events. */
\r
3050 AL_EXIT( AL_DBG_CM );
\r
3055 static inline int64_t
\r
3057 IN int64_t current_min,
\r
3058 IN kcep_t* const p_cep )
\r
3061 * The minimum timer interval is 50 milliseconds. This means
\r
3062 * 500000 100ns increments. Since __process_timewait divides the
\r
3063 * result in half (so that the worst cast timewait interval is 150%)
\r
3064 * we compensate for this here. Note that relative time values are
\r
3065 * expressed as negative.
\r
3067 #define MIN_TIMEWAIT_100NS -1000000
\r
3069 /* Still in timewait - try again next time. */
\r
3070 if( !current_min )
\r
3072 return min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS );
\r
3076 return max( current_min,
\r
3077 min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS ) );
\r
3083 * Timer callback to process CEPs in timewait state. Returns time in ms.
\r
3086 __process_timewait()
\r
3088 cl_list_item_t *p_item;
\r
3090 LARGE_INTEGER timeout;
\r
3091 int64_t min_timewait = 0;
\r
3093 AL_ENTER( AL_DBG_CM );
\r
3095 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3097 timeout.QuadPart = 0;
\r
3099 p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3100 while( p_item != cl_qlist_end( &gp_cep_mgr->timewait_list ) )
\r
3102 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3103 p_item = cl_qlist_next( p_item );
\r
3105 CL_ASSERT( p_cep->state == CEP_STATE_DESTROY ||
\r
3106 p_cep->state == CEP_STATE_TIMEWAIT );
\r
3108 CL_ASSERT( !p_cep->p_mad );
\r
3110 if( KeWaitForSingleObject( &p_cep->timewait_timer, Executive,
\r
3111 KernelMode, FALSE, &timeout ) != STATUS_SUCCESS )
\r
3113 /* Still in timewait - try again next time. */
\r
3114 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3118 if( p_cep->ref_cnt )
\r
3120 /* Send outstanding or destruction in progress. */
\r
3121 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3125 /* Remove from the timewait list. */
\r
3126 cl_qlist_remove_item( &gp_cep_mgr->timewait_list, &p_cep->timewait_item );
\r
3129 * Not in timewait. Remove the CEP from the maps - it should
\r
3130 * no longer be matched against.
\r
3132 __remove_cep( p_cep );
\r
3134 if( p_cep->state == CEP_STATE_DESTROY )
\r
3136 __destroy_cep( p_cep );
\r
3140 /* Move the CEP to the IDLE state so that it can be used again. */
\r
3141 p_cep->state = CEP_STATE_IDLE;
\r
3145 AL_EXIT( AL_DBG_CM );
\r
3146 return (uint32_t)(min_timewait / -20000);
\r
3151 * Timer callback to process CEPs in timewait state.
\r
3154 __cep_timewait_cb(
\r
3155 IN void *context )
\r
3157 KLOCK_QUEUE_HANDLE hdl;
\r
3158 uint32_t min_timewait;
\r
3160 AL_ENTER( AL_DBG_CM );
\r
3162 UNUSED_PARAM( context );
\r
3164 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3166 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
3168 min_timewait = __process_timewait();
\r
3170 if( cl_qlist_count( &gp_cep_mgr->timewait_list ) )
\r
3173 * Reset the timer for half of the shortest timeout - this results
\r
3174 * in a worst case timeout of 150% of timewait.
\r
3176 cl_timer_trim( &gp_cep_mgr->timewait_timer, min_timewait );
\r
3179 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
3181 AL_EXIT( AL_DBG_CM );
\r
3186 * Starts immediate cleanup of the CM. Invoked during al_obj destruction.
\r
3189 __destroying_cep_mgr(
\r
3190 IN al_obj_t* p_obj )
\r
3192 ib_api_status_t status;
\r
3193 KLOCK_QUEUE_HANDLE hdl;
\r
3194 cl_list_item_t *p_item;
\r
3196 LARGE_INTEGER timeout;
\r
3198 AL_ENTER( AL_DBG_CM );
\r
3200 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3201 UNUSED_PARAM( p_obj );
\r
3203 /* Deregister from PnP notifications. */
\r
3204 if( gp_cep_mgr->h_pnp )
\r
3206 status = ib_dereg_pnp(
\r
3207 gp_cep_mgr->h_pnp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
3208 if( status != IB_SUCCESS )
\r
3210 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r