2 * Copyright (c) 2005 SilverStorm Technologies. All rights reserved.
\r
3 * Portions Copyright (c) 2008 Microsoft Corporation. All rights reserved.
\r
5 * This software is available to you under the OpenIB.org BSD license
\r
8 * Redistribution and use in source and binary forms, with or
\r
9 * without modification, are permitted provided that the following
\r
10 * conditions are met:
\r
12 * - Redistributions of source code must retain the above
\r
13 * copyright notice, this list of conditions and the following
\r
16 * - Redistributions in binary form must reproduce the above
\r
17 * copyright notice, this list of conditions and the following
\r
18 * disclaimer in the documentation and/or other materials
\r
19 * provided with the distribution.
\r
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
\r
25 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
\r
26 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
34 #include <iba/ib_al.h>
\r
35 #include <complib/cl_vector.h>
\r
36 #include <complib/cl_rbmap.h>
\r
37 #include <complib/cl_qmap.h>
\r
38 #include <complib/cl_spinlock.h>
\r
39 #include <iba/ib_cm_ifc.h>
\r
40 #include "al_common.h"
\r
41 #include "al_cm_cep.h"
\r
42 #include "al_cm_conn.h"
\r
43 #include "al_cm_sidr.h"
\r
44 #include "al_debug.h"
\r
45 #if defined(EVENT_TRACING)
\r
49 #include "al_cm_cep.tmh"
\r
51 #include "ib_common.h"
\r
60 * The vector object uses a list item at the front of the buffers
\r
61 * it allocates. Take the list item into account so that allocations
\r
62 * are for full page sizes.
\r
64 #define CEP_CID_MIN \
\r
65 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
66 #define CEP_CID_GROW \
\r
67 ((PAGE_SIZE - sizeof(cl_list_item_t)) / sizeof(cep_cid_t))
\r
70 * We reserve the upper byte of the connection ID as a revolving counter so
\r
71 * that connections that are retried by the client change connection ID.
\r
72 * This counter is never zero, so it is OK to use all CIDs since we will never
\r
73 * have a full CID (base + counter) that is zero.
\r
74 * See the IB spec, section 12.9.8.7 for details about REJ retry.
\r
76 #define CEP_MAX_CID (0x00FFFFFF)
\r
77 #define CEP_MAX_CID_MASK (0x00FFFFFF)
\r
79 #define CEP_MAD_SQ_DEPTH (128)
\r
80 #define CEP_MAD_RQ_DEPTH (1) /* ignored. */
\r
81 #define CEP_MAD_SQ_SGE (1)
\r
82 #define CEP_MAD_RQ_SGE (1) /* ignored. */
\r
85 /* Global connection manager object. */
\r
86 typedef struct _al_cep_mgr
\r
94 /* Bitmap of CEPs, indexed by CID. */
\r
95 cl_vector_t cid_vector;
\r
98 /* List of active listens. */
\r
99 cl_rbmap_t listen_map;
\r
101 /* Map of CEP by remote CID and CA GUID. */
\r
102 cl_rbmap_t conn_id_map;
\r
103 /* Map of CEP by remote QPN, used for stale connection matching. */
\r
104 cl_rbmap_t conn_qp_map;
\r
106 NPAGED_LOOKASIDE_LIST cep_pool;
\r
107 NPAGED_LOOKASIDE_LIST req_pool;
\r
110 * Periodically walk the list of connections in the time wait state
\r
111 * and flush them as appropriate.
\r
113 cl_timer_t timewait_timer;
\r
114 cl_qlist_t timewait_list;
\r
116 ib_pnp_handle_t h_pnp;
\r
121 /* Per-port CM object. */
\r
122 typedef struct _cep_port_agent
\r
126 cl_map_item_t item;
\r
128 ib_ca_handle_t h_ca;
\r
129 ib_pd_handle_t h_pd;
\r
130 ib_qp_handle_t h_qp;
\r
131 ib_pool_key_t pool_key;
\r
132 ib_mad_svc_handle_t h_mad_svc;
\r
142 * Note: the REQ, REP, and LAP values must be 1, 2, and 4 respectively.
\r
143 * This allows shifting 1 << msg_mraed from an MRA to figure out for what
\r
144 * message the MRA was sent for.
\r
146 #define CEP_STATE_RCVD 0x10000000
\r
147 #define CEP_STATE_SENT 0x20000000
\r
148 #define CEP_STATE_MRA 0x01000000
\r
149 #define CEP_STATE_REQ 0x00000001
\r
150 #define CEP_STATE_REP 0x00000002
\r
151 #define CEP_STATE_LAP 0x00000004
\r
152 #define CEP_STATE_RTU 0x00000008
\r
153 #define CEP_STATE_DREQ 0x00000010
\r
154 #define CEP_STATE_DREP 0x00000020
\r
155 #define CEP_STATE_DESTROYING 0x00010000
\r
156 #define CEP_STATE_USER 0x00020000
\r
158 #define CEP_MSG_MASK 0x000000FF
\r
159 #define CEP_OP_MASK 0xF0000000
\r
161 #define CEP_STATE_PREP 0x00100000
\r
163 /* States match CM state transition diagrams from spec. */
\r
164 typedef enum _cep_state
\r
168 CEP_STATE_ESTABLISHED,
\r
169 CEP_STATE_TIMEWAIT,
\r
170 CEP_STATE_SREQ_SENT,
\r
171 CEP_STATE_SREQ_RCVD,
\r
173 CEP_STATE_DESTROY = CEP_STATE_DESTROYING,
\r
174 CEP_STATE_PRE_REQ = CEP_STATE_IDLE | CEP_STATE_PREP,
\r
175 CEP_STATE_REQ_RCVD = CEP_STATE_REQ | CEP_STATE_RCVD,
\r
176 CEP_STATE_PRE_REP = CEP_STATE_REQ_RCVD | CEP_STATE_PREP,
\r
177 CEP_STATE_REQ_SENT = CEP_STATE_REQ | CEP_STATE_SENT,
\r
178 CEP_STATE_REQ_MRA_RCVD = CEP_STATE_REQ_SENT | CEP_STATE_MRA,
\r
179 CEP_STATE_REQ_MRA_SENT = CEP_STATE_REQ_RCVD | CEP_STATE_MRA,
\r
180 CEP_STATE_PRE_REP_MRA_SENT = CEP_STATE_REQ_MRA_SENT | CEP_STATE_PREP,
\r
181 CEP_STATE_REP_RCVD = CEP_STATE_REP | CEP_STATE_RCVD,
\r
182 CEP_STATE_REP_SENT = CEP_STATE_REP | CEP_STATE_SENT,
\r
183 CEP_STATE_REP_MRA_RCVD = CEP_STATE_REP_SENT | CEP_STATE_MRA,
\r
184 CEP_STATE_REP_MRA_SENT = CEP_STATE_REP_RCVD | CEP_STATE_MRA,
\r
185 CEP_STATE_LAP_RCVD = CEP_STATE_LAP | CEP_STATE_RCVD,
\r
186 CEP_STATE_PRE_APR = CEP_STATE_LAP_RCVD | CEP_STATE_PREP,
\r
187 CEP_STATE_LAP_SENT = CEP_STATE_LAP | CEP_STATE_SENT,
\r
188 CEP_STATE_LAP_MRA_RCVD = CEP_STATE_LAP_SENT | CEP_STATE_MRA,
\r
189 CEP_STATE_LAP_MRA_SENT = CEP_STATE_LAP_RCVD | CEP_STATE_MRA,
\r
190 CEP_STATE_PRE_APR_MRA_SENT = CEP_STATE_LAP_MRA_SENT | CEP_STATE_PREP,
\r
191 CEP_STATE_DREQ_SENT = CEP_STATE_DREQ | CEP_STATE_SENT,
\r
192 CEP_STATE_DREQ_RCVD = CEP_STATE_DREQ | CEP_STATE_RCVD,
\r
193 CEP_STATE_DREQ_DESTROY = CEP_STATE_DREQ_SENT | CEP_STATE_DESTROYING
\r
198 /* Active side CEP state transitions:
\r
199 * al_create_cep -> IDLE
\r
200 * al_cep_pre_req -> PRE_REQ
\r
201 * al_cep_send_req -> REQ_SENT
\r
202 * Recv REQ MRA -> REQ_MRA_RCVD
\r
203 * Recv REP -> REP_RCVD
\r
204 * al_cep_mra -> REP_MRA_SENT
\r
205 * al_cep_rtu -> ESTABLISHED
\r
207 * Passive side CEP state transitions:
\r
208 * al_create_cep -> IDLE
\r
209 * Recv REQ -> REQ_RCVD
\r
210 * al_cep_mra* -> REQ_MRA_SENT
\r
211 * al_cep_pre_rep -> PRE_REP
\r
212 * al_cep_mra* -> PRE_REP_MRA_SENT
\r
213 * al_cep_send_rep -> REP_SENT
\r
214 * Recv RTU -> ESTABLISHED
\r
216 * *al_cep_mra can only be called once - either before or after PRE_REP.
\r
219 typedef struct _al_kcep_av
\r
223 uint16_t pkey_index;
\r
228 typedef struct _al_kcep
\r
233 struct _cep_cid *p_cid;
\r
237 /* Port guid for filtering incoming requests. */
\r
240 uint8_t* p_cmp_buf;
\r
241 uint8_t cmp_offset;
\r
246 /* Used to store connection structure with owning AL instance. */
\r
247 cl_list_item_t al_item;
\r
249 /* Flag to indicate whether a user is processing events. */
\r
250 boolean_t signalled;
\r
252 /* Destroy callback. */
\r
253 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
255 ib_mad_element_t *p_mad_head;
\r
256 ib_mad_element_t *p_mad_tail;
\r
257 al_pfn_cep_cb_t pfn_cb;
\r
261 /* MAP item for finding listen CEPs. */
\r
262 cl_rbmap_item_t listen_item;
\r
264 /* Map item for finding CEPs based on remote comm ID & CA GUID. */
\r
265 cl_rbmap_item_t rem_id_item;
\r
267 /* Map item for finding CEPs based on remote QP number. */
\r
268 cl_rbmap_item_t rem_qp_item;
\r
270 /* Communication ID's for the connection. */
\r
271 net32_t local_comm_id;
\r
272 net32_t remote_comm_id;
\r
274 net64_t local_ca_guid;
\r
275 net64_t remote_ca_guid;
\r
277 /* Remote QP, used for stale connection checking. */
\r
278 net32_t remote_qpn;
\r
280 /* Parameters to format QP modification structure. */
\r
284 * Note that we store the requested initiator depth as received in the REQ
\r
285 * and cap it when sending the REP to the actual capabilities of the HCA.
\r
288 uint8_t init_depth;
\r
289 uint8_t rnr_nak_timeout;
\r
292 * Local QP number, used for the "additional check" required
\r
297 /* PKEY to make sure a LAP is on the same partition. */
\r
301 * Primary and alternate path info, used to create the address vectors for
\r
302 * sending MADs, to locate the port CM agent to use for outgoing sends,
\r
303 * and for creating the address vectors for transitioning QPs.
\r
306 uint8_t idx_primary;
\r
308 /* Temporary AV and CEP port GUID used when processing LAP. */
\r
310 uint8_t alt_2pkt_life;
\r
312 /* maxium packet lifetime * 2 of any path used on a connection. */
\r
313 uint8_t max_2pkt_life;
\r
314 /* Given by the REP, used for alternate path setup. */
\r
315 uint8_t target_ack_delay;
\r
316 /* Stored to help calculate the local ACK delay in the LAP. */
\r
317 uint8_t local_ack_delay;
\r
319 /* Volatile to allow using atomic operations for state checks. */
\r
323 * Flag that indicates whether a connection took the active role during
\r
326 boolean_t was_active;
\r
329 * Handle to the sent MAD, used for cancelling. We store the handle to
\r
330 * the mad service so that we can properly cancel. This should not be a
\r
331 * problem since all outstanding sends should be completed before the
\r
332 * mad service completes its destruction and the handle becomes invalid.
\r
334 ib_mad_svc_handle_t h_mad_svc;
\r
335 ib_mad_element_t *p_send_mad;
\r
337 atomic32_t ref_cnt;
\r
339 /* MAD transaction ID to use when sending MADs. */
\r
342 /* Maximum retries per MAD. Set at REQ time, stored to retry LAP. */
\r
343 uint8_t max_cm_retries;
\r
344 /* Timeout value, in milliseconds. Set at REQ time, stored to retry LAP. */
\r
345 uint32_t retry_timeout;
\r
347 /* Timer that will be signalled when the CEP exits timewait. */
\r
348 KTIMER timewait_timer;
\r
349 LARGE_INTEGER timewait_time;
\r
350 cl_list_item_t timewait_item;
\r
353 * Pointer to a formatted MAD. The pre_req, pre_rep and pre_apr calls
\r
354 * allocate and format the MAD, and the send_req, send_rep and send_apr
\r
357 ib_mad_element_t *p_mad;
\r
359 /* Cache the last MAD sent for retransmission. */
\r
365 mad_cm_drep_t drep;
\r
370 * NDI stuff - TODO: manage above core kernel CM code
\r
373 /* IRP list head */
\r
374 LIST_ENTRY irp_que;
\r
376 /* private data of REQ, REP, REJ CM requests */
\r
378 uint8_t pdata[IB_REP_PDATA_SIZE];
\r
383 /* Structures stored in the CID vector. */
\r
384 typedef struct _cep_cid
\r
386 /* Owning AL handle. NULL if invalid. */
\r
387 ib_al_handle_t h_al;
\r
388 /* Pointer to CEP, or index of next free entry if h_al is NULL. */
\r
390 /* For REJ Retry support */
\r
396 /* Global instance of the CM agent. */
\r
397 al_cep_mgr_t *gp_cep_mgr = NULL;
\r
400 static ib_api_status_t
\r
402 IN kcep_t* const p_cep,
\r
403 IN const uint8_t* p_pdata OPTIONAL,
\r
404 IN uint8_t pdata_len,
\r
405 IN OUT mad_cm_drep_t* const p_drep );
\r
407 static ib_api_status_t
\r
409 IN kcep_t* const p_cep,
\r
410 IN ib_mad_element_t* p_mad );
\r
414 IN kcep_t* const p_cep );
\r
416 static inline uint32_t
\r
417 __calc_mad_timeout(
\r
418 IN const uint8_t pkt_life );
\r
422 IN kcep_t* const p_cep );
\r
425 __create_cep( void );
\r
429 IN kcep_t* const p_cep );
\r
433 IN kcep_t* const p_cep );
\r
437 IN kcep_t* const p_cep,
\r
438 IN ib_al_handle_t h_al,
\r
439 IN al_pfn_cep_cb_t pfn_cb,
\r
440 IN void* context );
\r
444 IN kcep_t* const p_cep );
\r
448 IN kcep_t* const p_cep );
\r
452 IN net32_t remote_comm_id,
\r
453 IN net64_t remote_ca_guid );
\r
458 IN net64_t port_guid,
\r
459 IN void *p_pdata );
\r
461 static inline kcep_t*
\r
463 IN ib_al_handle_t h_al OPTIONAL,
\r
466 static inline kcep_t*
\r
468 IN kcep_t* const p_new_cep );
\r
472 IN kcep_t* const p_cep );
\r
476 IN kcep_t* const p_cep );
\r
478 static ib_api_status_t
\r
480 IN kcep_t* const p_cep,
\r
481 IN net16_t attr_id,
\r
482 OUT cep_agent_t** const pp_port_cep,
\r
483 OUT ib_mad_element_t** const pp_mad );
\r
485 static ib_api_status_t
\r
487 IN cep_agent_t* const p_port_cep,
\r
488 IN ib_mad_element_t* const p_mad );
\r
490 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
491 static cep_agent_t*
\r
493 IN const ib_gid_t* const p_gid,
\r
494 IN const net16_t lid,
\r
495 IN const net16_t pkey,
\r
496 OUT uint16_t* const p_pkey_index );
\r
500 OUT net32_t* const p_cid );
\r
503 __process_cep_send_comp(
\r
504 IN cl_async_proc_item_t *p_item );
\r
507 /******************************************************************************
\r
508 * Per-port CEP agent
\r
509 ******************************************************************************/
\r
514 IN ib_mad_t* const p_mad,
\r
515 IN const kcep_t* const p_cep,
\r
516 IN net16_t attr_id )
\r
518 p_mad->base_ver = 1;
\r
519 p_mad->mgmt_class = IB_MCLASS_COMM_MGMT;
\r
520 p_mad->class_ver = IB_MCLASS_CM_VER_2;
\r
521 p_mad->method = IB_MAD_METHOD_SEND;
\r
523 p_mad->class_spec = 0;
\r
524 p_mad->trans_id = p_cep->tid;
\r
525 p_mad->attr_id = attr_id;
\r
527 p_mad->attr_mod = 0;
\r
531 /* Consumes the input MAD. */
\r
534 IN cep_agent_t* const p_port_cep,
\r
535 IN kcep_t* const p_cep,
\r
536 IN ib_mad_element_t* const p_mad,
\r
537 IN ib_rej_status_t reason )
\r
539 mad_cm_rej_t *p_rej;
\r
541 AL_ENTER( AL_DBG_CM );
\r
543 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
545 __format_mad_hdr( p_mad->p_mad_buf, p_cep, CM_REJ_ATTR_ID );
\r
547 p_rej->local_comm_id = p_cep->local_comm_id;
\r
548 p_rej->remote_comm_id = p_cep->remote_comm_id;
\r
549 p_rej->reason = reason;
\r
551 switch( p_cep->state )
\r
553 case CEP_STATE_REQ_RCVD:
\r
554 case CEP_STATE_REQ_MRA_SENT:
\r
555 case CEP_STATE_PRE_REP:
\r
556 case CEP_STATE_PRE_REP_MRA_SENT:
\r
557 conn_rej_set_msg_rejected( 0, p_rej );
\r
560 case CEP_STATE_REP_RCVD:
\r
561 case CEP_STATE_REP_MRA_SENT:
\r
562 conn_rej_set_msg_rejected( 1, p_rej );
\r
566 CL_ASSERT( reason == IB_REJ_TIMEOUT );
\r
567 conn_rej_set_msg_rejected( 2, p_rej );
\r
571 conn_rej_clr_rsvd_fields( p_rej );
\r
572 __cep_send_mad( p_port_cep, p_mad );
\r
574 AL_EXIT( AL_DBG_CM );
\r
580 IN cep_agent_t* const p_port_cep,
\r
581 IN kcep_t* const p_cep,
\r
582 IN const ib_mad_element_t* const p_mad )
\r
584 ib_api_status_t status;
\r
585 ib_mad_element_t *p_rej_mad;
\r
586 ib_mad_t *p_mad_buf;
\r
589 AL_ENTER( AL_DBG_CM );
\r
591 status = ib_get_mad( p_port_cep->pool_key, MAD_BLOCK_SIZE, &p_rej_mad );
\r
592 if( status != IB_SUCCESS )
\r
594 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
595 ("ib_get_mad returned %s\n", ib_get_err_str( status )) );
\r
599 /* Save the buffer pointers from the new element. */
\r
600 p_mad_buf = p_rej_mad->p_mad_buf;
\r
601 p_grh = p_rej_mad->p_grh;
\r
604 * Copy the input MAD element to the reject - this gives us
\r
605 * all appropriate addressing information.
\r
607 cl_memcpy( p_rej_mad, p_mad, sizeof(ib_mad_element_t) );
\r
608 cl_memcpy( p_grh, p_mad->p_grh, sizeof(ib_grh_t) );
\r
610 /* Restore the buffer pointers now that the copy is complete. */
\r
611 p_rej_mad->p_mad_buf = p_mad_buf;
\r
612 p_rej_mad->p_grh = p_grh;
\r
614 status = conn_rej_set_pdata( NULL, 0, (mad_cm_rej_t*)p_mad_buf );
\r
615 CL_ASSERT( status == IB_SUCCESS );
\r
617 /* Copy the local CA GUID into the ARI. */
\r
618 switch( p_mad->p_mad_buf->attr_id )
\r
620 case CM_REQ_ATTR_ID:
\r
621 status = conn_rej_set_ari(
\r
622 (uint8_t*)&p_cep->local_ca_guid,
\r
623 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
624 CL_ASSERT( status == IB_SUCCESS );
\r
625 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
628 case CM_REP_ATTR_ID:
\r
629 status = conn_rej_set_ari(
\r
630 (uint8_t*)&p_cep->local_ca_guid,
\r
631 sizeof(p_cep->local_ca_guid), (mad_cm_rej_t*)p_mad_buf );
\r
632 CL_ASSERT( status == IB_SUCCESS );
\r
633 __reject_mad( p_port_cep, p_cep, p_rej_mad, IB_REJ_TIMEOUT );
\r
637 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
638 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID );
\r
639 ib_put_mad( p_rej_mad );
\r
643 AL_EXIT( AL_DBG_CM );
\r
649 IN cep_agent_t* const p_port_cep,
\r
650 IN ib_mad_element_t* const p_mad,
\r
651 IN const ib_rej_status_t reason )
\r
653 mad_cm_req_t *p_req;
\r
654 mad_cm_rej_t *p_rej;
\r
656 AL_ENTER( AL_DBG_CM );
\r
658 CL_ASSERT( p_port_cep );
\r
659 CL_ASSERT( p_mad );
\r
660 CL_ASSERT( reason != 0 );
\r
662 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
663 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
666 * Format the reject information, overwriting the REQ data and send
\r
669 p_rej->hdr.attr_id = CM_REJ_ATTR_ID;
\r
670 p_rej->remote_comm_id = p_req->local_comm_id;
\r
671 p_rej->local_comm_id = 0;
\r
672 conn_rej_set_msg_rejected( 0, p_rej );
\r
673 p_rej->reason = reason;
\r
674 conn_rej_set_ari( NULL, 0, p_rej );
\r
675 conn_rej_set_pdata( NULL, 0, p_rej );
\r
676 conn_rej_clr_rsvd_fields( p_rej );
\r
678 p_mad->retry_cnt = 0;
\r
679 p_mad->send_opt = 0;
\r
680 p_mad->timeout_ms = 0;
\r
681 p_mad->resp_expected = FALSE;
\r
683 __cep_send_mad( p_port_cep, p_mad );
\r
685 AL_EXIT( AL_DBG_CM );
\r
691 IN kcep_t* const p_cep,
\r
692 IN const mad_cm_req_t* const p_req,
\r
693 IN const uint8_t idx )
\r
695 cep_agent_t *p_port_cep;
\r
696 const req_path_info_t *p_path;
\r
698 AL_ENTER( AL_DBG_CM );
\r
700 CL_ASSERT( p_cep );
\r
701 CL_ASSERT( p_req );
\r
703 cl_memclr( &p_cep->av[idx], sizeof(kcep_av_t) );
\r
705 p_path = &((&p_req->primary_path)[idx]);
\r
707 p_port_cep = __find_port_cep( &p_path->remote_gid,
\r
708 p_path->remote_lid, p_req->pkey, &p_cep->av[idx].pkey_index );
\r
712 p_cep->local_ca_guid = 0;
\r
713 AL_EXIT( AL_DBG_CM );
\r
718 p_cep->local_ca_guid = p_port_cep->h_ca->obj.p_ci_ca->verbs.guid;
\r
720 /* Check that CA GUIDs match if formatting the alternate path. */
\r
722 p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
724 AL_EXIT( AL_DBG_CM );
\r
729 * Pkey indeces must match if formating the alternat path - the QP
\r
730 * modify structure only allows for a single PKEY index to be specified.
\r
733 p_cep->av[0].pkey_index != p_cep->av[1].pkey_index )
\r
735 AL_EXIT( AL_DBG_CM );
\r
739 p_cep->av[idx].port_guid = p_port_cep->port_guid;
\r
740 p_cep->av[idx].attr.port_num = p_port_cep->port_num;
\r
742 p_cep->av[idx].attr.sl = conn_req_path_get_svc_lvl( p_path );
\r
743 p_cep->av[idx].attr.dlid = p_path->local_lid;
\r
745 if( !conn_req_path_get_subn_lcl( p_path ) )
\r
747 p_cep->av[idx].attr.grh_valid = TRUE;
\r
748 p_cep->av[idx].attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
749 1, p_path->traffic_class, conn_req_path_get_flow_lbl( p_path ) );
\r
750 p_cep->av[idx].attr.grh.hop_limit = p_path->hop_limit;
\r
751 p_cep->av[idx].attr.grh.dest_gid = p_path->local_gid;
\r
752 p_cep->av[idx].attr.grh.src_gid = p_path->remote_gid;
\r
756 p_cep->av[idx].attr.grh_valid = FALSE;
\r
758 p_cep->av[idx].attr.static_rate = conn_req_path_get_pkt_rate( p_path );
\r
759 p_cep->av[idx].attr.path_bits =
\r
760 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
763 * Note that while we never use the connected AV attributes internally,
\r
764 * we store them so we can pass them back to users.
\r
766 p_cep->av[idx].attr.conn.path_mtu = conn_req_get_mtu( p_req );
\r
767 p_cep->av[idx].attr.conn.local_ack_timeout =
\r
768 conn_req_path_get_lcl_ack_timeout( p_path );
\r
769 p_cep->av[idx].attr.conn.seq_err_retry_cnt =
\r
770 conn_req_get_retry_cnt( p_req );
\r
771 p_cep->av[idx].attr.conn.rnr_retry_cnt =
\r
772 conn_req_get_rnr_retry_cnt( p_req );
\r
774 AL_EXIT( AL_DBG_CM );
\r
779 * + Validates the path information provided in the REQ and stores the
\r
780 * associated CA attributes and port indeces.
\r
781 * + Transitions a connection object from active to passive in the peer case.
\r
782 * + Sets the path information in the connection and sets the CA GUID
\r
783 * in the REQ callback record.
\r
787 IN OUT kcep_t* const p_cep,
\r
788 IN OUT mad_cm_req_t* const p_req )
\r
790 AL_ENTER( AL_DBG_CM );
\r
792 p_cep->state = CEP_STATE_REQ_RCVD;
\r
793 p_cep->was_active = FALSE;
\r
795 p_cep->sid = p_req->sid;
\r
797 /* Store pertinent information in the connection. */
\r
798 p_cep->remote_comm_id = p_req->local_comm_id;
\r
799 p_cep->remote_ca_guid = p_req->local_ca_guid;
\r
801 p_cep->remote_qpn = conn_req_get_lcl_qpn( p_req );
\r
802 p_cep->local_qpn = 0;
\r
804 p_cep->retry_timeout =
\r
805 __calc_mad_timeout( conn_req_get_lcl_resp_timeout( p_req ) );
\r
807 /* Store the retry count. */
\r
808 p_cep->max_cm_retries = conn_req_get_max_cm_retries( p_req );
\r
811 * Copy the paths from the req_rec into the connection for
\r
812 * future use. Note that if the primary path is invalid,
\r
813 * the REP will fail.
\r
815 __format_req_av( p_cep, p_req, 0 );
\r
817 if( p_req->alternate_path.local_lid )
\r
818 __format_req_av( p_cep, p_req, 1 );
\r
820 cl_memclr( &p_cep->av[1], sizeof(kcep_av_t) );
\r
822 p_cep->idx_primary = 0;
\r
824 /* Store the maximum packet lifetime, used to calculate timewait. */
\r
825 p_cep->max_2pkt_life = conn_req_path_get_lcl_ack_timeout( &p_req->primary_path );
\r
826 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life,
\r
827 conn_req_path_get_lcl_ack_timeout( &p_req->alternate_path ) );
\r
830 * Make sure the target ack delay is cleared - the above
\r
831 * "packet life" includes it.
\r
833 p_cep->target_ack_delay = 0;
\r
835 /* Store the requested initiator depth. */
\r
836 p_cep->resp_res = conn_req_get_init_depth( p_req );
\r
839 * Store the provided responder resources. These turn into the local
\r
840 * QP's initiator depth.
\r
842 p_cep->init_depth = conn_req_get_resp_res( p_req );
\r
844 p_cep->sq_psn = conn_req_get_starting_psn( p_req );
\r
846 p_cep->tid = p_req->hdr.trans_id;
\r
847 /* copy mad info for cm handoff */
\r
848 /* TODO: Do need to support CM handoff? */
\r
849 //p_cep->mads.req = *p_req;
\r
851 /* Cache the private data. */
\r
852 p_cep->psize = IB_REQ_PDATA_SIZE;
\r
853 memcpy( p_cep->pdata, p_req->pdata, IB_REQ_PDATA_SIZE );
\r
855 AL_EXIT( AL_DBG_CM );
\r
859 /* Must be called with the CEP lock held. */
\r
862 IN cep_agent_t* const p_port_cep,
\r
863 IN kcep_t* const p_cep,
\r
864 IN ib_mad_element_t* const p_mad )
\r
866 AL_ENTER( AL_DBG_CM );
\r
868 CL_ASSERT( p_port_cep );
\r
869 CL_ASSERT( p_cep );
\r
870 CL_ASSERT( p_mad );
\r
872 /* Repeat the last mad sent for the connection. */
\r
873 switch( p_cep->state )
\r
875 case CEP_STATE_REQ_MRA_SENT: /* resend MRA(REQ) */
\r
876 case CEP_STATE_REP_MRA_SENT: /* resend MRA(REP) */
\r
877 case CEP_STATE_LAP_MRA_SENT: /* resend MRA(LAP) */
\r
878 case CEP_STATE_ESTABLISHED: /* resend RTU */
\r
879 case CEP_STATE_TIMEWAIT: /* resend the DREP */
\r
880 cl_memcpy( p_mad->p_mad_buf, &p_cep->mads, MAD_BLOCK_SIZE );
\r
881 p_mad->send_context1 = NULL;
\r
882 p_mad->send_context2 = NULL;
\r
883 __cep_send_mad( p_port_cep, p_mad );
\r
887 /* Return the MAD to the mad pool */
\r
888 ib_put_mad( p_mad );
\r
892 AL_EXIT( AL_DBG_CM );
\r
896 static ib_api_status_t
\r
898 IN kcep_t* const p_cep,
\r
899 IN ib_mad_element_t* const p_mad )
\r
901 ib_api_status_t status;
\r
902 mad_cm_rej_t *p_rej;
\r
904 AL_ENTER( AL_DBG_CM );
\r
908 ASSERT( p_mad->p_mad_buf );
\r
910 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
912 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
913 ("Request rejected p_rej %p, reason - %d.\n",
\r
914 p_rej, cl_ntoh16(p_rej->reason) ) );
\r
916 switch( p_cep->state )
\r
918 case CEP_STATE_REQ_SENT:
\r
920 * Ignore rejects with the status set to IB_REJ_INVALID_SID. We will
\r
921 * continue to retry (up to max_cm_retries) to connect to the remote
\r
922 * side. This is required to support peer-to-peer connections.
\r
924 if( p_cep->p2p && p_rej->reason == IB_REJ_INVALID_SID )
\r
926 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
927 ("Request rejected (invalid SID) - retrying.\n") );
\r
932 case CEP_STATE_REP_SENT:
\r
933 case CEP_STATE_REQ_MRA_RCVD:
\r
934 case CEP_STATE_REP_MRA_RCVD:
\r
935 /* Cancel any outstanding MAD. */
\r
936 if( p_cep->p_send_mad )
\r
938 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
939 p_cep->p_send_mad = NULL;
\r
943 case CEP_STATE_REQ_RCVD:
\r
944 case CEP_STATE_REP_RCVD:
\r
945 case CEP_STATE_REQ_MRA_SENT:
\r
946 case CEP_STATE_REP_MRA_SENT:
\r
947 case CEP_STATE_PRE_REP:
\r
948 case CEP_STATE_PRE_REP_MRA_SENT:
\r
949 if( p_cep->state & CEP_STATE_PREP )
\r
951 CL_ASSERT( p_cep->p_mad );
\r
952 ib_put_mad( p_cep->p_mad );
\r
953 p_cep->p_mad = NULL;
\r
955 /* Abort connection establishment. No transition to timewait. */
\r
956 __remove_cep( p_cep );
\r
957 p_cep->state = CEP_STATE_IDLE;
\r
960 case CEP_STATE_ESTABLISHED:
\r
961 case CEP_STATE_LAP_RCVD:
\r
962 case CEP_STATE_LAP_SENT:
\r
963 case CEP_STATE_LAP_MRA_RCVD:
\r
964 case CEP_STATE_LAP_MRA_SENT:
\r
965 case CEP_STATE_PRE_APR:
\r
966 case CEP_STATE_PRE_APR_MRA_SENT:
\r
967 if( p_cep->state & CEP_STATE_PREP )
\r
969 CL_ASSERT( p_cep->p_mad );
\r
970 ib_put_mad( p_cep->p_mad );
\r
971 p_cep->p_mad = NULL;
\r
973 p_cep->state = CEP_STATE_TIMEWAIT;
\r
974 __insert_timewait( p_cep );
\r
978 /* Ignore the REJ. */
\r
979 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REJ received in invalid state.\n") );
\r
981 ib_put_mad( p_mad );
\r
982 AL_EXIT( AL_DBG_CM );
\r
983 return IB_NO_MATCH;
\r
986 /* Cache the private data. */
\r
987 p_cep->psize = IB_REJ_PDATA_SIZE;
\r
988 memcpy( p_cep->pdata, p_rej->pdata, IB_REJ_PDATA_SIZE );
\r
990 status = __cep_queue_mad( p_cep, p_mad );
\r
992 AL_EXIT( AL_DBG_CM );
\r
997 static ib_api_status_t
\r
999 IN kcep_t* const p_cep )
\r
1001 ib_api_status_t status;
\r
1002 cep_agent_t *p_port_cep;
\r
1003 ib_mad_element_t *p_mad;
\r
1004 mad_cm_rej_t *p_rej;
\r
1006 status = __cep_get_mad( p_cep, CM_REJ_ATTR_ID, &p_port_cep, &p_mad );
\r
1007 if( status != IB_SUCCESS )
\r
1010 p_rej = ib_get_mad_buf( p_mad );
\r
1012 conn_rej_set_ari( NULL, 0, p_rej );
\r
1013 conn_rej_set_pdata( NULL, 0, p_rej );
\r
1015 p_rej->local_comm_id = p_cep->remote_comm_id;
\r
1016 p_rej->remote_comm_id = p_cep->local_comm_id;
\r
1017 p_rej->reason = IB_REJ_STALE_CONN;
\r
1019 switch( p_cep->state )
\r
1021 case CEP_STATE_REQ_RCVD:
\r
1022 case CEP_STATE_REQ_MRA_SENT:
\r
1023 case CEP_STATE_PRE_REP:
\r
1024 case CEP_STATE_PRE_REP_MRA_SENT:
\r
1025 conn_rej_set_msg_rejected( 0, p_rej );
\r
1028 case CEP_STATE_REQ_SENT:
\r
1029 case CEP_STATE_REP_RCVD:
\r
1030 case CEP_STATE_REP_MRA_SENT:
\r
1031 conn_rej_set_msg_rejected( 1, p_rej );
\r
1035 conn_rej_set_msg_rejected( 2, p_rej );
\r
1038 conn_rej_clr_rsvd_fields( p_rej );
\r
1040 return __process_rej( p_cep, p_mad );
\r
1046 IN cep_agent_t* const p_port_cep,
\r
1047 IN ib_mad_element_t* const p_mad )
\r
1049 ib_api_status_t status = IB_SUCCESS;
\r
1050 mad_cm_req_t *p_req;
\r
1051 kcep_t *p_cep, *p_new_cep, *p_stale_cep = NULL;
\r
1052 KLOCK_QUEUE_HANDLE hdl;
\r
1053 ib_rej_status_t reason;
\r
1055 AL_ENTER( AL_DBG_CM );
\r
1057 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1059 p_req = (mad_cm_req_t*)p_mad->p_mad_buf;
\r
1061 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1062 ("REQ: comm_id (x%x) qpn (x%x) received\n",
\r
1063 p_req->local_comm_id, conn_req_get_lcl_qpn( p_req )) );
\r
1065 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1067 if( conn_req_get_qp_type( p_req ) > IB_QPT_UNRELIABLE_CONN ||
\r
1068 conn_req_get_lcl_qpn( p_req ) == 0 )
\r
1070 /* Reserved value. Reject. */
\r
1071 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid transport type received.\n") );
\r
1072 reason = IB_REJ_INVALID_XPORT;
\r
1076 /* Match against pending connections using remote comm ID and CA GUID. */
\r
1077 p_cep = __lookup_by_id( p_req->local_comm_id, p_req->local_ca_guid );
\r
1080 /* Already received the REQ. */
\r
1081 switch( p_cep->state )
\r
1083 case CEP_STATE_REQ_MRA_SENT:
\r
1084 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1087 case CEP_STATE_TIMEWAIT:
\r
1088 case CEP_STATE_DESTROY:
\r
1089 /* Send a reject. */
\r
1090 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1091 ("REQ received for connection in TIME_WAIT state.\n") );
\r
1092 __reject_req( p_port_cep, p_mad, IB_REJ_STALE_CONN );
\r
1097 * Let regular retries repeat the MAD. If our last message was
\r
1098 * dropped, resending only adds to the congestion. If it wasn't
\r
1099 * dropped, then the remote CM will eventually process it, and
\r
1100 * we'd just be adding traffic.
\r
1102 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Duplicate REQ received.\n") );
\r
1103 ib_put_mad( p_mad );
\r
1105 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1106 AL_EXIT( AL_DBG_CM );
\r
1111 * Allocate a new CEP for the new request. This will
\r
1112 * prevent multiple identical REQs from queueing up for processing.
\r
1114 p_new_cep = __create_cep();
\r
1117 /* Reject the request for insufficient resources. */
\r
1118 reason = IB_REJ_INSUF_RESOURCES;
\r
1119 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
1120 ("al_create_cep failed\nREJ sent for insufficient resources.\n") );
\r
1124 __save_wire_req( p_new_cep, p_req );
\r
1127 * Match against listens using SID and compare data, also provide the receiving
\r
1128 * MAD service's port GUID so we can properly filter.
\r
1130 p_cep = __lookup_listen( p_req->sid, p_port_cep->port_guid, p_req->pdata );
\r
1133 __bind_cep( p_new_cep, p_cep->p_cid->h_al, p_cep->pfn_cb, NULL );
\r
1135 /* Add the new CEP to the map so that repeated REQs match up. */
\r
1136 p_stale_cep = __insert_cep( p_new_cep );
\r
1137 if( p_stale_cep != p_new_cep )
\r
1139 /* Duplicate - must be a stale connection. */
\r
1140 reason = IB_REJ_STALE_CONN;
\r
1141 /* Fail the local stale CEP. */
\r
1142 status = __process_stale( p_stale_cep );
\r
1146 /* __cep_queue_mad may complete a pending IRP */
\r
1147 p_mad->send_context1 = p_new_cep;
\r
1150 * Queue the mad - the return value indicates whether we should
\r
1151 * invoke the callback.
\r
1153 status = __cep_queue_mad( p_cep, p_mad );
\r
1160 case IB_UNSUPPORTED:
\r
1161 p_mad->send_context1 = NULL;
\r
1162 reason = IB_REJ_USER_DEFINED;
\r
1166 p_mad->send_context1 = NULL;
\r
1167 reason = IB_REJ_INSUF_RESOURCES;
\r
1173 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("No listens active!\n") );
\r
1175 /* Match against peer-to-peer requests using SID and compare data. */
\r
1176 //p_cep = __lookup_peer();
\r
1179 // p_mad->send_context2 = NULL;
\r
1180 // p_list_item = cl_qlist_find_from_head( &gp_cep_mgr->pending_list,
\r
1181 // __match_peer, p_req );
\r
1182 // if( p_list_item != cl_qlist_end( &gp_cep_mgr->pending_list ) )
\r
1184 // KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1185 // p_conn = PARENT_STRUCT( p_list_item, kcep_t, map_item );
\r
1186 // __peer_req( p_port_cep, p_conn, p_async_mad->p_mad );
\r
1187 // cl_free( p_async_mad );
\r
1188 // AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
1189 // ("REQ matched a peer-to-peer request.\n") );
\r
1192 // reason = IB_REJ_INVALID_SID;
\r
1197 /* No match found. Reject. */
\r
1198 reason = IB_REJ_INVALID_SID;
\r
1199 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REQ received but no match found.\n") );
\r
1204 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1206 /* Process any queued MADs for the CEP. */
\r
1207 if( status == IB_SUCCESS )
\r
1208 __process_cep( p_cep );
\r
1210 AL_EXIT( AL_DBG_CM );
\r
1214 __unbind_cep( p_new_cep );
\r
1218 * Move the CEP in the idle state so that we don't send a reject
\r
1219 * for it when cleaning up. Also clear the RQPN and RCID so that
\r
1220 * we don't try to remove it from our maps (since it isn't inserted).
\r
1222 p_new_cep->state = CEP_STATE_IDLE;
\r
1223 p_new_cep->remote_comm_id = 0;
\r
1224 p_new_cep->remote_qpn = 0;
\r
1225 __cleanup_cep( p_new_cep );
\r
1228 __reject_req( p_port_cep, p_mad, reason );
\r
1230 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1232 if( reason == IB_REJ_STALE_CONN && status == IB_SUCCESS )
\r
1233 __process_cep( p_stale_cep );
\r
1235 AL_EXIT( AL_DBG_CM );
\r
1241 IN OUT kcep_t* const p_cep,
\r
1242 IN const mad_cm_rep_t* const p_rep )
\r
1244 AL_ENTER( AL_DBG_CM );
\r
1246 /* The send should have been cancelled during MRA processing. */
\r
1247 p_cep->state = CEP_STATE_REP_RCVD;
\r
1249 /* Store pertinent information in the connection. */
\r
1250 p_cep->remote_comm_id = p_rep->local_comm_id;
\r
1251 p_cep->remote_ca_guid = p_rep->local_ca_guid;
\r
1253 p_cep->remote_qpn = conn_rep_get_lcl_qpn( p_rep );
\r
1255 /* Store the remote endpoint's target ACK delay. */
\r
1256 p_cep->target_ack_delay = conn_rep_get_target_ack_delay( p_rep );
\r
1258 /* Update the local ACK delay stored in the AV's. */
\r
1259 p_cep->av[0].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1260 p_cep->av[0].attr.conn.local_ack_timeout, p_cep->target_ack_delay );
\r
1261 p_cep->av[0].attr.conn.rnr_retry_cnt = conn_rep_get_rnr_retry_cnt( p_rep );
\r
1263 if( p_cep->av[1].port_guid )
\r
1265 p_cep->av[1].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(
\r
1266 p_cep->av[1].attr.conn.local_ack_timeout,
\r
1267 p_cep->target_ack_delay );
\r
1268 p_cep->av[1].attr.conn.rnr_retry_cnt =
\r
1269 p_cep->av[0].attr.conn.rnr_retry_cnt;
\r
1272 p_cep->init_depth = p_rep->resp_resources;
\r
1273 p_cep->resp_res = p_rep->initiator_depth;
\r
1275 p_cep->sq_psn = conn_rep_get_starting_psn( p_rep );
\r
1277 /* Cache the private data. */
\r
1278 p_cep->psize = IB_REP_PDATA_SIZE;
\r
1279 memcpy( p_cep->pdata, p_rep->pdata, IB_REP_PDATA_SIZE );
\r
1281 AL_EXIT( AL_DBG_CM );
\r
1287 IN ib_mad_element_t* const p_mad )
\r
1289 ib_api_status_t status;
\r
1290 mad_cm_mra_t *p_mra;
\r
1292 KLOCK_QUEUE_HANDLE hdl;
\r
1294 AL_ENTER( AL_DBG_CM );
\r
1296 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1298 p_mra = (mad_cm_mra_t*)p_mad->p_mad_buf;
\r
1300 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1301 p_cep = __lookup_cep( NULL, p_mra->remote_comm_id );
\r
1304 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1305 ("MRA received that could not be matched.\n") );
\r
1309 if( p_cep->remote_comm_id )
\r
1311 if( p_cep->remote_comm_id != p_mra->local_comm_id )
\r
1313 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1314 ("MRA received that could not be matched.\n") );
\r
1320 * Note that we don't update the CEP's remote comm ID - it messes up REP
\r
1321 * processing since a non-zero RCID implies the connection is in the RCID
\r
1322 * map. Adding it here requires checking there and conditionally adding
\r
1323 * it. Ignoring it is a valid thing to do.
\r
1325 if( !(p_cep->state & CEP_STATE_SENT) ||
\r
1326 (1 << conn_mra_get_msg_mraed( p_mra ) !=
\r
1327 (p_cep->state & CEP_MSG_MASK)) )
\r
1329 /* Invalid state. */
\r
1330 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("MRA received in invalid state.\n") );
\r
1334 /* Delay the current send. */
\r
1335 CL_ASSERT( p_cep->p_send_mad );
\r
1336 ib_delay_mad( p_cep->h_mad_svc, p_cep->p_send_mad,
\r
1337 __calc_mad_timeout( conn_mra_get_svc_timeout( p_mra ) ) +
\r
1338 __calc_mad_timeout( p_cep->max_2pkt_life - 1 ) );
\r
1340 /* We only invoke a single callback for MRA. */
\r
1341 if( p_cep->state & CEP_STATE_MRA )
\r
1343 /* Invalid state. */
\r
1344 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Already received MRA.\n") );
\r
1348 p_cep->state |= CEP_STATE_MRA;
\r
1350 status = __cep_queue_mad( p_cep, p_mad );
\r
1352 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1354 if( status == IB_SUCCESS )
\r
1355 __process_cep( p_cep );
\r
1357 AL_EXIT( AL_DBG_CM );
\r
1361 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1362 ib_put_mad( p_mad );
\r
1363 AL_EXIT( AL_DBG_CM );
\r
1369 IN ib_mad_element_t* const p_mad )
\r
1371 ib_api_status_t status;
\r
1372 mad_cm_rej_t *p_rej;
\r
1373 kcep_t *p_cep = NULL;
\r
1374 KLOCK_QUEUE_HANDLE hdl;
\r
1377 AL_ENTER( AL_DBG_CM );
\r
1379 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1381 p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;
\r
1383 /* Either one of the communication IDs must be set. */
\r
1384 if( !p_rej->remote_comm_id && !p_rej->local_comm_id )
\r
1387 /* Check the pending list by the remote CA GUID and connection ID. */
\r
1388 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1389 if( p_rej->remote_comm_id )
\r
1391 p_cep = __lookup_cep( NULL, p_rej->remote_comm_id );
\r
1393 else if( p_rej->reason == IB_REJ_TIMEOUT &&
\r
1394 conn_rej_get_ari_len( p_rej ) == sizeof(net64_t) )
\r
1396 cl_memcpy( &ca_guid, p_rej->ari, sizeof(net64_t) );
\r
1397 p_cep = __lookup_by_id( p_rej->local_comm_id, ca_guid );
\r
1405 if( p_cep->remote_comm_id &&
\r
1406 p_cep->remote_comm_id != p_rej->local_comm_id )
\r
1409 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1411 ib_put_mad( p_mad );
\r
1412 AL_EXIT( AL_DBG_CM );
\r
1416 status = __process_rej( p_cep, p_mad );
\r
1418 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1420 if( status == IB_SUCCESS )
\r
1421 __process_cep( p_cep );
\r
1423 AL_EXIT( AL_DBG_CM );
\r
1429 IN cep_agent_t* const p_port_cep,
\r
1430 IN ib_mad_element_t* const p_mad )
\r
1432 ib_api_status_t status;
\r
1433 mad_cm_rep_t *p_rep;
\r
1435 KLOCK_QUEUE_HANDLE hdl;
\r
1436 cep_state_t old_state;
\r
1438 AL_ENTER( AL_DBG_CM );
\r
1440 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1442 p_rep = (mad_cm_rep_t*)p_mad->p_mad_buf;
\r
1444 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1445 ("REP: comm_id (x%x) received\n", p_rep->local_comm_id ) );
\r
1447 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1448 p_cep = __lookup_cep( NULL, p_rep->remote_comm_id );
\r
1451 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1452 ib_put_mad( p_mad );
\r
1453 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1454 ("REP received that could not be matched.\n") );
\r
1458 switch( p_cep->state )
\r
1460 case CEP_STATE_REQ_MRA_RCVD:
\r
1461 case CEP_STATE_REQ_SENT:
\r
1462 old_state = p_cep->state;
\r
1463 /* Save pertinent information and change state. */
\r
1464 __save_wire_rep( p_cep, p_rep );
\r
1466 if( __insert_cep( p_cep ) != p_cep )
\r
1468 /* Roll back the state change. */
\r
1469 __reject_mad( p_port_cep, p_cep, p_mad, IB_REJ_STALE_CONN );
\r
1470 p_cep->state = old_state;
\r
1471 status = __process_stale( p_cep );
\r
1476 * Cancel any outstanding send. Note that we do this only after
\r
1477 * inserting the CEP - if we failed, then the send will timeout
\r
1478 * and we'll finish our way through the state machine.
\r
1480 if( p_cep->p_send_mad )
\r
1482 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1483 p_cep->p_send_mad = NULL;
\r
1486 status = __cep_queue_mad( p_cep, p_mad );
\r
1489 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1491 if( status == IB_SUCCESS )
\r
1492 __process_cep( p_cep );
\r
1494 AL_EXIT( AL_DBG_CM );
\r
1497 case CEP_STATE_ESTABLISHED:
\r
1498 case CEP_STATE_LAP_RCVD:
\r
1499 case CEP_STATE_LAP_SENT:
\r
1500 case CEP_STATE_LAP_MRA_RCVD:
\r
1501 case CEP_STATE_LAP_MRA_SENT:
\r
1502 case CEP_STATE_REP_MRA_SENT:
\r
1503 /* Repeate the MRA or RTU. */
\r
1504 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1508 ib_put_mad( p_mad );
\r
1509 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REP received in invalid state.\n") );
\r
1513 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1515 AL_EXIT( AL_DBG_CM );
\r
1521 IN ib_mad_element_t* const p_mad )
\r
1523 ib_api_status_t status;
\r
1524 mad_cm_rtu_t *p_rtu;
\r
1526 KLOCK_QUEUE_HANDLE hdl;
\r
1528 AL_ENTER( AL_DBG_CM );
\r
1530 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1532 p_rtu = (mad_cm_rtu_t*)p_mad->p_mad_buf;
\r
1534 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1535 ("RTU: comm_id (x%x) received\n", p_rtu->local_comm_id) );
\r
1537 /* Find the connection by local connection ID. */
\r
1538 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1539 p_cep = __lookup_cep( NULL, p_rtu->remote_comm_id );
\r
1540 if( !p_cep || p_cep->remote_comm_id != p_rtu->local_comm_id )
\r
1542 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received that could not be matched.\n") );
\r
1546 switch( p_cep->state )
\r
1548 case CEP_STATE_REP_SENT:
\r
1549 case CEP_STATE_REP_MRA_RCVD:
\r
1550 /* Cancel any outstanding send. */
\r
1551 if( p_cep->p_send_mad )
\r
1553 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1554 p_cep->p_send_mad = NULL;
\r
1557 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1559 status = __cep_queue_mad( p_cep, p_mad );
\r
1561 /* Update timewait time. */
\r
1562 __calc_timewait( p_cep );
\r
1564 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1566 if( status == IB_SUCCESS )
\r
1567 __process_cep( p_cep );
\r
1569 AL_EXIT( AL_DBG_CM );
\r
1573 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received in invalid state.\n") );
\r
1578 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1579 ib_put_mad( p_mad );
\r
1580 AL_EXIT( AL_DBG_CM );
\r
1586 IN cep_agent_t* const p_port_cep,
\r
1587 IN ib_mad_element_t* const p_mad )
\r
1589 ib_api_status_t status;
\r
1590 mad_cm_dreq_t *p_dreq;
\r
1592 KLOCK_QUEUE_HANDLE hdl;
\r
1594 AL_ENTER( AL_DBG_CM );
\r
1596 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1598 p_dreq = (mad_cm_dreq_t*)p_mad->p_mad_buf;
\r
1600 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
1601 ("DREQ: comm_id (x%x) qpn (x%x) received\n",
\r
1602 p_dreq->local_comm_id, conn_dreq_get_remote_qpn( p_dreq )) );
\r
1604 /* Find the connection by connection IDs. */
\r
1605 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1606 p_cep = __lookup_cep( NULL, p_dreq->remote_comm_id );
\r
1608 p_cep->remote_comm_id != p_dreq->local_comm_id ||
\r
1609 p_cep->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )
\r
1611 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received that could not be matched.\n") );
\r
1612 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1613 ib_put_mad( p_mad );
\r
1614 AL_EXIT( AL_DBG_CM );
\r
1618 switch( p_cep->state )
\r
1620 case CEP_STATE_REP_SENT:
\r
1621 case CEP_STATE_REP_MRA_RCVD:
\r
1622 case CEP_STATE_DREQ_SENT:
\r
1623 /* Cancel the outstanding MAD. */
\r
1624 if( p_cep->p_send_mad )
\r
1626 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1627 p_cep->p_send_mad = NULL;
\r
1630 /* Fall through and process as DREQ received case. */
\r
1631 case CEP_STATE_ESTABLISHED:
\r
1632 case CEP_STATE_LAP_RCVD:
\r
1633 case CEP_STATE_LAP_SENT:
\r
1634 case CEP_STATE_LAP_MRA_RCVD:
\r
1635 case CEP_STATE_LAP_MRA_SENT:
\r
1636 p_cep->state = CEP_STATE_DREQ_RCVD;
\r
1638 status = __cep_queue_mad( p_cep, p_mad );
\r
1640 /* Store the TID for use in the reply DREP. */
\r
1641 p_cep->tid = p_dreq->hdr.trans_id;
\r
1643 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1645 if( status == IB_SUCCESS )
\r
1646 __process_cep( p_cep );
\r
1647 AL_EXIT( AL_DBG_CM );
\r
1650 case CEP_STATE_TIMEWAIT:
\r
1651 case CEP_STATE_DESTROY:
\r
1652 /* Repeat the DREP. */
\r
1653 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1656 case CEP_STATE_DREQ_DESTROY:
\r
1657 /* Send the DREP with no private data. */
\r
1659 ib_put_mad( p_mad ); /* release DREQ MAD */
\r
1661 status = __cep_get_mad( p_cep, CM_DREP_ATTR_ID, &(cep_agent_t*)p_port_cep,
\r
1662 &(ib_mad_element_t*)p_mad );
\r
1663 if( status != IB_SUCCESS )
\r
1666 p_mad->p_mad_buf->attr_id = CM_DREP_ATTR_ID;
\r
1667 /* __format_drep returns always SUCCESS while no private data */
\r
1668 __format_drep( p_cep, NULL, 0, (mad_cm_drep_t*)p_mad->p_mad_buf );
\r
1669 __cep_send_mad( p_port_cep, p_mad );
\r
1673 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received in invalid state.\n") );
\r
1674 case CEP_STATE_DREQ_RCVD:
\r
1675 ib_put_mad( p_mad );
\r
1679 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1680 AL_EXIT( AL_DBG_CM );
\r
1686 IN ib_mad_element_t* const p_mad )
\r
1688 ib_api_status_t status;
\r
1689 mad_cm_drep_t *p_drep;
\r
1691 KLOCK_QUEUE_HANDLE hdl;
\r
1693 AL_ENTER( AL_DBG_CM );
\r
1695 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1697 p_drep = (mad_cm_drep_t*)p_mad->p_mad_buf;
\r
1699 /* Find the connection by local connection ID. */
\r
1700 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1701 p_cep = __lookup_cep( NULL, p_drep->remote_comm_id );
\r
1702 if( !p_cep || p_cep->remote_comm_id != p_drep->local_comm_id )
\r
1704 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received that could not be matched.\n") );
\r
1705 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1706 ib_put_mad( p_mad );
\r
1707 AL_EXIT( AL_DBG_CM );
\r
1711 if( p_cep->state != CEP_STATE_DREQ_SENT &&
\r
1712 p_cep->state != CEP_STATE_DREQ_DESTROY )
\r
1714 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received in invalid state.\n") );
\r
1716 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1717 ib_put_mad( p_mad );
\r
1718 AL_EXIT( AL_DBG_CM );
\r
1722 /* Cancel the DREQ. */
\r
1723 if( p_cep->p_send_mad )
\r
1725 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1726 p_cep->p_send_mad = NULL;
\r
1729 if( p_cep->state == CEP_STATE_DREQ_SENT )
\r
1731 p_cep->state = CEP_STATE_TIMEWAIT;
\r
1733 status = __cep_queue_mad( p_cep, p_mad );
\r
1737 /* State is DREQ_DESTROY - move to DESTROY to allow cleanup. */
\r
1738 CL_ASSERT( p_cep->state == CEP_STATE_DREQ_DESTROY );
\r
1739 p_cep->state = CEP_STATE_DESTROY;
\r
1741 ib_put_mad( p_mad );
\r
1742 status = IB_INVALID_STATE;
\r
1745 __insert_timewait( p_cep );
\r
1747 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1749 if( status == IB_SUCCESS )
\r
1750 __process_cep( p_cep );
\r
1752 AL_EXIT( AL_DBG_CM );
\r
1758 IN kcep_t* const p_cep,
\r
1759 IN const lap_path_info_t* const p_path )
\r
1761 cep_agent_t *p_port_cep;
\r
1763 AL_ENTER( AL_DBG_CM );
\r
1765 CL_ASSERT( p_cep );
\r
1766 CL_ASSERT( p_path );
\r
1768 cl_memclr( &p_cep->alt_av, sizeof(kcep_av_t) );
\r
1770 p_port_cep = __find_port_cep( &p_path->remote_gid, p_path->remote_lid,
\r
1771 p_cep->pkey, &p_cep->alt_av.pkey_index );
\r
1774 AL_EXIT( AL_DBG_CM );
\r
1778 if( p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )
\r
1780 AL_EXIT( AL_DBG_CM );
\r
1784 p_cep->alt_av.port_guid = p_port_cep->port_guid;
\r
1785 p_cep->alt_av.attr.port_num = p_port_cep->port_num;
\r
1787 p_cep->alt_av.attr.sl = conn_lap_path_get_svc_lvl( p_path );
\r
1788 p_cep->alt_av.attr.dlid = p_path->local_lid;
\r
1790 if( !conn_lap_path_get_subn_lcl( p_path ) )
\r
1792 p_cep->alt_av.attr.grh_valid = TRUE;
\r
1793 p_cep->alt_av.attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(
\r
1794 1, conn_lap_path_get_tclass( p_path ),
\r
1795 conn_lap_path_get_flow_lbl( p_path ) );
\r
1796 p_cep->alt_av.attr.grh.hop_limit = p_path->hop_limit;
\r
1797 p_cep->alt_av.attr.grh.dest_gid = p_path->local_gid;
\r
1798 p_cep->alt_av.attr.grh.src_gid = p_path->remote_gid;
\r
1802 p_cep->alt_av.attr.grh_valid = FALSE;
\r
1804 p_cep->alt_av.attr.static_rate = conn_lap_path_get_pkt_rate( p_path );
\r
1805 p_cep->alt_av.attr.path_bits =
\r
1806 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);
\r
1809 * Note that while we never use the connected AV attributes internally,
\r
1810 * we store them so we can pass them back to users. For the LAP, we
\r
1811 * first copy the settings from the current primary - MTU and retry
\r
1812 * counts are only specified in the REQ.
\r
1814 p_cep->alt_av.attr.conn = p_cep->av[p_cep->idx_primary].attr.conn;
\r
1815 p_cep->alt_av.attr.conn.local_ack_timeout =
\r
1816 conn_lap_path_get_lcl_ack_timeout( p_path );
\r
1818 AL_EXIT( AL_DBG_CM );
\r
1825 IN cep_agent_t* const p_port_cep,
\r
1826 IN ib_mad_element_t* const p_mad )
\r
1828 ib_api_status_t status;
\r
1829 mad_cm_lap_t *p_lap;
\r
1831 KLOCK_QUEUE_HANDLE hdl;
\r
1833 AL_ENTER( AL_DBG_CM );
\r
1835 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1837 p_lap = (mad_cm_lap_t*)p_mad->p_mad_buf;
\r
1839 /* Find the connection by local connection ID. */
\r
1840 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1841 p_cep = __lookup_cep( NULL, p_lap->remote_comm_id );
\r
1842 if( !p_cep || p_cep->remote_comm_id != p_lap->local_comm_id )
\r
1844 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1845 ib_put_mad( p_mad );
\r
1846 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received that could not be matched.\n") );
\r
1850 switch( p_cep->state )
\r
1852 case CEP_STATE_REP_SENT:
\r
1853 case CEP_STATE_REP_MRA_RCVD:
\r
1855 * These two cases handle the RTU being dropped. Receipt of
\r
1856 * a LAP indicates that the connection is established.
\r
1858 case CEP_STATE_ESTABLISHED:
\r
1860 * We don't check for other "established" states related to
\r
1861 * alternate path management (CEP_STATE_LAP_RCVD, etc)
\r
1864 /* We only support receiving LAP if we took the passive role. */
\r
1865 if( p_cep->was_active )
\r
1867 ib_put_mad( p_mad );
\r
1871 /* Store the transaction ID for use during the LAP exchange. */
\r
1872 p_cep->tid = p_lap->hdr.trans_id;
\r
1875 * Copy the path record into the connection for use when
\r
1876 * sending the APR and loading the path.
\r
1878 if( !__format_lap_av( p_cep, &p_lap->alternate_path ) )
\r
1880 /* Trap an invalid path. */
\r
1881 ib_put_mad( p_mad );
\r
1885 p_cep->state = CEP_STATE_LAP_RCVD;
\r
1887 status = __cep_queue_mad( p_cep, p_mad );
\r
1889 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1891 if( status == IB_SUCCESS )
\r
1892 __process_cep( p_cep );
\r
1894 AL_EXIT( AL_DBG_CM );
\r
1897 case CEP_STATE_LAP_MRA_SENT:
\r
1898 __repeat_mad( p_port_cep, p_cep, p_mad );
\r
1902 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received in invalid state.\n") );
\r
1903 ib_put_mad( p_mad );
\r
1907 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1908 AL_EXIT( AL_DBG_CM );
\r
1914 IN ib_mad_element_t* const p_mad )
\r
1916 ib_api_status_t status;
\r
1917 mad_cm_apr_t *p_apr;
\r
1919 KLOCK_QUEUE_HANDLE hdl;
\r
1921 AL_ENTER( AL_DBG_CM );
\r
1923 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1925 p_apr = (mad_cm_apr_t*)p_mad->p_mad_buf;
\r
1927 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
1928 p_cep = __lookup_cep( NULL, p_apr->remote_comm_id );
\r
1929 if( !p_cep || p_cep->remote_comm_id != p_apr->local_comm_id )
\r
1931 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received that could not be matched.\n") );
\r
1935 switch( p_cep->state )
\r
1937 case CEP_STATE_LAP_SENT:
\r
1938 case CEP_STATE_LAP_MRA_RCVD:
\r
1939 /* Cancel sending the LAP. */
\r
1940 if( p_cep->p_send_mad )
\r
1942 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );
\r
1943 p_cep->p_send_mad = NULL;
\r
1946 /* Copy the temporary alternate AV. */
\r
1947 p_cep->av[(p_cep->idx_primary + 1) & 0x1] = p_cep->alt_av;
\r
1949 /* Update the maximum packet lifetime. */
\r
1950 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life, p_cep->alt_2pkt_life );
\r
1952 /* Update the timewait time. */
\r
1953 __calc_timewait( p_cep );
\r
1955 p_cep->state = CEP_STATE_ESTABLISHED;
\r
1957 status = __cep_queue_mad( p_cep, p_mad );
\r
1959 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1961 if( status == IB_SUCCESS )
\r
1962 __process_cep( p_cep );
\r
1964 AL_EXIT( AL_DBG_CM );
\r
1968 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received in invalid state.\n") );
\r
1973 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
1974 ib_put_mad( p_mad );
\r
1975 AL_EXIT( AL_DBG_CM );
\r
1980 __cep_mad_recv_cb(
\r
1981 IN ib_mad_svc_handle_t h_mad_svc,
\r
1983 IN ib_mad_element_t *p_mad )
\r
1985 cep_agent_t *p_port_cep;
\r
1988 AL_ENTER( AL_DBG_CM );
\r
1990 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
1992 UNUSED_PARAM( h_mad_svc );
\r
1993 p_port_cep = (cep_agent_t*)context;
\r
1995 CL_ASSERT( p_mad->p_next == NULL );
\r
1997 p_hdr = (ib_mad_t*)p_mad->p_mad_buf;
\r
2000 * TODO: Add filtering in all the handlers for unsupported class version.
\r
2001 * See 12.6.7.2 Rejection Reason, code 31.
\r
2004 switch( p_hdr->attr_id )
\r
2006 case CM_REQ_ATTR_ID:
\r
2007 __req_handler( p_port_cep, p_mad );
\r
2010 case CM_MRA_ATTR_ID:
\r
2011 __mra_handler( p_mad );
\r
2014 case CM_REJ_ATTR_ID:
\r
2015 __rej_handler( p_mad );
\r
2018 case CM_REP_ATTR_ID:
\r
2019 __rep_handler( p_port_cep, p_mad );
\r
2022 case CM_RTU_ATTR_ID:
\r
2023 __rtu_handler( p_mad );
\r
2026 case CM_DREQ_ATTR_ID:
\r
2027 __dreq_handler( p_port_cep, p_mad );
\r
2030 case CM_DREP_ATTR_ID:
\r
2031 __drep_handler( p_mad );
\r
2034 case CM_LAP_ATTR_ID:
\r
2035 __lap_handler( p_port_cep, p_mad );
\r
2038 case CM_APR_ATTR_ID:
\r
2039 __apr_handler( p_mad );
\r
2042 case CM_SIDR_REQ_ATTR_ID:
\r
2043 // p_async_mad->item.pfn_callback = __process_cm_sidr_req;
\r
2046 case CM_SIDR_REP_ATTR_ID:
\r
2047 // p_async_mad->item.pfn_callback = __process_cm_sidr_rep;
\r
2051 ib_put_mad( p_mad );
\r
2052 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2053 ("Invalid CM MAD attribute ID.\n") );
\r
2057 AL_EXIT( AL_DBG_CM );
\r
2061 static inline cep_agent_t*
\r
2063 IN kcep_t* const p_cep )
\r
2065 cl_map_item_t *p_item;
\r
2067 CL_ASSERT( p_cep );
\r
2069 /* Look up the primary CEP port agent */
\r
2070 p_item = cl_qmap_get( &gp_cep_mgr->port_map,
\r
2071 p_cep->av[p_cep->idx_primary].port_guid );
\r
2072 if( p_item == cl_qmap_end( &gp_cep_mgr->port_map ) )
\r
2075 return PARENT_STRUCT( p_item, cep_agent_t, item );
\r
2079 static inline void
\r
2081 OUT ib_mad_element_t* const p_mad,
\r
2082 IN kcep_av_t* const p_av )
\r
2084 /* Set the addressing information in the MAD. */
\r
2085 p_mad->grh_valid = p_av->attr.grh_valid;
\r
2086 if( p_av->attr.grh_valid )
\r
2087 cl_memcpy( p_mad->p_grh, &p_av->attr.grh, sizeof(ib_grh_t) );
\r
2089 p_mad->remote_sl = p_av->attr.sl;
\r
2090 p_mad->remote_lid = p_av->attr.dlid;
\r
2091 p_mad->path_bits = p_av->attr.path_bits;
\r
2092 p_mad->pkey_index = p_av->pkey_index;
\r
2093 p_mad->remote_qp = IB_QP1;
\r
2094 p_mad->send_opt = IB_SEND_OPT_SIGNALED;
\r
2095 p_mad->remote_qkey = IB_QP1_WELL_KNOWN_Q_KEY;
\r
2096 /* Let the MAD service manage the AV for us. */
\r
2097 p_mad->h_av = NULL;
\r
2101 static ib_api_status_t
\r
2103 IN cep_agent_t* const p_port_cep,
\r
2104 IN ib_mad_element_t* const p_mad )
\r
2106 ib_api_status_t status;
\r
2108 AL_ENTER( AL_DBG_CM );
\r
2110 CL_ASSERT( p_port_cep );
\r
2111 CL_ASSERT( p_mad );
\r
2113 /* Use the mad's attributes already present */
\r
2114 p_mad->resp_expected = FALSE;
\r
2115 p_mad->retry_cnt = 0;
\r
2116 p_mad->timeout_ms = 0;
\r
2118 /* Clear the contexts since the send isn't associated with a CEP. */
\r
2119 p_mad->context1 = NULL;
\r
2120 p_mad->context2 = NULL;
\r
2122 status = ib_send_mad( p_port_cep->h_mad_svc, p_mad, NULL );
\r
2123 if( status != IB_SUCCESS )
\r
2125 ib_put_mad( p_mad );
\r
2126 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2127 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2130 AL_EXIT( AL_DBG_CM );
\r
2135 static ib_api_status_t
\r
2137 IN cep_agent_t* const p_port_cep,
\r
2138 IN kcep_t* const p_cep,
\r
2139 IN ib_mad_element_t* const p_mad )
\r
2141 ib_api_status_t status;
\r
2143 AL_ENTER( AL_DBG_CM );
\r
2145 CL_ASSERT( p_cep );
\r
2146 CL_ASSERT( p_mad );
\r
2147 CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||
\r
2148 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID ||
\r
2149 p_mad->p_mad_buf->attr_id == CM_LAP_ATTR_ID ||
\r
2150 p_mad->p_mad_buf->attr_id == CM_DREQ_ATTR_ID );
\r
2153 * REQ, REP, and DREQ are retried until either a response is
\r
2154 * received or the operation times out.
\r
2156 p_mad->resp_expected = TRUE;
\r
2157 p_mad->retry_cnt = p_cep->max_cm_retries;
\r
2158 p_mad->timeout_ms = p_cep->retry_timeout;
\r
2160 CL_ASSERT( !p_cep->p_send_mad );
\r
2162 /* Store the mad & mad service handle in the CEP for cancelling. */
\r
2163 p_cep->h_mad_svc = p_port_cep->h_mad_svc;
\r
2164 p_cep->p_send_mad = p_mad;
\r
2166 /* reference the connection for which we are sending the MAD. */
\r
2167 cl_atomic_inc( &p_cep->ref_cnt );
\r
2169 /* Set the context. */
\r
2170 p_mad->context1 = p_cep;
\r
2171 p_mad->context2 = NULL;
\r
2173 /* Fire in the hole! */
\r
2174 status = ib_send_mad( p_cep->h_mad_svc, p_mad, NULL );
\r
2175 if( status != IB_SUCCESS )
\r
2178 * Note that we don't need to check for destruction here since
\r
2179 * we're holding the global lock.
\r
2181 cl_atomic_dec( &p_cep->ref_cnt );
\r
2182 p_cep->p_send_mad = NULL;
\r
2183 ib_put_mad( p_mad );
\r
2184 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2185 ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );
\r
2188 AL_EXIT( AL_DBG_CM );
\r
2194 __cep_mad_send_cb(
\r
2195 IN ib_mad_svc_handle_t h_mad_svc,
\r
2197 IN ib_mad_element_t *p_mad )
\r
2199 ib_api_status_t status;
\r
2200 cep_agent_t *p_port_cep;
\r
2202 KLOCK_QUEUE_HANDLE hdl;
\r
2203 ib_pfn_destroy_cb_t pfn_destroy_cb;
\r
2204 void *cep_context;
\r
2206 AL_ENTER( AL_DBG_CM );
\r
2208 UNUSED_PARAM( h_mad_svc );
\r
2209 CL_ASSERT( p_mad->p_next == NULL );
\r
2210 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
2212 p_port_cep = (cep_agent_t*)context;
\r
2214 p_cep = (kcep_t*)p_mad->context1;
\r
2217 * The connection context is not set when performing immediate responses,
\r
2218 * such as repeating MADS.
\r
2222 ib_put_mad( p_mad );
\r
2223 AL_EXIT( AL_DBG_CM );
\r
2227 p_mad->context1 = NULL;
\r
2229 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
2230 /* Clear the sent MAD pointer so that we don't try cancelling again. */
\r
2231 if( p_cep->p_send_mad == p_mad )
\r
2232 p_cep->p_send_mad = NULL;
\r
2234 switch( p_mad->status )
\r
2236 case IB_WCS_SUCCESS:
\r
2237 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2238 ib_put_mad( p_mad );
\r
2241 case IB_WCS_CANCELED:
\r
2242 if( p_cep->state != CEP_STATE_REQ_SENT &&
\r
2243 p_cep->state != CEP_STATE_REQ_MRA_RCVD &&
\r
2244 p_cep->state != CEP_STATE_REP_SENT &&
\r
2245 p_cep->state != CEP_STATE_REP_MRA_RCVD &&
\r
2246 p_cep->state != CEP_STATE_LAP_SENT &&
\r
2247 p_cep->state != CEP_STATE_LAP_MRA_RCVD &&
\r
2248 p_cep->state != CEP_STATE_DREQ_SENT &&
\r
2249 p_cep->state != CEP_STATE_SREQ_SENT )
\r
2251 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2252 ib_put_mad( p_mad );
\r
2255 /* Treat as a timeout so we don't stall the state machine. */
\r
2256 p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;
\r
2258 /* Fall through. */
\r
2259 case IB_WCS_TIMEOUT_RETRY_ERR:
\r
2261 /* Timeout. Reject the connection. */
\r
2262 switch( p_cep->state )
\r
2264 case CEP_STATE_REQ_SENT:
\r
2265 case CEP_STATE_REQ_MRA_RCVD:
\r
2266 case CEP_STATE_REP_SENT:
\r
2267 case CEP_STATE_REP_MRA_RCVD:
\r
2268 /* Send the REJ. */
\r
2269 __reject_timeout( p_port_cep, p_cep, p_mad );
\r
2270 __remove_cep( p_cep );
\r
2271 p_cep->state = CEP_STATE_IDLE;
\r
2274 case CEP_STATE_DREQ_DESTROY:
\r
2275 p_cep->state = CEP_STATE_DESTROY;
\r
2276 __insert_timewait( p_cep );
\r
2277 /* Fall through. */
\r
2279 case CEP_STATE_DESTROY:
\r
2280 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2281 ib_put_mad( p_mad );
\r
2284 case CEP_STATE_DREQ_SENT:
\r
2286 * Make up a DREP mad so we can respond if we receive
\r
2287 * a DREQ while in timewait.
\r
2289 __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );
\r
2290 __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );
\r
2291 p_cep->state = CEP_STATE_TIMEWAIT;
\r
2292 __insert_timewait( p_cep );
\r
2298 status = __cep_queue_mad( p_cep, p_mad );
\r
2299 CL_ASSERT( status != IB_INVALID_STATE );
\r
2300 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
2302 if( status == IB_SUCCESS )
\r
2303 __process_cep( p_cep );
\r
2308 pfn_destroy_cb = p_cep->pfn_destroy_cb;
\r
2309 cep_context = p_cep->context;
\r
2311 if( !cl_atomic_dec( &p_cep->ref_cnt ) && pfn_destroy_cb )
\r
2312 pfn_destroy_cb( cep_context );
\r
2313 AL_EXIT( AL_DBG_CM );
\r
2318 __cep_qp_event_cb(
\r
2319 IN ib_async_event_rec_t *p_event_rec )
\r
2321 UNUSED_PARAM( p_event_rec );
\r
2324 * Most of the QP events are trapped by the real owner of the QP.
\r
2325 * For real events, the CM may not be able to do much anyways!
\r
2330 static ib_api_status_t
\r
2332 IN cep_agent_t* const p_port_cep,
\r
2333 IN const ib_port_attr_t* const p_port_attr )
\r
2335 ib_api_status_t status;
\r
2336 ib_qp_create_t qp_create;
\r
2337 ib_mad_svc_t mad_svc;
\r
2339 AL_ENTER( AL_DBG_CM );
\r
2342 * Create the PD alias. We use the port CM's al_obj_t as the context
\r
2343 * to allow using deref_al_obj as the destroy callback.
\r
2345 status = ib_alloc_pd( p_port_cep->h_ca, IB_PDT_ALIAS, &p_port_cep->obj,
\r
2346 &p_port_cep->h_pd );
\r
2347 if( status != IB_SUCCESS )
\r
2349 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2350 ("ib_alloc_pd failed with status %s\n", ib_get_err_str(status)) );
\r
2353 /* Reference the port object on behalf of the PD. */
\r
2354 ref_al_obj( &p_port_cep->obj );
\r
2356 /* Create the MAD QP. */
\r
2357 cl_memclr( &qp_create, sizeof( ib_qp_create_t ) );
\r
2358 qp_create.qp_type = IB_QPT_QP1_ALIAS;
\r
2359 qp_create.rq_depth = CEP_MAD_RQ_DEPTH;
\r
2360 qp_create.sq_depth = CEP_MAD_SQ_DEPTH;
\r
2361 qp_create.rq_sge = CEP_MAD_RQ_SGE;
\r
2362 qp_create.sq_sge = CEP_MAD_SQ_SGE;
\r
2363 qp_create.sq_signaled = TRUE;
\r
2365 * We use the port CM's al_obj_t as the context to allow using
\r
2366 * deref_al_obj as the destroy callback.
\r
2368 status = ib_get_spl_qp( p_port_cep->h_pd, p_port_attr->port_guid,
\r
2369 &qp_create, &p_port_cep->obj, __cep_qp_event_cb, &p_port_cep->pool_key,
\r
2370 &p_port_cep->h_qp );
\r
2371 if( status != IB_SUCCESS )
\r
2373 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2374 ("ib_get_spl_qp failed with status %s\n", ib_get_err_str(status)) );
\r
2377 /* Reference the port object on behalf of the QP. */
\r
2378 ref_al_obj( &p_port_cep->obj );
\r
2380 /* Create the MAD service. */
\r
2381 cl_memclr( &mad_svc, sizeof(mad_svc) );
\r
2382 mad_svc.mad_svc_context = p_port_cep;
\r
2383 mad_svc.pfn_mad_recv_cb = __cep_mad_recv_cb;
\r
2384 mad_svc.pfn_mad_send_cb = __cep_mad_send_cb;
\r
2385 mad_svc.support_unsol = TRUE;
\r
2386 mad_svc.mgmt_class = IB_MCLASS_COMM_MGMT;
\r
2387 mad_svc.mgmt_version = IB_MCLASS_CM_VER_2;
\r
2388 mad_svc.method_array[IB_MAD_METHOD_SEND] = TRUE;
\r
2390 ib_reg_mad_svc( p_port_cep->h_qp, &mad_svc, &p_port_cep->h_mad_svc );
\r
2391 if( status != IB_SUCCESS )
\r
2393 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2394 ("ib_reg_mad_svc failed with status %s\n", ib_get_err_str(status)) );
\r
2398 AL_EXIT( AL_DBG_CM );
\r
2399 return IB_SUCCESS;
\r
2404 * Performs immediate cleanup of resources.
\r
2407 __destroying_port_cep(
\r
2408 IN al_obj_t *p_obj )
\r
2410 cep_agent_t *p_port_cep;
\r
2411 KLOCK_QUEUE_HANDLE hdl;
\r
2413 AL_ENTER( AL_DBG_CM );
\r
2415 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2417 if( p_port_cep->port_guid )
\r
2419 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2420 cl_qmap_remove_item( &gp_cep_mgr->port_map, &p_port_cep->item );
\r
2421 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2424 if( p_port_cep->h_qp )
\r
2426 ib_destroy_qp( p_port_cep->h_qp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2427 p_port_cep->h_qp = NULL;
\r
2430 if( p_port_cep->h_pd )
\r
2432 ib_dealloc_pd( p_port_cep->h_pd, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
2433 p_port_cep->h_pd = NULL;
\r
2436 AL_EXIT( AL_DBG_CM );
\r
2442 * Release all resources allocated by a port CM agent. Finishes any cleanup
\r
2443 * for a port agent.
\r
2447 IN al_obj_t *p_obj )
\r
2449 cep_agent_t *p_port_cep;
\r
2450 ib_port_attr_mod_t port_attr_mod;
\r
2452 AL_ENTER( AL_DBG_CM );
\r
2454 p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );
\r
2456 if( p_port_cep->h_ca )
\r
2458 /* Update local port attributes */
\r
2459 port_attr_mod.cap.cm = FALSE;
\r
2460 ib_modify_ca( p_port_cep->h_ca, p_port_cep->port_num,
\r
2461 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2463 deref_al_obj( &p_port_cep->h_ca->obj );
\r
2466 destroy_al_obj( &p_port_cep->obj );
\r
2467 cl_free( p_port_cep );
\r
2469 AL_EXIT( AL_DBG_CM );
\r
2474 * Create a port agent for a given port.
\r
2476 static ib_api_status_t
\r
2477 __create_port_cep(
\r
2478 IN ib_pnp_port_rec_t *p_pnp_rec )
\r
2480 cep_agent_t *p_port_cep;
\r
2481 ib_api_status_t status;
\r
2482 ib_port_attr_mod_t port_attr_mod;
\r
2483 KLOCK_QUEUE_HANDLE hdl;
\r
2485 AL_ENTER( AL_DBG_CM );
\r
2487 /* calculate size of port_cm struct */
\r
2488 p_port_cep = (cep_agent_t*)cl_zalloc( sizeof(cep_agent_t) );
\r
2491 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2492 ("Failed to cl_zalloc port CM agent.\n") );
\r
2493 return IB_INSUFFICIENT_MEMORY;
\r
2496 construct_al_obj( &p_port_cep->obj, AL_OBJ_TYPE_CM );
\r
2498 status = init_al_obj( &p_port_cep->obj, p_port_cep, TRUE,
\r
2499 __destroying_port_cep, NULL, __free_port_cep );
\r
2500 if( status != IB_SUCCESS )
\r
2502 __free_port_cep( &p_port_cep->obj );
\r
2503 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2504 ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );
\r
2508 /* Attach to the global CM object. */
\r
2509 status = attach_al_obj( &gp_cep_mgr->obj, &p_port_cep->obj );
\r
2510 if( status != IB_SUCCESS )
\r
2512 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2513 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2514 ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );
\r
2518 p_port_cep->port_guid = p_pnp_rec->p_port_attr->port_guid;
\r
2519 p_port_cep->port_num = p_pnp_rec->p_port_attr->port_num;
\r
2520 p_port_cep->base_lid = p_pnp_rec->p_port_attr->lid;
\r
2522 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );
\r
2524 &gp_cep_mgr->port_map, p_port_cep->port_guid, &p_port_cep->item );
\r
2525 KeReleaseInStackQueuedSpinLock( &hdl );
\r
2527 /* Get a reference to the CA on which we are loading. */
\r
2528 p_port_cep->h_ca = acquire_ca( p_pnp_rec->p_ca_attr->ca_guid );
\r
2529 if( !p_port_cep->h_ca )
\r
2531 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2532 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("acquire_ca failed.\n") );
\r
2533 return IB_INVALID_GUID; }
\r
2535 status = __init_data_svc( p_port_cep, p_pnp_rec->p_port_attr );
\r
2536 if( status != IB_SUCCESS )
\r
2538 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );
\r
2539 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
2540 ("__init_data_svc failed with status %s.\n",
\r
2541 ib_get_err_str(status)) );
\r
2545 /* Update local port attributes */
\r
2546 cl_memclr( &port_attr_mod, sizeof(ib_port_attr_mod_t) );
\r
2547 port_attr_mod.cap.cm = TRUE;
\r
2548 status = ib_modify_ca( p_port_cep->h_ca, p_pnp_rec->p_port_attr->port_num,
\r
2549 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );
\r
2551 /* Update the PNP context to reference this port. */
\r
2552 p_pnp_rec->pnp_rec.context = p_port_cep;
\r
2554 /* Release the reference taken in init_al_obj. */
\r
2555 deref_al_obj( &p_port_cep->obj );
\r
2557 AL_EXIT( AL_DBG_CM );
\r
2558 return IB_SUCCESS;
\r
2562 /******************************************************************************
\r
2563 * Global CEP manager
\r
2564 ******************************************************************************/
\r
2568 OUT net32_t* const p_cid )
\r
2570 cl_status_t status;
\r
2571 uint32_t size, cid;
\r
2572 cep_cid_t *p_cep_cid;
\r
2574 AL_ENTER( AL_DBG_CM );
\r
2576 size = (uint32_t)cl_vector_get_size( &gp_cep_mgr->cid_vector );
\r
2577 cid = gp_cep_mgr->free_cid;
\r
2578 if( gp_cep_mgr->free_cid == size )
\r
2580 /* Grow the vector pool. */
\r
2582 cl_vector_set_size( &gp_cep_mgr->cid_vector, size + CEP_CID_GROW );
\r
2583 if( status != CL_SUCCESS )
\r
2585 AL_EXIT( AL_DBG_CM );
\r
2589 * Return the the start of the free list since the
\r
2590 * entry initializer incremented it.
\r
2592 gp_cep_mgr->free_cid = size;
\r
2595 /* Get the next free entry. */
\r
2596 p_cep_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, cid );
\r
2598 /* Update the next entry index. */
\r
2599 gp_cep_mgr->free_cid = (uint32_t)(uintn_t)p_cep_cid->p_cep;
\r
2603 AL_EXIT( AL_DBG_CM );
\r
2608 static inline kcep_t*
\r
2610 IN ib_al_handle_t h_al OPTIONAL,
\r
2616 /* Mask off the counter bits so we get the index in our vector. */
\r
2617 idx = cid & CEP_MAX_CID_MASK;
\r
2619 if( idx >= cl_vector_get_size( &gp_cep_mgr->cid_vector ) )
\r
2622 p_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, idx );
\r
2623 if( !p_cid->h_al )
\r
2627 * h_al is NULL when processing MADs, so we need to match on
\r
2628 * the actual local communication ID. If h_al is non-NULL, we
\r
2629 * are doing a lookup from a call to our API, and only need to match
\r
2630 * on the index in the vector (without the modifier).
\r
2634 if( p_cid->h_al != h_al )
\r
2637 else if( p_cid->p_cep->local_comm_id != cid )
\r
2642 return p_cid->p_cep;
\r
2647 * Lookup a CEP by remote comm ID and CA GUID.
\r
2651 IN net32_t remote_comm_id,
\r
2652 IN net64_t remote_ca_guid )
\r
2654 cl_rbmap_item_t *p_item;
\r
2657 AL_ENTER( AL_DBG_CM );
\r
2659 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2660 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2661 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2663 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2665 if( remote_comm_id < p_cep->remote_comm_id )
\r
2666 p_item = cl_rbmap_left( p_item );
\r
2667 else if( remote_comm_id > p_cep->remote_comm_id )
\r
2668 p_item = cl_rbmap_right( p_item );
\r
2669 else if( remote_ca_guid < p_cep->remote_ca_guid )
\r
2670 p_item = cl_rbmap_left( p_item );
\r
2671 else if( remote_ca_guid > p_cep->remote_ca_guid )
\r
2672 p_item = cl_rbmap_right( p_item );
\r
2677 AL_EXIT( AL_DBG_CM );
\r
2683 * Lookup a CEP by Service ID and private data.
\r
2688 IN net64_t port_guid,
\r
2689 IN uint8_t *p_pdata )
\r
2691 cl_rbmap_item_t *p_item;
\r
2695 AL_ENTER( AL_DBG_CM );
\r
2697 /* Match against pending connections using remote comm ID and CA GUID. */
\r
2698 p_item = cl_rbmap_root( &gp_cep_mgr->listen_map );
\r
2699 while( p_item != cl_rbmap_end( &gp_cep_mgr->listen_map ) )
\r
2701 p_cep = PARENT_STRUCT( p_item, kcep_t, listen_item );
\r
2703 if( sid == p_cep->sid )
\r
2705 else if( sid < p_cep->sid )
\r
2706 p_item = cl_rbmap_left( p_item );
\r
2708 p_item = cl_rbmap_right( p_item );
\r
2713 if( p_cep->port_guid != IB_ALL_PORTS )
\r
2715 if( port_guid == p_cep->port_guid )
\r
2717 else if( port_guid < p_cep->port_guid )
\r
2718 p_item = cl_rbmap_left( p_item );
\r
2720 p_item = cl_rbmap_right( p_item );
\r
2726 if( p_cep->p_cmp_buf && p_pdata )
\r
2728 cmp = cl_memcmp( &p_pdata[p_cep->cmp_offset],
\r
2729 p_cep->p_cmp_buf, p_cep->cmp_len );
\r
2733 else if( cmp < 0 )
\r
2734 p_item = cl_rbmap_left( p_item );
\r
2736 p_item = cl_rbmap_right( p_item );
\r
2738 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,
\r
2739 ("Svc ID match but compare buffer mismatch.\n") );
\r
2744 /* Everything matched. */
\r
2745 AL_EXIT( AL_DBG_CM );
\r
2749 AL_EXIT( AL_DBG_CM );
\r
2756 IN kcep_t* const p_new_cep )
\r
2759 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2760 boolean_t left = TRUE;
\r
2762 AL_ENTER( AL_DBG_CM );
\r
2764 p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );
\r
2765 p_insert_at = p_item;
\r
2766 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )
\r
2768 p_insert_at = p_item;
\r
2769 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2771 if( p_new_cep->remote_comm_id < p_cep->remote_comm_id )
\r
2772 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2773 else if( p_new_cep->remote_comm_id > p_cep->remote_comm_id )
\r
2774 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2775 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2776 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2777 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2778 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2781 AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
2782 ("WARNING: Duplicate remote CID and CA GUID.\n") );
\r
2788 &gp_cep_mgr->conn_id_map, p_insert_at, &p_new_cep->rem_id_item, left );
\r
2789 p_cep = p_new_cep;
\r
2792 AL_EXIT( AL_DBG_CM );
\r
2799 IN kcep_t* const p_new_cep )
\r
2802 cl_rbmap_item_t *p_item, *p_insert_at;
\r
2803 boolean_t left = TRUE;
\r
2805 AL_ENTER( AL_DBG_CM );
\r
2807 p_item = cl_rbmap_root( &gp_cep_mgr->conn_qp_map );
\r
2808 p_insert_at = p_item;
\r
2809 while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_qp_map ) )
\r
2811 p_insert_at = p_item;
\r
2812 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );
\r
2814 if( p_new_cep->remote_qpn < p_cep->remote_qpn )
\r
2815 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2816 else if( p_new_cep->remote_qpn > p_cep->remote_qpn )
\r
2817 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2818 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )
\r
2819 p_item = cl_rbmap_left( p_item ), left = TRUE;
\r
2820 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )
\r
2821 p_item = cl_rbmap_right( p_item ), left = FALSE;
\r
2824 AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,
\r
2825 ("WARNING: Duplicate remote QPN and CA GUID.\n") );
\r
2831 &gp_cep_mgr->conn_qp_map, p_insert_at, &p_new_cep->rem_qp_item, left );
\r
2832 p_cep = p_new_cep;
\r
2835 AL_EXIT( AL_DBG_CM );
\r
2840 static inline kcep_t*
\r
2842 IN kcep_t* const p_new_cep )
\r
2846 AL_ENTER( AL_DBG_CM );
\r
2848 p_cep = __insert_by_qpn( p_new_cep );
\r
2849 if( p_cep != p_new_cep )
\r
2852 p_cep = __insert_by_id( p_new_cep );
\r
2853 if( p_cep != p_new_cep )
\r
2855 cl_rbmap_remove_item(
\r
2856 &gp_cep_mgr->conn_qp_map, &p_new_cep->rem_qp_item );
\r
2859 * Clear the remote QPN and comm ID so that we don't try
\r
2860 * to remove the CEP from those maps.
\r
2862 p_new_cep->remote_qpn = 0;
\r
2863 p_new_cep->remote_comm_id = 0;
\r
2866 AL_EXIT( AL_DBG_CM );
\r
2871 static inline void
\r
2873 IN kcep_t* const p_cep )
\r
2875 AL_ENTER( AL_DBG_CM );
\r
2877 if( p_cep->remote_comm_id )
\r
2879 cl_rbmap_remove_item(
\r
2880 &gp_cep_mgr->conn_id_map, &p_cep->rem_id_item );
\r
2881 p_cep->remote_comm_id = 0;
\r
2883 if( p_cep->remote_qpn )
\r
2885 cl_rbmap_remove_item(
\r
2886 &gp_cep_mgr->conn_qp_map, &p_cep->rem_qp_item );
\r
2887 p_cep->remote_qpn = 0;
\r
2890 AL_EXIT( AL_DBG_CM );
\r
2896 IN ib_net16_t lid,
\r
2897 IN ib_net16_t port_lid,
\r
2902 uint16_t path_bits;
\r
2906 lid1 = CL_NTOH16(lid);
\r
2907 lid2 = CL_NTOH16(port_lid);
\r
2914 path_bits = (uint16_t)( (path_bits << 1) | 1 );
\r
2916 lid2 |= path_bits;
\r
2923 if (lid != port_lid)
\r
2931 static inline boolean_t
\r
2933 IN const ib_port_attr_t* const p_port_attr,
\r
2934 IN const ib_gid_t* const p_gid )
\r
2938 for( idx = 0; idx < p_port_attr->num_gids; idx++ )
\r
2941 p_gid, &p_port_attr->p_gid_table[idx], sizeof(ib_gid_t) ) )
\r
2950 static inline boolean_t
\r
2952 IN const ib_port_attr_t* const p_port_attr,
\r
2953 IN const net16_t pkey,
\r
2954 OUT uint16_t* const p_pkey_index )
\r
2958 for( idx = 0; idx < p_port_attr->num_pkeys; idx++ )
\r
2960 if( p_port_attr->p_pkey_table[idx] == pkey )
\r
2962 *p_pkey_index = idx;
\r
2971 /* Returns the 1-based port index of the CEP agent with the specified GID. */
\r
2972 static cep_agent_t*
\r
2974 IN const ib_gid_t* const p_gid,
\r
2975 IN const net16_t lid,
\r
2976 IN const net16_t pkey,
\r
2977 OUT uint16_t* const p_pkey_index )
\r
2979 cep_agent_t *p_port_cep;
\r
2980 cl_list_item_t *p_item;
\r
2981 const ib_port_attr_t *p_port_attr;
\r
2983 AL_ENTER( AL_DBG_CM );
\r
2985 cl_spinlock_acquire( &gp_cep_mgr->obj.lock );
\r
2986 for( p_item = cl_qlist_head( &gp_cep_mgr->obj.obj_list );
\r
2987 p_item != cl_qlist_end( &gp_cep_mgr->obj.obj_list );
\r
2988 p_item = cl_qlist_next( p_item ) )
\r
2990 p_port_cep = PARENT_STRUCT( p_item, cep_agent_t, obj.pool_item );
\r
2992 CL_ASSERT( p_port_cep->port_num );
\r
2994 ci_ca_lock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
2996 p_port_attr = p_port_cep->h_ca->obj.p_ci_ca->p_pnp_attr->p_port_attr;
\r
2997 p_port_attr += (p_port_cep->port_num - 1);
\r
2999 if( __is_lid_valid( lid, p_port_attr->lid, p_port_attr->lmc ) &&
\r
3000 __is_gid_valid( p_port_attr, p_gid ) &&
\r
3001 __get_pkey_index( p_port_attr, pkey, p_pkey_index ) )
\r
3003 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
3004 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
3005 AL_EXIT( AL_DBG_CM );
\r
3006 return p_port_cep;
\r
3009 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );
\r
3011 cl_spinlock_release( &gp_cep_mgr->obj.lock );
\r
3012 AL_EXIT( AL_DBG_CM );
\r
3018 * PnP callback for port event notifications.
\r
3020 static ib_api_status_t
\r
3022 IN ib_pnp_rec_t *p_pnp_rec )
\r
3024 ib_api_status_t status = IB_SUCCESS;
\r
3026 AL_ENTER( AL_DBG_CM );
\r
3028 switch( p_pnp_rec->pnp_event )
\r
3030 case IB_PNP_PORT_ADD:
\r
3031 /* Create the port agent. */
\r
3032 CL_ASSERT( !p_pnp_rec->context );
\r
3033 status = __create_port_cep( (ib_pnp_port_rec_t*)p_pnp_rec );
\r
3036 case IB_PNP_PORT_REMOVE:
\r
3037 CL_ASSERT( p_pnp_rec->context );
\r
3039 /* Destroy the port agent. */
\r
3040 ref_al_obj( &((cep_agent_t*)p_pnp_rec->context)->obj );
\r
3041 ((cep_agent_t*)p_pnp_rec->context)->obj.pfn_destroy(
\r
3042 &((cep_agent_t*)p_pnp_rec->context)->obj, NULL );
\r
3046 break; /* Ignore other PNP events. */
\r
3049 AL_EXIT( AL_DBG_CM );
\r
3054 static inline int64_t
\r
3056 IN int64_t current_min,
\r
3057 IN kcep_t* const p_cep )
\r
3060 * The minimum timer interval is 50 milliseconds. This means
\r
3061 * 500000 100ns increments. Since __process_timewait divides the
\r
3062 * result in half (so that the worst cast timewait interval is 150%)
\r
3063 * we compensate for this here. Note that relative time values are
\r
3064 * expressed as negative.
\r
3066 #define MIN_TIMEWAIT_100NS -1000000
\r
3068 /* Still in timewait - try again next time. */
\r
3069 if( !current_min )
\r
3071 return min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS );
\r
3075 return max( current_min,
\r
3076 min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS ) );
\r
3082 * Timer callback to process CEPs in timewait state. Returns time in ms.
\r
3085 __process_timewait()
\r
3087 cl_list_item_t *p_item;
\r
3089 LARGE_INTEGER timeout;
\r
3090 int64_t min_timewait = 0;
\r
3092 AL_ENTER( AL_DBG_CM );
\r
3094 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3096 timeout.QuadPart = 0;
\r
3098 p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );
\r
3099 while( p_item != cl_qlist_end( &gp_cep_mgr->timewait_list ) )
\r
3101 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );
\r
3102 p_item = cl_qlist_next( p_item );
\r
3104 CL_ASSERT( p_cep->state == CEP_STATE_DESTROY ||
\r
3105 p_cep->state == CEP_STATE_TIMEWAIT );
\r
3107 CL_ASSERT( !p_cep->p_mad );
\r
3109 if( KeWaitForSingleObject( &p_cep->timewait_timer, Executive,
\r
3110 KernelMode, FALSE, &timeout ) != STATUS_SUCCESS )
\r
3112 /* Still in timewait - try again next time. */
\r
3113 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3117 if( p_cep->ref_cnt )
\r
3119 /* Send outstanding or destruction in progress. */
\r
3120 min_timewait = __min_timewait( min_timewait, p_cep );
\r
3124 /* Remove from the timewait list. */
\r
3125 cl_qlist_remove_item( &gp_cep_mgr->timewait_list, &p_cep->timewait_item );
\r
3128 * Not in timewait. Remove the CEP from the maps - it should
\r
3129 * no longer be matched against.
\r
3131 __remove_cep( p_cep );
\r
3133 if( p_cep->state == CEP_STATE_DESTROY )
\r
3135 __destroy_cep( p_cep );
\r
3139 /* Move the CEP to the IDLE state so that it can be used again. */
\r
3140 p_cep->state = CEP_STATE_IDLE;
\r
3144 AL_EXIT( AL_DBG_CM );
\r
3145 return (uint32_t)(min_timewait / -20000);
\r
3150 * Timer callback to process CEPs in timewait state.
\r
3153 __cep_timewait_cb(
\r
3154 IN void *context )
\r
3156 KLOCK_QUEUE_HANDLE hdl;
\r
3157 uint32_t min_timewait;
\r
3159 AL_ENTER( AL_DBG_CM );
\r
3161 UNUSED_PARAM( context );
\r
3163 CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
\r
3165 KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );
\r
3167 min_timewait = __process_timewait();
\r
3169 if( cl_qlist_count( &gp_cep_mgr->timewait_list ) )
\r
3172 * Reset the timer for half of the shortest timeout - this results
\r
3173 * in a worst case timeout of 150% of timewait.
\r
3175 cl_timer_trim( &gp_cep_mgr->timewait_timer, min_timewait );
\r
3178 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );
\r
3180 AL_EXIT( AL_DBG_CM );
\r
3185 * Starts immediate cleanup of the CM. Invoked during al_obj destruction.
\r
3188 __destroying_cep_mgr(
\r
3189 IN al_obj_t* p_obj )
\r
3191 ib_api_status_t status;
\r
3192 KLOCK_QUEUE_HANDLE hdl;
\r
3193 cl_list_item_t *p_item;
\r
3195 LARGE_INTEGER timeout;
\r
3197 AL_ENTER( AL_DBG_CM );
\r
3199 CL_ASSERT( &gp_cep_mgr->obj == p_obj );
\r
3200 UNUSED_PARAM( p_obj );
\r
3202 /* Deregister from PnP notifications. */
\r
3203 if( gp_cep_mgr->h_pnp )
\r
3205 status = ib_dereg_pnp(
\r
3206 gp_cep_mgr->h_pnp, (ib_pfn_destroy_cb_t)deref_al_obj );
\r
3207 if( status != IB_SUCCESS )
\r
3209 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
3210 ("ib_dereg_pnp failed with status %s.\n",
\r
3211 ib_get_err_str(status)) );