2 * Copyright (c) 2005 SilverStorm Technologies. All rights reserved.
\r
4 * This software is available to you under the OpenIB.org BSD license
\r
7 * Redistribution and use in source and binary forms, with or
\r
8 * without modification, are permitted provided that the following
\r
9 * conditions are met:
\r
11 * - Redistributions of source code must retain the above
\r
12 * copyright notice, this list of conditions and the following
\r
15 * - Redistributions in binary form must reproduce the above
\r
16 * copyright notice, this list of conditions and the following
\r
17 * disclaimer in the documentation and/or other materials
\r
18 * provided with the distribution.
\r
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
\r
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
\r
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
33 #include <iba/ib_al.h>
\r
34 #include <complib/cl_vector.h>
\r
35 #include <complib/cl_rbmap.h>
\r
36 #include <complib/cl_qmap.h>
\r
37 #include <complib/cl_spinlock.h>
\r
38 #include "al_common.h"
\r
39 #include "al_cm_cep.h"
\r
40 #include "al_cm_conn.h"
\r
41 #include "al_cm_sidr.h"
\r
42 #include "al_debug.h"
\r
43 #include "ib_common.h"
\r
52 * The vector object uses a list item at the front of the buffers
\r
53 * it allocates. Take the list item into account so that allocations
\r
54 * are for full page sizes.
\r
56 #define CEP_CID_MIN \
\r
57 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
58 #define CEP_CID_GROW \
\r
59 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
62 * We reserve the upper byte of the connection ID as a revolving counter so
\r
63 * that connections that are retried by the client change connection ID.
\r
64 * This counter is never zero, so it is OK to use all CIDs since we will never
\r
65 * have a full CID (base + counter) that is zero.
\r
66 * See the IB spec, section 12.9.8.7 for details about REJ retry.
\r
68 #define CEP_MAX_CID (0x00FFFFFF)
\r
69 #define CEP_MAX_CID_MASK (0x00FFFFFF)
\r
71 #define CEP_MAD_SQ_DEPTH (128)
\r
72 #define CEP_MAD_RQ_DEPTH (1) /* ignored. */
\r
73 #define CEP_MAD_SQ_SGE (1)
\r
74 #define CEP_MAD_RQ_SGE (1) /* ignored. */
\r
77 /* Global connection manager object. */
\r
78 typedef struct _al_cep_mgr
\r
86 /* Bitmap of CEPs, indexed by CID. */
\r
87 cl_vector_t cid_vector;
\r
90 /* List of active listens. */
\r
91 cl_rbmap_t listen_map;
\r
93 /* Map of CEP by remote CID and CA GUID. */
\r
94 cl_rbmap_t conn_id_map;
\r
95 /* Map of CEP by remote QPN, used for stale connection matching. */
\r
96 cl_rbmap_t conn_qp_map;
\r
98 NPAGED_LOOKASIDE_LIST cep_pool;
\r
99 NPAGED_LOOKASIDE_LIST req_pool;
\r
102 * Periodically walk the list of connections in the time wait state
\r
103 * and flush them as appropriate.
\r
105 cl_timer_t timewait_timer;
\r
106 cl_qlist_t timewait_list;
\r
108 ib_pnp_handle_t h_pnp;
\r
113 /* Per-port CM object. */
\r
114 typedef struct _cep_port_agent
\r
118 cl_map_item_t item;
\r
120 ib_ca_handle_t h_ca;
\r
121 ib_pd_handle_t h_pd;
\r
122 ib_qp_handle_t h_qp;
\r
123 ib_pool_key_t pool_key;
\r
124 ib_mad_svc_handle_t h_mad_svc;
\r
134 * Note: the REQ, REP, and LAP values must be 1, 2, and 4 respectively.
\r
135 * This allows shifting 1 << msg_mraed from an MRA to figure out for what
\r
136 * message the MRA was sent for.
\r
138 #define CEP_STATE_RCVD 0x10000000
\r
139 #define CEP_STATE_SENT 0x20000000
\r
140 #define CEP_STATE_MRA 0x01000000
\r
141 #define CEP_STATE_REQ 0x00000001
\r
142 #define CEP_STATE_REP 0x00000002
\r
143 #define CEP_STATE_LAP 0x00000004
\r
144 #define CEP_STATE_RTU 0x00000008
\r
145 #define CEP_STATE_DREQ 0x00000010
\r
146 #define CEP_STATE_DREP 0x00000020
\r
147 #define CEP_STATE_DESTROYING 0x00010000
\r
148 #define CEP_STATE_USER 0x00020000
\r
150 #define CEP_MSG_MASK 0x000000FF
\r
151 #define CEP_OP_MASK 0xF0000000
\r
153 #define CEP_STATE_PREP 0x00100000
\r
155 /* States match CM state transition diagrams from spec. */
\r
156 typedef enum _cep_state
\r
160 CEP_STATE_ESTABLISHED,
\r
161 CEP_STATE_TIMEWAIT,
\r
162 CEP_STATE_SREQ_SENT,
\r
163 CEP_STATE_SREQ_RCVD,
\r
165 CEP_STATE_DESTROY = CEP_STATE_DESTROYING,
\r
166 CEP_STATE_PRE_REQ = CEP_STATE_IDLE | CEP_STATE_PREP,
\r
167 CEP_STATE_REQ_RCVD = CEP_STATE_REQ | CEP_STATE_RCVD,
\r
168 CEP_STATE_PRE_REP = CEP_STATE_REQ_RCVD | CEP_STATE_PREP,
\r
169 CEP_STATE_REQ_SENT = CEP_STATE_REQ | CEP_STATE_SENT,
\r
170 CEP_STATE_REQ_MRA_RCVD = CEP_STATE_REQ_SENT | CEP_STATE_MRA,
\r
171 CEP_STATE_REQ_MRA_SENT = CEP_STATE_REQ_RCVD | CEP_STATE_MRA,
\r
172 CEP_STATE_PRE_REP_MRA_SENT = CEP_STATE_REQ_MRA_SENT | CEP_STATE_PREP,
\r
173 CEP_STATE_REP_RCVD = CEP_STATE_REP | CEP_STATE_RCVD,
\r
174 CEP_STATE_REP_SENT = CEP_STATE_REP | CEP_STATE_SENT,
\r
175 CEP_STATE_REP_MRA_RCVD = CEP_STATE_REP_SENT | CEP_STATE_MRA,
\r
176 CEP_STATE_REP_MRA_SENT = CEP_STATE_REP_RCVD | CEP_STATE_MRA,
\r
177 CEP_STATE_LAP_RCVD = CEP_STATE_LAP | CEP_STATE_RCVD,
\r
178 CEP_STATE_PRE_APR = CEP_STATE_LAP_RCVD | CEP_STATE_PREP,
\r
179 CEP_STATE_LAP_SENT = CEP_STATE_LAP | CEP_STATE_SENT,
\r
180 CEP_STATE_LAP_MRA_RCVD = CEP_STATE_LAP_SENT | CEP_STATE_MRA,
\r
181 CEP_STATE_LAP_MRA_SENT = CEP_STATE_LAP_RCVD | CEP_STATE_MRA,
\r
182 CEP_STATE_PRE_APR_MRA_SENT = CEP_STATE_LAP_MRA_SENT | CEP_STATE_PREP,
\r
183 CEP_STATE_DREQ_SENT = CEP_STATE_DREQ | CEP_STATE_SENT,
\r
184 CEP_STATE_DREQ_RCVD = CEP_STATE_DREQ | CEP_STATE_RCVD,
\r
185 CEP_STATE_DREQ_DESTROY = CEP_STATE_DREQ_SENT | CEP_STATE_DESTROYING
\r
190 /* Active side CEP state transitions:
\r
191 * al_create_cep -> IDLE
\r
192 * al_cep_pre_req -> PRE_REQ
\r
193 * al_cep_send_req -> REQ_SENT
\r
194 * Recv REQ MRA -> REQ_MRA_RCVD
\r
195 * Recv REP -> REP_RCVD
\r
196 * al_cep_mra -> REP_MRA_SENT
\r
197 * al_cep_rtu -> ESTABLISHED
\r
199 * Passive side CEP state transitions:
\r
200 * al_create_cep -> IDLE
\r
201 * Recv REQ -> REQ_RCVD
\r
202 * al_cep_mra* -> REQ_MRA_SENT
\r
203 * al_cep_pre_rep -> PRE_REP
\r
204 * al_cep_mra* -> PRE_REP_MRA_SENT
\r
205 * al_cep_send_rep -> REP_SENT
\r
206 * Recv RTU -> ESTABLISHED
\r
208 * *al_cep_mra can only be called once - either before or after PRE_REP.
\r
211 typedef struct _al_kcep_av
\r
215 uint16_t pkey_index;
\r
220 typedef struct _al_kcep
\r
225 struct _cep_cid *p_cid;
\r
229 /* Port guid for filtering incoming requests. */
\r
232 uint8_t* __ptr64 p_cmp_buf;
\r
233 uint8_t cmp_offset;
\r
238 /* Used to store connection structure with owning AL instance. */
\r
239 cl_list_item_t al_item;
\r
241 /* Flag to indicate whether a user is processing events. */
\r
242 boolean_t signalled;
\r
244 /* Destroy callback. */
\r
245 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
247 ib_mad_element_t *p_mad_head;
\r
248 ib_mad_element_t *p_mad_tail;
\r
249 al_pfn_cep_cb_t pfn_cb;
\r
253 /* MAP item for finding listen CEPs. */
\r
254 cl_rbmap_item_t listen_item;
\r
256 /* Map item for finding CEPs based on remote comm ID & CA GUID. */
\r
257 cl_rbmap_item_t rem_id_item;
\r
259 /* Map item for finding CEPs based on remote QP number. */
\r
260 cl_rbmap_item_t rem_qp_item;
\r
262 /* Communication ID's for the connection. */
\r
263 net32_t local_comm_id;
\r
264 net32_t remote_comm_id;
\r
266 net64_t local_ca_guid;
\r
267 net64_t remote_ca_guid;
\r
269 /* Remote QP, used for stale connection checking. */
\r
270 net32_t remote_qpn;
\r
272 /* Parameters to format QP modification structure. */
\r
276 uint8_t init_depth;
\r
277 uint8_t rnr_nak_timeout;
\r
280 * Local QP number, used for the "additional check" required
\r
285 /* PKEY to make sure a LAP is on the same partition. */
\r
288 /* Initiator depth as received in the REQ. */
\r
289 uint8_t req_init_depth;
\r
292 * Primary and alternate path info, used to create the address vectors for
\r
293 * sending MADs, to locate the port CM agent to use for outgoing sends,
\r
294 * and for creating the address vectors for transitioning QPs.
\r
297 uint8_t idx_primary;
\r
299 /* Temporary AV and CEP port GUID used when processing LAP. */
\r
301 uint8_t alt_2pkt_life;
\r
303 /* maxium packet lifetime * 2 of any path used on a connection. */
\r
304 uint8_t max_2pkt_life;
\r
305 /* Given by the REP, used for alternate path setup. */
\r
306 uint8_t target_ack_delay;
\r
307 /* Stored to help calculate the local ACK delay in the LAP. */
\r
308 uint8_t local_ack_delay;
\r
310 /* Volatile to allow using atomic operations for state checks. */
\r
314 * Flag that indicates whether a connection took the active role during
\r
317 boolean_t was_active;
\r
320 * Handle to the sent MAD, used for cancelling. We store the handle to
\r
321 * the mad service so that we can properly cancel. This should not be a
\r
322 * problem since all outstanding sends should be completed before the
\r
323 * mad service completes its destruction and the handle becomes invalid.
\r
325 ib_mad_svc_handle_t h_mad_svc;
\r
326 ib_mad_element_t *p_send_mad;
\r
328 /* Number of outstanding MADs. Delays destruction of CEP destruction. */
\r
329 atomic32_t ref_cnt;
\r
331 /* MAD transaction ID to use when sending MADs. */
\r
334 /* Maximum retries per MAD. Set at REQ time, stored to retry LAP. */
\r
335 uint8_t max_cm_retries;
\r
336 /* Timeout value, in milliseconds. Set at REQ time, stored to retry LAP. */
\r
337 uint32_t retry_timeout;
\r
339 /* Timer that will be signalled when the CEP exits timewait. */
\r
340 KTIMER timewait_timer;
\r
341 LARGE_INTEGER timewait_time;
\r
342 cl_list_item_t timewait_item;
\r
345 * Pointer to a formatted MAD. The pre_req, pre_rep and pre_apr calls
\r
346 * allocate and format the MAD, and the send_req, send_rep and send_apr
\r
349 ib_mad_element_t *p_mad;
\r
351 /* Cache the last MAD sent for retransmission. */
\r
357 mad_cm_drep_t drep;
\r
364 /* Structures stored in the CID vector. */
\r
365 typedef struct _cep_cid
\r
367 /* Owning AL handle. NULL if invalid. */
\r
368 ib_al_handle_t h_al;
\r
369 /* Pointer to CEP, or index of next free entry if h_al is NULL. */
\r
371 /* For REJ Retry support */
\r
377 /* Global instance of the CM agent. */
\r
378 al_cep_mgr_t *gp_cep_mgr = NULL;
\r
381 static ib_api_status_t
\r
383 IN kcep_t* const p_cep,
\r
384 IN const uint8_t* p_pdata OPTIONAL,
\r
385 IN uint8_t pdata_len,
\r
386 IN OUT mad_cm_drep_t* const p_drep );
\r
388 static ib_api_status_t
\r
390 IN kcep_t* const p_cep,
\r
391 IN ib_mad_element_t* p_mad );
\r
395 IN kcep_t* const p_cep );
\r
397 static inline uint32_t
\r
398 __calc_mad_timeout(
\r
399 IN const uint8_t pkt_life );
\r
403 IN kcep_t* const p_cep );
\r
406 __create_cep( void );
\r
410 IN kcep_t* const p_cep );
\r
414 IN kcep_t* const p_cep );
\r
418 IN kcep_t* const p_cep,
\r
419 IN ib_al_handle_t h_al,
\r
420 IN al_pfn_cep_cb_t pfn_cb,
\r
421 IN void* __ptr64 context );
\r
425 IN kcep_t* const p_cep );
\r
429 IN kcep_t* const p_cep );
\r
433 IN net32_t remote_comm_id,
\r
434 IN net64_t remote_ca_guid );
\r
439 IN net64_t port_guid,
\r
440 IN void *p_pdata );
\r
442 static inline kcep_t*
\r
444 IN ib_al_handle_t h_al OPTIONAL,
\r
447 static inline kcep_t*
\r
449 IN kcep_t* const p_new_cep );
\r
453 IN kcep_t* const p_cep );
\r
457 IN kcep_t* const p_cep );
\r
459 static ib_api_status_t
\r
461 IN kcep_t* const p_cep,
\r
462 IN net16_t attr_id,
\r
463 OUT cep_agent_t** const pp_port_cep,
\r
464 OUT ib_mad_element_t** const pp_mad );
\r
466 static ib_api_status_t
\r
468 IN cep_agent_t* const p_port_cep,
\r
469 IN ib_mad_element_t* const p_mad );
\r
471 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
472 static cep_agent_t*
\r
474 IN const ib_gid_t* const p_gid,
\r
475 IN const net16_t lid,
\r
476 IN const net16_t pkey,
\r
477 OUT uint16_t* const p_pkey_index );
\r
481 OUT net32_t* const p_cid );
\r
484 __process_cep_send_comp(
\r
485 IN cl_async_proc_item_t *p_item );
\r
488 /******************************************************************************
\r
489 * Per-port CEP agent
\r
490 ******************************************************************************/
\r
495 IN ib_mad_t* const p_mad,
\r
496 IN const kcep_t* const p_cep,
\r
497 IN net16_t attr_id )
\r
499 p_mad->base_ver = 1;
\r
500 p_mad->mgmt_class = IB_MCLASS_COMM_MGMT;
\r
501 p_mad->class_ver = IB_MCLASS_CM_VER_2;
\r
502 p_mad->method = IB_MAD_METHOD_SEND;
\r
504 p_mad->class_spec = 0;
\r
505 p_mad->trans_id = p_cep->tid;
\r
506 p_mad->attr_id = attr_id;
\r
508 p_mad->attr_mod = 0;
\r
512 /* Consumes the input MAD. */
\r
515 IN cep_agent_t* const p_port_cep,
\r
516 IN kcep_t* const p_cep,
\r
517 IN ib_mad_element_t* const p_mad,
\r
518 IN ib_rej_status_t reason )
\r
520 mad_cm_rej_t *p_rej;
\r
522 AL_ENTER( AL_DBG_CM );
\r
524 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
526 __format_mad_hdr( p_mad->p_mad_buf, p_cep, CM_REJ_ATTR_ID );
\r
528 p_rej->local_comm_id = p_cep->local_comm_id;
\r
529 p_rej->remote_comm_id = p_cep->remote_comm_id;
\r
530 p_rej->reason = reason;
\r
532 switch( p_cep->state )
\r
534 case CEP_STATE_REQ_RCVD:
\r
535 case CEP_STATE_REQ_MRA_SENT:
\r
536 case CEP_STATE_PRE_REP:
\r
537 case CEP_STATE_PRE_REP_MRA_SENT:
\r
538 conn_rej_set_msg_rejected( 0, p_rej );
\r
541 case CEP_STATE_REP_RCVD:
\r
542 case CEP_STATE_REP_MRA_SENT:
\r
543 conn_rej_set_msg_rejected( 1, p_rej );
\r
547 CL_ASSERT( reason == IB_REJ_TIMEOUT );
\r
548 conn_rej_set_msg_rejected( 2, p_rej );
\r
552 conn_rej_clr_rsvd_fields( p_rej );
\r
553 __cep_send_mad( p_port_cep, p_mad );
\r
555 AL_EXIT( AL_DBG_CM );
\r
561 IN cep_agent_t* const p_port_cep,
\r
562 IN kcep_t* const p_cep,
\r
563 IN const ib_mad_element_t* const p_mad )
\r
565 ib_api_status_t status;
\r
566 ib_mad_element_t *p_rej_mad;
\r
567 ib_mad_t *p_mad_buf;
\r
570 AL_ENTER( AL_DBG_CM );
\r
572 status = ib_get_mad( p_port_cep->pool_key, MAD_BLOCK_SIZE, &p_rej_mad );
\r
573 if( status != IB_SUCCESS )
\r
575 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
576 ("ib_get_mad returned %s\n", ib_get_err_str( status )) );
\r
580 /* Save the buffer pointers from the new element. */
\r
581 p_mad_buf = p_rej_mad->p_mad_buf;
\r
582 p_grh = p_rej_mad->p_grh;
\r
585 * Copy the input MAD element to the reject - this gives us
\r
586 * all appropriate addressing information.
\r
588 cl_memcpy( p_rej_mad, p_mad, sizeof(ib_mad_element_t) );
\r
589 cl_memcpy( p_grh, p_mad->p_grh, sizeof(ib_grh_t) );
\r
591 /* Restore the buffer pointers now that the copy is complete. */
\r
592 p_rej_mad->p_mad_buf = p_mad_buf;
\r
593 p_rej_mad->p_grh = p_grh;
\r
595 status = conn_rej_set_pdata( NULL, 0, (mad_cm_rej_t*)p_mad_buf );
\r
596 CL_ASSERT( status == IB_SUCCESS );
\r
598 /* Copy the local CA GUID into the ARI. */
\r
599 switch( p_mad->p_mad_buf->attr_id )
\r
601 case CM_REQ_ATTR_ID:
\r
602 status = conn_rej_set_ari(
\r
603 (uint8_t*)&p_cep->local_ca_guid,
\r
604 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
605 CL_ASSERT( status == IB_SUCCESS );
\r
606 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
609 case CM_REP_ATTR_ID:
\r
610 status = conn_rej_set_ari(
\r
611 (uint8_t*)&p_cep->local_ca_guid,
\r
612 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
613 CL_ASSERT( status == IB_SUCCESS );
\r
614 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
618 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
619 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID );
\r
620 ib_put_mad( p_rej_mad );
\r
624 AL_EXIT( AL_DBG_CM );
\r
630 IN cep_agent_t* const p_port_cep,
\r
631 IN ib_mad_element_t* const p_mad,
\r
632 IN const ib_rej_status_t reason )
\r
634 mad_cm_req_t *p_req;
\r
635 mad_cm_rej_t *p_rej;
\r
637 AL_ENTER( AL_DBG_CM );
\r
639 CL_ASSERT( p_port_cep );
\r
640 CL_ASSERT( p_mad );
\r
641 CL_ASSERT( reason != 0 );
\r
643 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
644 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
647 * Format the reject information, overwriting the REQ data and send
\r
650 p_rej->hdr.attr_id = CM_REJ_ATTR_ID;
\r
651 p_rej->remote_comm_id = p_req->local_comm_id;
\r
652 p_rej->local_comm_id = 0;
\r
653 conn_rej_set_msg_rejected( 0, p_rej );
\r
654 p_rej->reason = reason;
\r
655 conn_rej_set_ari( NULL, 0, p_rej );
\r
656 conn_rej_set_pdata( NULL, 0, p_rej );
\r
657 conn_rej_clr_rsvd_fields( p_rej );
\r
659 p_mad->retry_cnt = 0;
\r
660 p_mad->send_opt = 0;
\r
661 p_mad->timeout_ms = 0;
\r
662 p_mad->resp_expected = FALSE;
\r
664 __cep_send_mad( p_port_cep, p_mad );
\r
666 AL_EXIT( AL_DBG_CM );
\r
672 IN kcep_t* const p_cep,
\r
673 IN const mad_cm_req_t* const p_req,
\r
674 IN const uint8_t idx )
\r
676 cep_agent_t *p_port_cep;
\r
677 const req_path_info_t *p_path;
\r
679 AL_ENTER( AL_DBG_CM );
\r
681 CL_ASSERT( p_cep );
\r
682 CL_ASSERT( p_req );
\r
684 cl_memclr( &p_cep->av[idx], sizeof(kcep_av_t) );
\r
686 p_path = &((&p_req->primary_path)[idx]);
\r
688 p_port_cep = __find_port_cep( &p_path->remote_gid,
\r
689 p_path->remote_lid, p_req->pkey, &p_cep->av[idx].pkey_index );
\r
693 p_cep->local_ca_guid = 0;
\r
694 AL_EXIT( AL_DBG_CM );
\r
699 p_cep->local_ca_guid = p_port_cep->h_ca->obj.p_ci_ca->verbs.guid;
\r
701 /* Check that CA GUIDs match if formatting the alternate path. */
\r
703 p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
705 AL_EXIT( AL_DBG_CM );
\r
710 * Pkey indeces must match if formating the alternat path - the QP
\r
711 * modify structure only allows for a single PKEY index to be specified.
\r
714 p_cep->av[0].pkey_index != p_cep->av[1].pkey_index )
\r
716 AL_EXIT( AL_DBG_CM );
\r
720 p_cep->av[idx].port_guid = p_port_cep->port_guid;
\r
721 p_cep->av[idx].attr.port_num = p_port_cep->port_num;
\r
723 p_cep->av[idx].attr.sl = conn_req_path_get_svc_lvl( p_path );
\r
724 p_cep->av[idx].attr.dlid = p_path->local_lid;
\r
726 if( !conn_req_path_get_subn_lcl( p_path ) )
\r
728 p_cep->av[idx].attr.grh_valid = TRUE;
\r
729 p_cep->av[idx].attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
730 1, p_path->traffic_class, conn_req_path_get_flow_lbl( p_path ) );
\r
731 p_cep->av[idx].attr.grh.hop_limit = p_path->hop_limit;
\r
732 p_cep->av[idx].attr.grh.dest_gid = p_path->local_gid;
\r
733 p_cep->av[idx].attr.grh.src_gid = p_path->remote_gid;
\r
737 p_cep->av[idx].attr.grh_valid = FALSE;
\r
739 p_cep->av[idx].attr.static_rate = conn_req_path_get_pkt_rate( p_path );
\r
740 p_cep->av[idx].attr.path_bits =
\r
741 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
744 * Note that while we never use the connected AV attributes internally,
\r
745 * we store them so we can pass them back to users.
\r
747 p_cep->av[idx].attr.conn.path_mtu = conn_req_get_mtu( p_req );
\r
748 p_cep->av[idx].attr.conn.local_ack_timeout =
\r
749 conn_req_path_get_lcl_ack_timeout( p_path );
\r
750 p_cep->av[idx].attr.conn.seq_err_retry_cnt =
\r
751 conn_req_get_retry_cnt( p_req );
\r
752 p_cep->av[idx].attr.conn.rnr_retry_cnt =
\r
753 conn_req_get_rnr_retry_cnt( p_req );
\r
755 AL_EXIT( AL_DBG_CM );
\r
760 * + Validates the path information provided in the REQ and stores the
\r
761 * associated CA attributes and port indeces.
\r
762 * + Transitions a connection object from active to passive in the peer case.
\r
763 * + Sets the path information in the connection and sets the CA GUID
\r
764 * in the REQ callback record.
\r
768 IN OUT kcep_t* const p_cep,
\r
769 IN OUT mad_cm_req_t* const p_req )
\r
771 AL_ENTER( AL_DBG_CM );
\r
773 p_cep->state = CEP_STATE_REQ_RCVD;
\r
774 p_cep->was_active = FALSE;
\r
776 p_cep->sid = p_req->sid;
\r
778 /* Store pertinent information in the connection. */
\r
779 p_cep->remote_comm_id = p_req->local_comm_id;
\r
780 p_cep->remote_ca_guid = p_req->local_ca_guid;
\r
782 p_cep->remote_qpn = conn_req_get_lcl_qpn( p_req );
\r
783 p_cep->local_qpn = 0;
\r
785 p_cep->retry_timeout =
\r
786 __calc_mad_timeout( conn_req_get_lcl_resp_timeout( p_req ) );
\r
788 /* Store the retry count. */
\r
789 p_cep->max_cm_retries = conn_req_get_max_cm_retries( p_req );
\r
792 * Copy the paths from the req_rec into the connection for
\r
793 * future use. Note that if the primary path is invalid,
\r
794 * the REP will fail.
\r
796 __format_req_av( p_cep, p_req, 0 );
\r
798 if( p_req->alternate_path.local_lid )
\r
799 __format_req_av( p_cep, p_req, 1 );
\r
801 cl_memclr( &p_cep->av[1], sizeof(kcep_av_t) );
\r
803 p_cep->idx_primary = 0;
\r
805 /* Store the maximum packet lifetime, used to calculate timewait. */
\r
806 p_cep->max_2pkt_life = conn_req_path_get_lcl_ack_timeout( &p_req->primary_path );
\r
807 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life,
\r
808 conn_req_path_get_lcl_ack_timeout( &p_req->alternate_path ) );
\r
811 * Make sure the target ack delay is cleared - the above
\r
812 * "packet life" includes it.
\r
814 p_cep->target_ack_delay = 0;
\r
816 /* Store the requested initiator depth. */
\r
817 p_cep->req_init_depth = conn_req_get_init_depth( p_req );
\r
820 * Store the provided responder resources. These turn into the local
\r
821 * QP's initiator depth.
\r
823 p_cep->init_depth = conn_req_get_resp_res( p_req );
\r
825 p_cep->sq_psn = conn_req_get_starting_psn( p_req );
\r
827 p_cep->tid = p_req->hdr.trans_id;
\r
828 /* copy mad info for cm handoff */
\r
829 /* TODO: Do need to support CM handoff? */
\r
830 //p_cep->mads.req = *p_req;
\r
832 AL_EXIT( AL_DBG_CM );
\r
836 /* Must be called with the CEP lock held. */
\r
839 IN cep_agent_t* const p_port_cep,
\r
840 IN kcep_t* const p_cep,
\r
841 IN ib_mad_element_t* const p_mad )
\r
843 AL_ENTER( AL_DBG_CM );
\r
845 CL_ASSERT( p_port_cep );
\r
846 CL_ASSERT( p_cep );
\r
847 CL_ASSERT( p_mad );
\r
849 /* Repeat the last mad sent for the connection. */
\r
850 switch( p_cep->state )
\r
852 case CEP_STATE_REQ_MRA_SENT: /* resend MRA(REQ) */
\r
853 case CEP_STATE_REP_MRA_SENT: /* resend MRA(REP) */
\r
854 case CEP_STATE_LAP_MRA_SENT: /* resend MRA(LAP) */
\r
855 case CEP_STATE_ESTABLISHED: /* resend RTU */
\r
856 case CEP_STATE_TIMEWAIT: /* resend the DREP */
\r
857 cl_memcpy( p_mad->p_mad_buf, &p_cep->mads, MAD_BLOCK_SIZE );
\r
858 p_mad->send_context1 = NULL;
\r
859 p_mad->send_context2 = NULL;
\r
860 __cep_send_mad( p_port_cep, p_mad );
\r
864 /* Return the MAD to the mad pool */
\r
865 ib_put_mad( p_mad );
\r
869 AL_EXIT( AL_DBG_CM );
\r
873 static ib_api_status_t
\r
875 IN kcep_t* const p_cep,
\r
876 IN ib_mad_element_t* const p_mad )
\r
878 ib_api_status_t status;
\r
879 mad_cm_rej_t *p_rej;
\r
881 AL_ENTER( AL_DBG_CM );
\r
885 ASSERT( p_mad->p_mad_buf );
\r
887 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
889 switch( p_cep->state )
\r
891 case CEP_STATE_REQ_SENT:
\r
893 * Ignore rejects with the status set to IB_REJ_INVALID_SID. We will
\r
894 * continue to retry (up to max_cm_retries) to connect to the remote
\r
895 * side. This is required to support peer-to-peer connections and
\r
896 * clients that try to connect before the server comes up.
\r
898 if( p_rej->reason == IB_REJ_INVALID_SID )
\r
900 AL_TRACE( AL_DBG_CM,
\r
901 ("Request rejected (invalid SID) - retrying.\n") );
\r
906 case CEP_STATE_REP_SENT:
\r
907 case CEP_STATE_REQ_MRA_RCVD:
\r
908 case CEP_STATE_REP_MRA_RCVD:
\r
909 /* Cancel any outstanding MAD. */
\r
910 if( p_cep->p_send_mad )
\r
912 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
913 p_cep->p_send_mad = NULL;
\r
917 case CEP_STATE_REQ_RCVD:
\r
918 case CEP_STATE_REP_RCVD:
\r
919 case CEP_STATE_REQ_MRA_SENT:
\r
920 case CEP_STATE_REP_MRA_SENT:
\r
921 case CEP_STATE_PRE_REP:
\r
922 case CEP_STATE_PRE_REP_MRA_SENT:
\r
923 if( p_cep->state & CEP_STATE_PREP )
\r
925 CL_ASSERT( p_cep->p_mad );
\r
926 ib_put_mad( p_cep->p_mad );
\r
927 p_cep->p_mad = NULL;
\r
929 /* Abort connection establishment. No transition to timewait. */
\r
930 __remove_cep( p_cep );
\r
931 p_cep->state = CEP_STATE_IDLE;
\r
934 case CEP_STATE_ESTABLISHED:
\r
935 case CEP_STATE_LAP_RCVD:
\r
936 case CEP_STATE_LAP_SENT:
\r
937 case CEP_STATE_LAP_MRA_RCVD:
\r
938 case CEP_STATE_LAP_MRA_SENT:
\r
939 case CEP_STATE_PRE_APR:
\r
940 case CEP_STATE_PRE_APR_MRA_SENT:
\r
941 if( p_cep->state & CEP_STATE_PREP )
\r
943 CL_ASSERT( p_cep->p_mad );
\r
944 ib_put_mad( p_cep->p_mad );
\r
945 p_cep->p_mad = NULL;
\r
947 p_cep->state = CEP_STATE_TIMEWAIT;
\r
948 __insert_timewait( p_cep );
\r
952 /* Ignore the REJ. */
\r
953 AL_TRACE( AL_DBG_CM, ("REJ received in invalid state.\n") );
\r
955 ib_put_mad( p_mad );
\r
956 AL_EXIT( AL_DBG_CM );
\r
957 return IB_NO_MATCH;
\r
960 status = __cep_queue_mad( p_cep, p_mad );
\r
962 AL_EXIT( AL_DBG_CM );
\r
967 static ib_api_status_t
\r
969 IN kcep_t* const p_cep )
\r
971 ib_api_status_t status;
\r
972 cep_agent_t *p_port_cep;
\r
973 ib_mad_element_t *p_mad;
\r
974 mad_cm_rej_t *p_rej;
\r
976 status = __cep_get_mad( p_cep, CM_REJ_ATTR_ID, &p_port_cep, &p_mad );
\r
977 if( status != IB_SUCCESS )
\r
980 p_rej = ib_get_mad_buf( p_mad );
\r
982 conn_rej_set_ari( NULL, 0, p_rej );
\r
983 conn_rej_set_pdata( NULL, 0, p_rej );
\r
985 p_rej->local_comm_id = p_cep->remote_comm_id;
\r
986 p_rej->remote_comm_id = p_cep->local_comm_id;
\r
987 p_rej->reason = IB_REJ_STALE_CONN;
\r
989 switch( p_cep->state )
\r
991 case CEP_STATE_REQ_RCVD:
\r
992 case CEP_STATE_REQ_MRA_SENT:
\r
993 case CEP_STATE_PRE_REP:
\r
994 case CEP_STATE_PRE_REP_MRA_SENT:
\r
995 conn_rej_set_msg_rejected( 0, p_rej );
\r
998 case CEP_STATE_REQ_SENT:
\r
999 case CEP_STATE_REP_RCVD:
\r
1000 case CEP_STATE_REP_MRA_SENT:
\r
1001 conn_rej_set_msg_rejected( 1, p_rej );
\r
1005 conn_rej_set_msg_rejected( 2, p_rej );
\r
1008 conn_rej_clr_rsvd_fields( p_rej );
\r
1010 return __process_rej( p_cep, p_mad );
\r
1016 IN cep_agent_t* const p_port_cep,
\r
1017 IN ib_mad_element_t* const p_mad )
\r
1019 ib_api_status_t status = IB_SUCCESS;
\r
1020 mad_cm_req_t *p_req;
\r
1021 kcep_t *p_cep, *p_new_cep, *p_stale_cep = NULL;
\r
1022 KLOCK_QUEUE_HANDLE hdl;
\r
1023 ib_rej_status_t reason;
\r
1025 AL_ENTER( AL_DBG_CM );
\r
1027 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1029 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
1031 AL_TRACE( AL_DBG_CM,
\r
1032 ("REQ: comm_id (x%x) qpn (x%x) received\n",
\r
1033 p_req->local_comm_id, conn_req_get_lcl_qpn( p_req )) );
\r
1035 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1037 if( conn_req_get_qp_type( p_req ) > IB_QPT_UNRELIABLE_CONN )
\r
1039 /* Reserved value. Reject. */
\r
1040 AL_TRACE( AL_DBG_ERROR, ("Invalid transport type received.\n") );
\r
1041 reason = IB_REJ_INVALID_XPORT;
\r
1045 /* Match against pending connections using remote comm ID and CA GUID. */
\r
1046 p_cep = __lookup_by_id( p_req->local_comm_id, p_req->local_ca_guid );
\r
1049 /* Already received the REQ. */
\r
1050 switch( p_cep->state )
\r
1052 case CEP_STATE_REQ_MRA_SENT:
\r
1053 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1056 case CEP_STATE_TIMEWAIT:
\r
1057 case CEP_STATE_DESTROY:
\r
1058 /* Send a reject. */
\r
1059 AL_TRACE( AL_DBG_CM,
\r
1060 ("REQ received for connection in TIME_WAIT state.\n") );
\r
1061 __reject_req( p_port_cep, p_mad, IB_REJ_STALE_CONN );
\r
1066 * Let regular retries repeat the MAD. If our last message was
\r
1067 * dropped, resending only adds to the congestion. If it wasn't
\r
1068 * dropped, then the remote CM will eventually process it, and
\r
1069 * we'd just be adding traffic.
\r
1071 AL_TRACE( AL_DBG_CM, ("Duplicate REQ received.\n") );
\r
1072 ib_put_mad( p_mad );
\r
1074 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1075 AL_EXIT( AL_DBG_CM );
\r
1080 * Allocate a new CEP for the new request. This will
\r
1081 * prevent multiple identical REQs from queueing up for processing.
\r
1083 p_new_cep = __create_cep();
\r
1086 /* Reject the request for insufficient resources. */
\r
1087 reason = IB_REJ_INSUF_RESOURCES;
\r
1088 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
1089 ("al_create_cep failed\nREJ sent for insufficient resources.\n") );
\r
1093 __save_wire_req( p_new_cep, p_req );
\r
1096 * Match against listens using SID and compare data, also provide the receiving
\r
1097 * MAD service's port GUID so we can properly filter.
\r
1099 p_cep = __lookup_listen( p_req->sid, p_port_cep->port_guid, p_req->pdata );
\r
1102 __bind_cep( p_new_cep, p_cep->p_cid->h_al, p_cep->pfn_cb, NULL );
\r
1104 /* Add the new CEP to the map so that repeated REQs match up. */
\r
1105 p_stale_cep = __insert_cep( p_new_cep );
\r
1106 if( p_stale_cep != p_new_cep )
\r
1108 /* Duplicate - must be a stale connection. */
\r
1109 reason = IB_REJ_STALE_CONN;
\r
1110 /* Fail the local stale CEP. */
\r
1111 status = __process_stale( p_stale_cep );
\r
1116 * Queue the mad - the return value indicates whether we should
\r
1117 * invoke the callback.
\r
1119 status = __cep_queue_mad( p_cep, p_mad );
\r
1124 p_mad->send_context1 = p_new_cep;
\r
1128 reason = IB_REJ_INSUF_RESOURCES;
\r
1134 AL_TRACE( AL_DBG_CM, ("No listens active!\n") );
\r
1136 /* Match against peer-to-peer requests using SID and compare data. */
\r
1137 //p_cep = __lookup_peer();
\r
1140 // p_mad->send_context2 = NULL;
\r
1141 // p_list_item = cl_qlist_find_from_head( &gp_cep_mgr->pending_list,
\r
1142 // __match_peer, p_req );
\r
1143 // if( p_list_item != cl_qlist_end( &gp_cep_mgr->pending_list ) )
\r
1145 // KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1146 // p_conn = PARENT_STRUCT( p_list_item, kcep_t, map_item );
\r
1147 // __peer_req( p_port_cep, p_conn, p_async_mad->p_mad );
\r
1148 // cl_free( p_async_mad );
\r
1149 // CL_TRACE_EXIT( AL_DBG_CM, g_al_dbg_lvl,
\r
1150 // ("REQ matched a peer-to-peer request.\n") );
\r
1153 // reason = IB_REJ_INVALID_SID;
\r
1158 /* No match found. Reject. */
\r
1159 reason = IB_REJ_INVALID_SID;
\r
1160 AL_TRACE( AL_DBG_CM, ("REQ received but no match found.\n") );
\r
1165 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1167 /* Process any queued MADs for the CEP. */
\r
1168 if( status == IB_SUCCESS )
\r
1169 __process_cep( p_cep );
\r
1171 AL_EXIT( AL_DBG_CM );
\r
1175 __unbind_cep( p_new_cep );
\r
1179 * Move the CEP in the idle state so that we don't send a reject
\r
1180 * for it when cleaning up. Also clear the RQPN and RCID so that
\r
1181 * we don't try to remove it from our maps (since it isn't inserted).
\r
1183 p_new_cep->state = CEP_STATE_IDLE;
\r
1184 p_new_cep->remote_comm_id = 0;
\r
1185 p_new_cep->remote_qpn = 0;
\r
1186 __cleanup_cep( p_new_cep );
\r
1189 __reject_req( p_port_cep, p_mad, reason );
\r
1191 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1193 if( reason == IB_REJ_STALE_CONN && status == IB_SUCCESS )
\r
1194 __process_cep( p_stale_cep );
\r
1196 AL_EXIT( AL_DBG_CM );
\r
1202 IN OUT kcep_t* const p_cep,
\r
1203 IN const mad_cm_rep_t* const p_rep )
\r
1205 AL_ENTER( AL_DBG_CM );
\r
1207 /* The send should have been cancelled during MRA processing. */
\r
1208 p_cep->state = CEP_STATE_REP_RCVD;
\r
1210 /* Store pertinent information in the connection. */
\r
1211 p_cep->remote_comm_id = p_rep->local_comm_id;
\r
1212 p_cep->remote_ca_guid = p_rep->local_ca_guid;
\r
1214 p_cep->remote_qpn = conn_rep_get_lcl_qpn( p_rep );
\r
1216 /* Store the remote endpoint's target ACK delay. */
\r
1217 p_cep->target_ack_delay = conn_rep_get_target_ack_delay( p_rep );
\r
1219 /* Update the local ACK delay stored in the AV's. */
\r
1220 p_cep->av[0].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1221 p_cep->av[0].attr.conn.local_ack_timeout, p_cep->target_ack_delay );
\r
1222 p_cep->av[0].attr.conn.rnr_retry_cnt = conn_rep_get_rnr_retry_cnt( p_rep );
\r
1224 if( p_cep->av[1].port_guid )
\r
1226 p_cep->av[1].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1227 p_cep->av[1].attr.conn.local_ack_timeout,
\r
1228 p_cep->target_ack_delay );
\r
1229 p_cep->av[1].attr.conn.rnr_retry_cnt =
\r
1230 p_cep->av[0].attr.conn.rnr_retry_cnt;
\r
1233 p_cep->init_depth = p_rep->resp_resources;
\r
1234 p_cep->resp_res = p_rep->initiator_depth;
\r
1236 p_cep->sq_psn = conn_rep_get_starting_psn( p_rep );
\r
1238 AL_EXIT( AL_DBG_CM );
\r
1244 IN ib_mad_element_t* const p_mad )
\r
1246 ib_api_status_t status;
\r
1247 mad_cm_mra_t *p_mra;
\r
1249 KLOCK_QUEUE_HANDLE hdl;
\r
1251 AL_ENTER( AL_DBG_CM );
\r
1253 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1255 p_mra = (mad_cm_mra_t*)p_mad->p_mad_buf;
\r
1257 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1258 p_cep = __lookup_cep( NULL, p_mra->remote_comm_id );
\r
1261 AL_TRACE( AL_DBG_CM,
\r
1262 ("MRA received that could not be matched.\n") );
\r
1266 if( p_cep->remote_comm_id )
\r
1268 if( p_cep->remote_comm_id != p_mra->local_comm_id )
\r
1270 AL_TRACE( AL_DBG_CM,
\r
1271 ("MRA received that could not be matched.\n") );
\r
1277 * Note that we don't update the CEP's remote comm ID - it messes up REP
\r
1278 * processing since a non-zero RCID implies the connection is in the RCID
\r
1279 * map. Adding it here requires checking there and conditionally adding
\r
1280 * it. Ignoring it is a valid thing to do.
\r
1282 if( !(p_cep->state & CEP_STATE_SENT) ||
\r
1283 (1 << conn_mra_get_msg_mraed( p_mra ) !=
\r
1284 (p_cep->state & CEP_MSG_MASK)) )
\r
1286 /* Invalid state. */
\r
1287 AL_TRACE( AL_DBG_CM, ("MRA received in invalid state.\n") );
\r
1291 /* Delay the current send. */
\r
1292 CL_ASSERT( p_cep->p_send_mad );
\r
1293 ib_delay_mad( p_cep->h_mad_svc, p_cep->p_send_mad,
\r
1294 __calc_mad_timeout( conn_mra_get_svc_timeout( p_mra ) ) +
\r
1295 __calc_mad_timeout( p_cep->max_2pkt_life - 1 ) );
\r
1297 /* We only invoke a single callback for MRA. */
\r
1298 if( p_cep->state & CEP_STATE_MRA )
\r
1300 /* Invalid state. */
\r
1301 AL_TRACE( AL_DBG_CM, ("Already received MRA.\n") );
\r
1305 p_cep->state |= CEP_STATE_MRA;
\r
1307 status = __cep_queue_mad( p_cep, p_mad );
\r
1309 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1311 if( status == IB_SUCCESS )
\r
1312 __process_cep( p_cep );
\r
1314 AL_EXIT( AL_DBG_CM );
\r
1318 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1319 ib_put_mad( p_mad );
\r
1320 AL_EXIT( AL_DBG_CM );
\r
1326 IN ib_mad_element_t* const p_mad )
\r
1328 ib_api_status_t status;
\r
1329 mad_cm_rej_t *p_rej;
\r
1330 kcep_t *p_cep = NULL;
\r
1331 KLOCK_QUEUE_HANDLE hdl;
\r
1334 AL_ENTER( AL_DBG_CM );
\r
1336 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1338 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
1340 /* Either one of the communication IDs must be set. */
\r
1341 if( !p_rej->remote_comm_id && !p_rej->local_comm_id )
\r
1344 /* Check the pending list by the remote CA GUID and connection ID. */
\r
1345 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1346 if( p_rej->remote_comm_id )
\r
1348 p_cep = __lookup_cep( NULL, p_rej->remote_comm_id );
\r
1350 else if( p_rej->reason == IB_REJ_TIMEOUT &&
\r
1351 conn_rej_get_ari_len( p_rej ) == sizeof(net64_t) )
\r
1353 cl_memcpy( &ca_guid, p_rej->ari, sizeof(net64_t) );
\r
1354 p_cep = __lookup_by_id( p_rej->local_comm_id, ca_guid );
\r
1362 if( p_cep->remote_comm_id &&
\r
1363 p_cep->remote_comm_id != p_rej->local_comm_id )
\r
1366 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1368 ib_put_mad( p_mad );
\r
1369 AL_EXIT( AL_DBG_CM );
\r
1372 status = __process_rej( p_cep, p_mad );
\r
1374 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1376 if( status == IB_SUCCESS )
\r
1377 __process_cep( p_cep );
\r
1379 AL_EXIT( AL_DBG_CM );
\r
1385 IN cep_agent_t* const p_port_cep,
\r
1386 IN ib_mad_element_t* const p_mad )
\r
1388 ib_api_status_t status;
\r
1389 mad_cm_rep_t *p_rep;
\r
1391 KLOCK_QUEUE_HANDLE hdl;
\r
1392 cep_state_t old_state;
\r
1394 AL_ENTER( AL_DBG_CM );
\r
1396 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1398 p_rep = (mad_cm_rep_t*)p_mad->p_mad_buf;
\r
1400 AL_TRACE( AL_DBG_CM,
\r
1401 ("REP: comm_id (x%x) received\n", p_rep->local_comm_id ) );
\r
1403 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1404 p_cep = __lookup_cep( NULL, p_rep->remote_comm_id );
\r
1407 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1408 ib_put_mad( p_mad );
\r
1409 AL_TRACE_EXIT( AL_DBG_CM,
\r
1410 ("REP received that could not be matched.\n") );
\r
1414 switch( p_cep->state )
\r
1416 case CEP_STATE_REQ_MRA_RCVD:
\r
1417 case CEP_STATE_REQ_SENT:
\r
1418 old_state = p_cep->state;
\r
1419 /* Save pertinent information and change state. */
\r
1420 __save_wire_rep( p_cep, p_rep );
\r
1422 if( __insert_cep( p_cep ) != p_cep )
\r
1424 /* Roll back the state change. */
\r
1425 __reject_mad( p_port_cep, p_cep, p_mad, IB_REJ_STALE_CONN );
\r
1426 p_cep->state = old_state;
\r
1427 status = __process_stale( p_cep );
\r
1432 * Cancel any outstanding send. Note that we do this only after
\r
1433 * inserting the CEP - if we failed, then the send will timeout
\r
1434 * and we'll finish our way through the state machine.
\r
1436 if( p_cep->p_send_mad )
\r
1438 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1439 p_cep->p_send_mad = NULL;
\r
1442 status = __cep_queue_mad( p_cep, p_mad );
\r
1445 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1447 if( status == IB_SUCCESS )
\r
1448 __process_cep( p_cep );
\r
1450 AL_EXIT( AL_DBG_CM );
\r
1453 case CEP_STATE_ESTABLISHED:
\r
1454 case CEP_STATE_LAP_RCVD:
\r
1455 case CEP_STATE_LAP_SENT:
\r
1456 case CEP_STATE_LAP_MRA_RCVD:
\r
1457 case CEP_STATE_LAP_MRA_SENT:
\r
1458 case CEP_STATE_REP_MRA_SENT:
\r
1459 /* Repeate the MRA or RTU. */
\r
1460 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1464 ib_put_mad( p_mad );
\r
1465 AL_TRACE( AL_DBG_CM, ("REP received in invalid state.\n") );
\r
1469 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1471 AL_EXIT( AL_DBG_CM );
\r
1477 IN ib_mad_element_t* const p_mad )
\r
1479 ib_api_status_t status;
\r
1480 mad_cm_rtu_t *p_rtu;
\r
1482 KLOCK_QUEUE_HANDLE hdl;
\r
1484 AL_ENTER( AL_DBG_CM );
\r
1486 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1488 p_rtu = (mad_cm_rtu_t*)p_mad->p_mad_buf;
\r
1490 AL_TRACE( AL_DBG_CM,
\r
1491 ("RTU: comm_id (x%x) received\n", p_rtu->local_comm_id) );
\r
1493 /* Find the connection by local connection ID. */
\r
1494 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1495 p_cep = __lookup_cep( NULL, p_rtu->remote_comm_id );
\r
1496 if( !p_cep || p_cep->remote_comm_id != p_rtu->local_comm_id )
\r
1498 AL_TRACE( AL_DBG_CM, ("RTU received that could not be matched.\n") );
\r
1502 switch( p_cep->state )
\r
1504 case CEP_STATE_REP_SENT:
\r
1505 case CEP_STATE_REP_MRA_RCVD:
\r
1506 /* Cancel any outstanding send. */
\r
1507 if( p_cep->p_send_mad )
\r
1509 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1510 p_cep->p_send_mad = NULL;
\r
1513 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1515 status = __cep_queue_mad( p_cep, p_mad );
\r
1517 /* Update timewait time. */
\r
1518 __calc_timewait( p_cep );
\r
1520 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1522 if( status == IB_SUCCESS )
\r
1523 __process_cep( p_cep );
\r
1525 AL_EXIT( AL_DBG_CM );
\r
1529 AL_TRACE( AL_DBG_CM, ("RTU received in invalid state.\n") );
\r
1534 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1535 ib_put_mad( p_mad );
\r
1536 AL_EXIT( AL_DBG_CM );
\r
1542 IN cep_agent_t* const p_port_cep,
\r
1543 IN ib_mad_element_t* const p_mad )
\r
1545 ib_api_status_t status;
\r
1546 mad_cm_dreq_t *p_dreq;
\r
1548 KLOCK_QUEUE_HANDLE hdl;
\r
1550 AL_ENTER( AL_DBG_CM );
\r
1552 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1554 p_dreq = (mad_cm_dreq_t*)p_mad->p_mad_buf;
\r
1556 AL_TRACE( AL_DBG_CM,
\r
1557 ("DREQ: comm_id (x%x) qpn (x%x) received\n",
\r
1558 p_dreq->local_comm_id, conn_dreq_get_remote_qpn( p_dreq )) );
\r
1560 /* Find the connection by connection IDs. */
\r
1561 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1562 p_cep = __lookup_cep( NULL, p_dreq->remote_comm_id );
\r
1564 p_cep->remote_comm_id != p_dreq->local_comm_id ||
\r
1565 p_cep->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )
\r
1567 AL_TRACE( AL_DBG_CM, ("DREQ received that could not be matched.\n") );
\r
1568 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1569 ib_put_mad( p_mad );
\r
1570 AL_EXIT( AL_DBG_CM );
\r
1574 switch( p_cep->state )
\r
1576 case CEP_STATE_REP_SENT:
\r
1577 case CEP_STATE_REP_MRA_RCVD:
\r
1578 case CEP_STATE_DREQ_SENT:
\r
1579 /* Cancel the outstanding MAD. */
\r
1580 if( p_cep->p_send_mad )
\r
1582 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1583 p_cep->p_send_mad = NULL;
\r
1586 /* Fall through and process as DREQ received case. */
\r
1587 case CEP_STATE_ESTABLISHED:
\r
1588 case CEP_STATE_LAP_RCVD:
\r
1589 case CEP_STATE_LAP_SENT:
\r
1590 case CEP_STATE_LAP_MRA_RCVD:
\r
1591 case CEP_STATE_LAP_MRA_SENT:
\r
1592 p_cep->state = CEP_STATE_DREQ_RCVD;
\r
1594 status = __cep_queue_mad( p_cep, p_mad );
\r
1596 /* Store the TID for use in the reply DREP. */
\r
1597 p_cep->tid = p_dreq->hdr.trans_id;
\r
1599 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1601 if( status == IB_SUCCESS )
\r
1602 __process_cep( p_cep );
\r
1603 AL_EXIT( AL_DBG_CM );
\r
1606 case CEP_STATE_TIMEWAIT:
\r
1607 case CEP_STATE_DESTROY:
\r
1608 /* Repeat the DREP. */
\r
1609 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1613 AL_TRACE( AL_DBG_CM, ("DREQ received in invalid state.\n") );
\r
1614 case CEP_STATE_DREQ_RCVD:
\r
1615 ib_put_mad( p_mad );
\r
1619 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1620 AL_EXIT( AL_DBG_CM );
\r
1626 IN ib_mad_element_t* const p_mad )
\r
1628 ib_api_status_t status;
\r
1629 mad_cm_drep_t *p_drep;
\r
1631 KLOCK_QUEUE_HANDLE hdl;
\r
1633 AL_ENTER( AL_DBG_CM );
\r
1635 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1637 p_drep = (mad_cm_drep_t*)p_mad->p_mad_buf;
\r
1639 /* Find the connection by local connection ID. */
\r
1640 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1641 p_cep = __lookup_cep( NULL, p_drep->remote_comm_id );
\r
1642 if( !p_cep || p_cep->remote_comm_id != p_drep->local_comm_id )
\r
1644 AL_TRACE( AL_DBG_CM, ("DREP received that could not be matched.\n") );
\r
1645 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1646 ib_put_mad( p_mad );
\r
1647 AL_EXIT( AL_DBG_CM );
\r
1651 if( p_cep->state != CEP_STATE_DREQ_SENT &&
\r
1652 p_cep->state != CEP_STATE_DREQ_DESTROY )
\r
1654 AL_TRACE( AL_DBG_CM, ("DREP received in invalid state.\n") );
\r
1656 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1657 ib_put_mad( p_mad );
\r
1658 AL_EXIT( AL_DBG_CM );
\r
1662 /* Cancel the DREQ. */
\r
1663 if( p_cep->p_send_mad )
\r
1665 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1666 p_cep->p_send_mad = NULL;
\r
1669 if( p_cep->state == CEP_STATE_DREQ_SENT )
\r
1671 p_cep->state = CEP_STATE_TIMEWAIT;
\r
1673 status = __cep_queue_mad( p_cep, p_mad );
\r
1677 /* State is DREQ_DESTROY - move to DESTROY to allow cleanup. */
\r
1678 CL_ASSERT( p_cep->state == CEP_STATE_DREQ_DESTROY );
\r
1679 p_cep->state = CEP_STATE_DESTROY;
\r
1681 ib_put_mad( p_mad );
\r
1682 status = IB_INVALID_STATE;
\r
1685 __insert_timewait( p_cep );
\r
1687 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1689 if( status == IB_SUCCESS )
\r
1690 __process_cep( p_cep );
\r
1692 AL_EXIT( AL_DBG_CM );
\r
1698 IN kcep_t* const p_cep,
\r
1699 IN const lap_path_info_t* const p_path )
\r
1701 cep_agent_t *p_port_cep;
\r
1703 AL_ENTER( AL_DBG_CM );
\r
1705 CL_ASSERT( p_cep );
\r
1706 CL_ASSERT( p_path );
\r
1708 cl_memclr( &p_cep->alt_av, sizeof(kcep_av_t) );
\r
1710 p_port_cep = __find_port_cep( &p_path->remote_gid, p_path->remote_lid,
\r
1711 p_cep->pkey, &p_cep->alt_av.pkey_index );
\r
1714 AL_EXIT( AL_DBG_CM );
\r
1718 if( p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
1720 AL_EXIT( AL_DBG_CM );
\r
1724 p_cep->alt_av.port_guid = p_port_cep->port_guid;
\r
1725 p_cep->alt_av.attr.port_num = p_port_cep->port_num;
\r
1727 p_cep->alt_av.attr.sl = conn_lap_path_get_svc_lvl( p_path );
\r
1728 p_cep->alt_av.attr.dlid = p_path->local_lid;
\r
1730 if( !conn_lap_path_get_subn_lcl( p_path ) )
\r
1732 p_cep->alt_av.attr.grh_valid = TRUE;
\r
1733 p_cep->alt_av.attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
1734 1, conn_lap_path_get_tclass( p_path ),
\r
1735 conn_lap_path_get_flow_lbl( p_path ) );
\r
1736 p_cep->alt_av.attr.grh.hop_limit = p_path->hop_limit;
\r
1737 p_cep->alt_av.attr.grh.dest_gid = p_path->local_gid;
\r
1738 p_cep->alt_av.attr.grh.src_gid = p_path->remote_gid;
\r
1742 p_cep->alt_av.attr.grh_valid = FALSE;
\r
1744 p_cep->alt_av.attr.static_rate = conn_lap_path_get_pkt_rate( p_path );
\r
1745 p_cep->alt_av.attr.path_bits =
\r
1746 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
1749 * Note that while we never use the connected AV attributes internally,
\r
1750 * we store them so we can pass them back to users. For the LAP, we
\r
1751 * first copy the settings from the current primary - MTU and retry
\r
1752 * counts are only specified in the REQ.
\r
1754 p_cep->alt_av.attr.conn = p_cep->av[p_cep->idx_primary].attr.conn;
\r
1755 p_cep->alt_av.attr.conn.local_ack_timeout =
\r
1756 conn_lap_path_get_lcl_ack_timeout( p_path );
\r
1758 AL_EXIT( AL_DBG_CM );
\r
1765 IN cep_agent_t* const p_port_cep,
\r
1766 IN ib_mad_element_t* const p_mad )
\r
1768 ib_api_status_t status;
\r
1769 mad_cm_lap_t *p_lap;
\r
1771 KLOCK_QUEUE_HANDLE hdl;
\r
1773 AL_ENTER( AL_DBG_CM );
\r
1775 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1777 p_lap = (mad_cm_lap_t*)p_mad->p_mad_buf;
\r
1779 /* Find the connection by local connection ID. */
\r
1780 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1781 p_cep = __lookup_cep( NULL, p_lap->remote_comm_id );
\r
1782 if( !p_cep || p_cep->remote_comm_id != p_lap->local_comm_id )
\r
1784 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1785 ib_put_mad( p_mad );
\r
1786 AL_TRACE_EXIT( AL_DBG_CM, ("LAP received that could not be matched.\n") );
\r
1790 switch( p_cep->state )
\r
1792 case CEP_STATE_REP_SENT:
\r
1793 case CEP_STATE_REP_MRA_RCVD:
\r
1795 * These two cases handle the RTU being dropped. Receipt of
\r
1796 * a LAP indicates that the connection is established.
\r
1798 case CEP_STATE_ESTABLISHED:
\r
1800 * We don't check for other "established" states related to
\r
1801 * alternate path management (CEP_STATE_LAP_RCVD, etc)
\r
1804 /* We only support receiving LAP if we took the passive role. */
\r
1805 if( p_cep->was_active )
\r
1807 ib_put_mad( p_mad );
\r
1811 /* Store the transaction ID for use during the LAP exchange. */
\r
1812 p_cep->tid = p_lap->hdr.trans_id;
\r
1815 * Copy the path record into the connection for use when
\r
1816 * sending the APR and loading the path.
\r
1818 if( !__format_lap_av( p_cep, &p_lap->alternate_path ) )
\r
1820 /* Trap an invalid path. */
\r
1821 ib_put_mad( p_mad );
\r
1825 p_cep->state = CEP_STATE_LAP_RCVD;
\r
1827 status = __cep_queue_mad( p_cep, p_mad );
\r
1829 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1831 if( status == IB_SUCCESS )
\r
1832 __process_cep( p_cep );
\r
1834 AL_EXIT( AL_DBG_CM );
\r
1837 case CEP_STATE_LAP_MRA_SENT:
\r
1838 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1842 AL_TRACE( AL_DBG_CM, ("LAP received in invalid state.\n") );
\r
1843 ib_put_mad( p_mad );
\r
1847 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1848 AL_EXIT( AL_DBG_CM );
\r
1854 IN ib_mad_element_t* const p_mad )
\r
1856 ib_api_status_t status;
\r
1857 mad_cm_apr_t *p_apr;
\r
1859 KLOCK_QUEUE_HANDLE hdl;
\r
1861 AL_ENTER( AL_DBG_CM );
\r
1863 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1865 p_apr = (mad_cm_apr_t*)p_mad->p_mad_buf;
\r
1867 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1868 p_cep = __lookup_cep( NULL, p_apr->remote_comm_id );
\r
1869 if( !p_cep || p_cep->remote_comm_id != p_apr->local_comm_id )
\r
1871 AL_TRACE( AL_DBG_CM, ("APR received that could not be matched.\n") );
\r
1875 switch( p_cep->state )
\r
1877 case CEP_STATE_LAP_SENT:
\r
1878 case CEP_STATE_LAP_MRA_RCVD:
\r
1879 /* Cancel sending the LAP. */
\r
1880 if( p_cep->p_send_mad )
\r
1882 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1883 p_cep->p_send_mad = NULL;
\r
1886 /* Copy the temporary alternate AV. */
\r
1887 p_cep->av[(p_cep->idx_primary + 1) & 0x1] = p_cep->alt_av;
\r
1889 /* Update the maximum packet lifetime. */
\r
1890 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life, p_cep->alt_2pkt_life );
\r
1892 /* Update the timewait time. */
\r
1893 __calc_timewait( p_cep );
\r
1895 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1897 status = __cep_queue_mad( p_cep, p_mad );
\r
1899 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1901 if( status == IB_SUCCESS )
\r
1902 __process_cep( p_cep );
\r
1904 AL_EXIT( AL_DBG_CM );
\r
1908 AL_TRACE( AL_DBG_CM, ("APR received in invalid state.\n") );
\r
1913 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1914 ib_put_mad( p_mad );
\r
1915 AL_EXIT( AL_DBG_CM );
\r
1920 __cep_mad_recv_cb(
\r
1921 IN ib_mad_svc_handle_t h_mad_svc,
\r
1923 IN ib_mad_element_t *p_mad )
\r
1925 cep_agent_t *p_port_cep;
\r
1928 AL_ENTER( AL_DBG_CM );
\r
1930 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1932 UNUSED_PARAM( h_mad_svc );
\r
1933 p_port_cep = (cep_agent_t*)context;
\r
1935 CL_ASSERT( p_mad->p_next == NULL );
\r
1937 p_hdr = (ib_mad_t*)p_mad->p_mad_buf;
\r
1940 * TODO: Add filtering in all the handlers for unsupported class version.
\r
1941 * See 12.6.7.2 Rejection Reason, code 31.
\r
1944 switch( p_hdr->attr_id )
\r
1946 case CM_REQ_ATTR_ID:
\r
1947 __req_handler( p_port_cep, p_mad );
\r
1950 case CM_MRA_ATTR_ID:
\r
1951 __mra_handler( p_mad );
\r
1954 case CM_REJ_ATTR_ID:
\r
1955 __rej_handler( p_mad );
\r
1958 case CM_REP_ATTR_ID:
\r
1959 __rep_handler( p_port_cep, p_mad );
\r
1962 case CM_RTU_ATTR_ID:
\r
1963 __rtu_handler( p_mad );
\r
1966 case CM_DREQ_ATTR_ID:
\r
1967 __dreq_handler( p_port_cep, p_mad );
\r
1970 case CM_DREP_ATTR_ID:
\r
1971 __drep_handler( p_mad );
\r
1974 case CM_LAP_ATTR_ID:
\r
1975 __lap_handler( p_port_cep, p_mad );
\r
1978 case CM_APR_ATTR_ID:
\r
1979 __apr_handler( p_mad );
\r
1982 case CM_SIDR_REQ_ATTR_ID:
\r
1983 // p_async_mad->item.pfn_callback = __process_cm_sidr_req;
\r
1986 case CM_SIDR_REP_ATTR_ID:
\r
1987 // p_async_mad->item.pfn_callback = __process_cm_sidr_rep;
\r
1991 ib_put_mad( p_mad );
\r
1992 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
1993 ("Invalid CM MAD attribute ID.\n") );
\r
1997 AL_EXIT( AL_DBG_CM );
\r
2001 static inline cep_agent_t*
\r
2003 IN kcep_t* const p_cep )
\r
2005 cl_map_item_t *p_item;
\r
2007 CL_ASSERT( p_cep );
\r
2009 /* Look up the primary CEP port agent */
\r
2010 p_item = cl_qmap_get( &gp_cep_mgr->port_map,
\r
2011 p_cep->av[p_cep->idx_primary].port_guid );
\r
2012 if( p_item == cl_qmap_end( &gp_cep_mgr->port_map ) )
\r
2015 return PARENT_STRUCT( p_item, cep_agent_t, item );
\r
2019 static inline void
\r
2021 OUT ib_mad_element_t* const p_mad,
\r
2022 IN kcep_av_t* const p_av )
\r
2024 /* Set the addressing information in the MAD. */
\r
2025 p_mad->grh_valid = p_av->attr.grh_valid;
\r
2026 if( p_av->attr.grh_valid )
\r
2027 cl_memcpy( p_mad->p_grh, &p_av->attr.grh, sizeof(ib_grh_t) );
\r
2029 p_mad->remote_sl = p_av->attr.sl;
\r
2030 p_mad->remote_lid = p_av->attr.dlid;
\r
2031 p_mad->path_bits = p_av->attr.path_bits;
\r
2032 p_mad->pkey_index = p_av->pkey_index;
\r
2033 p_mad->remote_qp = IB_QP1;
\r
2034 p_mad->send_opt = IB_SEND_OPT_SIGNALED;
\r
2035 p_mad->remote_qkey = IB_QP1_WELL_KNOWN_Q_KEY;
\r
2036 /* Let the MAD service manage the AV for us. */
\r
2037 p_mad->h_av = NULL;
\r
2041 static ib_api_status_t
\r
2043 IN cep_agent_t* const p_port_cep,
\r
2044 IN ib_mad_element_t* const p_mad )
\r
2046 ib_api_status_t status;
\r
2048 AL_ENTER( AL_DBG_CM );
\r
2050 CL_ASSERT( p_port_cep );
\r
2051 CL_ASSERT( p_mad );
\r
2053 /* Use the mad's attributes already present */
\r
2054 p_mad->resp_expected = FALSE;
\r
2055 p_mad->retry_cnt = 0;
\r
2056 p_mad->timeout_ms = 0;
\r
2058 /* Clear the contexts since the send isn't associated with a CEP. */
\r
2059 p_mad->context1 = NULL;
\r
2060 p_mad->context2 = NULL;
\r
2062 status = ib_send_mad( p_port_cep->h_mad_svc, p_mad, NULL );
\r
2063 if( status != IB_SUCCESS )
\r
2065 ib_put_mad( p_mad );
\r
2066 AL_TRACE( AL_DBG_ERROR,
\r
2067 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2070 AL_EXIT( AL_DBG_CM );
\r
2075 static ib_api_status_t
\r
2077 IN cep_agent_t* const p_port_cep,
\r
2078 IN kcep_t* const p_cep,
\r
2079 IN ib_mad_element_t* const p_mad )
\r
2081 ib_api_status_t status;
\r
2083 AL_ENTER( AL_DBG_CM );
\r
2085 CL_ASSERT( p_cep );
\r
2086 CL_ASSERT( p_mad );
\r
2087 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
2088 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID ||
\r
2089 p_mad->p_mad_buf->attr_id == CM_LAP_ATTR_ID ||
\r
2090 p_mad->p_mad_buf->attr_id == CM_DREQ_ATTR_ID );
\r
2093 * REQ, REP, and DREQ are retried until either a response is
\r
2094 * received or the operation times out.
\r
2096 p_mad->resp_expected = TRUE;
\r
2097 p_mad->retry_cnt = p_cep->max_cm_retries;
\r
2098 p_mad->timeout_ms = p_cep->retry_timeout;
\r
2100 CL_ASSERT( !p_cep->p_send_mad );
\r
2102 /* Store the mad & mad service handle in the CEP for cancelling. */
\r
2103 p_cep->h_mad_svc = p_port_cep->h_mad_svc;
\r
2104 p_cep->p_send_mad = p_mad;
\r
2106 /* reference the connection for which we are sending the MAD. */
\r
2107 cl_atomic_inc( &p_cep->ref_cnt );
\r
2109 /* Set the context. */
\r
2110 p_mad->context1 = p_cep;
\r
2111 p_mad->context2 = NULL;
\r
2113 /* Fire in the hole! */
\r
2114 status = ib_send_mad( p_cep->h_mad_svc, p_mad, NULL );
\r
2115 if( status != IB_SUCCESS )
\r
2118 * Note that we don't need to check for destruction here since
\r
2119 * we're holding the global lock.
\r
2121 cl_atomic_dec( &p_cep->ref_cnt );
\r
2122 p_cep->p_send_mad = NULL;
\r
2123 ib_put_mad( p_mad );
\r
2124 AL_TRACE( AL_DBG_ERROR,
\r
2125 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2128 AL_EXIT( AL_DBG_CM );
\r
2134 __cep_mad_send_cb(
\r
2135 IN ib_mad_svc_handle_t h_mad_svc,
\r
2137 IN ib_mad_element_t *p_mad )
\r
2139 ib_api_status_t status;
\r
2140 cep_agent_t *p_port_cep;
\r
2142 KLOCK_QUEUE_HANDLE hdl;
\r
2143 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
2144 void *cep_context;
\r
2146 AL_ENTER( AL_DBG_CM );
\r
2148 UNUSED_PARAM( h_mad_svc );
\r
2149 CL_ASSERT( p_mad->p_next == NULL );
\r
2150 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
2152 p_port_cep = (cep_agent_t*)context;
\r
2154 p_cep = (kcep_t* __ptr64)p_mad->context1;
\r
2157 * The connection context is not set when performing immediate responses,
\r
2158 * such as repeating MADS.
\r
2162 ib_put_mad( p_mad );
\r
2163 AL_EXIT( AL_DBG_CM );
\r
2167 p_mad->context1 = NULL;
\r
2169 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
2170 /* Clear the sent MAD pointer so that we don't try cancelling again. */
\r
2171 if( p_cep->p_send_mad == p_mad )
\r
2172 p_cep->p_send_mad = NULL;
\r
2174 switch( p_mad->status )
\r
2176 case IB_WCS_SUCCESS:
\r
2177 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2178 ib_put_mad( p_mad );
\r
2181 case IB_WCS_CANCELED:
\r
2182 if( p_cep->state != CEP_STATE_REQ_SENT &&
\r
2183 p_cep->state != CEP_STATE_REQ_MRA_RCVD &&
\r
2184 p_cep->state != CEP_STATE_REP_SENT &&
\r
2185 p_cep->state != CEP_STATE_REP_MRA_RCVD &&
\r
2186 p_cep->state != CEP_STATE_LAP_SENT &&
\r
2187 p_cep->state != CEP_STATE_LAP_MRA_RCVD &&
\r
2188 p_cep->state != CEP_STATE_DREQ_SENT &&
\r
2189 p_cep->state != CEP_STATE_SREQ_SENT )
\r
2191 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2192 ib_put_mad( p_mad );
\r
2195 /* Treat as a timeout so we don't stall the state machine. */
\r
2196 p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;
\r
2198 /* Fall through. */
\r
2199 case IB_WCS_TIMEOUT_RETRY_ERR:
\r
2201 /* Timeout. Reject the connection. */
\r
2202 switch( p_cep->state )
\r
2204 case CEP_STATE_REQ_SENT:
\r
2205 case CEP_STATE_REQ_MRA_RCVD:
\r
2206 case CEP_STATE_REP_SENT:
\r
2207 case CEP_STATE_REP_MRA_RCVD:
\r
2208 /* Send the REJ. */
\r
2209 __reject_timeout( p_port_cep, p_cep, p_mad );
\r
2210 __remove_cep( p_cep );
\r
2211 p_cep->state = CEP_STATE_IDLE;
\r
2214 case CEP_STATE_DREQ_DESTROY:
\r
2215 p_cep->state = CEP_STATE_DESTROY;
\r
2216 __insert_timewait( p_cep );
\r
2217 /* Fall through. */
\r
2219 case CEP_STATE_DESTROY:
\r
2220 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2221 ib_put_mad( p_mad );
\r
2224 case CEP_STATE_DREQ_SENT:
\r
2226 * Make up a DREP mad so we can respond if we receive
\r
2227 * a DREQ while in timewait.
\r
2229 __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );
\r
2230 __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );
\r
2231 p_cep->state = CEP_STATE_TIMEWAIT;
\r
2232 __insert_timewait( p_cep );
\r
2238 status = __cep_queue_mad( p_cep, p_mad );
\r
2239 CL_ASSERT( status != IB_INVALID_STATE );
\r
2240 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2242 if( status == IB_SUCCESS )
\r
2243 __process_cep( p_cep );
\r
2248 pfn_destroy_cb = p_cep->pfn_destroy_cb;
\r
2249 cep_context = p_cep->context;
\r
2251 if( !cl_atomic_dec( &p_cep->ref_cnt ) && pfn_destroy_cb )
\r
2252 pfn_destroy_cb( cep_context );
\r
2253 AL_EXIT( AL_DBG_CM );
\r
2258 __cep_qp_event_cb(
\r
2259 IN ib_async_event_rec_t *p_event_rec )
\r
2261 UNUSED_PARAM( p_event_rec );
\r
2264 * Most of the QP events are trapped by the real owner of the QP.
\r
2265 * For real events, the CM may not be able to do much anyways!
\r
2270 static ib_api_status_t
\r
2272 IN cep_agent_t* const p_port_cep,
\r
2273 IN const ib_port_attr_t* const p_port_attr )
\r
2275 ib_api_status_t status;
\r
2276 ib_qp_create_t qp_create;
\r
2277 ib_mad_svc_t mad_svc;
\r
2279 AL_ENTER( AL_DBG_CM );
\r
2282 * Create the PD alias. We use the port CM's al_obj_t as the context
\r
2283 * to allow using deref_al_obj as the destroy callback.
\r
2285 status = ib_alloc_pd( p_port_cep->h_ca, IB_PDT_ALIAS, &p_port_cep->obj,
\r
2286 &p_port_cep->h_pd );
\r
2287 if( status != IB_SUCCESS )
\r
2289 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2290 ("ib_alloc_pd failed with status %s\n", ib_get_err_str(status)) );
\r
2293 /* Reference the port object on behalf of the PD. */
\r
2294 ref_al_obj( &p_port_cep->obj );
\r
2296 /* Create the MAD QP. */
\r
2297 cl_memclr( &qp_create, sizeof( ib_qp_create_t ) );
\r
2298 qp_create.qp_type = IB_QPT_QP1_ALIAS;
\r
2299 qp_create.rq_depth = CEP_MAD_RQ_DEPTH;
\r
2300 qp_create.sq_depth = CEP_MAD_SQ_DEPTH;
\r
2301 qp_create.rq_sge = CEP_MAD_RQ_SGE;
\r
2302 qp_create.sq_sge = CEP_MAD_SQ_SGE;
\r
2303 qp_create.sq_signaled = TRUE;
\r
2305 * We use the port CM's al_obj_t as the context to allow using
\r
2306 * deref_al_obj as the destroy callback.
\r
2308 status = ib_get_spl_qp( p_port_cep->h_pd, p_port_attr->port_guid,
\r
2309 &qp_create, &p_port_cep->obj, __cep_qp_event_cb, &p_port_cep->pool_key,
\r
2310 &p_port_cep->h_qp );
\r
2311 if( status != IB_SUCCESS )
\r
2313 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2314 ("ib_get_spl_qp failed with status %s\n", ib_get_err_str(status)) );
\r
2317 /* Reference the port object on behalf of the QP. */
\r
2318 ref_al_obj( &p_port_cep->obj );
\r
2320 /* Create the MAD service. */
\r
2321 cl_memclr( &mad_svc, sizeof(mad_svc) );
\r
2322 mad_svc.mad_svc_context = p_port_cep;
\r
2323 mad_svc.pfn_mad_recv_cb = __cep_mad_recv_cb;
\r
2324 mad_svc.pfn_mad_send_cb = __cep_mad_send_cb;
\r
2325 mad_svc.support_unsol = TRUE;
\r
2326 mad_svc.mgmt_class = IB_MCLASS_COMM_MGMT;
\r
2327 mad_svc.mgmt_version = IB_MCLASS_CM_VER_2;
\r
2328 mad_svc.method_array[IB_MAD_METHOD_SEND] = TRUE;
\r
2330 ib_reg_mad_svc( p_port_cep->h_qp, &mad_svc, &p_port_cep->h_mad_svc );
\r
2331 if( status != IB_SUCCESS )
\r
2333 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2334 ("ib_reg_mad_svc failed with status %s\n", ib_get_err_str(status)) );
\r
2338 AL_EXIT( AL_DBG_CM );
\r
2339 return IB_SUCCESS;
\r
2344 * Performs immediate cleanup of resources.
\r
2347 __destroying_port_cep(
\r
2348 IN al_obj_t *p_obj )
\r
2350 cep_agent_t *p_port_cep;
\r
2351 KLOCK_QUEUE_HANDLE hdl;
\r
2353 AL_ENTER( AL_DBG_CM );
\r
2355 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2357 if( p_port_cep->port_guid )
\r
2359 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2360 cl_qmap_remove_item( &gp_cep_mgr->port_map, &p_port_cep->item );
\r
2361 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2364 if( p_port_cep->h_qp )
\r
2366 ib_destroy_qp( p_port_cep->h_qp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2367 p_port_cep->h_qp = NULL;
\r
2370 if( p_port_cep->h_pd )
\r
2372 ib_dealloc_pd( p_port_cep->h_pd, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2373 p_port_cep->h_pd = NULL;
\r
2376 AL_EXIT( AL_DBG_CM );
\r
2382 * Release all resources allocated by a port CM agent. Finishes any cleanup
\r
2383 * for a port agent.
\r
2387 IN al_obj_t *p_obj )
\r
2389 cep_agent_t *p_port_cep;
\r
2390 ib_port_attr_mod_t port_attr_mod;
\r
2392 AL_ENTER( AL_DBG_CM );
\r
2394 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2396 if( p_port_cep->h_ca )
\r
2398 /* Update local port attributes */
\r
2399 port_attr_mod.cap.cm = FALSE;
\r
2400 ib_modify_ca( p_port_cep->h_ca, p_port_cep->port_num,
\r
2401 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2403 deref_al_obj( &p_port_cep->h_ca->obj );
\r
2406 destroy_al_obj( &p_port_cep->obj );
\r
2407 cl_free( p_port_cep );
\r
2409 AL_EXIT( AL_DBG_CM );
\r
2414 * Create a port agent for a given port.
\r
2416 static ib_api_status_t
\r
2417 __create_port_cep(
\r
2418 IN ib_pnp_port_rec_t *p_pnp_rec )
\r
2420 cep_agent_t *p_port_cep;
\r
2421 ib_api_status_t status;
\r
2422 ib_port_attr_mod_t port_attr_mod;
\r
2423 KLOCK_QUEUE_HANDLE hdl;
\r
2425 AL_ENTER( AL_DBG_CM );
\r
2427 /* calculate size of port_cm struct */
\r
2428 p_port_cep = (cep_agent_t*)cl_zalloc( sizeof(cep_agent_t) );
\r
2431 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2432 ("Failed to cl_zalloc port CM agent.\n") );
\r
2433 return IB_INSUFFICIENT_MEMORY;
\r
2436 construct_al_obj( &p_port_cep->obj, AL_OBJ_TYPE_CM );
\r
2438 status = init_al_obj( &p_port_cep->obj, p_port_cep, TRUE,
\r
2439 __destroying_port_cep, NULL, __free_port_cep );
\r
2440 if( status != IB_SUCCESS )
\r
2442 __free_port_cep( &p_port_cep->obj );
\r
2443 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2444 ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );
\r
2448 /* Attach to the global CM object. */
\r
2449 status = attach_al_obj( &gp_cep_mgr->obj, &p_port_cep->obj );
\r
2450 if( status != IB_SUCCESS )
\r
2452 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2453 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2454 ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );
\r
2458 p_port_cep->port_guid = p_pnp_rec->p_port_attr->port_guid;
\r
2459 p_port_cep->port_num = p_pnp_rec->p_port_attr->port_num;
\r
2460 p_port_cep->base_lid = p_pnp_rec->p_port_attr->lid;
\r
2462 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2464 &gp_cep_mgr->port_map, p_port_cep->port_guid, &p_port_cep->item );
\r
2465 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2467 /* Get a reference to the CA on which we are loading. */
\r
2468 p_port_cep->h_ca = acquire_ca( p_pnp_rec->p_ca_attr->ca_guid );
\r
2469 if( !p_port_cep->h_ca )
\r
2471 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2472 AL_TRACE_EXIT( AL_DBG_ERROR, ("acquire_ca failed.\n") );
\r
2473 return IB_INVALID_GUID; }
\r
2475 status = __init_data_svc( p_port_cep, p_pnp_rec->p_port_attr );
\r
2476 if( status != IB_SUCCESS )
\r
2478 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2479 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2480 ("__init_data_svc failed with status %s.\n",
\r
2481 ib_get_err_str(status)) );
\r
2485 /* Update local port attributes */
\r
2486 cl_memclr( &port_attr_mod, sizeof(ib_port_attr_mod_t) );
\r
2487 port_attr_mod.cap.cm = TRUE;
\r
2488 status = ib_modify_ca( p_port_cep->h_ca, p_pnp_rec->p_port_attr->port_num,
\r
2489 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2491 /* Update the PNP context to reference this port. */
\r
2492 p_pnp_rec->pnp_rec.context = p_port_cep;
\r
2494 /* Release the reference taken in init_al_obj. */
\r
2495 deref_al_obj( &p_port_cep->obj );
\r
2497 AL_EXIT( AL_DBG_CM );
\r
2498 return IB_SUCCESS;
\r
2502 /******************************************************************************
\r
2503 * Global CEP manager
\r
2504 ******************************************************************************/
\r
2508 OUT net32_t* const p_cid )
\r
2510 cl_status_t status;
\r
2511 uint32_t size, cid;
\r
2512 cep_cid_t *p_cep_cid;
\r
2514 AL_ENTER( AL_DBG_CM );
\r
2516 size = (uint32_t)cl_vector_get_size( &gp_cep_mgr->cid_vector );
\r
2517 cid = gp_cep_mgr->free_cid;
\r
2518 if( gp_cep_mgr->free_cid == size )
\r
2520 /* Grow the vector pool. */
\r
2522 cl_vector_set_size( &gp_cep_mgr->cid_vector, size + CEP_CID_GROW );
\r
2523 if( status != CL_SUCCESS )
\r
2525 AL_EXIT( AL_DBG_CM );
\r
2529 * Return the the start of the free list since the
\r
2530 * entry initializer incremented it.
\r
2532 gp_cep_mgr->free_cid = size;
\r
2535 /* Get the next free entry. */
\r
2536 p_cep_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, cid );
\r
2538 /* Update the next entry index. */
\r
2539 gp_cep_mgr->free_cid = (uint32_t)(uintn_t)p_cep_cid->p_cep;
\r
2543 AL_EXIT( AL_DBG_CM );
\r
2548 static inline kcep_t*
\r
2550 IN ib_al_handle_t h_al OPTIONAL,
\r
2556 /* Mask off the counter bits so we get the index in our vector. */
\r
2557 idx = cid & CEP_MAX_CID_MASK;
\r
2559 if( idx >= cl_vector_get_size( &gp_cep_mgr->cid_vector ) )
\r
2562 p_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, idx );
\r
2563 if( !p_cid->h_al )
\r
2567 * h_al is NULL when processing MADs, so we need to match on
\r
2568 * the actual local communication ID. If h_al is non-NULL, we
\r
2569 * are doing a lookup from a call to our API, and only need to match
\r
2570 * on the index in the vector (without the modifier).
\r
2574 if( p_cid->h_al != h_al )
\r
2577 else if( p_cid->p_cep->local_comm_id != cid )
\r
2582 return p_cid->p_cep;
\r
2587 * Lookup a CEP by remote comm ID and CA GUID.
\r
2591 IN net32_t remote_comm_id,
\r
2592 IN net64_t remote_ca_guid )
\r
2594 cl_rbmap_item_t *p_item;
\r
2597 AL_ENTER( AL_DBG_CM );
\r
2599 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2600 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2601 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2603 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2605 if( remote_comm_id < p_cep->remote_comm_id )
\r
2606 p_item = cl_rbmap_left( p_item );
\r
2607 else if( remote_comm_id > p_cep->remote_comm_id )
\r
2608 p_item = cl_rbmap_right( p_item );
\r
2609 else if( remote_ca_guid < p_cep->remote_ca_guid )
\r
2610 p_item = cl_rbmap_left( p_item );
\r
2611 else if( remote_ca_guid > p_cep->remote_ca_guid )
\r
2612 p_item = cl_rbmap_right( p_item );
\r
2617 AL_EXIT( AL_DBG_CM );
\r
2623 * Lookup a CEP by Service ID and private data.
\r
2628 IN net64_t port_guid,
\r
2629 IN uint8_t *p_pdata )
\r
2631 cl_rbmap_item_t *p_item;
\r
2635 AL_ENTER( AL_DBG_CM );
\r
2637 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2638 p_item = cl_rbmap_root( &gp_cep_mgr->listen_map );
\r
2639 while( p_item != cl_rbmap_end( &gp_cep_mgr->listen_map ) )
\r
2641 p_cep = PARENT_STRUCT( p_item, kcep_t, listen_item );
\r
2643 if( sid == p_cep->sid )
\r
2645 else if( sid < p_cep->sid )
\r
2646 p_item = cl_rbmap_left( p_item );
\r
2648 p_item = cl_rbmap_right( p_item );
\r
2653 if( p_cep->port_guid != IB_ALL_PORTS )
\r
2655 if( port_guid == p_cep->port_guid )
\r
2657 else if( port_guid < p_cep->port_guid )
\r
2658 p_item = cl_rbmap_left( p_item );
\r
2660 p_item = cl_rbmap_right( p_item );
\r
2666 if( p_cep->p_cmp_buf && p_pdata )
\r
2668 cmp = cl_memcmp( &p_pdata[p_cep->cmp_offset],
\r
2669 p_cep->p_cmp_buf, p_cep->cmp_len );
\r
2673 else if( cmp < 0 )
\r
2674 p_item = cl_rbmap_left( p_item );
\r
2676 p_item = cl_rbmap_right( p_item );
\r
2678 AL_TRACE( AL_DBG_CM,
\r
2679 ("Svc ID match but compare buffer mismatch.\n") );
\r
2684 /* Everything matched. */
\r
2685 AL_EXIT( AL_DBG_CM );
\r
2689 AL_EXIT( AL_DBG_CM );
\r
2696 IN kcep_t* const p_new_cep )
\r
2699 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2700 boolean_t left = TRUE;
\r
2702 AL_ENTER( AL_DBG_CM );
\r
2704 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2705 p_insert_at = p_item;
\r
2706 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2708 p_insert_at = p_item;
\r
2709 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2711 if( p_new_cep->remote_comm_id < p_cep->remote_comm_id )
\r
2712 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2713 else if( p_new_cep->remote_comm_id > p_cep->remote_comm_id )
\r
2714 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2715 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2716 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2717 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2718 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2721 AL_TRACE( AL_DBG_CM | AL_DBG_WARN,
\r
2722 ("WARNING: Duplicate remote CID and CA GUID.\n") );
\r
2728 &gp_cep_mgr->conn_id_map, p_insert_at, &p_new_cep->rem_id_item, left );
\r
2729 p_cep = p_new_cep;
\r
2732 AL_EXIT( AL_DBG_CM );
\r
2739 IN kcep_t* const p_new_cep )
\r
2742 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2743 boolean_t left = TRUE;
\r
2745 AL_ENTER( AL_DBG_CM );
\r
2747 p_item = cl_rbmap_root( &gp_cep_mgr->conn_qp_map );
\r
2748 p_insert_at = p_item;
\r
2749 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_qp_map ) )
\r
2751 p_insert_at = p_item;
\r
2752 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2754 if( p_new_cep->remote_qpn < p_cep->remote_qpn )
\r
2755 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2756 else if( p_new_cep->remote_qpn > p_cep->remote_qpn )
\r
2757 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2758 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2759 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2760 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2761 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2764 AL_TRACE( AL_DBG_CM | AL_DBG_WARN,
\r
2765 ("WARNING: Duplicate remote QPN and CA GUID.\n") );
\r
2771 &gp_cep_mgr->conn_qp_map, p_insert_at, &p_new_cep->rem_qp_item, left );
\r
2772 p_cep = p_new_cep;
\r
2775 AL_EXIT( AL_DBG_CM );
\r
2780 static inline kcep_t*
\r
2782 IN kcep_t* const p_new_cep )
\r
2786 AL_ENTER( AL_DBG_CM );
\r
2788 p_cep = __insert_by_qpn( p_new_cep );
\r
2789 if( p_cep != p_new_cep )
\r
2792 p_cep = __insert_by_id( p_new_cep );
\r
2793 if( p_cep != p_new_cep )
\r
2795 cl_rbmap_remove_item(
\r
2796 &gp_cep_mgr->conn_qp_map, &p_new_cep->rem_qp_item );
\r
2797 p_cep->remote_qpn = 0;
\r
2801 AL_EXIT( AL_DBG_CM );
\r
2806 static inline void
\r
2808 IN kcep_t* const p_cep )
\r
2810 AL_ENTER( AL_DBG_CM );
\r
2812 if( p_cep->remote_comm_id )
\r
2814 cl_rbmap_remove_item(
\r
2815 &gp_cep_mgr->conn_id_map, &p_cep->rem_id_item );
\r
2816 p_cep->remote_comm_id = 0;
\r
2818 if( p_cep->remote_qpn )
\r
2820 cl_rbmap_remove_item(
\r
2821 &gp_cep_mgr->conn_qp_map, &p_cep->rem_qp_item );
\r
2822 p_cep->remote_qpn = 0;
\r
2825 AL_EXIT( AL_DBG_CM );
\r
2831 IN ib_net16_t lid,
\r
2832 IN ib_net16_t port_lid,
\r
2837 uint16_t path_bits;
\r
2841 lid1 = CL_NTOH16(lid);
\r
2842 lid2 = CL_NTOH16(port_lid);
\r
2849 path_bits = (uint16_t)( (path_bits << 1) | 1 );
\r
2851 lid2 |= path_bits;
\r
2858 if (lid != port_lid)
\r
2866 static inline boolean_t
\r
2868 IN const ib_port_attr_t* const p_port_attr,
\r
2869 IN const ib_gid_t* const p_gid )
\r
2873 for( idx = 0; idx < p_port_attr->num_gids; idx++ )
\r
2876 p_gid, &p_port_attr->p_gid_table[idx], sizeof(ib_gid_t) ) )
\r
2885 static inline boolean_t
\r
2887 IN const ib_port_attr_t* const p_port_attr,
\r
2888 IN const net16_t pkey,
\r
2889 OUT uint16_t* const p_pkey_index )
\r
2893 for( idx = 0; idx < p_port_attr->num_pkeys; idx++ )
\r
2895 if( p_port_attr->p_pkey_table[idx] == pkey )
\r
2897 *p_pkey_index = idx;
\r
2906 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
2907 static cep_agent_t*
\r
2909 IN const ib_gid_t* const p_gid,
\r
2910 IN const net16_t lid,
\r
2911 IN const net16_t pkey,
\r
2912 OUT uint16_t* const p_pkey_index )
\r
2914 cep_agent_t *p_port_cep;
\r
2915 cl_list_item_t *p_item;
\r
2916 const ib_port_attr_t *p_port_attr;
\r
2918 AL_ENTER( AL_DBG_CM );
\r
2920 cl_spinlock_acquire( &gp_cep_mgr->obj.lock );
\r
2921 for( p_item = cl_qlist_head( &gp_cep_mgr->obj.obj_list );
\r
2922 p_item != cl_qlist_end( &gp_cep_mgr->obj.obj_list );
\r
2923 p_item = cl_qlist_next( p_item ) )
\r
2925 p_port_cep = PARENT_STRUCT( p_item, cep_agent_t, obj.pool_item );
\r
2927 CL_ASSERT( p_port_cep->port_num );
\r
2929 ci_ca_lock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2931 p_port_attr = p_port_cep->h_ca->obj.p_ci_ca->p_pnp_attr->p_port_attr;
\r
2932 p_port_attr += (p_port_cep->port_num - 1);
\r
2934 if( __is_lid_valid( lid, p_port_attr->lid, p_port_attr->lmc ) &&
\r
2935 __is_gid_valid( p_port_attr, p_gid ) &&
\r
2936 __get_pkey_index( p_port_attr, pkey, p_pkey_index ) )
\r
2938 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2939 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
2940 AL_EXIT( AL_DBG_CM );
\r
2941 return p_port_cep;
\r
2944 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2946 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
2947 AL_EXIT( AL_DBG_CM );
\r
2953 * PnP callback for port event notifications.
\r
2955 static ib_api_status_t
\r
2957 IN ib_pnp_rec_t *p_pnp_rec )
\r
2959 ib_api_status_t status = IB_SUCCESS;
\r
2961 AL_ENTER( AL_DBG_CM );
\r
2963 switch( p_pnp_rec->pnp_event )
\r
2965 case IB_PNP_PORT_ADD:
\r
2966 /* Create the port agent. */
\r
2967 CL_ASSERT( !p_pnp_rec->context );
\r
2968 status = __create_port_cep( (ib_pnp_port_rec_t*)p_pnp_rec );
\r
2971 case IB_PNP_PORT_REMOVE:
\r
2972 CL_ASSERT( p_pnp_rec->context );
\r
2974 /* Destroy the port agent. */
\r
2975 ref_al_obj( &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj );
\r
2976 ((cep_agent_t* __ptr64)p_pnp_rec->context)->obj.pfn_destroy(
\r
2977 &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj, NULL );
\r
2981 break; /* Ignore other PNP events. */
\r
2984 AL_EXIT( AL_DBG_CM );
\r
2989 static inline int64_t
\r
2991 IN int64_t current_min,
\r
2992 IN kcep_t* const p_cep )
\r
2995 * The minimum timer interval is 50 milliseconds. This means
\r
2996 * 500000 100ns increments. Since __process_timewait divides the
\r
2997 * result in half (so that the worst cast timewait interval is 150%)
\r
2998 * we compensate for this here. Note that relative time values are
\r
2999 * expressed as negative.
\r
3001 #define MIN_TIMEWAIT_100NS -1000000
\r
3003 /* Still in timewait - try again next time. */
\r
3004 if( !current_min )
\r
3006 return min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS );
\r
3010 return max( current_min,
\r
3011 min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS ) );
\r
3017 * Timer callback to process CEPs in timewait state. Returns time in ms.
\r
3020 __process_timewait()
\r
3022 cl_list_item_t *p_item;
\r
3024 LARGE_INTEGER timeout;
\r
3025 int64_t min_timewait = 0;
\r
3027 AL_ENTER( AL_DBG_CM );
\r
3029 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3031 timeout.QuadPart = 0;
\r
3033 p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3034 while( p_item != cl_qlist_end( &gp_cep_mgr->timewait_list ) )
\r
3036 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3037 p_item = cl_qlist_next( p_item );
\r
3039 CL_ASSERT( p_cep->state == CEP_STATE_DESTROY ||
\r
3040 p_cep->state == CEP_STATE_TIMEWAIT );
\r
3042 CL_ASSERT( !p_cep->p_mad );
\r
3044 if( KeWaitForSingleObject( &p_cep->timewait_timer, Executive,
\r
3045 KernelMode, FALSE, &timeout ) != STATUS_SUCCESS )
\r
3047 /* Still in timewait - try again next time. */
\r
3048 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3052 if( p_cep->ref_cnt )
\r
3054 /* Send outstanding or destruction in progress. */
\r
3055 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3059 /* Remove from the timewait list. */
\r
3060 cl_qlist_remove_item( &gp_cep_mgr->timewait_list, &p_cep->timewait_item );
\r
3063 * Not in timewait. Remove the CEP from the maps - it should
\r
3064 * no longer be matched against.
\r
3066 __remove_cep( p_cep );
\r
3068 if( p_cep->state == CEP_STATE_DESTROY )
\r
3070 __destroy_cep( p_cep );
\r
3074 /* Move the CEP to the IDLE state so that it can be used again. */
\r
3075 p_cep->state = CEP_STATE_IDLE;
\r
3079 AL_EXIT( AL_DBG_CM );
\r
3080 return (uint32_t)(min_timewait / -20000);
\r
3085 * Timer callback to process CEPs in timewait state.
\r
3088 __cep_timewait_cb(
\r
3089 IN void *context )
\r
3091 KLOCK_QUEUE_HANDLE hdl;
\r
3092 uint32_t min_timewait;
\r
3094 AL_ENTER( AL_DBG_CM );
\r
3096 UNUSED_PARAM( context );
\r
3098 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3100 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
3102 min_timewait = __process_timewait();
\r
3104 if( cl_qlist_count( &gp_cep_mgr->timewait_list ) )
\r
3107 * Reset the timer for half of the shortest timeout - this results
\r
3108 * in a worst case timeout of 150% of timewait.
\r
3110 cl_timer_trim( &gp_cep_mgr->timewait_timer, min_timewait );
\r
3113 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
3115 AL_EXIT( AL_DBG_CM );
\r
3120 * Starts immediate cleanup of the CM. Invoked during al_obj destruction.
\r
3123 __destroying_cep_mgr(
\r
3124 IN al_obj_t* p_obj )
\r
3126 ib_api_status_t status;
\r
3127 KLOCK_QUEUE_HANDLE hdl;
\r
3128 cl_list_item_t *p_item;
\r
3130 LARGE_INTEGER timeout;
\r
3132 AL_ENTER( AL_DBG_CM );
\r
3134 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3135 UNUSED_PARAM( p_obj );
\r
3137 /* Deregister from PnP notifications. */
\r
3138 if( gp_cep_mgr->h_pnp )
\r
3140 status = ib_dereg_pnp(
\r
3141 gp_cep_mgr->h_pnp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
3142 if( status != IB_SUCCESS )
\r
3144 CL_TRACE( AL_DBG_ERROR, g_al_dbg_lvl,
\r
3145 ("ib_dereg_pnp failed with status %s.\n",
\r
3146 ib_get_err_str(status)) );
\r
3147 deref_al_obj( &gp_cep_mgr->obj );
\r
3151 /* Cancel all timewait timers. */
\r
3152 timeout.QuadPart = 0;
\r
3153 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
3154 for( p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3155 p_item != cl_qlist_end( &gp_cep_mgr->timewait_list );
\r
3156 p_item = cl_qlist_next( p_item ) )
\r
3158 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3159 KeSetTimer( &p_cep->timewait_timer, timeout, NULL );
\r
3161 __process_timewait();
\r
3162 KeReleaseInStackQueuedSpinLock( &hdl );
\r
3164 AL_EXIT( AL_DBG_CM );
\r
3169 * Frees the global CEP agent. Invoked during al_obj destruction.
\r
3173 IN al_obj_t* p_obj )
\r
3175 AL_ENTER( AL_DBG_CM );
\r
3177 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3178 /* All listen request should have been cleaned up by this point. */
\r
3179 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->listen_map ) );
\r
3180 /* All connections should have been cancelled/disconnected by now. */
\r
3181 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_id_map ) );
\r
3182 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_qp_map ) );
\r
3184 cl_vector_destroy( &gp_cep_mgr->cid_vector );
\r
3186 cl_timer_destroy( &gp_cep_mgr->timewait_timer );
\r
3189 * All CM port agents should have been destroyed by now via the
\r
3190 * standard child object destruction provided by the al_obj.
\r
3192 ExDeleteNPagedLookasideList( &gp_cep_mgr->cep_pool );
\r
3193 destroy_al_obj( p_obj );
\r
3195 cl_free( gp_cep_mgr );
\r
3196 gp_cep_mgr = NULL;
\r
3198 AL_EXIT( AL_DBG_CM );
\r
3202 static cl_status_t
\r
3204 IN void* const p_element,
\r
3205 IN void* context )
\r
3209 UNUSED_PARAM( context );
\r
3211 p_cid = (cep_cid_t*)p_element;
\r
3213 p_cid->h_al = NULL;
\r
3214 p_cid->p_cep = (kcep_t*)(uintn_t)++gp_cep_mgr->free_cid;
\r
3215 p_cid->modifier = 0;
\r
3217 return CL_SUCCESS;
\r
3222 * Allocates and initialized the global CM agent.
\r
3226 IN al_obj_t* const &n