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
1373 status = __process_rej( p_cep, p_mad );
\r
1375 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1377 if( status == IB_SUCCESS )
\r
1378 __process_cep( p_cep );
\r
1380 AL_EXIT( AL_DBG_CM );
\r
1386 IN cep_agent_t* const p_port_cep,
\r
1387 IN ib_mad_element_t* const p_mad )
\r
1389 ib_api_status_t status;
\r
1390 mad_cm_rep_t *p_rep;
\r
1392 KLOCK_QUEUE_HANDLE hdl;
\r
1393 cep_state_t old_state;
\r
1395 AL_ENTER( AL_DBG_CM );
\r
1397 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1399 p_rep = (mad_cm_rep_t*)p_mad->p_mad_buf;
\r
1401 AL_TRACE( AL_DBG_CM,
\r
1402 ("REP: comm_id (x%x) received\n", p_rep->local_comm_id ) );
\r
1404 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1405 p_cep = __lookup_cep( NULL, p_rep->remote_comm_id );
\r
1408 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1409 ib_put_mad( p_mad );
\r
1410 AL_TRACE_EXIT( AL_DBG_CM,
\r
1411 ("REP received that could not be matched.\n") );
\r
1415 switch( p_cep->state )
\r
1417 case CEP_STATE_REQ_MRA_RCVD:
\r
1418 case CEP_STATE_REQ_SENT:
\r
1419 old_state = p_cep->state;
\r
1420 /* Save pertinent information and change state. */
\r
1421 __save_wire_rep( p_cep, p_rep );
\r
1423 if( __insert_cep( p_cep ) != p_cep )
\r
1425 /* Roll back the state change. */
\r
1426 __reject_mad( p_port_cep, p_cep, p_mad, IB_REJ_STALE_CONN );
\r
1427 p_cep->state = old_state;
\r
1428 status = __process_stale( p_cep );
\r
1433 * Cancel any outstanding send. Note that we do this only after
\r
1434 * inserting the CEP - if we failed, then the send will timeout
\r
1435 * and we'll finish our way through the state machine.
\r
1437 if( p_cep->p_send_mad )
\r
1439 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1440 p_cep->p_send_mad = NULL;
\r
1443 status = __cep_queue_mad( p_cep, p_mad );
\r
1446 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1448 if( status == IB_SUCCESS )
\r
1449 __process_cep( p_cep );
\r
1451 AL_EXIT( AL_DBG_CM );
\r
1454 case CEP_STATE_ESTABLISHED:
\r
1455 case CEP_STATE_LAP_RCVD:
\r
1456 case CEP_STATE_LAP_SENT:
\r
1457 case CEP_STATE_LAP_MRA_RCVD:
\r
1458 case CEP_STATE_LAP_MRA_SENT:
\r
1459 case CEP_STATE_REP_MRA_SENT:
\r
1460 /* Repeate the MRA or RTU. */
\r
1461 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1465 ib_put_mad( p_mad );
\r
1466 AL_TRACE( AL_DBG_CM, ("REP received in invalid state.\n") );
\r
1470 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1472 AL_EXIT( AL_DBG_CM );
\r
1478 IN ib_mad_element_t* const p_mad )
\r
1480 ib_api_status_t status;
\r
1481 mad_cm_rtu_t *p_rtu;
\r
1483 KLOCK_QUEUE_HANDLE hdl;
\r
1485 AL_ENTER( AL_DBG_CM );
\r
1487 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1489 p_rtu = (mad_cm_rtu_t*)p_mad->p_mad_buf;
\r
1491 AL_TRACE( AL_DBG_CM,
\r
1492 ("RTU: comm_id (x%x) received\n", p_rtu->local_comm_id) );
\r
1494 /* Find the connection by local connection ID. */
\r
1495 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1496 p_cep = __lookup_cep( NULL, p_rtu->remote_comm_id );
\r
1497 if( !p_cep || p_cep->remote_comm_id != p_rtu->local_comm_id )
\r
1499 AL_TRACE( AL_DBG_CM, ("RTU received that could not be matched.\n") );
\r
1503 switch( p_cep->state )
\r
1505 case CEP_STATE_REP_SENT:
\r
1506 case CEP_STATE_REP_MRA_RCVD:
\r
1507 /* Cancel any outstanding send. */
\r
1508 if( p_cep->p_send_mad )
\r
1510 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1511 p_cep->p_send_mad = NULL;
\r
1514 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1516 status = __cep_queue_mad( p_cep, p_mad );
\r
1518 /* Update timewait time. */
\r
1519 __calc_timewait( p_cep );
\r
1521 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1523 if( status == IB_SUCCESS )
\r
1524 __process_cep( p_cep );
\r
1526 AL_EXIT( AL_DBG_CM );
\r
1530 AL_TRACE( AL_DBG_CM, ("RTU received in invalid state.\n") );
\r
1535 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1536 ib_put_mad( p_mad );
\r
1537 AL_EXIT( AL_DBG_CM );
\r
1543 IN cep_agent_t* const p_port_cep,
\r
1544 IN ib_mad_element_t* const p_mad )
\r
1546 ib_api_status_t status;
\r
1547 mad_cm_dreq_t *p_dreq;
\r
1549 KLOCK_QUEUE_HANDLE hdl;
\r
1551 AL_ENTER( AL_DBG_CM );
\r
1553 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1555 p_dreq = (mad_cm_dreq_t*)p_mad->p_mad_buf;
\r
1557 AL_TRACE( AL_DBG_CM,
\r
1558 ("DREQ: comm_id (x%x) qpn (x%x) received\n",
\r
1559 p_dreq->local_comm_id, conn_dreq_get_remote_qpn( p_dreq )) );
\r
1561 /* Find the connection by connection IDs. */
\r
1562 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1563 p_cep = __lookup_cep( NULL, p_dreq->remote_comm_id );
\r
1565 p_cep->remote_comm_id != p_dreq->local_comm_id ||
\r
1566 p_cep->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )
\r
1568 AL_TRACE( AL_DBG_CM, ("DREQ received that could not be matched.\n") );
\r
1569 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1570 ib_put_mad( p_mad );
\r
1571 AL_EXIT( AL_DBG_CM );
\r
1575 switch( p_cep->state )
\r
1577 case CEP_STATE_REP_SENT:
\r
1578 case CEP_STATE_REP_MRA_RCVD:
\r
1579 case CEP_STATE_DREQ_SENT:
\r
1580 /* Cancel the outstanding MAD. */
\r
1581 if( p_cep->p_send_mad )
\r
1583 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1584 p_cep->p_send_mad = NULL;
\r
1587 /* Fall through and process as DREQ received case. */
\r
1588 case CEP_STATE_ESTABLISHED:
\r
1589 case CEP_STATE_LAP_RCVD:
\r
1590 case CEP_STATE_LAP_SENT:
\r
1591 case CEP_STATE_LAP_MRA_RCVD:
\r
1592 case CEP_STATE_LAP_MRA_SENT:
\r
1593 p_cep->state = CEP_STATE_DREQ_RCVD;
\r
1595 status = __cep_queue_mad( p_cep, p_mad );
\r
1597 /* Store the TID for use in the reply DREP. */
\r
1598 p_cep->tid = p_dreq->hdr.trans_id;
\r
1600 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1602 if( status == IB_SUCCESS )
\r
1603 __process_cep( p_cep );
\r
1604 AL_EXIT( AL_DBG_CM );
\r
1607 case CEP_STATE_TIMEWAIT:
\r
1608 case CEP_STATE_DESTROY:
\r
1609 /* Repeat the DREP. */
\r
1610 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1614 AL_TRACE( AL_DBG_CM, ("DREQ received in invalid state.\n") );
\r
1615 case CEP_STATE_DREQ_RCVD:
\r
1616 ib_put_mad( p_mad );
\r
1620 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1621 AL_EXIT( AL_DBG_CM );
\r
1627 IN ib_mad_element_t* const p_mad )
\r
1629 ib_api_status_t status;
\r
1630 mad_cm_drep_t *p_drep;
\r
1632 KLOCK_QUEUE_HANDLE hdl;
\r
1634 AL_ENTER( AL_DBG_CM );
\r
1636 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1638 p_drep = (mad_cm_drep_t*)p_mad->p_mad_buf;
\r
1640 /* Find the connection by local connection ID. */
\r
1641 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1642 p_cep = __lookup_cep( NULL, p_drep->remote_comm_id );
\r
1643 if( !p_cep || p_cep->remote_comm_id != p_drep->local_comm_id )
\r
1645 AL_TRACE( AL_DBG_CM, ("DREP received that could not be matched.\n") );
\r
1646 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1647 ib_put_mad( p_mad );
\r
1648 AL_EXIT( AL_DBG_CM );
\r
1652 if( p_cep->state != CEP_STATE_DREQ_SENT &&
\r
1653 p_cep->state != CEP_STATE_DREQ_DESTROY )
\r
1655 AL_TRACE( AL_DBG_CM, ("DREP received in invalid state.\n") );
\r
1657 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1658 ib_put_mad( p_mad );
\r
1659 AL_EXIT( AL_DBG_CM );
\r
1663 /* Cancel the DREQ. */
\r
1664 if( p_cep->p_send_mad )
\r
1666 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1667 p_cep->p_send_mad = NULL;
\r
1670 if( p_cep->state == CEP_STATE_DREQ_SENT )
\r
1672 p_cep->state = CEP_STATE_TIMEWAIT;
\r
1674 status = __cep_queue_mad( p_cep, p_mad );
\r
1678 /* State is DREQ_DESTROY - move to DESTROY to allow cleanup. */
\r
1679 CL_ASSERT( p_cep->state == CEP_STATE_DREQ_DESTROY );
\r
1680 p_cep->state = CEP_STATE_DESTROY;
\r
1682 ib_put_mad( p_mad );
\r
1683 status = IB_INVALID_STATE;
\r
1686 __insert_timewait( p_cep );
\r
1688 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1690 if( status == IB_SUCCESS )
\r
1691 __process_cep( p_cep );
\r
1693 AL_EXIT( AL_DBG_CM );
\r
1699 IN kcep_t* const p_cep,
\r
1700 IN const lap_path_info_t* const p_path )
\r
1702 cep_agent_t *p_port_cep;
\r
1704 AL_ENTER( AL_DBG_CM );
\r
1706 CL_ASSERT( p_cep );
\r
1707 CL_ASSERT( p_path );
\r
1709 cl_memclr( &p_cep->alt_av, sizeof(kcep_av_t) );
\r
1711 p_port_cep = __find_port_cep( &p_path->remote_gid, p_path->remote_lid,
\r
1712 p_cep->pkey, &p_cep->alt_av.pkey_index );
\r
1715 AL_EXIT( AL_DBG_CM );
\r
1719 if( p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
1721 AL_EXIT( AL_DBG_CM );
\r
1725 p_cep->alt_av.port_guid = p_port_cep->port_guid;
\r
1726 p_cep->alt_av.attr.port_num = p_port_cep->port_num;
\r
1728 p_cep->alt_av.attr.sl = conn_lap_path_get_svc_lvl( p_path );
\r
1729 p_cep->alt_av.attr.dlid = p_path->local_lid;
\r
1731 if( !conn_lap_path_get_subn_lcl( p_path ) )
\r
1733 p_cep->alt_av.attr.grh_valid = TRUE;
\r
1734 p_cep->alt_av.attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
1735 1, conn_lap_path_get_tclass( p_path ),
\r
1736 conn_lap_path_get_flow_lbl( p_path ) );
\r
1737 p_cep->alt_av.attr.grh.hop_limit = p_path->hop_limit;
\r
1738 p_cep->alt_av.attr.grh.dest_gid = p_path->local_gid;
\r
1739 p_cep->alt_av.attr.grh.src_gid = p_path->remote_gid;
\r
1743 p_cep->alt_av.attr.grh_valid = FALSE;
\r
1745 p_cep->alt_av.attr.static_rate = conn_lap_path_get_pkt_rate( p_path );
\r
1746 p_cep->alt_av.attr.path_bits =
\r
1747 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
1750 * Note that while we never use the connected AV attributes internally,
\r
1751 * we store them so we can pass them back to users. For the LAP, we
\r
1752 * first copy the settings from the current primary - MTU and retry
\r
1753 * counts are only specified in the REQ.
\r
1755 p_cep->alt_av.attr.conn = p_cep->av[p_cep->idx_primary].attr.conn;
\r
1756 p_cep->alt_av.attr.conn.local_ack_timeout =
\r
1757 conn_lap_path_get_lcl_ack_timeout( p_path );
\r
1759 AL_EXIT( AL_DBG_CM );
\r
1766 IN cep_agent_t* const p_port_cep,
\r
1767 IN ib_mad_element_t* const p_mad )
\r
1769 ib_api_status_t status;
\r
1770 mad_cm_lap_t *p_lap;
\r
1772 KLOCK_QUEUE_HANDLE hdl;
\r
1774 AL_ENTER( AL_DBG_CM );
\r
1776 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1778 p_lap = (mad_cm_lap_t*)p_mad->p_mad_buf;
\r
1780 /* Find the connection by local connection ID. */
\r
1781 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1782 p_cep = __lookup_cep( NULL, p_lap->remote_comm_id );
\r
1783 if( !p_cep || p_cep->remote_comm_id != p_lap->local_comm_id )
\r
1785 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1786 ib_put_mad( p_mad );
\r
1787 AL_TRACE_EXIT( AL_DBG_CM, ("LAP received that could not be matched.\n") );
\r
1791 switch( p_cep->state )
\r
1793 case CEP_STATE_REP_SENT:
\r
1794 case CEP_STATE_REP_MRA_RCVD:
\r
1796 * These two cases handle the RTU being dropped. Receipt of
\r
1797 * a LAP indicates that the connection is established.
\r
1799 case CEP_STATE_ESTABLISHED:
\r
1801 * We don't check for other "established" states related to
\r
1802 * alternate path management (CEP_STATE_LAP_RCVD, etc)
\r
1805 /* We only support receiving LAP if we took the passive role. */
\r
1806 if( p_cep->was_active )
\r
1808 ib_put_mad( p_mad );
\r
1812 /* Store the transaction ID for use during the LAP exchange. */
\r
1813 p_cep->tid = p_lap->hdr.trans_id;
\r
1816 * Copy the path record into the connection for use when
\r
1817 * sending the APR and loading the path.
\r
1819 if( !__format_lap_av( p_cep, &p_lap->alternate_path ) )
\r
1821 /* Trap an invalid path. */
\r
1822 ib_put_mad( p_mad );
\r
1826 p_cep->state = CEP_STATE_LAP_RCVD;
\r
1828 status = __cep_queue_mad( p_cep, p_mad );
\r
1830 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1832 if( status == IB_SUCCESS )
\r
1833 __process_cep( p_cep );
\r
1835 AL_EXIT( AL_DBG_CM );
\r
1838 case CEP_STATE_LAP_MRA_SENT:
\r
1839 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1843 AL_TRACE( AL_DBG_CM, ("LAP received in invalid state.\n") );
\r
1844 ib_put_mad( p_mad );
\r
1848 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1849 AL_EXIT( AL_DBG_CM );
\r
1855 IN ib_mad_element_t* const p_mad )
\r
1857 ib_api_status_t status;
\r
1858 mad_cm_apr_t *p_apr;
\r
1860 KLOCK_QUEUE_HANDLE hdl;
\r
1862 AL_ENTER( AL_DBG_CM );
\r
1864 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1866 p_apr = (mad_cm_apr_t*)p_mad->p_mad_buf;
\r
1868 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1869 p_cep = __lookup_cep( NULL, p_apr->remote_comm_id );
\r
1870 if( !p_cep || p_cep->remote_comm_id != p_apr->local_comm_id )
\r
1872 AL_TRACE( AL_DBG_CM, ("APR received that could not be matched.\n") );
\r
1876 switch( p_cep->state )
\r
1878 case CEP_STATE_LAP_SENT:
\r
1879 case CEP_STATE_LAP_MRA_RCVD:
\r
1880 /* Cancel sending the LAP. */
\r
1881 if( p_cep->p_send_mad )
\r
1883 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1884 p_cep->p_send_mad = NULL;
\r
1887 /* Copy the temporary alternate AV. */
\r
1888 p_cep->av[(p_cep->idx_primary + 1) & 0x1] = p_cep->alt_av;
\r
1890 /* Update the maximum packet lifetime. */
\r
1891 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life, p_cep->alt_2pkt_life );
\r
1893 /* Update the timewait time. */
\r
1894 __calc_timewait( p_cep );
\r
1896 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1898 status = __cep_queue_mad( p_cep, p_mad );
\r
1900 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1902 if( status == IB_SUCCESS )
\r
1903 __process_cep( p_cep );
\r
1905 AL_EXIT( AL_DBG_CM );
\r
1909 AL_TRACE( AL_DBG_CM, ("APR received in invalid state.\n") );
\r
1914 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1915 ib_put_mad( p_mad );
\r
1916 AL_EXIT( AL_DBG_CM );
\r
1921 __cep_mad_recv_cb(
\r
1922 IN ib_mad_svc_handle_t h_mad_svc,
\r
1924 IN ib_mad_element_t *p_mad )
\r
1926 cep_agent_t *p_port_cep;
\r
1929 AL_ENTER( AL_DBG_CM );
\r
1931 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1933 UNUSED_PARAM( h_mad_svc );
\r
1934 p_port_cep = (cep_agent_t*)context;
\r
1936 CL_ASSERT( p_mad->p_next == NULL );
\r
1938 p_hdr = (ib_mad_t*)p_mad->p_mad_buf;
\r
1941 * TODO: Add filtering in all the handlers for unsupported class version.
\r
1942 * See 12.6.7.2 Rejection Reason, code 31.
\r
1945 switch( p_hdr->attr_id )
\r
1947 case CM_REQ_ATTR_ID:
\r
1948 __req_handler( p_port_cep, p_mad );
\r
1951 case CM_MRA_ATTR_ID:
\r
1952 __mra_handler( p_mad );
\r
1955 case CM_REJ_ATTR_ID:
\r
1956 __rej_handler( p_mad );
\r
1959 case CM_REP_ATTR_ID:
\r
1960 __rep_handler( p_port_cep, p_mad );
\r
1963 case CM_RTU_ATTR_ID:
\r
1964 __rtu_handler( p_mad );
\r
1967 case CM_DREQ_ATTR_ID:
\r
1968 __dreq_handler( p_port_cep, p_mad );
\r
1971 case CM_DREP_ATTR_ID:
\r
1972 __drep_handler( p_mad );
\r
1975 case CM_LAP_ATTR_ID:
\r
1976 __lap_handler( p_port_cep, p_mad );
\r
1979 case CM_APR_ATTR_ID:
\r
1980 __apr_handler( p_mad );
\r
1983 case CM_SIDR_REQ_ATTR_ID:
\r
1984 // p_async_mad->item.pfn_callback = __process_cm_sidr_req;
\r
1987 case CM_SIDR_REP_ATTR_ID:
\r
1988 // p_async_mad->item.pfn_callback = __process_cm_sidr_rep;
\r
1992 ib_put_mad( p_mad );
\r
1993 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
1994 ("Invalid CM MAD attribute ID.\n") );
\r
1998 AL_EXIT( AL_DBG_CM );
\r
2002 static inline cep_agent_t*
\r
2004 IN kcep_t* const p_cep )
\r
2006 cl_map_item_t *p_item;
\r
2008 CL_ASSERT( p_cep );
\r
2010 /* Look up the primary CEP port agent */
\r
2011 p_item = cl_qmap_get( &gp_cep_mgr->port_map,
\r
2012 p_cep->av[p_cep->idx_primary].port_guid );
\r
2013 if( p_item == cl_qmap_end( &gp_cep_mgr->port_map ) )
\r
2016 return PARENT_STRUCT( p_item, cep_agent_t, item );
\r
2020 static inline void
\r
2022 OUT ib_mad_element_t* const p_mad,
\r
2023 IN kcep_av_t* const p_av )
\r
2025 /* Set the addressing information in the MAD. */
\r
2026 p_mad->grh_valid = p_av->attr.grh_valid;
\r
2027 if( p_av->attr.grh_valid )
\r
2028 cl_memcpy( p_mad->p_grh, &p_av->attr.grh, sizeof(ib_grh_t) );
\r
2030 p_mad->remote_sl = p_av->attr.sl;
\r
2031 p_mad->remote_lid = p_av->attr.dlid;
\r
2032 p_mad->path_bits = p_av->attr.path_bits;
\r
2033 p_mad->pkey_index = p_av->pkey_index;
\r
2034 p_mad->remote_qp = IB_QP1;
\r
2035 p_mad->send_opt = IB_SEND_OPT_SIGNALED;
\r
2036 p_mad->remote_qkey = IB_QP1_WELL_KNOWN_Q_KEY;
\r
2037 /* Let the MAD service manage the AV for us. */
\r
2038 p_mad->h_av = NULL;
\r
2042 static ib_api_status_t
\r
2044 IN cep_agent_t* const p_port_cep,
\r
2045 IN ib_mad_element_t* const p_mad )
\r
2047 ib_api_status_t status;
\r
2049 AL_ENTER( AL_DBG_CM );
\r
2051 CL_ASSERT( p_port_cep );
\r
2052 CL_ASSERT( p_mad );
\r
2054 /* Use the mad's attributes already present */
\r
2055 p_mad->resp_expected = FALSE;
\r
2056 p_mad->retry_cnt = 0;
\r
2057 p_mad->timeout_ms = 0;
\r
2059 /* Clear the contexts since the send isn't associated with a CEP. */
\r
2060 p_mad->context1 = NULL;
\r
2061 p_mad->context2 = NULL;
\r
2063 status = ib_send_mad( p_port_cep->h_mad_svc, p_mad, NULL );
\r
2064 if( status != IB_SUCCESS )
\r
2066 ib_put_mad( p_mad );
\r
2067 AL_TRACE( AL_DBG_ERROR,
\r
2068 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2071 AL_EXIT( AL_DBG_CM );
\r
2076 static ib_api_status_t
\r
2078 IN cep_agent_t* const p_port_cep,
\r
2079 IN kcep_t* const p_cep,
\r
2080 IN ib_mad_element_t* const p_mad )
\r
2082 ib_api_status_t status;
\r
2084 AL_ENTER( AL_DBG_CM );
\r
2086 CL_ASSERT( p_cep );
\r
2087 CL_ASSERT( p_mad );
\r
2088 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
2089 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID ||
\r
2090 p_mad->p_mad_buf->attr_id == CM_LAP_ATTR_ID ||
\r
2091 p_mad->p_mad_buf->attr_id == CM_DREQ_ATTR_ID );
\r
2094 * REQ, REP, and DREQ are retried until either a response is
\r
2095 * received or the operation times out.
\r
2097 p_mad->resp_expected = TRUE;
\r
2098 p_mad->retry_cnt = p_cep->max_cm_retries;
\r
2099 p_mad->timeout_ms = p_cep->retry_timeout;
\r
2101 CL_ASSERT( !p_cep->p_send_mad );
\r
2103 /* Store the mad & mad service handle in the CEP for cancelling. */
\r
2104 p_cep->h_mad_svc = p_port_cep->h_mad_svc;
\r
2105 p_cep->p_send_mad = p_mad;
\r
2107 /* reference the connection for which we are sending the MAD. */
\r
2108 cl_atomic_inc( &p_cep->ref_cnt );
\r
2110 /* Set the context. */
\r
2111 p_mad->context1 = p_cep;
\r
2112 p_mad->context2 = NULL;
\r
2114 /* Fire in the hole! */
\r
2115 status = ib_send_mad( p_cep->h_mad_svc, p_mad, NULL );
\r
2116 if( status != IB_SUCCESS )
\r
2119 * Note that we don't need to check for destruction here since
\r
2120 * we're holding the global lock.
\r
2122 cl_atomic_dec( &p_cep->ref_cnt );
\r
2123 p_cep->p_send_mad = NULL;
\r
2124 ib_put_mad( p_mad );
\r
2125 AL_TRACE( AL_DBG_ERROR,
\r
2126 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2129 AL_EXIT( AL_DBG_CM );
\r
2135 __cep_mad_send_cb(
\r
2136 IN ib_mad_svc_handle_t h_mad_svc,
\r
2138 IN ib_mad_element_t *p_mad )
\r
2140 ib_api_status_t status;
\r
2141 cep_agent_t *p_port_cep;
\r
2143 KLOCK_QUEUE_HANDLE hdl;
\r
2144 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
2145 void *cep_context;
\r
2147 AL_ENTER( AL_DBG_CM );
\r
2149 UNUSED_PARAM( h_mad_svc );
\r
2150 CL_ASSERT( p_mad->p_next == NULL );
\r
2151 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
2153 p_port_cep = (cep_agent_t*)context;
\r
2155 p_cep = (kcep_t* __ptr64)p_mad->context1;
\r
2158 * The connection context is not set when performing immediate responses,
\r
2159 * such as repeating MADS.
\r
2163 ib_put_mad( p_mad );
\r
2164 AL_EXIT( AL_DBG_CM );
\r
2168 p_mad->context1 = NULL;
\r
2170 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
2171 /* Clear the sent MAD pointer so that we don't try cancelling again. */
\r
2172 if( p_cep->p_send_mad == p_mad )
\r
2173 p_cep->p_send_mad = NULL;
\r
2175 switch( p_mad->status )
\r
2177 case IB_WCS_SUCCESS:
\r
2178 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2179 ib_put_mad( p_mad );
\r
2182 case IB_WCS_CANCELED:
\r
2183 if( p_cep->state != CEP_STATE_REQ_SENT &&
\r
2184 p_cep->state != CEP_STATE_REQ_MRA_RCVD &&
\r
2185 p_cep->state != CEP_STATE_REP_SENT &&
\r
2186 p_cep->state != CEP_STATE_REP_MRA_RCVD &&
\r
2187 p_cep->state != CEP_STATE_LAP_SENT &&
\r
2188 p_cep->state != CEP_STATE_LAP_MRA_RCVD &&
\r
2189 p_cep->state != CEP_STATE_DREQ_SENT &&
\r
2190 p_cep->state != CEP_STATE_SREQ_SENT )
\r
2192 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2193 ib_put_mad( p_mad );
\r
2196 /* Treat as a timeout so we don't stall the state machine. */
\r
2197 p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;
\r
2199 /* Fall through. */
\r
2200 case IB_WCS_TIMEOUT_RETRY_ERR:
\r
2202 /* Timeout. Reject the connection. */
\r
2203 switch( p_cep->state )
\r
2205 case CEP_STATE_REQ_SENT:
\r
2206 case CEP_STATE_REQ_MRA_RCVD:
\r
2207 case CEP_STATE_REP_SENT:
\r
2208 case CEP_STATE_REP_MRA_RCVD:
\r
2209 /* Send the REJ. */
\r
2210 __reject_timeout( p_port_cep, p_cep, p_mad );
\r
2211 __remove_cep( p_cep );
\r
2212 p_cep->state = CEP_STATE_IDLE;
\r
2215 case CEP_STATE_DREQ_DESTROY:
\r
2216 p_cep->state = CEP_STATE_DESTROY;
\r
2217 __insert_timewait( p_cep );
\r
2218 /* Fall through. */
\r
2220 case CEP_STATE_DESTROY:
\r
2221 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2222 ib_put_mad( p_mad );
\r
2225 case CEP_STATE_DREQ_SENT:
\r
2227 * Make up a DREP mad so we can respond if we receive
\r
2228 * a DREQ while in timewait.
\r
2230 __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );
\r
2231 __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );
\r
2232 p_cep->state = CEP_STATE_TIMEWAIT;
\r
2233 __insert_timewait( p_cep );
\r
2239 status = __cep_queue_mad( p_cep, p_mad );
\r
2240 CL_ASSERT( status != IB_INVALID_STATE );
\r
2241 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2243 if( status == IB_SUCCESS )
\r
2244 __process_cep( p_cep );
\r
2249 pfn_destroy_cb = p_cep->pfn_destroy_cb;
\r
2250 cep_context = p_cep->context;
\r
2252 if( !cl_atomic_dec( &p_cep->ref_cnt ) && pfn_destroy_cb )
\r
2253 pfn_destroy_cb( cep_context );
\r
2254 AL_EXIT( AL_DBG_CM );
\r
2259 __cep_qp_event_cb(
\r
2260 IN ib_async_event_rec_t *p_event_rec )
\r
2262 UNUSED_PARAM( p_event_rec );
\r
2265 * Most of the QP events are trapped by the real owner of the QP.
\r
2266 * For real events, the CM may not be able to do much anyways!
\r
2271 static ib_api_status_t
\r
2273 IN cep_agent_t* const p_port_cep,
\r
2274 IN const ib_port_attr_t* const p_port_attr )
\r
2276 ib_api_status_t status;
\r
2277 ib_qp_create_t qp_create;
\r
2278 ib_mad_svc_t mad_svc;
\r
2280 AL_ENTER( AL_DBG_CM );
\r
2283 * Create the PD alias. We use the port CM's al_obj_t as the context
\r
2284 * to allow using deref_al_obj as the destroy callback.
\r
2286 status = ib_alloc_pd( p_port_cep->h_ca, IB_PDT_ALIAS, &p_port_cep->obj,
\r
2287 &p_port_cep->h_pd );
\r
2288 if( status != IB_SUCCESS )
\r
2290 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2291 ("ib_alloc_pd failed with status %s\n", ib_get_err_str(status)) );
\r
2294 /* Reference the port object on behalf of the PD. */
\r
2295 ref_al_obj( &p_port_cep->obj );
\r
2297 /* Create the MAD QP. */
\r
2298 cl_memclr( &qp_create, sizeof( ib_qp_create_t ) );
\r
2299 qp_create.qp_type = IB_QPT_QP1_ALIAS;
\r
2300 qp_create.rq_depth = CEP_MAD_RQ_DEPTH;
\r
2301 qp_create.sq_depth = CEP_MAD_SQ_DEPTH;
\r
2302 qp_create.rq_sge = CEP_MAD_RQ_SGE;
\r
2303 qp_create.sq_sge = CEP_MAD_SQ_SGE;
\r
2304 qp_create.sq_signaled = TRUE;
\r
2306 * We use the port CM's al_obj_t as the context to allow using
\r
2307 * deref_al_obj as the destroy callback.
\r
2309 status = ib_get_spl_qp( p_port_cep->h_pd, p_port_attr->port_guid,
\r
2310 &qp_create, &p_port_cep->obj, __cep_qp_event_cb, &p_port_cep->pool_key,
\r
2311 &p_port_cep->h_qp );
\r
2312 if( status != IB_SUCCESS )
\r
2314 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2315 ("ib_get_spl_qp failed with status %s\n", ib_get_err_str(status)) );
\r
2318 /* Reference the port object on behalf of the QP. */
\r
2319 ref_al_obj( &p_port_cep->obj );
\r
2321 /* Create the MAD service. */
\r
2322 cl_memclr( &mad_svc, sizeof(mad_svc) );
\r
2323 mad_svc.mad_svc_context = p_port_cep;
\r
2324 mad_svc.pfn_mad_recv_cb = __cep_mad_recv_cb;
\r
2325 mad_svc.pfn_mad_send_cb = __cep_mad_send_cb;
\r
2326 mad_svc.support_unsol = TRUE;
\r
2327 mad_svc.mgmt_class = IB_MCLASS_COMM_MGMT;
\r
2328 mad_svc.mgmt_version = IB_MCLASS_CM_VER_2;
\r
2329 mad_svc.method_array[IB_MAD_METHOD_SEND] = TRUE;
\r
2331 ib_reg_mad_svc( p_port_cep->h_qp, &mad_svc, &p_port_cep->h_mad_svc );
\r
2332 if( status != IB_SUCCESS )
\r
2334 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2335 ("ib_reg_mad_svc failed with status %s\n", ib_get_err_str(status)) );
\r
2339 AL_EXIT( AL_DBG_CM );
\r
2340 return IB_SUCCESS;
\r
2345 * Performs immediate cleanup of resources.
\r
2348 __destroying_port_cep(
\r
2349 IN al_obj_t *p_obj )
\r
2351 cep_agent_t *p_port_cep;
\r
2352 KLOCK_QUEUE_HANDLE hdl;
\r
2354 AL_ENTER( AL_DBG_CM );
\r
2356 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2358 if( p_port_cep->port_guid )
\r
2360 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2361 cl_qmap_remove_item( &gp_cep_mgr->port_map, &p_port_cep->item );
\r
2362 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2365 if( p_port_cep->h_qp )
\r
2367 ib_destroy_qp( p_port_cep->h_qp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2368 p_port_cep->h_qp = NULL;
\r
2371 if( p_port_cep->h_pd )
\r
2373 ib_dealloc_pd( p_port_cep->h_pd, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2374 p_port_cep->h_pd = NULL;
\r
2377 AL_EXIT( AL_DBG_CM );
\r
2383 * Release all resources allocated by a port CM agent. Finishes any cleanup
\r
2384 * for a port agent.
\r
2388 IN al_obj_t *p_obj )
\r
2390 cep_agent_t *p_port_cep;
\r
2391 ib_port_attr_mod_t port_attr_mod;
\r
2393 AL_ENTER( AL_DBG_CM );
\r
2395 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2397 if( p_port_cep->h_ca )
\r
2399 /* Update local port attributes */
\r
2400 port_attr_mod.cap.cm = FALSE;
\r
2401 ib_modify_ca( p_port_cep->h_ca, p_port_cep->port_num,
\r
2402 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2404 deref_al_obj( &p_port_cep->h_ca->obj );
\r
2407 destroy_al_obj( &p_port_cep->obj );
\r
2408 cl_free( p_port_cep );
\r
2410 AL_EXIT( AL_DBG_CM );
\r
2415 * Create a port agent for a given port.
\r
2417 static ib_api_status_t
\r
2418 __create_port_cep(
\r
2419 IN ib_pnp_port_rec_t *p_pnp_rec )
\r
2421 cep_agent_t *p_port_cep;
\r
2422 ib_api_status_t status;
\r
2423 ib_port_attr_mod_t port_attr_mod;
\r
2424 KLOCK_QUEUE_HANDLE hdl;
\r
2426 AL_ENTER( AL_DBG_CM );
\r
2428 /* calculate size of port_cm struct */
\r
2429 p_port_cep = (cep_agent_t*)cl_zalloc( sizeof(cep_agent_t) );
\r
2432 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2433 ("Failed to cl_zalloc port CM agent.\n") );
\r
2434 return IB_INSUFFICIENT_MEMORY;
\r
2437 construct_al_obj( &p_port_cep->obj, AL_OBJ_TYPE_CM );
\r
2439 status = init_al_obj( &p_port_cep->obj, p_port_cep, TRUE,
\r
2440 __destroying_port_cep, NULL, __free_port_cep );
\r
2441 if( status != IB_SUCCESS )
\r
2443 __free_port_cep( &p_port_cep->obj );
\r
2444 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2445 ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );
\r
2449 /* Attach to the global CM object. */
\r
2450 status = attach_al_obj( &gp_cep_mgr->obj, &p_port_cep->obj );
\r
2451 if( status != IB_SUCCESS )
\r
2453 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2454 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2455 ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );
\r
2459 p_port_cep->port_guid = p_pnp_rec->p_port_attr->port_guid;
\r
2460 p_port_cep->port_num = p_pnp_rec->p_port_attr->port_num;
\r
2461 p_port_cep->base_lid = p_pnp_rec->p_port_attr->lid;
\r
2463 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2465 &gp_cep_mgr->port_map, p_port_cep->port_guid, &p_port_cep->item );
\r
2466 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2468 /* Get a reference to the CA on which we are loading. */
\r
2469 p_port_cep->h_ca = acquire_ca( p_pnp_rec->p_ca_attr->ca_guid );
\r
2470 if( !p_port_cep->h_ca )
\r
2472 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2473 AL_TRACE_EXIT( AL_DBG_ERROR, ("acquire_ca failed.\n") );
\r
2474 return IB_INVALID_GUID; }
\r
2476 status = __init_data_svc( p_port_cep, p_pnp_rec->p_port_attr );
\r
2477 if( status != IB_SUCCESS )
\r
2479 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2480 AL_TRACE_EXIT( AL_DBG_ERROR,
\r
2481 ("__init_data_svc failed with status %s.\n",
\r
2482 ib_get_err_str(status)) );
\r
2486 /* Update local port attributes */
\r
2487 cl_memclr( &port_attr_mod, sizeof(ib_port_attr_mod_t) );
\r
2488 port_attr_mod.cap.cm = TRUE;
\r
2489 status = ib_modify_ca( p_port_cep->h_ca, p_pnp_rec->p_port_attr->port_num,
\r
2490 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2492 /* Update the PNP context to reference this port. */
\r
2493 p_pnp_rec->pnp_rec.context = p_port_cep;
\r
2495 /* Release the reference taken in init_al_obj. */
\r
2496 deref_al_obj( &p_port_cep->obj );
\r
2498 AL_EXIT( AL_DBG_CM );
\r
2499 return IB_SUCCESS;
\r
2503 /******************************************************************************
\r
2504 * Global CEP manager
\r
2505 ******************************************************************************/
\r
2509 OUT net32_t* const p_cid )
\r
2511 cl_status_t status;
\r
2512 uint32_t size, cid;
\r
2513 cep_cid_t *p_cep_cid;
\r
2515 AL_ENTER( AL_DBG_CM );
\r
2517 size = (uint32_t)cl_vector_get_size( &gp_cep_mgr->cid_vector );
\r
2518 cid = gp_cep_mgr->free_cid;
\r
2519 if( gp_cep_mgr->free_cid == size )
\r
2521 /* Grow the vector pool. */
\r
2523 cl_vector_set_size( &gp_cep_mgr->cid_vector, size + CEP_CID_GROW );
\r
2524 if( status != CL_SUCCESS )
\r
2526 AL_EXIT( AL_DBG_CM );
\r
2530 * Return the the start of the free list since the
\r
2531 * entry initializer incremented it.
\r
2533 gp_cep_mgr->free_cid = size;
\r
2536 /* Get the next free entry. */
\r
2537 p_cep_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, cid );
\r
2539 /* Update the next entry index. */
\r
2540 gp_cep_mgr->free_cid = (uint32_t)(uintn_t)p_cep_cid->p_cep;
\r
2544 AL_EXIT( AL_DBG_CM );
\r
2549 static inline kcep_t*
\r
2551 IN ib_al_handle_t h_al OPTIONAL,
\r
2557 /* Mask off the counter bits so we get the index in our vector. */
\r
2558 idx = cid & CEP_MAX_CID_MASK;
\r
2560 if( idx >= cl_vector_get_size( &gp_cep_mgr->cid_vector ) )
\r
2563 p_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, idx );
\r
2564 if( !p_cid->h_al )
\r
2568 * h_al is NULL when processing MADs, so we need to match on
\r
2569 * the actual local communication ID. If h_al is non-NULL, we
\r
2570 * are doing a lookup from a call to our API, and only need to match
\r
2571 * on the index in the vector (without the modifier).
\r
2575 if( p_cid->h_al != h_al )
\r
2578 else if( p_cid->p_cep->local_comm_id != cid )
\r
2583 return p_cid->p_cep;
\r
2588 * Lookup a CEP by remote comm ID and CA GUID.
\r
2592 IN net32_t remote_comm_id,
\r
2593 IN net64_t remote_ca_guid )
\r
2595 cl_rbmap_item_t *p_item;
\r
2598 AL_ENTER( AL_DBG_CM );
\r
2600 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2601 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2602 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2604 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2606 if( remote_comm_id < p_cep->remote_comm_id )
\r
2607 p_item = cl_rbmap_left( p_item );
\r
2608 else if( remote_comm_id > p_cep->remote_comm_id )
\r
2609 p_item = cl_rbmap_right( p_item );
\r
2610 else if( remote_ca_guid < p_cep->remote_ca_guid )
\r
2611 p_item = cl_rbmap_left( p_item );
\r
2612 else if( remote_ca_guid > p_cep->remote_ca_guid )
\r
2613 p_item = cl_rbmap_right( p_item );
\r
2618 AL_EXIT( AL_DBG_CM );
\r
2624 * Lookup a CEP by Service ID and private data.
\r
2629 IN net64_t port_guid,
\r
2630 IN uint8_t *p_pdata )
\r
2632 cl_rbmap_item_t *p_item;
\r
2636 AL_ENTER( AL_DBG_CM );
\r
2638 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2639 p_item = cl_rbmap_root( &gp_cep_mgr->listen_map );
\r
2640 while( p_item != cl_rbmap_end( &gp_cep_mgr->listen_map ) )
\r
2642 p_cep = PARENT_STRUCT( p_item, kcep_t, listen_item );
\r
2644 if( sid == p_cep->sid )
\r
2646 else if( sid < p_cep->sid )
\r
2647 p_item = cl_rbmap_left( p_item );
\r
2649 p_item = cl_rbmap_right( p_item );
\r
2654 if( p_cep->port_guid != IB_ALL_PORTS )
\r
2656 if( port_guid == p_cep->port_guid )
\r
2658 else if( port_guid < p_cep->port_guid )
\r
2659 p_item = cl_rbmap_left( p_item );
\r
2661 p_item = cl_rbmap_right( p_item );
\r
2667 if( p_cep->p_cmp_buf && p_pdata )
\r
2669 cmp = cl_memcmp( &p_pdata[p_cep->cmp_offset],
\r
2670 p_cep->p_cmp_buf, p_cep->cmp_len );
\r
2674 else if( cmp < 0 )
\r
2675 p_item = cl_rbmap_left( p_item );
\r
2677 p_item = cl_rbmap_right( p_item );
\r
2679 AL_TRACE( AL_DBG_CM,
\r
2680 ("Svc ID match but compare buffer mismatch.\n") );
\r
2685 /* Everything matched. */
\r
2686 AL_EXIT( AL_DBG_CM );
\r
2690 AL_EXIT( AL_DBG_CM );
\r
2697 IN kcep_t* const p_new_cep )
\r
2700 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2701 boolean_t left = TRUE;
\r
2703 AL_ENTER( AL_DBG_CM );
\r
2705 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2706 p_insert_at = p_item;
\r
2707 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2709 p_insert_at = p_item;
\r
2710 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2712 if( p_new_cep->remote_comm_id < p_cep->remote_comm_id )
\r
2713 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2714 else if( p_new_cep->remote_comm_id > p_cep->remote_comm_id )
\r
2715 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2716 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2717 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2718 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2719 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2722 AL_TRACE( AL_DBG_CM | AL_DBG_WARN,
\r
2723 ("WARNING: Duplicate remote CID and CA GUID.\n") );
\r
2729 &gp_cep_mgr->conn_id_map, p_insert_at, &p_new_cep->rem_id_item, left );
\r
2730 p_cep = p_new_cep;
\r
2733 AL_EXIT( AL_DBG_CM );
\r
2740 IN kcep_t* const p_new_cep )
\r
2743 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2744 boolean_t left = TRUE;
\r
2746 AL_ENTER( AL_DBG_CM );
\r
2748 p_item = cl_rbmap_root( &gp_cep_mgr->conn_qp_map );
\r
2749 p_insert_at = p_item;
\r
2750 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_qp_map ) )
\r
2752 p_insert_at = p_item;
\r
2753 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2755 if( p_new_cep->remote_qpn < p_cep->remote_qpn )
\r
2756 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2757 else if( p_new_cep->remote_qpn > p_cep->remote_qpn )
\r
2758 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2759 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2760 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2761 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2762 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2765 AL_TRACE( AL_DBG_CM | AL_DBG_WARN,
\r
2766 ("WARNING: Duplicate remote QPN and CA GUID.\n") );
\r
2772 &gp_cep_mgr->conn_qp_map, p_insert_at, &p_new_cep->rem_qp_item, left );
\r
2773 p_cep = p_new_cep;
\r
2776 AL_EXIT( AL_DBG_CM );
\r
2781 static inline kcep_t*
\r
2783 IN kcep_t* const p_new_cep )
\r
2787 AL_ENTER( AL_DBG_CM );
\r
2789 p_cep = __insert_by_qpn( p_new_cep );
\r
2790 if( p_cep != p_new_cep )
\r
2793 p_cep = __insert_by_id( p_new_cep );
\r
2794 if( p_cep != p_new_cep )
\r
2796 cl_rbmap_remove_item(
\r
2797 &gp_cep_mgr->conn_qp_map, &p_new_cep->rem_qp_item );
\r
2798 p_cep->remote_qpn = 0;
\r
2802 AL_EXIT( AL_DBG_CM );
\r
2807 static inline void
\r
2809 IN kcep_t* const p_cep )
\r
2811 AL_ENTER( AL_DBG_CM );
\r
2813 if( p_cep->remote_comm_id )
\r
2815 cl_rbmap_remove_item(
\r
2816 &gp_cep_mgr->conn_id_map, &p_cep->rem_id_item );
\r
2817 p_cep->remote_comm_id = 0;
\r
2819 if( p_cep->remote_qpn )
\r
2821 cl_rbmap_remove_item(
\r
2822 &gp_cep_mgr->conn_qp_map, &p_cep->rem_qp_item );
\r
2823 p_cep->remote_qpn = 0;
\r
2826 AL_EXIT( AL_DBG_CM );
\r
2832 IN ib_net16_t lid,
\r
2833 IN ib_net16_t port_lid,
\r
2838 uint16_t path_bits;
\r
2842 lid1 = CL_NTOH16(lid);
\r
2843 lid2 = CL_NTOH16(port_lid);
\r
2850 path_bits = (uint16_t)( (path_bits << 1) | 1 );
\r
2852 lid2 |= path_bits;
\r
2859 if (lid != port_lid)
\r
2867 static inline boolean_t
\r
2869 IN const ib_port_attr_t* const p_port_attr,
\r
2870 IN const ib_gid_t* const p_gid )
\r
2874 for( idx = 0; idx < p_port_attr->num_gids; idx++ )
\r
2877 p_gid, &p_port_attr->p_gid_table[idx], sizeof(ib_gid_t) ) )
\r
2886 static inline boolean_t
\r
2888 IN const ib_port_attr_t* const p_port_attr,
\r
2889 IN const net16_t pkey,
\r
2890 OUT uint16_t* const p_pkey_index )
\r
2894 for( idx = 0; idx < p_port_attr->num_pkeys; idx++ )
\r
2896 if( p_port_attr->p_pkey_table[idx] == pkey )
\r
2898 *p_pkey_index = idx;
\r
2907 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
2908 static cep_agent_t*
\r
2910 IN const ib_gid_t* const p_gid,
\r
2911 IN const net16_t lid,
\r
2912 IN const net16_t pkey,
\r
2913 OUT uint16_t* const p_pkey_index )
\r
2915 cep_agent_t *p_port_cep;
\r
2916 cl_list_item_t *p_item;
\r
2917 const ib_port_attr_t *p_port_attr;
\r
2919 AL_ENTER( AL_DBG_CM );
\r
2921 cl_spinlock_acquire( &gp_cep_mgr->obj.lock );
\r
2922 for( p_item = cl_qlist_head( &gp_cep_mgr->obj.obj_list );
\r
2923 p_item != cl_qlist_end( &gp_cep_mgr->obj.obj_list );
\r
2924 p_item = cl_qlist_next( p_item ) )
\r
2926 p_port_cep = PARENT_STRUCT( p_item, cep_agent_t, obj.pool_item );
\r
2928 CL_ASSERT( p_port_cep->port_num );
\r
2930 ci_ca_lock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2932 p_port_attr = p_port_cep->h_ca->obj.p_ci_ca->p_pnp_attr->p_port_attr;
\r
2933 p_port_attr += (p_port_cep->port_num - 1);
\r
2935 if( __is_lid_valid( lid, p_port_attr->lid, p_port_attr->lmc ) &&
\r
2936 __is_gid_valid( p_port_attr, p_gid ) &&
\r
2937 __get_pkey_index( p_port_attr, pkey, p_pkey_index ) )
\r
2939 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2940 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
2941 AL_EXIT( AL_DBG_CM );
\r
2942 return p_port_cep;
\r
2945 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2947 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
2948 AL_EXIT( AL_DBG_CM );
\r
2954 * PnP callback for port event notifications.
\r
2956 static ib_api_status_t
\r
2958 IN ib_pnp_rec_t *p_pnp_rec )
\r
2960 ib_api_status_t status = IB_SUCCESS;
\r
2962 AL_ENTER( AL_DBG_CM );
\r
2964 switch( p_pnp_rec->pnp_event )
\r
2966 case IB_PNP_PORT_ADD:
\r
2967 /* Create the port agent. */
\r
2968 CL_ASSERT( !p_pnp_rec->context );
\r
2969 status = __create_port_cep( (ib_pnp_port_rec_t*)p_pnp_rec );
\r
2972 case IB_PNP_PORT_REMOVE:
\r
2973 CL_ASSERT( p_pnp_rec->context );
\r
2975 /* Destroy the port agent. */
\r
2976 ref_al_obj( &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj );
\r
2977 ((cep_agent_t* __ptr64)p_pnp_rec->context)->obj.pfn_destroy(
\r
2978 &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj, NULL );
\r
2982 break; /* Ignore other PNP events. */
\r
2985 AL_EXIT( AL_DBG_CM );
\r
2990 static inline int64_t
\r
2992 IN int64_t current_min,
\r
2993 IN kcep_t* const p_cep )
\r
2996 * The minimum timer interval is 50 milliseconds. This means
\r
2997 * 500000 100ns increments. Since __process_timewait divides the
\r
2998 * result in half (so that the worst cast timewait interval is 150%)
\r
2999 * we compensate for this here. Note that relative time values are
\r
3000 * expressed as negative.
\r
3002 #define MIN_TIMEWAIT_100NS -1000000
\r
3004 /* Still in timewait - try again next time. */
\r
3005 if( !current_min )
\r
3007 return min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS );
\r
3011 return max( current_min,
\r
3012 min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS ) );
\r
3018 * Timer callback to process CEPs in timewait state. Returns time in ms.
\r
3021 __process_timewait()
\r
3023 cl_list_item_t *p_item;
\r
3025 LARGE_INTEGER timeout;
\r
3026 int64_t min_timewait = 0;
\r
3028 AL_ENTER( AL_DBG_CM );
\r
3030 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3032 timeout.QuadPart = 0;
\r
3034 p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3035 while( p_item != cl_qlist_end( &gp_cep_mgr->timewait_list ) )
\r
3037 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3038 p_item = cl_qlist_next( p_item );
\r
3040 CL_ASSERT( p_cep->state == CEP_STATE_DESTROY ||
\r
3041 p_cep->state == CEP_STATE_TIMEWAIT );
\r
3043 CL_ASSERT( !p_cep->p_mad );
\r
3045 if( KeWaitForSingleObject( &p_cep->timewait_timer, Executive,
\r
3046 KernelMode, FALSE, &timeout ) != STATUS_SUCCESS )
\r
3048 /* Still in timewait - try again next time. */
\r
3049 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3053 if( p_cep->ref_cnt )
\r
3055 /* Send outstanding or destruction in progress. */
\r
3056 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3060 /* Remove from the timewait list. */
\r
3061 cl_qlist_remove_item( &gp_cep_mgr->timewait_list, &p_cep->timewait_item );
\r
3064 * Not in timewait. Remove the CEP from the maps - it should
\r
3065 * no longer be matched against.
\r
3067 __remove_cep( p_cep );
\r
3069 if( p_cep->state == CEP_STATE_DESTROY )
\r
3071 __destroy_cep( p_cep );
\r
3075 /* Move the CEP to the IDLE state so that it can be used again. */
\r
3076 p_cep->state = CEP_STATE_IDLE;
\r
3080 AL_EXIT( AL_DBG_CM );
\r
3081 return (uint32_t)(min_timewait / -20000);
\r
3086 * Timer callback to process CEPs in timewait state.
\r
3089 __cep_timewait_cb(
\r
3090 IN void *context )
\r
3092 KLOCK_QUEUE_HANDLE hdl;
\r
3093 uint32_t min_timewait;
\r
3095 AL_ENTER( AL_DBG_CM );
\r
3097 UNUSED_PARAM( context );
\r
3099 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3101 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
3103 min_timewait = __process_timewait();
\r
3105 if( cl_qlist_count( &gp_cep_mgr->timewait_list ) )
\r
3108 * Reset the timer for half of the shortest timeout - this results
\r
3109 * in a worst case timeout of 150% of timewait.
\r
3111 cl_timer_trim( &gp_cep_mgr->timewait_timer, min_timewait );
\r
3114 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
3116 AL_EXIT( AL_DBG_CM );
\r
3121 * Starts immediate cleanup of the CM. Invoked during al_obj destruction.
\r
3124 __destroying_cep_mgr(
\r
3125 IN al_obj_t* p_obj )
\r
3127 ib_api_status_t status;
\r
3128 KLOCK_QUEUE_HANDLE hdl;
\r
3129 cl_list_item_t *p_item;
\r
3131 LARGE_INTEGER timeout;
\r
3133 AL_ENTER( AL_DBG_CM );
\r
3135 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3136 UNUSED_PARAM( p_obj );
\r
3138 /* Deregister from PnP notifications. */
\r
3139 if( gp_cep_mgr->h_pnp )
\r
3141 status = ib_dereg_pnp(
\r
3142 gp_cep_mgr->h_pnp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
3143 if( status != IB_SUCCESS )
\r
3145 CL_TRACE( AL_DBG_ERROR, g_al_dbg_lvl,
\r
3146 ("ib_dereg_pnp failed with status %s.\n",
\r
3147 ib_get_err_str(status)) );
\r
3148 deref_al_obj( &gp_cep_mgr->obj );
\r
3152 /* Cancel all timewait timers. */
\r
3153 timeout.QuadPart = 0;
\r
3154 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
3155 for( p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3156 p_item != cl_qlist_end( &gp_cep_mgr->timewait_list );
\r
3157 p_item = cl_qlist_next( p_item ) )
\r
3159 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3160 KeSetTimer( &p_cep->timewait_timer, timeout, NULL );
\r
3162 __process_timewait();
\r
3163 KeReleaseInStackQueuedSpinLock( &hdl );
\r
3165 AL_EXIT( AL_DBG_CM );
\r
3170 * Frees the global CEP agent. Invoked during al_obj destruction.
\r
3174 IN al_obj_t* p_obj )
\r
3176 AL_ENTER( AL_DBG_CM );
\r
3178 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3179 /* All listen request should have been cleaned up by this point. */
\r
3180 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->listen_map ) );
\r
3181 /* All connections should have been cancelled/disconnected by now. */
\r
3182 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_id_map ) );
\r
3183 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_qp_map ) );
\r
3185 cl_vector_destroy( &gp_cep_mgr->cid_vector );
\r
3187 cl_timer_destroy( &gp_cep_mgr->timewait_timer );
\r
3190 * All CM port agents should have been destroyed by now via the
\r
3191 * standard child object destruction provided by the al_obj.
\r
3193 ExDeleteNPagedLookasideList( &gp_cep_mgr->cep_pool );
\r
3194 destroy_al_obj( p_obj );
\r
3196 cl_free( gp_cep_mgr );
\r
3197 gp_cep_mgr = NULL;
\r
3199 AL_EXIT( AL_DBG_CM );
\r
3203 static cl_status_t
\r
3205 IN void* const p_element,
\r
3206 IN void* context )
\r
3210 UNUSED_PARAM( context );
\r
3212 p_cid = (cep_cid_t*)p_element;
\r
3214 p_cid->h_al = NULL;
\r
3215 p_cid->p_cep = (kcep_t*)(uintn_t)++gp_cep_mgr->free_cid;
\r
3216 p_cid->modifier = 0;
\r
3218 return CL_SUCCESS;
\r
3223 * Allocates and initialized the global CM agent.
\r