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 #if defined(EVENT_TRACING)
\r
47 #include "al_cm_cep.tmh"
\r
49 #include "ib_common.h"
\r
58 * The vector object uses a list item at the front of the buffers
\r
59 * it allocates. Take the list item into account so that allocations
\r
60 * are for full page sizes.
\r
62 #define CEP_CID_MIN \
\r
63 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
64 #define CEP_CID_GROW \
\r
65 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
68 * We reserve the upper byte of the connection ID as a revolving counter so
\r
69 * that connections that are retried by the client change connection ID.
\r
70 * This counter is never zero, so it is OK to use all CIDs since we will never
\r
71 * have a full CID (base + counter) that is zero.
\r
72 * See the IB spec, section 12.9.8.7 for details about REJ retry.
\r
74 #define CEP_MAX_CID (0x00FFFFFF)
\r
75 #define CEP_MAX_CID_MASK (0x00FFFFFF)
\r
77 #define CEP_MAD_SQ_DEPTH (128)
\r
78 #define CEP_MAD_RQ_DEPTH (1) /* ignored. */
\r
79 #define CEP_MAD_SQ_SGE (1)
\r
80 #define CEP_MAD_RQ_SGE (1) /* ignored. */
\r
83 /* Global connection manager object. */
\r
84 typedef struct _al_cep_mgr
\r
92 /* Bitmap of CEPs, indexed by CID. */
\r
93 cl_vector_t cid_vector;
\r
96 /* List of active listens. */
\r
97 cl_rbmap_t listen_map;
\r
99 /* Map of CEP by remote CID and CA GUID. */
\r
100 cl_rbmap_t conn_id_map;
\r
101 /* Map of CEP by remote QPN, used for stale connection matching. */
\r
102 cl_rbmap_t conn_qp_map;
\r
104 NPAGED_LOOKASIDE_LIST cep_pool;
\r
105 NPAGED_LOOKASIDE_LIST req_pool;
\r
108 * Periodically walk the list of connections in the time wait state
\r
109 * and flush them as appropriate.
\r
111 cl_timer_t timewait_timer;
\r
112 cl_qlist_t timewait_list;
\r
114 ib_pnp_handle_t h_pnp;
\r
119 /* Per-port CM object. */
\r
120 typedef struct _cep_port_agent
\r
124 cl_map_item_t item;
\r
126 ib_ca_handle_t h_ca;
\r
127 ib_pd_handle_t h_pd;
\r
128 ib_qp_handle_t h_qp;
\r
129 ib_pool_key_t pool_key;
\r
130 ib_mad_svc_handle_t h_mad_svc;
\r
140 * Note: the REQ, REP, and LAP values must be 1, 2, and 4 respectively.
\r
141 * This allows shifting 1 << msg_mraed from an MRA to figure out for what
\r
142 * message the MRA was sent for.
\r
144 #define CEP_STATE_RCVD 0x10000000
\r
145 #define CEP_STATE_SENT 0x20000000
\r
146 #define CEP_STATE_MRA 0x01000000
\r
147 #define CEP_STATE_REQ 0x00000001
\r
148 #define CEP_STATE_REP 0x00000002
\r
149 #define CEP_STATE_LAP 0x00000004
\r
150 #define CEP_STATE_RTU 0x00000008
\r
151 #define CEP_STATE_DREQ 0x00000010
\r
152 #define CEP_STATE_DREP 0x00000020
\r
153 #define CEP_STATE_DESTROYING 0x00010000
\r
154 #define CEP_STATE_USER 0x00020000
\r
156 #define CEP_MSG_MASK 0x000000FF
\r
157 #define CEP_OP_MASK 0xF0000000
\r
159 #define CEP_STATE_PREP 0x00100000
\r
161 /* States match CM state transition diagrams from spec. */
\r
162 typedef enum _cep_state
\r
166 CEP_STATE_ESTABLISHED,
\r
167 CEP_STATE_TIMEWAIT,
\r
168 CEP_STATE_SREQ_SENT,
\r
169 CEP_STATE_SREQ_RCVD,
\r
171 CEP_STATE_DESTROY = CEP_STATE_DESTROYING,
\r
172 CEP_STATE_PRE_REQ = CEP_STATE_IDLE | CEP_STATE_PREP,
\r
173 CEP_STATE_REQ_RCVD = CEP_STATE_REQ | CEP_STATE_RCVD,
\r
174 CEP_STATE_PRE_REP = CEP_STATE_REQ_RCVD | CEP_STATE_PREP,
\r
175 CEP_STATE_REQ_SENT = CEP_STATE_REQ | CEP_STATE_SENT,
\r
176 CEP_STATE_REQ_MRA_RCVD = CEP_STATE_REQ_SENT | CEP_STATE_MRA,
\r
177 CEP_STATE_REQ_MRA_SENT = CEP_STATE_REQ_RCVD | CEP_STATE_MRA,
\r
178 CEP_STATE_PRE_REP_MRA_SENT = CEP_STATE_REQ_MRA_SENT | CEP_STATE_PREP,
\r
179 CEP_STATE_REP_RCVD = CEP_STATE_REP | CEP_STATE_RCVD,
\r
180 CEP_STATE_REP_SENT = CEP_STATE_REP | CEP_STATE_SENT,
\r
181 CEP_STATE_REP_MRA_RCVD = CEP_STATE_REP_SENT | CEP_STATE_MRA,
\r
182 CEP_STATE_REP_MRA_SENT = CEP_STATE_REP_RCVD | CEP_STATE_MRA,
\r
183 CEP_STATE_LAP_RCVD = CEP_STATE_LAP | CEP_STATE_RCVD,
\r
184 CEP_STATE_PRE_APR = CEP_STATE_LAP_RCVD | CEP_STATE_PREP,
\r
185 CEP_STATE_LAP_SENT = CEP_STATE_LAP | CEP_STATE_SENT,
\r
186 CEP_STATE_LAP_MRA_RCVD = CEP_STATE_LAP_SENT | CEP_STATE_MRA,
\r
187 CEP_STATE_LAP_MRA_SENT = CEP_STATE_LAP_RCVD | CEP_STATE_MRA,
\r
188 CEP_STATE_PRE_APR_MRA_SENT = CEP_STATE_LAP_MRA_SENT | CEP_STATE_PREP,
\r
189 CEP_STATE_DREQ_SENT = CEP_STATE_DREQ | CEP_STATE_SENT,
\r
190 CEP_STATE_DREQ_RCVD = CEP_STATE_DREQ | CEP_STATE_RCVD,
\r
191 CEP_STATE_DREQ_DESTROY = CEP_STATE_DREQ_SENT | CEP_STATE_DESTROYING
\r
196 /* Active side CEP state transitions:
\r
197 * al_create_cep -> IDLE
\r
198 * al_cep_pre_req -> PRE_REQ
\r
199 * al_cep_send_req -> REQ_SENT
\r
200 * Recv REQ MRA -> REQ_MRA_RCVD
\r
201 * Recv REP -> REP_RCVD
\r
202 * al_cep_mra -> REP_MRA_SENT
\r
203 * al_cep_rtu -> ESTABLISHED
\r
205 * Passive side CEP state transitions:
\r
206 * al_create_cep -> IDLE
\r
207 * Recv REQ -> REQ_RCVD
\r
208 * al_cep_mra* -> REQ_MRA_SENT
\r
209 * al_cep_pre_rep -> PRE_REP
\r
210 * al_cep_mra* -> PRE_REP_MRA_SENT
\r
211 * al_cep_send_rep -> REP_SENT
\r
212 * Recv RTU -> ESTABLISHED
\r
214 * *al_cep_mra can only be called once - either before or after PRE_REP.
\r
217 typedef struct _al_kcep_av
\r
221 uint16_t pkey_index;
\r
226 typedef struct _al_kcep
\r
231 struct _cep_cid *p_cid;
\r
235 /* Port guid for filtering incoming requests. */
\r
238 uint8_t* __ptr64 p_cmp_buf;
\r
239 uint8_t cmp_offset;
\r
244 /* Used to store connection structure with owning AL instance. */
\r
245 cl_list_item_t al_item;
\r
247 /* Flag to indicate whether a user is processing events. */
\r
248 boolean_t signalled;
\r
250 /* Destroy callback. */
\r
251 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
253 ib_mad_element_t *p_mad_head;
\r
254 ib_mad_element_t *p_mad_tail;
\r
255 al_pfn_cep_cb_t pfn_cb;
\r
259 /* MAP item for finding listen CEPs. */
\r
260 cl_rbmap_item_t listen_item;
\r
262 /* Map item for finding CEPs based on remote comm ID & CA GUID. */
\r
263 cl_rbmap_item_t rem_id_item;
\r
265 /* Map item for finding CEPs based on remote QP number. */
\r
266 cl_rbmap_item_t rem_qp_item;
\r
268 /* Communication ID's for the connection. */
\r
269 net32_t local_comm_id;
\r
270 net32_t remote_comm_id;
\r
272 net64_t local_ca_guid;
\r
273 net64_t remote_ca_guid;
\r
275 /* Remote QP, used for stale connection checking. */
\r
276 net32_t remote_qpn;
\r
278 /* Parameters to format QP modification structure. */
\r
282 uint8_t init_depth;
\r
283 uint8_t rnr_nak_timeout;
\r
286 * Local QP number, used for the "additional check" required
\r
291 /* PKEY to make sure a LAP is on the same partition. */
\r
294 /* Initiator depth as received in the REQ. */
\r
295 uint8_t req_init_depth;
\r
298 * Primary and alternate path info, used to create the address vectors for
\r
299 * sending MADs, to locate the port CM agent to use for outgoing sends,
\r
300 * and for creating the address vectors for transitioning QPs.
\r
303 uint8_t idx_primary;
\r
305 /* Temporary AV and CEP port GUID used when processing LAP. */
\r
307 uint8_t alt_2pkt_life;
\r
309 /* maxium packet lifetime * 2 of any path used on a connection. */
\r
310 uint8_t max_2pkt_life;
\r
311 /* Given by the REP, used for alternate path setup. */
\r
312 uint8_t target_ack_delay;
\r
313 /* Stored to help calculate the local ACK delay in the LAP. */
\r
314 uint8_t local_ack_delay;
\r
316 /* Volatile to allow using atomic operations for state checks. */
\r
320 * Flag that indicates whether a connection took the active role during
\r
323 boolean_t was_active;
\r
326 * Handle to the sent MAD, used for cancelling. We store the handle to
\r
327 * the mad service so that we can properly cancel. This should not be a
\r
328 * problem since all outstanding sends should be completed before the
\r
329 * mad service completes its destruction and the handle becomes invalid.
\r
331 ib_mad_svc_handle_t h_mad_svc;
\r
332 ib_mad_element_t *p_send_mad;
\r
334 /* Number of outstanding MADs. Delays destruction of CEP destruction. */
\r
335 atomic32_t ref_cnt;
\r
337 /* MAD transaction ID to use when sending MADs. */
\r
340 /* Maximum retries per MAD. Set at REQ time, stored to retry LAP. */
\r
341 uint8_t max_cm_retries;
\r
342 /* Timeout value, in milliseconds. Set at REQ time, stored to retry LAP. */
\r
343 uint32_t retry_timeout;
\r
345 /* Timer that will be signalled when the CEP exits timewait. */
\r
346 KTIMER timewait_timer;
\r
347 LARGE_INTEGER timewait_time;
\r
348 cl_list_item_t timewait_item;
\r
351 * Pointer to a formatted MAD. The pre_req, pre_rep and pre_apr calls
\r
352 * allocate and format the MAD, and the send_req, send_rep and send_apr
\r
355 ib_mad_element_t *p_mad;
\r
357 /* Cache the last MAD sent for retransmission. */
\r
363 mad_cm_drep_t drep;
\r
370 /* Structures stored in the CID vector. */
\r
371 typedef struct _cep_cid
\r
373 /* Owning AL handle. NULL if invalid. */
\r
374 ib_al_handle_t h_al;
\r
375 /* Pointer to CEP, or index of next free entry if h_al is NULL. */
\r
377 /* For REJ Retry support */
\r
383 /* Global instance of the CM agent. */
\r
384 al_cep_mgr_t *gp_cep_mgr = NULL;
\r
387 static ib_api_status_t
\r
389 IN kcep_t* const p_cep,
\r
390 IN const uint8_t* p_pdata OPTIONAL,
\r
391 IN uint8_t pdata_len,
\r
392 IN OUT mad_cm_drep_t* const p_drep );
\r
394 static ib_api_status_t
\r
396 IN kcep_t* const p_cep,
\r
397 IN ib_mad_element_t* p_mad );
\r
401 IN kcep_t* const p_cep );
\r
403 static inline uint32_t
\r
404 __calc_mad_timeout(
\r
405 IN const uint8_t pkt_life );
\r
409 IN kcep_t* const p_cep );
\r
412 __create_cep( void );
\r
416 IN kcep_t* const p_cep );
\r
420 IN kcep_t* const p_cep );
\r
424 IN kcep_t* const p_cep,
\r
425 IN ib_al_handle_t h_al,
\r
426 IN al_pfn_cep_cb_t pfn_cb,
\r
427 IN void* __ptr64 context );
\r
431 IN kcep_t* const p_cep );
\r
435 IN kcep_t* const p_cep );
\r
439 IN net32_t remote_comm_id,
\r
440 IN net64_t remote_ca_guid );
\r
445 IN net64_t port_guid,
\r
446 IN void *p_pdata );
\r
448 static inline kcep_t*
\r
450 IN ib_al_handle_t h_al OPTIONAL,
\r
453 static inline kcep_t*
\r
455 IN kcep_t* const p_new_cep );
\r
459 IN kcep_t* const p_cep );
\r
463 IN kcep_t* const p_cep );
\r
465 static ib_api_status_t
\r
467 IN kcep_t* const p_cep,
\r
468 IN net16_t attr_id,
\r
469 OUT cep_agent_t** const pp_port_cep,
\r
470 OUT ib_mad_element_t** const pp_mad );
\r
472 static ib_api_status_t
\r
474 IN cep_agent_t* const p_port_cep,
\r
475 IN ib_mad_element_t* const p_mad );
\r
477 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
478 static cep_agent_t*
\r
480 IN const ib_gid_t* const p_gid,
\r
481 IN const net16_t lid,
\r
482 IN const net16_t pkey,
\r
483 OUT uint16_t* const p_pkey_index );
\r
487 OUT net32_t* const p_cid );
\r
490 __process_cep_send_comp(
\r
491 IN cl_async_proc_item_t *p_item );
\r
494 /******************************************************************************
\r
495 * Per-port CEP agent
\r
496 ******************************************************************************/
\r
501 IN ib_mad_t* const p_mad,
\r
502 IN const kcep_t* const p_cep,
\r
503 IN net16_t attr_id )
\r
505 p_mad->base_ver = 1;
\r
506 p_mad->mgmt_class = IB_MCLASS_COMM_MGMT;
\r
507 p_mad->class_ver = IB_MCLASS_CM_VER_2;
\r
508 p_mad->method = IB_MAD_METHOD_SEND;
\r
510 p_mad->class_spec = 0;
\r
511 p_mad->trans_id = p_cep->tid;
\r
512 p_mad->attr_id = attr_id;
\r
514 p_mad->attr_mod = 0;
\r
518 /* Consumes the input MAD. */
\r
521 IN cep_agent_t* const p_port_cep,
\r
522 IN kcep_t* const p_cep,
\r
523 IN ib_mad_element_t* const p_mad,
\r
524 IN ib_rej_status_t reason )
\r
526 mad_cm_rej_t *p_rej;
\r
528 AL_ENTER( AL_DBG_CM );
\r
530 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
532 __format_mad_hdr( p_mad->p_mad_buf, p_cep, CM_REJ_ATTR_ID );
\r
534 p_rej->local_comm_id = p_cep->local_comm_id;
\r
535 p_rej->remote_comm_id = p_cep->remote_comm_id;
\r
536 p_rej->reason = reason;
\r
538 switch( p_cep->state )
\r
540 case CEP_STATE_REQ_RCVD:
\r
541 case CEP_STATE_REQ_MRA_SENT:
\r
542 case CEP_STATE_PRE_REP:
\r
543 case CEP_STATE_PRE_REP_MRA_SENT:
\r
544 conn_rej_set_msg_rejected( 0, p_rej );
\r
547 case CEP_STATE_REP_RCVD:
\r
548 case CEP_STATE_REP_MRA_SENT:
\r
549 conn_rej_set_msg_rejected( 1, p_rej );
\r
553 CL_ASSERT( reason == IB_REJ_TIMEOUT );
\r
554 conn_rej_set_msg_rejected( 2, p_rej );
\r
558 conn_rej_clr_rsvd_fields( p_rej );
\r
559 __cep_send_mad( p_port_cep, p_mad );
\r
561 AL_EXIT( AL_DBG_CM );
\r
567 IN cep_agent_t* const p_port_cep,
\r
568 IN kcep_t* const p_cep,
\r
569 IN const ib_mad_element_t* const p_mad )
\r
571 ib_api_status_t status;
\r
572 ib_mad_element_t *p_rej_mad;
\r
573 ib_mad_t *p_mad_buf;
\r
576 AL_ENTER( AL_DBG_CM );
\r
578 status = ib_get_mad( p_port_cep->pool_key, MAD_BLOCK_SIZE, &p_rej_mad );
\r
579 if( status != IB_SUCCESS )
\r
581 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
582 ("ib_get_mad returned %s\n", ib_get_err_str( status )) );
\r
586 /* Save the buffer pointers from the new element. */
\r
587 p_mad_buf = p_rej_mad->p_mad_buf;
\r
588 p_grh = p_rej_mad->p_grh;
\r
591 * Copy the input MAD element to the reject - this gives us
\r
592 * all appropriate addressing information.
\r
594 cl_memcpy( p_rej_mad, p_mad, sizeof(ib_mad_element_t) );
\r
595 cl_memcpy( p_grh, p_mad->p_grh, sizeof(ib_grh_t) );
\r
597 /* Restore the buffer pointers now that the copy is complete. */
\r
598 p_rej_mad->p_mad_buf = p_mad_buf;
\r
599 p_rej_mad->p_grh = p_grh;
\r
601 status = conn_rej_set_pdata( NULL, 0, (mad_cm_rej_t*)p_mad_buf );
\r
602 CL_ASSERT( status == IB_SUCCESS );
\r
604 /* Copy the local CA GUID into the ARI. */
\r
605 switch( p_mad->p_mad_buf->attr_id )
\r
607 case CM_REQ_ATTR_ID:
\r
608 status = conn_rej_set_ari(
\r
609 (uint8_t*)&p_cep->local_ca_guid,
\r
610 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
611 CL_ASSERT( status == IB_SUCCESS );
\r
612 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
615 case CM_REP_ATTR_ID:
\r
616 status = conn_rej_set_ari(
\r
617 (uint8_t*)&p_cep->local_ca_guid,
\r
618 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
619 CL_ASSERT( status == IB_SUCCESS );
\r
620 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
624 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
625 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID );
\r
626 ib_put_mad( p_rej_mad );
\r
630 AL_EXIT( AL_DBG_CM );
\r
636 IN cep_agent_t* const p_port_cep,
\r
637 IN ib_mad_element_t* const p_mad,
\r
638 IN const ib_rej_status_t reason )
\r
640 mad_cm_req_t *p_req;
\r
641 mad_cm_rej_t *p_rej;
\r
643 AL_ENTER( AL_DBG_CM );
\r
645 CL_ASSERT( p_port_cep );
\r
646 CL_ASSERT( p_mad );
\r
647 CL_ASSERT( reason != 0 );
\r
649 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
650 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
653 * Format the reject information, overwriting the REQ data and send
\r
656 p_rej->hdr.attr_id = CM_REJ_ATTR_ID;
\r
657 p_rej->remote_comm_id = p_req->local_comm_id;
\r
658 p_rej->local_comm_id = 0;
\r
659 conn_rej_set_msg_rejected( 0, p_rej );
\r
660 p_rej->reason = reason;
\r
661 conn_rej_set_ari( NULL, 0, p_rej );
\r
662 conn_rej_set_pdata( NULL, 0, p_rej );
\r
663 conn_rej_clr_rsvd_fields( p_rej );
\r
665 p_mad->retry_cnt = 0;
\r
666 p_mad->send_opt = 0;
\r
667 p_mad->timeout_ms = 0;
\r
668 p_mad->resp_expected = FALSE;
\r
670 __cep_send_mad( p_port_cep, p_mad );
\r
672 AL_EXIT( AL_DBG_CM );
\r
678 IN kcep_t* const p_cep,
\r
679 IN const mad_cm_req_t* const p_req,
\r
680 IN const uint8_t idx )
\r
682 cep_agent_t *p_port_cep;
\r
683 const req_path_info_t *p_path;
\r
685 AL_ENTER( AL_DBG_CM );
\r
687 CL_ASSERT( p_cep );
\r
688 CL_ASSERT( p_req );
\r
690 cl_memclr( &p_cep->av[idx], sizeof(kcep_av_t) );
\r
692 p_path = &((&p_req->primary_path)[idx]);
\r
694 p_port_cep = __find_port_cep( &p_path->remote_gid,
\r
695 p_path->remote_lid, p_req->pkey, &p_cep->av[idx].pkey_index );
\r
699 p_cep->local_ca_guid = 0;
\r
700 AL_EXIT( AL_DBG_CM );
\r
705 p_cep->local_ca_guid = p_port_cep->h_ca->obj.p_ci_ca->verbs.guid;
\r
707 /* Check that CA GUIDs match if formatting the alternate path. */
\r
709 p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
711 AL_EXIT( AL_DBG_CM );
\r
716 * Pkey indeces must match if formating the alternat path - the QP
\r
717 * modify structure only allows for a single PKEY index to be specified.
\r
720 p_cep->av[0].pkey_index != p_cep->av[1].pkey_index )
\r
722 AL_EXIT( AL_DBG_CM );
\r
726 p_cep->av[idx].port_guid = p_port_cep->port_guid;
\r
727 p_cep->av[idx].attr.port_num = p_port_cep->port_num;
\r
729 p_cep->av[idx].attr.sl = conn_req_path_get_svc_lvl( p_path );
\r
730 p_cep->av[idx].attr.dlid = p_path->local_lid;
\r
732 if( !conn_req_path_get_subn_lcl( p_path ) )
\r
734 p_cep->av[idx].attr.grh_valid = TRUE;
\r
735 p_cep->av[idx].attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
736 1, p_path->traffic_class, conn_req_path_get_flow_lbl( p_path ) );
\r
737 p_cep->av[idx].attr.grh.hop_limit = p_path->hop_limit;
\r
738 p_cep->av[idx].attr.grh.dest_gid = p_path->local_gid;
\r
739 p_cep->av[idx].attr.grh.src_gid = p_path->remote_gid;
\r
743 p_cep->av[idx].attr.grh_valid = FALSE;
\r
745 p_cep->av[idx].attr.static_rate = conn_req_path_get_pkt_rate( p_path );
\r
746 p_cep->av[idx].attr.path_bits =
\r
747 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
750 * Note that while we never use the connected AV attributes internally,
\r
751 * we store them so we can pass them back to users.
\r
753 p_cep->av[idx].attr.conn.path_mtu = conn_req_get_mtu( p_req );
\r
754 p_cep->av[idx].attr.conn.local_ack_timeout =
\r
755 conn_req_path_get_lcl_ack_timeout( p_path );
\r
756 p_cep->av[idx].attr.conn.seq_err_retry_cnt =
\r
757 conn_req_get_retry_cnt( p_req );
\r
758 p_cep->av[idx].attr.conn.rnr_retry_cnt =
\r
759 conn_req_get_rnr_retry_cnt( p_req );
\r
761 AL_EXIT( AL_DBG_CM );
\r
766 * + Validates the path information provided in the REQ and stores the
\r
767 * associated CA attributes and port indeces.
\r
768 * + Transitions a connection object from active to passive in the peer case.
\r
769 * + Sets the path information in the connection and sets the CA GUID
\r
770 * in the REQ callback record.
\r
774 IN OUT kcep_t* const p_cep,
\r
775 IN OUT mad_cm_req_t* const p_req )
\r
777 AL_ENTER( AL_DBG_CM );
\r
779 p_cep->state = CEP_STATE_REQ_RCVD;
\r
780 p_cep->was_active = FALSE;
\r
782 p_cep->sid = p_req->sid;
\r
784 /* Store pertinent information in the connection. */
\r
785 p_cep->remote_comm_id = p_req->local_comm_id;
\r
786 p_cep->remote_ca_guid = p_req->local_ca_guid;
\r
788 p_cep->remote_qpn = conn_req_get_lcl_qpn( p_req );
\r
789 p_cep->local_qpn = 0;
\r
791 p_cep->retry_timeout =
\r
792 __calc_mad_timeout( conn_req_get_lcl_resp_timeout( p_req ) );
\r
794 /* Store the retry count. */
\r
795 p_cep->max_cm_retries = conn_req_get_max_cm_retries( p_req );
\r
798 * Copy the paths from the req_rec into the connection for
\r
799 * future use. Note that if the primary path is invalid,
\r
800 * the REP will fail.
\r
802 __format_req_av( p_cep, p_req, 0 );
\r
804 if( p_req->alternate_path.local_lid )
\r
805 __format_req_av( p_cep, p_req, 1 );
\r
807 cl_memclr( &p_cep->av[1], sizeof(kcep_av_t) );
\r
809 p_cep->idx_primary = 0;
\r
811 /* Store the maximum packet lifetime, used to calculate timewait. */
\r
812 p_cep->max_2pkt_life = conn_req_path_get_lcl_ack_timeout( &p_req->primary_path );
\r
813 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life,
\r
814 conn_req_path_get_lcl_ack_timeout( &p_req->alternate_path ) );
\r
817 * Make sure the target ack delay is cleared - the above
\r
818 * "packet life" includes it.
\r
820 p_cep->target_ack_delay = 0;
\r
822 /* Store the requested initiator depth. */
\r
823 p_cep->req_init_depth = conn_req_get_init_depth( p_req );
\r
826 * Store the provided responder resources. These turn into the local
\r
827 * QP's initiator depth.
\r
829 p_cep->init_depth = conn_req_get_resp_res( p_req );
\r
831 p_cep->sq_psn = conn_req_get_starting_psn( p_req );
\r
833 p_cep->tid = p_req->hdr.trans_id;
\r
834 /* copy mad info for cm handoff */
\r
835 /* TODO: Do need to support CM handoff? */
\r
836 //p_cep->mads.req = *p_req;
\r
838 AL_EXIT( AL_DBG_CM );
\r
842 /* Must be called with the CEP lock held. */
\r
845 IN cep_agent_t* const p_port_cep,
\r
846 IN kcep_t* const p_cep,
\r
847 IN ib_mad_element_t* const p_mad )
\r
849 AL_ENTER( AL_DBG_CM );
\r
851 CL_ASSERT( p_port_cep );
\r
852 CL_ASSERT( p_cep );
\r
853 CL_ASSERT( p_mad );
\r
855 /* Repeat the last mad sent for the connection. */
\r
856 switch( p_cep->state )
\r
858 case CEP_STATE_REQ_MRA_SENT: /* resend MRA(REQ) */
\r
859 case CEP_STATE_REP_MRA_SENT: /* resend MRA(REP) */
\r
860 case CEP_STATE_LAP_MRA_SENT: /* resend MRA(LAP) */
\r
861 case CEP_STATE_ESTABLISHED: /* resend RTU */
\r
862 case CEP_STATE_TIMEWAIT: /* resend the DREP */
\r
863 cl_memcpy( p_mad->p_mad_buf, &p_cep->mads, MAD_BLOCK_SIZE );
\r
864 p_mad->send_context1 = NULL;
\r
865 p_mad->send_context2 = NULL;
\r
866 __cep_send_mad( p_port_cep, p_mad );
\r
870 /* Return the MAD to the mad pool */
\r
871 ib_put_mad( p_mad );
\r
875 AL_EXIT( AL_DBG_CM );
\r
879 static ib_api_status_t
\r
881 IN kcep_t* const p_cep,
\r
882 IN ib_mad_element_t* const p_mad )
\r
884 ib_api_status_t status;
\r
885 mad_cm_rej_t *p_rej;
\r
887 AL_ENTER( AL_DBG_CM );
\r
891 ASSERT( p_mad->p_mad_buf );
\r
893 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
895 switch( p_cep->state )
\r
897 case CEP_STATE_REQ_SENT:
\r
899 * Ignore rejects with the status set to IB_REJ_INVALID_SID. We will
\r
900 * continue to retry (up to max_cm_retries) to connect to the remote
\r
901 * side. This is required to support peer-to-peer connections and
\r
902 * clients that try to connect before the server comes up.
\r
904 if( p_rej->reason == IB_REJ_INVALID_SID )
\r
906 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
907 ("Request rejected (invalid SID) - retrying.\n") );
\r
912 case CEP_STATE_REP_SENT:
\r
913 case CEP_STATE_REQ_MRA_RCVD:
\r
914 case CEP_STATE_REP_MRA_RCVD:
\r
915 /* Cancel any outstanding MAD. */
\r
916 if( p_cep->p_send_mad )
\r
918 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
919 p_cep->p_send_mad = NULL;
\r
923 case CEP_STATE_REQ_RCVD:
\r
924 case CEP_STATE_REP_RCVD:
\r
925 case CEP_STATE_REQ_MRA_SENT:
\r
926 case CEP_STATE_REP_MRA_SENT:
\r
927 case CEP_STATE_PRE_REP:
\r
928 case CEP_STATE_PRE_REP_MRA_SENT:
\r
929 if( p_cep->state & CEP_STATE_PREP )
\r
931 CL_ASSERT( p_cep->p_mad );
\r
932 ib_put_mad( p_cep->p_mad );
\r
933 p_cep->p_mad = NULL;
\r
935 /* Abort connection establishment. No transition to timewait. */
\r
936 __remove_cep( p_cep );
\r
937 p_cep->state = CEP_STATE_IDLE;
\r
940 case CEP_STATE_ESTABLISHED:
\r
941 case CEP_STATE_LAP_RCVD:
\r
942 case CEP_STATE_LAP_SENT:
\r
943 case CEP_STATE_LAP_MRA_RCVD:
\r
944 case CEP_STATE_LAP_MRA_SENT:
\r
945 case CEP_STATE_PRE_APR:
\r
946 case CEP_STATE_PRE_APR_MRA_SENT:
\r
947 if( p_cep->state & CEP_STATE_PREP )
\r
949 CL_ASSERT( p_cep->p_mad );
\r
950 ib_put_mad( p_cep->p_mad );
\r
951 p_cep->p_mad = NULL;
\r
953 p_cep->state = CEP_STATE_TIMEWAIT;
\r
954 __insert_timewait( p_cep );
\r
958 /* Ignore the REJ. */
\r
959 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REJ received in invalid state.\n") );
\r
961 ib_put_mad( p_mad );
\r
962 AL_EXIT( AL_DBG_CM );
\r
963 return IB_NO_MATCH;
\r
966 status = __cep_queue_mad( p_cep, p_mad );
\r
968 AL_EXIT( AL_DBG_CM );
\r
973 static ib_api_status_t
\r
975 IN kcep_t* const p_cep )
\r
977 ib_api_status_t status;
\r
978 cep_agent_t *p_port_cep;
\r
979 ib_mad_element_t *p_mad;
\r
980 mad_cm_rej_t *p_rej;
\r
982 status = __cep_get_mad( p_cep, CM_REJ_ATTR_ID, &p_port_cep, &p_mad );
\r
983 if( status != IB_SUCCESS )
\r
986 p_rej = ib_get_mad_buf( p_mad );
\r
988 conn_rej_set_ari( NULL, 0, p_rej );
\r
989 conn_rej_set_pdata( NULL, 0, p_rej );
\r
991 p_rej->local_comm_id = p_cep->remote_comm_id;
\r
992 p_rej->remote_comm_id = p_cep->local_comm_id;
\r
993 p_rej->reason = IB_REJ_STALE_CONN;
\r
995 switch( p_cep->state )
\r
997 case CEP_STATE_REQ_RCVD:
\r
998 case CEP_STATE_REQ_MRA_SENT:
\r
999 case CEP_STATE_PRE_REP:
\r
1000 case CEP_STATE_PRE_REP_MRA_SENT:
\r
1001 conn_rej_set_msg_rejected( 0, p_rej );
\r
1004 case CEP_STATE_REQ_SENT:
\r
1005 case CEP_STATE_REP_RCVD:
\r
1006 case CEP_STATE_REP_MRA_SENT:
\r
1007 conn_rej_set_msg_rejected( 1, p_rej );
\r
1011 conn_rej_set_msg_rejected( 2, p_rej );
\r
1014 conn_rej_clr_rsvd_fields( p_rej );
\r
1016 return __process_rej( p_cep, p_mad );
\r
1022 IN cep_agent_t* const p_port_cep,
\r
1023 IN ib_mad_element_t* const p_mad )
\r
1025 ib_api_status_t status = IB_SUCCESS;
\r
1026 mad_cm_req_t *p_req;
\r
1027 kcep_t *p_cep, *p_new_cep, *p_stale_cep = NULL;
\r
1028 KLOCK_QUEUE_HANDLE hdl;
\r
1029 ib_rej_status_t reason;
\r
1031 AL_ENTER( AL_DBG_CM );
\r
1033 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1035 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
1037 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1038 ("REQ: comm_id (x%x) qpn (x%x) received\n",
\r
1039 p_req->local_comm_id, conn_req_get_lcl_qpn( p_req )) );
\r
1041 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1043 if( conn_req_get_qp_type( p_req ) > IB_QPT_UNRELIABLE_CONN )
\r
1045 /* Reserved value. Reject. */
\r
1046 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid transport type received.\n") );
\r
1047 reason = IB_REJ_INVALID_XPORT;
\r
1051 /* Match against pending connections using remote comm ID and CA GUID. */
\r
1052 p_cep = __lookup_by_id( p_req->local_comm_id, p_req->local_ca_guid );
\r
1055 /* Already received the REQ. */
\r
1056 switch( p_cep->state )
\r
1058 case CEP_STATE_REQ_MRA_SENT:
\r
1059 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1062 case CEP_STATE_TIMEWAIT:
\r
1063 case CEP_STATE_DESTROY:
\r
1064 /* Send a reject. */
\r
1065 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1066 ("REQ received for connection in TIME_WAIT state.\n") );
\r
1067 __reject_req( p_port_cep, p_mad, IB_REJ_STALE_CONN );
\r
1072 * Let regular retries repeat the MAD. If our last message was
\r
1073 * dropped, resending only adds to the congestion. If it wasn't
\r
1074 * dropped, then the remote CM will eventually process it, and
\r
1075 * we'd just be adding traffic.
\r
1077 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Duplicate REQ received.\n") );
\r
1078 ib_put_mad( p_mad );
\r
1080 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1081 AL_EXIT( AL_DBG_CM );
\r
1086 * Allocate a new CEP for the new request. This will
\r
1087 * prevent multiple identical REQs from queueing up for processing.
\r
1089 p_new_cep = __create_cep();
\r
1092 /* Reject the request for insufficient resources. */
\r
1093 reason = IB_REJ_INSUF_RESOURCES;
\r
1094 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
1095 ("al_create_cep failed\nREJ sent for insufficient resources.\n") );
\r
1099 __save_wire_req( p_new_cep, p_req );
\r
1102 * Match against listens using SID and compare data, also provide the receiving
\r
1103 * MAD service's port GUID so we can properly filter.
\r
1105 p_cep = __lookup_listen( p_req->sid, p_port_cep->port_guid, p_req->pdata );
\r
1108 __bind_cep( p_new_cep, p_cep->p_cid->h_al, p_cep->pfn_cb, NULL );
\r
1110 /* Add the new CEP to the map so that repeated REQs match up. */
\r
1111 p_stale_cep = __insert_cep( p_new_cep );
\r
1112 if( p_stale_cep != p_new_cep )
\r
1114 /* Duplicate - must be a stale connection. */
\r
1115 reason = IB_REJ_STALE_CONN;
\r
1116 /* Fail the local stale CEP. */
\r
1117 status = __process_stale( p_stale_cep );
\r
1122 * Queue the mad - the return value indicates whether we should
\r
1123 * invoke the callback.
\r
1125 status = __cep_queue_mad( p_cep, p_mad );
\r
1130 p_mad->send_context1 = p_new_cep;
\r
1134 reason = IB_REJ_INSUF_RESOURCES;
\r
1140 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("No listens active!\n") );
\r
1142 /* Match against peer-to-peer requests using SID and compare data. */
\r
1143 //p_cep = __lookup_peer();
\r
1146 // p_mad->send_context2 = NULL;
\r
1147 // p_list_item = cl_qlist_find_from_head( &gp_cep_mgr->pending_list,
\r
1148 // __match_peer, p_req );
\r
1149 // if( p_list_item != cl_qlist_end( &gp_cep_mgr->pending_list ) )
\r
1151 // KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1152 // p_conn = PARENT_STRUCT( p_list_item, kcep_t, map_item );
\r
1153 // __peer_req( p_port_cep, p_conn, p_async_mad->p_mad );
\r
1154 // cl_free( p_async_mad );
\r
1155 // AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
1156 // ("REQ matched a peer-to-peer request.\n") );
\r
1159 // reason = IB_REJ_INVALID_SID;
\r
1164 /* No match found. Reject. */
\r
1165 reason = IB_REJ_INVALID_SID;
\r
1166 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REQ received but no match found.\n") );
\r
1171 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1173 /* Process any queued MADs for the CEP. */
\r
1174 if( status == IB_SUCCESS )
\r
1175 __process_cep( p_cep );
\r
1177 AL_EXIT( AL_DBG_CM );
\r
1181 __unbind_cep( p_new_cep );
\r
1185 * Move the CEP in the idle state so that we don't send a reject
\r
1186 * for it when cleaning up. Also clear the RQPN and RCID so that
\r
1187 * we don't try to remove it from our maps (since it isn't inserted).
\r
1189 p_new_cep->state = CEP_STATE_IDLE;
\r
1190 p_new_cep->remote_comm_id = 0;
\r
1191 p_new_cep->remote_qpn = 0;
\r
1192 __cleanup_cep( p_new_cep );
\r
1195 __reject_req( p_port_cep, p_mad, reason );
\r
1197 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1199 if( reason == IB_REJ_STALE_CONN && status == IB_SUCCESS )
\r
1200 __process_cep( p_stale_cep );
\r
1202 AL_EXIT( AL_DBG_CM );
\r
1208 IN OUT kcep_t* const p_cep,
\r
1209 IN const mad_cm_rep_t* const p_rep )
\r
1211 AL_ENTER( AL_DBG_CM );
\r
1213 /* The send should have been cancelled during MRA processing. */
\r
1214 p_cep->state = CEP_STATE_REP_RCVD;
\r
1216 /* Store pertinent information in the connection. */
\r
1217 p_cep->remote_comm_id = p_rep->local_comm_id;
\r
1218 p_cep->remote_ca_guid = p_rep->local_ca_guid;
\r
1220 p_cep->remote_qpn = conn_rep_get_lcl_qpn( p_rep );
\r
1222 /* Store the remote endpoint's target ACK delay. */
\r
1223 p_cep->target_ack_delay = conn_rep_get_target_ack_delay( p_rep );
\r
1225 /* Update the local ACK delay stored in the AV's. */
\r
1226 p_cep->av[0].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1227 p_cep->av[0].attr.conn.local_ack_timeout, p_cep->target_ack_delay );
\r
1228 p_cep->av[0].attr.conn.rnr_retry_cnt = conn_rep_get_rnr_retry_cnt( p_rep );
\r
1230 if( p_cep->av[1].port_guid )
\r
1232 p_cep->av[1].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1233 p_cep->av[1].attr.conn.local_ack_timeout,
\r
1234 p_cep->target_ack_delay );
\r
1235 p_cep->av[1].attr.conn.rnr_retry_cnt =
\r
1236 p_cep->av[0].attr.conn.rnr_retry_cnt;
\r
1239 p_cep->init_depth = p_rep->resp_resources;
\r
1240 p_cep->resp_res = p_rep->initiator_depth;
\r
1242 p_cep->sq_psn = conn_rep_get_starting_psn( p_rep );
\r
1244 AL_EXIT( AL_DBG_CM );
\r
1250 IN ib_mad_element_t* const p_mad )
\r
1252 ib_api_status_t status;
\r
1253 mad_cm_mra_t *p_mra;
\r
1255 KLOCK_QUEUE_HANDLE hdl;
\r
1257 AL_ENTER( AL_DBG_CM );
\r
1259 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1261 p_mra = (mad_cm_mra_t*)p_mad->p_mad_buf;
\r
1263 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1264 p_cep = __lookup_cep( NULL, p_mra->remote_comm_id );
\r
1267 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1268 ("MRA received that could not be matched.\n") );
\r
1272 if( p_cep->remote_comm_id )
\r
1274 if( p_cep->remote_comm_id != p_mra->local_comm_id )
\r
1276 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1277 ("MRA received that could not be matched.\n") );
\r
1283 * Note that we don't update the CEP's remote comm ID - it messes up REP
\r
1284 * processing since a non-zero RCID implies the connection is in the RCID
\r
1285 * map. Adding it here requires checking there and conditionally adding
\r
1286 * it. Ignoring it is a valid thing to do.
\r
1288 if( !(p_cep->state & CEP_STATE_SENT) ||
\r
1289 (1 << conn_mra_get_msg_mraed( p_mra ) !=
\r
1290 (p_cep->state & CEP_MSG_MASK)) )
\r
1292 /* Invalid state. */
\r
1293 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("MRA received in invalid state.\n") );
\r
1297 /* Delay the current send. */
\r
1298 CL_ASSERT( p_cep->p_send_mad );
\r
1299 ib_delay_mad( p_cep->h_mad_svc, p_cep->p_send_mad,
\r
1300 __calc_mad_timeout( conn_mra_get_svc_timeout( p_mra ) ) +
\r
1301 __calc_mad_timeout( p_cep->max_2pkt_life - 1 ) );
\r
1303 /* We only invoke a single callback for MRA. */
\r
1304 if( p_cep->state & CEP_STATE_MRA )
\r
1306 /* Invalid state. */
\r
1307 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Already received MRA.\n") );
\r
1311 p_cep->state |= CEP_STATE_MRA;
\r
1313 status = __cep_queue_mad( p_cep, p_mad );
\r
1315 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1317 if( status == IB_SUCCESS )
\r
1318 __process_cep( p_cep );
\r
1320 AL_EXIT( AL_DBG_CM );
\r
1324 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1325 ib_put_mad( p_mad );
\r
1326 AL_EXIT( AL_DBG_CM );
\r
1332 IN ib_mad_element_t* const p_mad )
\r
1334 ib_api_status_t status;
\r
1335 mad_cm_rej_t *p_rej;
\r
1336 kcep_t *p_cep = NULL;
\r
1337 KLOCK_QUEUE_HANDLE hdl;
\r
1340 AL_ENTER( AL_DBG_CM );
\r
1342 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1344 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
1346 /* Either one of the communication IDs must be set. */
\r
1347 if( !p_rej->remote_comm_id && !p_rej->local_comm_id )
\r
1350 /* Check the pending list by the remote CA GUID and connection ID. */
\r
1351 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1352 if( p_rej->remote_comm_id )
\r
1354 p_cep = __lookup_cep( NULL, p_rej->remote_comm_id );
\r
1356 else if( p_rej->reason == IB_REJ_TIMEOUT &&
\r
1357 conn_rej_get_ari_len( p_rej ) == sizeof(net64_t) )
\r
1359 cl_memcpy( &ca_guid, p_rej->ari, sizeof(net64_t) );
\r
1360 p_cep = __lookup_by_id( p_rej->local_comm_id, ca_guid );
\r
1368 if( p_cep->remote_comm_id &&
\r
1369 p_cep->remote_comm_id != p_rej->local_comm_id )
\r
1372 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1374 ib_put_mad( p_mad );
\r
1375 AL_EXIT( AL_DBG_CM );
\r
1379 status = __process_rej( p_cep, p_mad );
\r
1381 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1383 if( status == IB_SUCCESS )
\r
1384 __process_cep( p_cep );
\r
1386 AL_EXIT( AL_DBG_CM );
\r
1392 IN cep_agent_t* const p_port_cep,
\r
1393 IN ib_mad_element_t* const p_mad )
\r
1395 ib_api_status_t status;
\r
1396 mad_cm_rep_t *p_rep;
\r
1398 KLOCK_QUEUE_HANDLE hdl;
\r
1399 cep_state_t old_state;
\r
1401 AL_ENTER( AL_DBG_CM );
\r
1403 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1405 p_rep = (mad_cm_rep_t*)p_mad->p_mad_buf;
\r
1407 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1408 ("REP: comm_id (x%x) received\n", p_rep->local_comm_id ) );
\r
1410 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1411 p_cep = __lookup_cep( NULL, p_rep->remote_comm_id );
\r
1414 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1415 ib_put_mad( p_mad );
\r
1416 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1417 ("REP received that could not be matched.\n") );
\r
1421 switch( p_cep->state )
\r
1423 case CEP_STATE_REQ_MRA_RCVD:
\r
1424 case CEP_STATE_REQ_SENT:
\r
1425 old_state = p_cep->state;
\r
1426 /* Save pertinent information and change state. */
\r
1427 __save_wire_rep( p_cep, p_rep );
\r
1429 if( __insert_cep( p_cep ) != p_cep )
\r
1431 /* Roll back the state change. */
\r
1432 __reject_mad( p_port_cep, p_cep, p_mad, IB_REJ_STALE_CONN );
\r
1433 p_cep->state = old_state;
\r
1434 status = __process_stale( p_cep );
\r
1439 * Cancel any outstanding send. Note that we do this only after
\r
1440 * inserting the CEP - if we failed, then the send will timeout
\r
1441 * and we'll finish our way through the state machine.
\r
1443 if( p_cep->p_send_mad )
\r
1445 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1446 p_cep->p_send_mad = NULL;
\r
1449 status = __cep_queue_mad( p_cep, p_mad );
\r
1452 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1454 if( status == IB_SUCCESS )
\r
1455 __process_cep( p_cep );
\r
1457 AL_EXIT( AL_DBG_CM );
\r
1460 case CEP_STATE_ESTABLISHED:
\r
1461 case CEP_STATE_LAP_RCVD:
\r
1462 case CEP_STATE_LAP_SENT:
\r
1463 case CEP_STATE_LAP_MRA_RCVD:
\r
1464 case CEP_STATE_LAP_MRA_SENT:
\r
1465 case CEP_STATE_REP_MRA_SENT:
\r
1466 /* Repeate the MRA or RTU. */
\r
1467 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1471 ib_put_mad( p_mad );
\r
1472 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REP received in invalid state.\n") );
\r
1476 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1478 AL_EXIT( AL_DBG_CM );
\r
1484 IN ib_mad_element_t* const p_mad )
\r
1486 ib_api_status_t status;
\r
1487 mad_cm_rtu_t *p_rtu;
\r
1489 KLOCK_QUEUE_HANDLE hdl;
\r
1491 AL_ENTER( AL_DBG_CM );
\r
1493 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1495 p_rtu = (mad_cm_rtu_t*)p_mad->p_mad_buf;
\r
1497 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1498 ("RTU: comm_id (x%x) received\n", p_rtu->local_comm_id) );
\r
1500 /* Find the connection by local connection ID. */
\r
1501 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1502 p_cep = __lookup_cep( NULL, p_rtu->remote_comm_id );
\r
1503 if( !p_cep || p_cep->remote_comm_id != p_rtu->local_comm_id )
\r
1505 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received that could not be matched.\n") );
\r
1509 switch( p_cep->state )
\r
1511 case CEP_STATE_REP_SENT:
\r
1512 case CEP_STATE_REP_MRA_RCVD:
\r
1513 /* Cancel any outstanding send. */
\r
1514 if( p_cep->p_send_mad )
\r
1516 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1517 p_cep->p_send_mad = NULL;
\r
1520 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1522 status = __cep_queue_mad( p_cep, p_mad );
\r
1524 /* Update timewait time. */
\r
1525 __calc_timewait( p_cep );
\r
1527 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1529 if( status == IB_SUCCESS )
\r
1530 __process_cep( p_cep );
\r
1532 AL_EXIT( AL_DBG_CM );
\r
1536 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received in invalid state.\n") );
\r
1541 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1542 ib_put_mad( p_mad );
\r
1543 AL_EXIT( AL_DBG_CM );
\r
1549 IN cep_agent_t* const p_port_cep,
\r
1550 IN ib_mad_element_t* const p_mad )
\r
1552 ib_api_status_t status;
\r
1553 mad_cm_dreq_t *p_dreq;
\r
1555 KLOCK_QUEUE_HANDLE hdl;
\r
1557 AL_ENTER( AL_DBG_CM );
\r
1559 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1561 p_dreq = (mad_cm_dreq_t*)p_mad->p_mad_buf;
\r
1563 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1564 ("DREQ: comm_id (x%x) qpn (x%x) received\n",
\r
1565 p_dreq->local_comm_id, conn_dreq_get_remote_qpn( p_dreq )) );
\r
1567 /* Find the connection by connection IDs. */
\r
1568 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1569 p_cep = __lookup_cep( NULL, p_dreq->remote_comm_id );
\r
1571 p_cep->remote_comm_id != p_dreq->local_comm_id ||
\r
1572 p_cep->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )
\r
1574 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received that could not be matched.\n") );
\r
1575 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1576 ib_put_mad( p_mad );
\r
1577 AL_EXIT( AL_DBG_CM );
\r
1581 switch( p_cep->state )
\r
1583 case CEP_STATE_REP_SENT:
\r
1584 case CEP_STATE_REP_MRA_RCVD:
\r
1585 case CEP_STATE_DREQ_SENT:
\r
1586 /* Cancel the outstanding MAD. */
\r
1587 if( p_cep->p_send_mad )
\r
1589 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1590 p_cep->p_send_mad = NULL;
\r
1593 /* Fall through and process as DREQ received case. */
\r
1594 case CEP_STATE_ESTABLISHED:
\r
1595 case CEP_STATE_LAP_RCVD:
\r
1596 case CEP_STATE_LAP_SENT:
\r
1597 case CEP_STATE_LAP_MRA_RCVD:
\r
1598 case CEP_STATE_LAP_MRA_SENT:
\r
1599 p_cep->state = CEP_STATE_DREQ_RCVD;
\r
1601 status = __cep_queue_mad( p_cep, p_mad );
\r
1603 /* Store the TID for use in the reply DREP. */
\r
1604 p_cep->tid = p_dreq->hdr.trans_id;
\r
1606 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1608 if( status == IB_SUCCESS )
\r
1609 __process_cep( p_cep );
\r
1610 AL_EXIT( AL_DBG_CM );
\r
1613 case CEP_STATE_TIMEWAIT:
\r
1614 case CEP_STATE_DESTROY:
\r
1615 /* Repeat the DREP. */
\r
1616 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1620 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received in invalid state.\n") );
\r
1621 case CEP_STATE_DREQ_RCVD:
\r
1622 ib_put_mad( p_mad );
\r
1626 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1627 AL_EXIT( AL_DBG_CM );
\r
1633 IN ib_mad_element_t* const p_mad )
\r
1635 ib_api_status_t status;
\r
1636 mad_cm_drep_t *p_drep;
\r
1638 KLOCK_QUEUE_HANDLE hdl;
\r
1640 AL_ENTER( AL_DBG_CM );
\r
1642 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1644 p_drep = (mad_cm_drep_t*)p_mad->p_mad_buf;
\r
1646 /* Find the connection by local connection ID. */
\r
1647 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1648 p_cep = __lookup_cep( NULL, p_drep->remote_comm_id );
\r
1649 if( !p_cep || p_cep->remote_comm_id != p_drep->local_comm_id )
\r
1651 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received that could not be matched.\n") );
\r
1652 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1653 ib_put_mad( p_mad );
\r
1654 AL_EXIT( AL_DBG_CM );
\r
1658 if( p_cep->state != CEP_STATE_DREQ_SENT &&
\r
1659 p_cep->state != CEP_STATE_DREQ_DESTROY )
\r
1661 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received in invalid state.\n") );
\r
1663 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1664 ib_put_mad( p_mad );
\r
1665 AL_EXIT( AL_DBG_CM );
\r
1669 /* Cancel the DREQ. */
\r
1670 if( p_cep->p_send_mad )
\r
1672 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1673 p_cep->p_send_mad = NULL;
\r
1676 if( p_cep->state == CEP_STATE_DREQ_SENT )
\r
1678 p_cep->state = CEP_STATE_TIMEWAIT;
\r
1680 status = __cep_queue_mad( p_cep, p_mad );
\r
1684 /* State is DREQ_DESTROY - move to DESTROY to allow cleanup. */
\r
1685 CL_ASSERT( p_cep->state == CEP_STATE_DREQ_DESTROY );
\r
1686 p_cep->state = CEP_STATE_DESTROY;
\r
1688 ib_put_mad( p_mad );
\r
1689 status = IB_INVALID_STATE;
\r
1692 __insert_timewait( p_cep );
\r
1694 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1696 if( status == IB_SUCCESS )
\r
1697 __process_cep( p_cep );
\r
1699 AL_EXIT( AL_DBG_CM );
\r
1705 IN kcep_t* const p_cep,
\r
1706 IN const lap_path_info_t* const p_path )
\r
1708 cep_agent_t *p_port_cep;
\r
1710 AL_ENTER( AL_DBG_CM );
\r
1712 CL_ASSERT( p_cep );
\r
1713 CL_ASSERT( p_path );
\r
1715 cl_memclr( &p_cep->alt_av, sizeof(kcep_av_t) );
\r
1717 p_port_cep = __find_port_cep( &p_path->remote_gid, p_path->remote_lid,
\r
1718 p_cep->pkey, &p_cep->alt_av.pkey_index );
\r
1721 AL_EXIT( AL_DBG_CM );
\r
1725 if( p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
1727 AL_EXIT( AL_DBG_CM );
\r
1731 p_cep->alt_av.port_guid = p_port_cep->port_guid;
\r
1732 p_cep->alt_av.attr.port_num = p_port_cep->port_num;
\r
1734 p_cep->alt_av.attr.sl = conn_lap_path_get_svc_lvl( p_path );
\r
1735 p_cep->alt_av.attr.dlid = p_path->local_lid;
\r
1737 if( !conn_lap_path_get_subn_lcl( p_path ) )
\r
1739 p_cep->alt_av.attr.grh_valid = TRUE;
\r
1740 p_cep->alt_av.attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
1741 1, conn_lap_path_get_tclass( p_path ),
\r
1742 conn_lap_path_get_flow_lbl( p_path ) );
\r
1743 p_cep->alt_av.attr.grh.hop_limit = p_path->hop_limit;
\r
1744 p_cep->alt_av.attr.grh.dest_gid = p_path->local_gid;
\r
1745 p_cep->alt_av.attr.grh.src_gid = p_path->remote_gid;
\r
1749 p_cep->alt_av.attr.grh_valid = FALSE;
\r
1751 p_cep->alt_av.attr.static_rate = conn_lap_path_get_pkt_rate( p_path );
\r
1752 p_cep->alt_av.attr.path_bits =
\r
1753 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
1756 * Note that while we never use the connected AV attributes internally,
\r
1757 * we store them so we can pass them back to users. For the LAP, we
\r
1758 * first copy the settings from the current primary - MTU and retry
\r
1759 * counts are only specified in the REQ.
\r
1761 p_cep->alt_av.attr.conn = p_cep->av[p_cep->idx_primary].attr.conn;
\r
1762 p_cep->alt_av.attr.conn.local_ack_timeout =
\r
1763 conn_lap_path_get_lcl_ack_timeout( p_path );
\r
1765 AL_EXIT( AL_DBG_CM );
\r
1772 IN cep_agent_t* const p_port_cep,
\r
1773 IN ib_mad_element_t* const p_mad )
\r
1775 ib_api_status_t status;
\r
1776 mad_cm_lap_t *p_lap;
\r
1778 KLOCK_QUEUE_HANDLE hdl;
\r
1780 AL_ENTER( AL_DBG_CM );
\r
1782 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1784 p_lap = (mad_cm_lap_t*)p_mad->p_mad_buf;
\r
1786 /* Find the connection by local connection ID. */
\r
1787 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1788 p_cep = __lookup_cep( NULL, p_lap->remote_comm_id );
\r
1789 if( !p_cep || p_cep->remote_comm_id != p_lap->local_comm_id )
\r
1791 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1792 ib_put_mad( p_mad );
\r
1793 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received that could not be matched.\n") );
\r
1797 switch( p_cep->state )
\r
1799 case CEP_STATE_REP_SENT:
\r
1800 case CEP_STATE_REP_MRA_RCVD:
\r
1802 * These two cases handle the RTU being dropped. Receipt of
\r
1803 * a LAP indicates that the connection is established.
\r
1805 case CEP_STATE_ESTABLISHED:
\r
1807 * We don't check for other "established" states related to
\r
1808 * alternate path management (CEP_STATE_LAP_RCVD, etc)
\r
1811 /* We only support receiving LAP if we took the passive role. */
\r
1812 if( p_cep->was_active )
\r
1814 ib_put_mad( p_mad );
\r
1818 /* Store the transaction ID for use during the LAP exchange. */
\r
1819 p_cep->tid = p_lap->hdr.trans_id;
\r
1822 * Copy the path record into the connection for use when
\r
1823 * sending the APR and loading the path.
\r
1825 if( !__format_lap_av( p_cep, &p_lap->alternate_path ) )
\r
1827 /* Trap an invalid path. */
\r
1828 ib_put_mad( p_mad );
\r
1832 p_cep->state = CEP_STATE_LAP_RCVD;
\r
1834 status = __cep_queue_mad( p_cep, p_mad );
\r
1836 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1838 if( status == IB_SUCCESS )
\r
1839 __process_cep( p_cep );
\r
1841 AL_EXIT( AL_DBG_CM );
\r
1844 case CEP_STATE_LAP_MRA_SENT:
\r
1845 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1849 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received in invalid state.\n") );
\r
1850 ib_put_mad( p_mad );
\r
1854 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1855 AL_EXIT( AL_DBG_CM );
\r
1861 IN ib_mad_element_t* const p_mad )
\r
1863 ib_api_status_t status;
\r
1864 mad_cm_apr_t *p_apr;
\r
1866 KLOCK_QUEUE_HANDLE hdl;
\r
1868 AL_ENTER( AL_DBG_CM );
\r
1870 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1872 p_apr = (mad_cm_apr_t*)p_mad->p_mad_buf;
\r
1874 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1875 p_cep = __lookup_cep( NULL, p_apr->remote_comm_id );
\r
1876 if( !p_cep || p_cep->remote_comm_id != p_apr->local_comm_id )
\r
1878 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received that could not be matched.\n") );
\r
1882 switch( p_cep->state )
\r
1884 case CEP_STATE_LAP_SENT:
\r
1885 case CEP_STATE_LAP_MRA_RCVD:
\r
1886 /* Cancel sending the LAP. */
\r
1887 if( p_cep->p_send_mad )
\r
1889 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1890 p_cep->p_send_mad = NULL;
\r
1893 /* Copy the temporary alternate AV. */
\r
1894 p_cep->av[(p_cep->idx_primary + 1) & 0x1] = p_cep->alt_av;
\r
1896 /* Update the maximum packet lifetime. */
\r
1897 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life, p_cep->alt_2pkt_life );
\r
1899 /* Update the timewait time. */
\r
1900 __calc_timewait( p_cep );
\r
1902 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1904 status = __cep_queue_mad( p_cep, p_mad );
\r
1906 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1908 if( status == IB_SUCCESS )
\r
1909 __process_cep( p_cep );
\r
1911 AL_EXIT( AL_DBG_CM );
\r
1915 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received in invalid state.\n") );
\r
1920 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1921 ib_put_mad( p_mad );
\r
1922 AL_EXIT( AL_DBG_CM );
\r
1927 __cep_mad_recv_cb(
\r
1928 IN ib_mad_svc_handle_t h_mad_svc,
\r
1930 IN ib_mad_element_t *p_mad )
\r
1932 cep_agent_t *p_port_cep;
\r
1935 AL_ENTER( AL_DBG_CM );
\r
1937 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1939 UNUSED_PARAM( h_mad_svc );
\r
1940 p_port_cep = (cep_agent_t*)context;
\r
1942 CL_ASSERT( p_mad->p_next == NULL );
\r
1944 p_hdr = (ib_mad_t*)p_mad->p_mad_buf;
\r
1947 * TODO: Add filtering in all the handlers for unsupported class version.
\r
1948 * See 12.6.7.2 Rejection Reason, code 31.
\r
1951 switch( p_hdr->attr_id )
\r
1953 case CM_REQ_ATTR_ID:
\r
1954 __req_handler( p_port_cep, p_mad );
\r
1957 case CM_MRA_ATTR_ID:
\r
1958 __mra_handler( p_mad );
\r
1961 case CM_REJ_ATTR_ID:
\r
1962 __rej_handler( p_mad );
\r
1965 case CM_REP_ATTR_ID:
\r
1966 __rep_handler( p_port_cep, p_mad );
\r
1969 case CM_RTU_ATTR_ID:
\r
1970 __rtu_handler( p_mad );
\r
1973 case CM_DREQ_ATTR_ID:
\r
1974 __dreq_handler( p_port_cep, p_mad );
\r
1977 case CM_DREP_ATTR_ID:
\r
1978 __drep_handler( p_mad );
\r
1981 case CM_LAP_ATTR_ID:
\r
1982 __lap_handler( p_port_cep, p_mad );
\r
1985 case CM_APR_ATTR_ID:
\r
1986 __apr_handler( p_mad );
\r
1989 case CM_SIDR_REQ_ATTR_ID:
\r
1990 // p_async_mad->item.pfn_callback = __process_cm_sidr_req;
\r
1993 case CM_SIDR_REP_ATTR_ID:
\r
1994 // p_async_mad->item.pfn_callback = __process_cm_sidr_rep;
\r
1998 ib_put_mad( p_mad );
\r
1999 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2000 ("Invalid CM MAD attribute ID.\n") );
\r
2004 AL_EXIT( AL_DBG_CM );
\r
2008 static inline cep_agent_t*
\r
2010 IN kcep_t* const p_cep )
\r
2012 cl_map_item_t *p_item;
\r
2014 CL_ASSERT( p_cep );
\r
2016 /* Look up the primary CEP port agent */
\r
2017 p_item = cl_qmap_get( &gp_cep_mgr->port_map,
\r
2018 p_cep->av[p_cep->idx_primary].port_guid );
\r
2019 if( p_item == cl_qmap_end( &gp_cep_mgr->port_map ) )
\r
2022 return PARENT_STRUCT( p_item, cep_agent_t, item );
\r
2026 static inline void
\r
2028 OUT ib_mad_element_t* const p_mad,
\r
2029 IN kcep_av_t* const p_av )
\r
2031 /* Set the addressing information in the MAD. */
\r
2032 p_mad->grh_valid = p_av->attr.grh_valid;
\r
2033 if( p_av->attr.grh_valid )
\r
2034 cl_memcpy( p_mad->p_grh, &p_av->attr.grh, sizeof(ib_grh_t) );
\r
2036 p_mad->remote_sl = p_av->attr.sl;
\r
2037 p_mad->remote_lid = p_av->attr.dlid;
\r
2038 p_mad->path_bits = p_av->attr.path_bits;
\r
2039 p_mad->pkey_index = p_av->pkey_index;
\r
2040 p_mad->remote_qp = IB_QP1;
\r
2041 p_mad->send_opt = IB_SEND_OPT_SIGNALED;
\r
2042 p_mad->remote_qkey = IB_QP1_WELL_KNOWN_Q_KEY;
\r
2043 /* Let the MAD service manage the AV for us. */
\r
2044 p_mad->h_av = NULL;
\r
2048 static ib_api_status_t
\r
2050 IN cep_agent_t* const p_port_cep,
\r
2051 IN ib_mad_element_t* const p_mad )
\r
2053 ib_api_status_t status;
\r
2055 AL_ENTER( AL_DBG_CM );
\r
2057 CL_ASSERT( p_port_cep );
\r
2058 CL_ASSERT( p_mad );
\r
2060 /* Use the mad's attributes already present */
\r
2061 p_mad->resp_expected = FALSE;
\r
2062 p_mad->retry_cnt = 0;
\r
2063 p_mad->timeout_ms = 0;
\r
2065 /* Clear the contexts since the send isn't associated with a CEP. */
\r
2066 p_mad->context1 = NULL;
\r
2067 p_mad->context2 = NULL;
\r
2069 status = ib_send_mad( p_port_cep->h_mad_svc, p_mad, NULL );
\r
2070 if( status != IB_SUCCESS )
\r
2072 ib_put_mad( p_mad );
\r
2073 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2074 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2077 AL_EXIT( AL_DBG_CM );
\r
2082 static ib_api_status_t
\r
2084 IN cep_agent_t* const p_port_cep,
\r
2085 IN kcep_t* const p_cep,
\r
2086 IN ib_mad_element_t* const p_mad )
\r
2088 ib_api_status_t status;
\r
2090 AL_ENTER( AL_DBG_CM );
\r
2092 CL_ASSERT( p_cep );
\r
2093 CL_ASSERT( p_mad );
\r
2094 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
2095 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID ||
\r
2096 p_mad->p_mad_buf->attr_id == CM_LAP_ATTR_ID ||
\r
2097 p_mad->p_mad_buf->attr_id == CM_DREQ_ATTR_ID );
\r
2100 * REQ, REP, and DREQ are retried until either a response is
\r
2101 * received or the operation times out.
\r
2103 p_mad->resp_expected = TRUE;
\r
2104 p_mad->retry_cnt = p_cep->max_cm_retries;
\r
2105 p_mad->timeout_ms = p_cep->retry_timeout;
\r
2107 CL_ASSERT( !p_cep->p_send_mad );
\r
2109 /* Store the mad & mad service handle in the CEP for cancelling. */
\r
2110 p_cep->h_mad_svc = p_port_cep->h_mad_svc;
\r
2111 p_cep->p_send_mad = p_mad;
\r
2113 /* reference the connection for which we are sending the MAD. */
\r
2114 cl_atomic_inc( &p_cep->ref_cnt );
\r
2116 /* Set the context. */
\r
2117 p_mad->context1 = p_cep;
\r
2118 p_mad->context2 = NULL;
\r
2120 /* Fire in the hole! */
\r
2121 status = ib_send_mad( p_cep->h_mad_svc, p_mad, NULL );
\r
2122 if( status != IB_SUCCESS )
\r
2125 * Note that we don't need to check for destruction here since
\r
2126 * we're holding the global lock.
\r
2128 cl_atomic_dec( &p_cep->ref_cnt );
\r
2129 p_cep->p_send_mad = NULL;
\r
2130 ib_put_mad( p_mad );
\r
2131 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2132 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2135 AL_EXIT( AL_DBG_CM );
\r
2141 __cep_mad_send_cb(
\r
2142 IN ib_mad_svc_handle_t h_mad_svc,
\r
2144 IN ib_mad_element_t *p_mad )
\r
2146 ib_api_status_t status;
\r
2147 cep_agent_t *p_port_cep;
\r
2149 KLOCK_QUEUE_HANDLE hdl;
\r
2150 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
2151 void *cep_context;
\r
2153 AL_ENTER( AL_DBG_CM );
\r
2155 UNUSED_PARAM( h_mad_svc );
\r
2156 CL_ASSERT( p_mad->p_next == NULL );
\r
2157 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
2159 p_port_cep = (cep_agent_t*)context;
\r
2161 p_cep = (kcep_t* __ptr64)p_mad->context1;
\r
2164 * The connection context is not set when performing immediate responses,
\r
2165 * such as repeating MADS.
\r
2169 ib_put_mad( p_mad );
\r
2170 AL_EXIT( AL_DBG_CM );
\r
2174 p_mad->context1 = NULL;
\r
2176 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
2177 /* Clear the sent MAD pointer so that we don't try cancelling again. */
\r
2178 if( p_cep->p_send_mad == p_mad )
\r
2179 p_cep->p_send_mad = NULL;
\r
2181 switch( p_mad->status )
\r
2183 case IB_WCS_SUCCESS:
\r
2184 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2185 ib_put_mad( p_mad );
\r
2188 case IB_WCS_CANCELED:
\r
2189 if( p_cep->state != CEP_STATE_REQ_SENT &&
\r
2190 p_cep->state != CEP_STATE_REQ_MRA_RCVD &&
\r
2191 p_cep->state != CEP_STATE_REP_SENT &&
\r
2192 p_cep->state != CEP_STATE_REP_MRA_RCVD &&
\r
2193 p_cep->state != CEP_STATE_LAP_SENT &&
\r
2194 p_cep->state != CEP_STATE_LAP_MRA_RCVD &&
\r
2195 p_cep->state != CEP_STATE_DREQ_SENT &&
\r
2196 p_cep->state != CEP_STATE_SREQ_SENT )
\r
2198 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2199 ib_put_mad( p_mad );
\r
2202 /* Treat as a timeout so we don't stall the state machine. */
\r
2203 p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;
\r
2205 /* Fall through. */
\r
2206 case IB_WCS_TIMEOUT_RETRY_ERR:
\r
2208 /* Timeout. Reject the connection. */
\r
2209 switch( p_cep->state )
\r
2211 case CEP_STATE_REQ_SENT:
\r
2212 case CEP_STATE_REQ_MRA_RCVD:
\r
2213 case CEP_STATE_REP_SENT:
\r
2214 case CEP_STATE_REP_MRA_RCVD:
\r
2215 /* Send the REJ. */
\r
2216 __reject_timeout( p_port_cep, p_cep, p_mad );
\r
2217 __remove_cep( p_cep );
\r
2218 p_cep->state = CEP_STATE_IDLE;
\r
2221 case CEP_STATE_DREQ_DESTROY:
\r
2222 p_cep->state = CEP_STATE_DESTROY;
\r
2223 __insert_timewait( p_cep );
\r
2224 /* Fall through. */
\r
2226 case CEP_STATE_DESTROY:
\r
2227 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2228 ib_put_mad( p_mad );
\r
2231 case CEP_STATE_DREQ_SENT:
\r
2233 * Make up a DREP mad so we can respond if we receive
\r
2234 * a DREQ while in timewait.
\r
2236 __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );
\r
2237 __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );
\r
2238 p_cep->state = CEP_STATE_TIMEWAIT;
\r
2239 __insert_timewait( p_cep );
\r
2245 status = __cep_queue_mad( p_cep, p_mad );
\r
2246 CL_ASSERT( status != IB_INVALID_STATE );
\r
2247 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2249 if( status == IB_SUCCESS )
\r
2250 __process_cep( p_cep );
\r
2255 pfn_destroy_cb = p_cep->pfn_destroy_cb;
\r
2256 cep_context = p_cep->context;
\r
2258 if( !cl_atomic_dec( &p_cep->ref_cnt ) && pfn_destroy_cb )
\r
2259 pfn_destroy_cb( cep_context );
\r
2260 AL_EXIT( AL_DBG_CM );
\r
2265 __cep_qp_event_cb(
\r
2266 IN ib_async_event_rec_t *p_event_rec )
\r
2268 UNUSED_PARAM( p_event_rec );
\r
2271 * Most of the QP events are trapped by the real owner of the QP.
\r
2272 * For real events, the CM may not be able to do much anyways!
\r
2277 static ib_api_status_t
\r
2279 IN cep_agent_t* const p_port_cep,
\r
2280 IN const ib_port_attr_t* const p_port_attr )
\r
2282 ib_api_status_t status;
\r
2283 ib_qp_create_t qp_create;
\r
2284 ib_mad_svc_t mad_svc;
\r
2286 AL_ENTER( AL_DBG_CM );
\r
2289 * Create the PD alias. We use the port CM's al_obj_t as the context
\r
2290 * to allow using deref_al_obj as the destroy callback.
\r
2292 status = ib_alloc_pd( p_port_cep->h_ca, IB_PDT_ALIAS, &p_port_cep->obj,
\r
2293 &p_port_cep->h_pd );
\r
2294 if( status != IB_SUCCESS )
\r
2296 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2297 ("ib_alloc_pd failed with status %s\n", ib_get_err_str(status)) );
\r
2300 /* Reference the port object on behalf of the PD. */
\r
2301 ref_al_obj( &p_port_cep->obj );
\r
2303 /* Create the MAD QP. */
\r
2304 cl_memclr( &qp_create, sizeof( ib_qp_create_t ) );
\r
2305 qp_create.qp_type = IB_QPT_QP1_ALIAS;
\r
2306 qp_create.rq_depth = CEP_MAD_RQ_DEPTH;
\r
2307 qp_create.sq_depth = CEP_MAD_SQ_DEPTH;
\r
2308 qp_create.rq_sge = CEP_MAD_RQ_SGE;
\r
2309 qp_create.sq_sge = CEP_MAD_SQ_SGE;
\r
2310 qp_create.sq_signaled = TRUE;
\r
2312 * We use the port CM's al_obj_t as the context to allow using
\r
2313 * deref_al_obj as the destroy callback.
\r
2315 status = ib_get_spl_qp( p_port_cep->h_pd, p_port_attr->port_guid,
\r
2316 &qp_create, &p_port_cep->obj, __cep_qp_event_cb, &p_port_cep->pool_key,
\r
2317 &p_port_cep->h_qp );
\r
2318 if( status != IB_SUCCESS )
\r
2320 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2321 ("ib_get_spl_qp failed with status %s\n", ib_get_err_str(status)) );
\r
2324 /* Reference the port object on behalf of the QP. */
\r
2325 ref_al_obj( &p_port_cep->obj );
\r
2327 /* Create the MAD service. */
\r
2328 cl_memclr( &mad_svc, sizeof(mad_svc) );
\r
2329 mad_svc.mad_svc_context = p_port_cep;
\r
2330 mad_svc.pfn_mad_recv_cb = __cep_mad_recv_cb;
\r
2331 mad_svc.pfn_mad_send_cb = __cep_mad_send_cb;
\r
2332 mad_svc.support_unsol = TRUE;
\r
2333 mad_svc.mgmt_class = IB_MCLASS_COMM_MGMT;
\r
2334 mad_svc.mgmt_version = IB_MCLASS_CM_VER_2;
\r
2335 mad_svc.method_array[IB_MAD_METHOD_SEND] = TRUE;
\r
2337 ib_reg_mad_svc( p_port_cep->h_qp, &mad_svc, &p_port_cep->h_mad_svc );
\r
2338 if( status != IB_SUCCESS )
\r
2340 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2341 ("ib_reg_mad_svc failed with status %s\n", ib_get_err_str(status)) );
\r
2345 AL_EXIT( AL_DBG_CM );
\r
2346 return IB_SUCCESS;
\r
2351 * Performs immediate cleanup of resources.
\r
2354 __destroying_port_cep(
\r
2355 IN al_obj_t *p_obj )
\r
2357 cep_agent_t *p_port_cep;
\r
2358 KLOCK_QUEUE_HANDLE hdl;
\r
2360 AL_ENTER( AL_DBG_CM );
\r
2362 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2364 if( p_port_cep->port_guid )
\r
2366 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2367 cl_qmap_remove_item( &gp_cep_mgr->port_map, &p_port_cep->item );
\r
2368 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2371 if( p_port_cep->h_qp )
\r
2373 ib_destroy_qp( p_port_cep->h_qp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2374 p_port_cep->h_qp = NULL;
\r
2377 if( p_port_cep->h_pd )
\r
2379 ib_dealloc_pd( p_port_cep->h_pd, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2380 p_port_cep->h_pd = NULL;
\r
2383 AL_EXIT( AL_DBG_CM );
\r
2389 * Release all resources allocated by a port CM agent. Finishes any cleanup
\r
2390 * for a port agent.
\r
2394 IN al_obj_t *p_obj )
\r
2396 cep_agent_t *p_port_cep;
\r
2397 ib_port_attr_mod_t port_attr_mod;
\r
2399 AL_ENTER( AL_DBG_CM );
\r
2401 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2403 if( p_port_cep->h_ca )
\r
2405 /* Update local port attributes */
\r
2406 port_attr_mod.cap.cm = FALSE;
\r
2407 ib_modify_ca( p_port_cep->h_ca, p_port_cep->port_num,
\r
2408 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2410 deref_al_obj( &p_port_cep->h_ca->obj );
\r
2413 destroy_al_obj( &p_port_cep->obj );
\r
2414 cl_free( p_port_cep );
\r
2416 AL_EXIT( AL_DBG_CM );
\r
2421 * Create a port agent for a given port.
\r
2423 static ib_api_status_t
\r
2424 __create_port_cep(
\r
2425 IN ib_pnp_port_rec_t *p_pnp_rec )
\r
2427 cep_agent_t *p_port_cep;
\r
2428 ib_api_status_t status;
\r
2429 ib_port_attr_mod_t port_attr_mod;
\r
2430 KLOCK_QUEUE_HANDLE hdl;
\r
2432 AL_ENTER( AL_DBG_CM );
\r
2434 /* calculate size of port_cm struct */
\r
2435 p_port_cep = (cep_agent_t*)cl_zalloc( sizeof(cep_agent_t) );
\r
2438 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2439 ("Failed to cl_zalloc port CM agent.\n") );
\r
2440 return IB_INSUFFICIENT_MEMORY;
\r
2443 construct_al_obj( &p_port_cep->obj, AL_OBJ_TYPE_CM );
\r
2445 status = init_al_obj( &p_port_cep->obj, p_port_cep, TRUE,
\r
2446 __destroying_port_cep, NULL, __free_port_cep );
\r
2447 if( status != IB_SUCCESS )
\r
2449 __free_port_cep( &p_port_cep->obj );
\r
2450 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2451 ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );
\r
2455 /* Attach to the global CM object. */
\r
2456 status = attach_al_obj( &gp_cep_mgr->obj, &p_port_cep->obj );
\r
2457 if( status != IB_SUCCESS )
\r
2459 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2460 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2461 ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );
\r
2465 p_port_cep->port_guid = p_pnp_rec->p_port_attr->port_guid;
\r
2466 p_port_cep->port_num = p_pnp_rec->p_port_attr->port_num;
\r
2467 p_port_cep->base_lid = p_pnp_rec->p_port_attr->lid;
\r
2469 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2471 &gp_cep_mgr->port_map, p_port_cep->port_guid, &p_port_cep->item );
\r
2472 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2474 /* Get a reference to the CA on which we are loading. */
\r
2475 p_port_cep->h_ca = acquire_ca( p_pnp_rec->p_ca_attr->ca_guid );
\r
2476 if( !p_port_cep->h_ca )
\r
2478 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2479 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("acquire_ca failed.\n") );
\r
2480 return IB_INVALID_GUID; }
\r
2482 status = __init_data_svc( p_port_cep, p_pnp_rec->p_port_attr );
\r
2483 if( status != IB_SUCCESS )
\r
2485 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2486 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2487 ("__init_data_svc failed with status %s.\n",
\r
2488 ib_get_err_str(status)) );
\r
2492 /* Update local port attributes */
\r
2493 cl_memclr( &port_attr_mod, sizeof(ib_port_attr_mod_t) );
\r
2494 port_attr_mod.cap.cm = TRUE;
\r
2495 status = ib_modify_ca( p_port_cep->h_ca, p_pnp_rec->p_port_attr->port_num,
\r
2496 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2498 /* Update the PNP context to reference this port. */
\r
2499 p_pnp_rec->pnp_rec.context = p_port_cep;
\r
2501 /* Release the reference taken in init_al_obj. */
\r
2502 deref_al_obj( &p_port_cep->obj );
\r
2504 AL_EXIT( AL_DBG_CM );
\r
2505 return IB_SUCCESS;
\r
2509 /******************************************************************************
\r
2510 * Global CEP manager
\r
2511 ******************************************************************************/
\r
2515 OUT net32_t* const p_cid )
\r
2517 cl_status_t status;
\r
2518 uint32_t size, cid;
\r
2519 cep_cid_t *p_cep_cid;
\r
2521 AL_ENTER( AL_DBG_CM );
\r
2523 size = (uint32_t)cl_vector_get_size( &gp_cep_mgr->cid_vector );
\r
2524 cid = gp_cep_mgr->free_cid;
\r
2525 if( gp_cep_mgr->free_cid == size )
\r
2527 /* Grow the vector pool. */
\r
2529 cl_vector_set_size( &gp_cep_mgr->cid_vector, size + CEP_CID_GROW );
\r
2530 if( status != CL_SUCCESS )
\r
2532 AL_EXIT( AL_DBG_CM );
\r
2536 * Return the the start of the free list since the
\r
2537 * entry initializer incremented it.
\r
2539 gp_cep_mgr->free_cid = size;
\r
2542 /* Get the next free entry. */
\r
2543 p_cep_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, cid );
\r
2545 /* Update the next entry index. */
\r
2546 gp_cep_mgr->free_cid = (uint32_t)(uintn_t)p_cep_cid->p_cep;
\r
2550 AL_EXIT( AL_DBG_CM );
\r
2555 static inline kcep_t*
\r
2557 IN ib_al_handle_t h_al OPTIONAL,
\r
2563 /* Mask off the counter bits so we get the index in our vector. */
\r
2564 idx = cid & CEP_MAX_CID_MASK;
\r
2566 if( idx >= cl_vector_get_size( &gp_cep_mgr->cid_vector ) )
\r
2569 p_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, idx );
\r
2570 if( !p_cid->h_al )
\r
2574 * h_al is NULL when processing MADs, so we need to match on
\r
2575 * the actual local communication ID. If h_al is non-NULL, we
\r
2576 * are doing a lookup from a call to our API, and only need to match
\r
2577 * on the index in the vector (without the modifier).
\r
2581 if( p_cid->h_al != h_al )
\r
2584 else if( p_cid->p_cep->local_comm_id != cid )
\r
2589 return p_cid->p_cep;
\r
2594 * Lookup a CEP by remote comm ID and CA GUID.
\r
2598 IN net32_t remote_comm_id,
\r
2599 IN net64_t remote_ca_guid )
\r
2601 cl_rbmap_item_t *p_item;
\r
2604 AL_ENTER( AL_DBG_CM );
\r
2606 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2607 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2608 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2610 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2612 if( remote_comm_id < p_cep->remote_comm_id )
\r
2613 p_item = cl_rbmap_left( p_item );
\r
2614 else if( remote_comm_id > p_cep->remote_comm_id )
\r
2615 p_item = cl_rbmap_right( p_item );
\r
2616 else if( remote_ca_guid < p_cep->remote_ca_guid )
\r
2617 p_item = cl_rbmap_left( p_item );
\r
2618 else if( remote_ca_guid > p_cep->remote_ca_guid )
\r
2619 p_item = cl_rbmap_right( p_item );
\r
2624 AL_EXIT( AL_DBG_CM );
\r
2630 * Lookup a CEP by Service ID and private data.
\r
2635 IN net64_t port_guid,
\r
2636 IN uint8_t *p_pdata )
\r
2638 cl_rbmap_item_t *p_item;
\r
2642 AL_ENTER( AL_DBG_CM );
\r
2644 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2645 p_item = cl_rbmap_root( &gp_cep_mgr->listen_map );
\r
2646 while( p_item != cl_rbmap_end( &gp_cep_mgr->listen_map ) )
\r
2648 p_cep = PARENT_STRUCT( p_item, kcep_t, listen_item );
\r
2650 if( sid == p_cep->sid )
\r
2652 else if( sid < p_cep->sid )
\r
2653 p_item = cl_rbmap_left( p_item );
\r
2655 p_item = cl_rbmap_right( p_item );
\r
2660 if( p_cep->port_guid != IB_ALL_PORTS )
\r
2662 if( port_guid == p_cep->port_guid )
\r
2664 else if( port_guid < p_cep->port_guid )
\r
2665 p_item = cl_rbmap_left( p_item );
\r
2667 p_item = cl_rbmap_right( p_item );
\r
2673 if( p_cep->p_cmp_buf && p_pdata )
\r
2675 cmp = cl_memcmp( &p_pdata[p_cep->cmp_offset],
\r
2676 p_cep->p_cmp_buf, p_cep->cmp_len );
\r
2680 else if( cmp < 0 )
\r
2681 p_item = cl_rbmap_left( p_item );
\r
2683 p_item = cl_rbmap_right( p_item );
\r
2685 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
2686 ("Svc ID match but compare buffer mismatch.\n") );
\r
2691 /* Everything matched. */
\r
2692 AL_EXIT( AL_DBG_CM );
\r
2696 AL_EXIT( AL_DBG_CM );
\r
2703 IN kcep_t* const p_new_cep )
\r
2706 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2707 boolean_t left = TRUE;
\r
2709 AL_ENTER( AL_DBG_CM );
\r
2711 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2712 p_insert_at = p_item;
\r
2713 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2715 p_insert_at = p_item;
\r
2716 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2718 if( p_new_cep->remote_comm_id < p_cep->remote_comm_id )
\r
2719 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2720 else if( p_new_cep->remote_comm_id > p_cep->remote_comm_id )
\r
2721 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2722 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2723 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2724 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2725 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2728 AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
2729 ("WARNING: Duplicate remote CID and CA GUID.\n") );
\r
2735 &gp_cep_mgr->conn_id_map, p_insert_at, &p_new_cep->rem_id_item, left );
\r
2736 p_cep = p_new_cep;
\r
2739 AL_EXIT( AL_DBG_CM );
\r
2746 IN kcep_t* const p_new_cep )
\r
2749 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2750 boolean_t left = TRUE;
\r
2752 AL_ENTER( AL_DBG_CM );
\r
2754 p_item = cl_rbmap_root( &gp_cep_mgr->conn_qp_map );
\r
2755 p_insert_at = p_item;
\r
2756 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_qp_map ) )
\r
2758 p_insert_at = p_item;
\r
2759 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2761 if( p_new_cep->remote_qpn < p_cep->remote_qpn )
\r
2762 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2763 else if( p_new_cep->remote_qpn > p_cep->remote_qpn )
\r
2764 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2765 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2766 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2767 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2768 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2771 AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
2772 ("WARNING: Duplicate remote QPN and CA GUID.\n") );
\r
2778 &gp_cep_mgr->conn_qp_map, p_insert_at, &p_new_cep->rem_qp_item, left );
\r
2779 p_cep = p_new_cep;
\r
2782 AL_EXIT( AL_DBG_CM );
\r
2787 static inline kcep_t*
\r
2789 IN kcep_t* const p_new_cep )
\r
2793 AL_ENTER( AL_DBG_CM );
\r
2795 p_cep = __insert_by_qpn( p_new_cep );
\r
2796 if( p_cep != p_new_cep )
\r
2799 p_cep = __insert_by_id( p_new_cep );
\r
2800 if( p_cep != p_new_cep )
\r
2802 cl_rbmap_remove_item(
\r
2803 &gp_cep_mgr->conn_qp_map, &p_new_cep->rem_qp_item );
\r
2804 p_cep->remote_qpn = 0;
\r
2808 AL_EXIT( AL_DBG_CM );
\r
2813 static inline void
\r
2815 IN kcep_t* const p_cep )
\r
2817 AL_ENTER( AL_DBG_CM );
\r
2819 if( p_cep->remote_comm_id )
\r
2821 cl_rbmap_remove_item(
\r
2822 &gp_cep_mgr->conn_id_map, &p_cep->rem_id_item );
\r
2823 p_cep->remote_comm_id = 0;
\r
2825 if( p_cep->remote_qpn )
\r
2827 cl_rbmap_remove_item(
\r
2828 &gp_cep_mgr->conn_qp_map, &p_cep->rem_qp_item );
\r
2829 p_cep->remote_qpn = 0;
\r
2832 AL_EXIT( AL_DBG_CM );
\r
2838 IN ib_net16_t lid,
\r
2839 IN ib_net16_t port_lid,
\r
2844 uint16_t path_bits;
\r
2848 lid1 = CL_NTOH16(lid);
\r
2849 lid2 = CL_NTOH16(port_lid);
\r
2856 path_bits = (uint16_t)( (path_bits << 1) | 1 );
\r
2858 lid2 |= path_bits;
\r
2865 if (lid != port_lid)
\r
2873 static inline boolean_t
\r
2875 IN const ib_port_attr_t* const p_port_attr,
\r
2876 IN const ib_gid_t* const p_gid )
\r
2880 for( idx = 0; idx < p_port_attr->num_gids; idx++ )
\r
2883 p_gid, &p_port_attr->p_gid_table[idx], sizeof(ib_gid_t) ) )
\r
2892 static inline boolean_t
\r
2894 IN const ib_port_attr_t* const p_port_attr,
\r
2895 IN const net16_t pkey,
\r
2896 OUT uint16_t* const p_pkey_index )
\r
2900 for( idx = 0; idx < p_port_attr->num_pkeys; idx++ )
\r
2902 if( p_port_attr->p_pkey_table[idx] == pkey )
\r
2904 *p_pkey_index = idx;
\r
2913 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
2914 static cep_agent_t*
\r
2916 IN const ib_gid_t* const p_gid,
\r
2917 IN const net16_t lid,
\r
2918 IN const net16_t pkey,
\r
2919 OUT uint16_t* const p_pkey_index )
\r
2921 cep_agent_t *p_port_cep;
\r
2922 cl_list_item_t *p_item;
\r
2923 const ib_port_attr_t *p_port_attr;
\r
2925 AL_ENTER( AL_DBG_CM );
\r
2927 cl_spinlock_acquire( &gp_cep_mgr->obj.lock );
\r
2928 for( p_item = cl_qlist_head( &gp_cep_mgr->obj.obj_list );
\r
2929 p_item != cl_qlist_end( &gp_cep_mgr->obj.obj_list );
\r
2930 p_item = cl_qlist_next( p_item ) )
\r
2932 p_port_cep = PARENT_STRUCT( p_item, cep_agent_t, obj.pool_item );
\r
2934 CL_ASSERT( p_port_cep->port_num );
\r
2936 ci_ca_lock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2938 p_port_attr = p_port_cep->h_ca->obj.p_ci_ca->p_pnp_attr->p_port_attr;
\r
2939 p_port_attr += (p_port_cep->port_num - 1);
\r
2941 if( __is_lid_valid( lid, p_port_attr->lid, p_port_attr->lmc ) &&
\r
2942 __is_gid_valid( p_port_attr, p_gid ) &&
\r
2943 __get_pkey_index( p_port_attr, pkey, p_pkey_index ) )
\r
2945 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
2948 return p_port_cep;
\r
2951 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2953 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
2954 AL_EXIT( AL_DBG_CM );
\r
2960 * PnP callback for port event notifications.
\r
2962 static ib_api_status_t
\r
2964 IN ib_pnp_rec_t *p_pnp_rec )
\r
2966 ib_api_status_t status = IB_SUCCESS;
\r
2968 AL_ENTER( AL_DBG_CM );
\r
2970 switch( p_pnp_rec->pnp_event )
\r
2972 case IB_PNP_PORT_ADD:
\r
2973 /* Create the port agent. */
\r
2974 CL_ASSERT( !p_pnp_rec->context );
\r
2975 status = __create_port_cep( (ib_pnp_port_rec_t*)p_pnp_rec );
\r
2978 case IB_PNP_PORT_REMOVE:
\r
2979 CL_ASSERT( p_pnp_rec->context );
\r
2981 /* Destroy the port agent. */
\r
2982 ref_al_obj( &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj );
\r
2983 ((cep_agent_t* __ptr64)p_pnp_rec->context)->obj.pfn_destroy(
\r
2984 &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj, NULL );
\r
2988 break; /* Ignore other PNP events. */
\r
2991 AL_EXIT( AL_DBG_CM );
\r
2996 static inline int64_t
\r
2998 IN int64_t current_min,
\r
2999 IN kcep_t* const p_cep )
\r
3002 * The minimum timer interval is 50 milliseconds. This means
\r
3003 * 500000 100ns increments. Since __process_timewait divides the
\r
3004 * result in half (so that the worst cast timewait interval is 150%)
\r
3005 * we compensate for this here. Note that relative time values are
\r
3006 * expressed as negative.
\r
3008 #define MIN_TIMEWAIT_100NS -1000000
\r
3010 /* Still in timewait - try again next time. */
\r
3011 if( !current_min )
\r
3013 return min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS );
\r
3017 return max( current_min,
\r
3018 min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS ) );
\r
3024 * Timer callback to process CEPs in timewait state. Returns time in ms.
\r
3027 __process_timewait()
\r
3029 cl_list_item_t *p_item;
\r
3031 LARGE_INTEGER timeout;
\r
3032 int64_t min_timewait = 0;
\r
3034 AL_ENTER( AL_DBG_CM );
\r
3036 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3038 timeout.QuadPart = 0;
\r
3040 p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3041 while( p_item != cl_qlist_end( &gp_cep_mgr->timewait_list ) )
\r
3043 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3044 p_item = cl_qlist_next( p_item );
\r
3046 CL_ASSERT( p_cep->state == CEP_STATE_DESTROY ||
\r
3047 p_cep->state == CEP_STATE_TIMEWAIT );
\r
3049 CL_ASSERT( !p_cep->p_mad );
\r
3051 if( KeWaitForSingleObject( &p_cep->timewait_timer, Executive,
\r
3052 KernelMode, FALSE, &timeout ) != STATUS_SUCCESS )
\r
3054 /* Still in timewait - try again next time. */
\r
3055 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3059 if( p_cep->ref_cnt )
\r
3061 /* Send outstanding or destruction in progress. */
\r
3062 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3066 /* Remove from the timewait list. */
\r
3067 cl_qlist_remove_item( &gp_cep_mgr->timewait_list, &p_cep->timewait_item );
\r
3070 * Not in timewait. Remove the CEP from the maps - it should
\r
3071 * no longer be matched against.
\r
3073 __remove_cep( p_cep );
\r
3075 if( p_cep->state == CEP_STATE_DESTROY )
\r
3077 __destroy_cep( p_cep );
\r
3081 /* Move the CEP to the IDLE state so that it can be used again. */
\r
3082 p_cep->state = CEP_STATE_IDLE;
\r
3086 AL_EXIT( AL_DBG_CM );
\r
3087 return (uint32_t)(min_timewait / -20000);
\r
3092 * Timer callback to process CEPs in timewait state.
\r
3095 __cep_timewait_cb(
\r
3096 IN void *context )
\r
3098 KLOCK_QUEUE_HANDLE hdl;
\r
3099 uint32_t min_timewait;
\r
3101 AL_ENTER( AL_DBG_CM );
\r
3103 UNUSED_PARAM( context );
\r
3105 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3107 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
3109 min_timewait = __process_timewait();
\r
3111 if( cl_qlist_count( &gp_cep_mgr->timewait_list ) )
\r
3114 * Reset the timer for half of the shortest timeout - this results
\r
3115 * in a worst case timeout of 150% of timewait.
\r
3117 cl_timer_trim( &gp_cep_mgr->timewait_timer, min_timewait );
\r
3120 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
3122 AL_EXIT( AL_DBG_CM );
\r
3127 * Starts immediate cleanup of the CM. Invoked during al_obj destruction.
\r
3130 __destroying_cep_mgr(
\r
3131 IN al_obj_t* p_obj )
\r
3133 ib_api_status_t status;
\r
3134 KLOCK_QUEUE_HANDLE hdl;
\r
3135 cl_list_item_t *p_item;
\r
3137 LARGE_INTEGER timeout;
\r
3139 AL_ENTER( AL_DBG_CM );
\r
3141 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3142 UNUSED_PARAM( p_obj );
\r
3144 /* Deregister from PnP notifications. */
\r
3145 if( gp_cep_mgr->h_pnp )
\r
3147 status = ib_dereg_pnp(
\r
3148 gp_cep_mgr->h_pnp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
3149 if( status != IB_SUCCESS )
\r
3151 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
3152 ("ib_dereg_pnp failed with status %s.\n",
\r
3153 ib_get_err_str(status)) );
\r
3154 deref_al_obj( &gp_cep_mgr->obj );
\r
3158 /* Cancel all timewait timers. */
\r
3159 timeout.QuadPart = 0;
\r
3160 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
3161 for( p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3162 p_item != cl_qlist_end( &gp_cep_mgr->timewait_list );
\r
3163 p_item = cl_qlist_next( p_item ) )
\r
3165 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3166 KeSetTimer( &p_cep->timewait_timer, timeout, NULL );
\r
3168 __process_timewait();
\r
3169 KeReleaseInStackQueuedSpinLock( &hdl );
\r
3171 AL_EXIT( AL_DBG_CM );
\r
3176 * Frees the global CEP agent. Invoked during al_obj destruction.
\r
3180 IN al_obj_t* p_obj )
\r
3182 AL_ENTER( AL_DBG_CM );
\r
3184 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3185 /* All listen request should have been cleaned up by this point. */
\r
3186 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->listen_map ) );
\r
3187 /* All connections should have been cancelled/disconnected by now. */
\r
3188 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_id_map ) );
\r
3189 CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_qp_map ) );
\r
3191 cl_vector_destroy( &gp_cep_mgr->cid_vector );
\r
3193 cl_timer_destroy( &gp_cep_mgr->timewait_timer );
\r
3196 * All CM port agents should have been destroyed by now via the
\r
3197 * standard child object destruction provided by the al_obj.
\r
3199 ExDeleteNPagedLookasideList( &gp_cep_mgr->cep_pool );
\r
3200 destroy_al_obj( p_obj );
\r
3202 cl_free( gp_cep_mgr );
\r
3203 gp_cep_mgr = NULL;
\r
3205 AL_EXIT( AL_DBG_CM );
\r
3209 static cl_status_t
\r
3211 IN void* const p_element,
\r
3212 IN void* context )
\r