51ea1c313f8cf38b1866f09d6993eba83a691fe3
[mirror/winof/.git] / core / al / kernel / al_cm_cep.c
1 /*\r
2  * Copyright (c) 2005 SilverStorm Technologies.  All rights reserved.\r
3  *\r
4  * This software is available to you under the OpenIB.org BSD license\r
5  * below:\r
6  *\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
10  *\r
11  *      - Redistributions of source code must retain the above\r
12  *        copyright notice, this list of conditions and the following\r
13  *        disclaimer.\r
14  *\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
19  *\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
27  * SOFTWARE.\r
28  *\r
29  * $Id$\r
30  */\r
31 \r
32 \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
44 #ifdef offsetof\r
45 #undef offsetof\r
46 #endif\r
47 #include "al_cm_cep.tmh"\r
48 #endif\r
49 #include "ib_common.h"\r
50 #include "al_mgr.h"\r
51 #include "al_ca.h"\r
52 #include "al.h"\r
53 #include "al_mad.h"\r
54 #include "al_qp.h"\r
55 \r
56 \r
57 /*\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
61  */\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
66 \r
67 /*\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
73  */\r
74 #define CEP_MAX_CID                                     (0x00FFFFFF)\r
75 #define CEP_MAX_CID_MASK                        (0x00FFFFFF)\r
76 \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
81 \r
82 \r
83 /* Global connection manager object. */\r
84 typedef struct _al_cep_mgr\r
85 {\r
86         al_obj_t                                obj;\r
87 \r
88         cl_qmap_t                               port_map;\r
89 \r
90         KSPIN_LOCK                              lock;\r
91 \r
92         /* Bitmap of CEPs, indexed by CID. */\r
93         cl_vector_t                             cid_vector;\r
94         uint32_t                                free_cid;\r
95 \r
96         /* List of active listens. */\r
97         cl_rbmap_t                              listen_map;\r
98 \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
103 \r
104         NPAGED_LOOKASIDE_LIST   cep_pool;\r
105         NPAGED_LOOKASIDE_LIST   req_pool;\r
106 \r
107         /*\r
108          * Periodically walk the list of connections in the time wait state\r
109          * and flush them as appropriate.\r
110          */\r
111         cl_timer_t                              timewait_timer;\r
112         cl_qlist_t                              timewait_list;\r
113 \r
114         ib_pnp_handle_t                 h_pnp;\r
115 \r
116 }       al_cep_mgr_t;\r
117 \r
118 \r
119 /* Per-port CM object. */\r
120 typedef struct _cep_port_agent\r
121 {\r
122         al_obj_t                        obj;\r
123 \r
124         cl_map_item_t           item;\r
125 \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
131 \r
132         net64_t                         port_guid;\r
133         uint8_t                         port_num;\r
134         net16_t                         base_lid;\r
135 \r
136 }       cep_agent_t;\r
137 \r
138 \r
139 /*\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
143  */\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
155 \r
156 #define CEP_MSG_MASK                    0x000000FF\r
157 #define CEP_OP_MASK                             0xF0000000\r
158 \r
159 #define CEP_STATE_PREP                  0x00100000\r
160 \r
161 /* States match CM state transition diagrams from spec. */\r
162 typedef enum _cep_state\r
163 {\r
164         CEP_STATE_IDLE,\r
165         CEP_STATE_LISTEN,\r
166         CEP_STATE_ESTABLISHED,\r
167         CEP_STATE_TIMEWAIT,\r
168         CEP_STATE_SREQ_SENT,\r
169         CEP_STATE_SREQ_RCVD,\r
170         CEP_STATE_ERROR,\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
192 \r
193 } cep_state_t;\r
194 \r
195 \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
204 *\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
213 *\r
214 *       *al_cep_mra can only be called once - either before or after PRE_REP.\r
215 */\r
216 \r
217 typedef struct _al_kcep_av\r
218 {\r
219         ib_av_attr_t                            attr;\r
220         net64_t                                         port_guid;\r
221         uint16_t                                        pkey_index;\r
222 \r
223 }       kcep_av_t;\r
224 \r
225 \r
226 typedef struct _al_kcep\r
227 {\r
228         net32_t                                         cid;\r
229         void*                                           context;\r
230 \r
231         struct _cep_cid                         *p_cid;\r
232 \r
233         net64_t                                         sid;\r
234 \r
235         /* Port guid for filtering incoming requests. */\r
236         net64_t                                         port_guid;\r
237 \r
238         uint8_t* __ptr64                        p_cmp_buf;\r
239         uint8_t                                         cmp_offset;\r
240         uint8_t                                         cmp_len;\r
241 \r
242         boolean_t                                       p2p;\r
243 \r
244         /* Used to store connection structure with owning AL instance. */\r
245         cl_list_item_t                          al_item;\r
246 \r
247         /* Flag to indicate whether a user is processing events. */\r
248         boolean_t                                       signalled;\r
249 \r
250         /* Destroy callback. */\r
251         ib_pfn_destroy_cb_t                     pfn_destroy_cb;\r
252 \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
256 \r
257         IRP                                                     *p_irp;\r
258 \r
259         /* MAP item for finding listen CEPs. */\r
260         cl_rbmap_item_t                         listen_item;\r
261 \r
262         /* Map item for finding CEPs based on remote comm ID & CA GUID. */\r
263         cl_rbmap_item_t                         rem_id_item;\r
264 \r
265         /* Map item for finding CEPs based on remote QP number. */\r
266         cl_rbmap_item_t                         rem_qp_item;\r
267 \r
268         /* Communication ID's for the connection. */\r
269         net32_t                                         local_comm_id;\r
270         net32_t                                         remote_comm_id;\r
271 \r
272         net64_t                                         local_ca_guid;\r
273         net64_t                                         remote_ca_guid;\r
274 \r
275         /* Remote QP, used for stale connection checking. */\r
276         net32_t                                         remote_qpn;\r
277 \r
278         /* Parameters to format QP modification structure. */\r
279         net32_t                                         sq_psn;\r
280         net32_t                                         rq_psn;\r
281         uint8_t                                         resp_res;\r
282         uint8_t                                         init_depth;\r
283         uint8_t                                         rnr_nak_timeout;\r
284 \r
285         /*\r
286          * Local QP number, used for the "additional check" required\r
287          * of the DREQ.\r
288          */\r
289         net32_t                                         local_qpn;\r
290 \r
291         /* PKEY to make sure a LAP is on the same partition. */\r
292         net16_t                                         pkey;\r
293 \r
294         /* Initiator depth as received in the REQ. */\r
295         uint8_t                                         req_init_depth;\r
296 \r
297         /*\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
301          */\r
302         kcep_av_t                                       av[2];\r
303         uint8_t                                         idx_primary;\r
304 \r
305         /* Temporary AV and CEP port GUID used when processing LAP. */\r
306         kcep_av_t                                       alt_av;\r
307         uint8_t                                         alt_2pkt_life;\r
308 \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
315 \r
316         /* Volatile to allow using atomic operations for state checks. */\r
317         cep_state_t                                     state;\r
318 \r
319         /*\r
320          * Flag that indicates whether a connection took the active role during\r
321          * establishment. \r
322          */\r
323         boolean_t                                       was_active;\r
324 \r
325         /*\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
330          */\r
331         ib_mad_svc_handle_t                     h_mad_svc;\r
332         ib_mad_element_t                        *p_send_mad;\r
333 \r
334         /* Number of outstanding MADs.  Delays destruction of CEP destruction. */\r
335         atomic32_t                                      ref_cnt;\r
336 \r
337         /* MAD transaction ID to use when sending MADs. */\r
338         uint64_t                                        tid;\r
339 \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
344 \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
349 \r
350         /*\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
353          * calls send it.\r
354          */\r
355         ib_mad_element_t                        *p_mad;\r
356 \r
357         /* Cache the last MAD sent for retransmission. */\r
358         union _mads\r
359         {\r
360                 ib_mad_t                                hdr;\r
361                 mad_cm_mra_t                    mra;\r
362                 mad_cm_rtu_t                    rtu;\r
363                 mad_cm_drep_t                   drep;\r
364 \r
365         }       mads;\r
366 \r
367 }       kcep_t;\r
368 \r
369 \r
370 /* Structures stored in the CID vector. */\r
371 typedef struct _cep_cid\r
372 {\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
376         kcep_t                  *p_cep;\r
377         /* For REJ Retry support */\r
378         uint8_t                 modifier;\r
379 \r
380 }       cep_cid_t;\r
381 \r
382 \r
383 /* Global instance of the CM agent. */\r
384 al_cep_mgr_t            *gp_cep_mgr = NULL;\r
385 \r
386 \r
387 static ib_api_status_t\r
388 __format_drep(\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
393 \r
394 static ib_api_status_t\r
395 __cep_queue_mad(\r
396         IN                              kcep_t* const                           p_cep,\r
397         IN                              ib_mad_element_t*                       p_mad );\r
398 \r
399 static inline void\r
400 __process_cep(\r
401         IN                              kcep_t* const                           p_cep );\r
402 \r
403 static inline uint32_t\r
404 __calc_mad_timeout(\r
405         IN              const   uint8_t                                         pkt_life );\r
406 \r
407 static inline void\r
408 __calc_timewait(\r
409         IN                              kcep_t* const                           p_cep );\r
410 \r
411 static kcep_t*\r
412 __create_cep( void );\r
413 \r
414 static int32_t\r
415 __cleanup_cep(\r
416         IN                              kcep_t* const                           p_cep );\r
417 \r
418 static void\r
419 __destroy_cep(\r
420         IN                              kcep_t* const                           p_cep );\r
421 \r
422 static inline void\r
423 __bind_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
428 \r
429 static inline void\r
430 __unbind_cep(\r
431         IN                              kcep_t* const                           p_cep );\r
432 \r
433 static void\r
434 __pre_destroy_cep(\r
435         IN                              kcep_t* const                           p_cep );\r
436 \r
437 static kcep_t*\r
438 __lookup_by_id(\r
439         IN                              net32_t                                         remote_comm_id,\r
440         IN                              net64_t                                         remote_ca_guid );\r
441 \r
442 static kcep_t*\r
443 __lookup_listen(\r
444         IN                              net64_t                                         sid,\r
445         IN                              net64_t                                         port_guid,\r
446         IN                              void                                            *p_pdata );\r
447 \r
448 static inline kcep_t*\r
449 __lookup_cep(\r
450         IN                              ib_al_handle_t                          h_al OPTIONAL,\r
451         IN                              net32_t                                         cid );\r
452 \r
453 static inline kcep_t*\r
454 __insert_cep(\r
455         IN                              kcep_t* const                           p_new_cep );\r
456 \r
457 static inline void\r
458 __remove_cep(\r
459         IN                              kcep_t* const                           p_cep );\r
460 \r
461 static inline void\r
462 __insert_timewait(\r
463         IN                              kcep_t* const                           p_cep );\r
464 \r
465 static ib_api_status_t\r
466 __cep_get_mad(\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
471 \r
472 static ib_api_status_t\r
473 __cep_send_mad(\r
474         IN                              cep_agent_t* const                      p_port_cep,\r
475         IN                              ib_mad_element_t* const         p_mad );\r
476 \r
477 /* Returns the 1-based port index of the CEP agent with the specified GID. */\r
478 static cep_agent_t*\r
479 __find_port_cep(\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
484 \r
485 static cep_cid_t*\r
486 __get_lcid(\r
487                 OUT                     net32_t* const                          p_cid );\r
488 \r
489 static void\r
490 __process_cep_send_comp(\r
491         IN                              cl_async_proc_item_t            *p_item );\r
492 \r
493 \r
494 /******************************************************************************\r
495 * Per-port CEP agent\r
496 ******************************************************************************/\r
497 \r
498 \r
499 static inline void\r
500 __format_mad_hdr(\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
504 {\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
509         p_mad->status = 0;\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
513         p_mad->resv = 0;\r
514         p_mad->attr_mod = 0;\r
515 }\r
516 \r
517 \r
518 /* Consumes the input MAD. */\r
519 static void\r
520 __reject_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
525 {\r
526         mad_cm_rej_t            *p_rej;\r
527 \r
528         AL_ENTER( AL_DBG_CM );\r
529 \r
530         p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;\r
531 \r
532         __format_mad_hdr( p_mad->p_mad_buf, p_cep, CM_REJ_ATTR_ID );\r
533 \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
537 \r
538         switch( p_cep->state )\r
539         {\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
545                 break;\r
546 \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
550                 break;\r
551 \r
552         default:\r
553                 CL_ASSERT( reason == IB_REJ_TIMEOUT );\r
554                 conn_rej_set_msg_rejected( 2, p_rej );\r
555                 break;\r
556         }\r
557 \r
558         conn_rej_clr_rsvd_fields( p_rej );\r
559         __cep_send_mad( p_port_cep, p_mad );\r
560 \r
561         AL_EXIT( AL_DBG_CM );\r
562 }\r
563 \r
564 \r
565 static void\r
566 __reject_timeout(\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
570 {\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
574         ib_grh_t                        *p_grh;\r
575 \r
576         AL_ENTER( AL_DBG_CM );\r
577 \r
578         status = ib_get_mad( p_port_cep->pool_key, MAD_BLOCK_SIZE, &p_rej_mad );\r
579         if( status != IB_SUCCESS )\r
580         {\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
583                 return;\r
584         }\r
585 \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
589 \r
590         /*\r
591          * Copy the input MAD element to the reject - this gives us\r
592          * all appropriate addressing information.\r
593          */\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
596 \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
600 \r
601         status = conn_rej_set_pdata( NULL, 0, (mad_cm_rej_t*)p_mad_buf );\r
602         CL_ASSERT( status == IB_SUCCESS );\r
603 \r
604         /* Copy the local CA GUID into the ARI. */\r
605         switch( p_mad->p_mad_buf->attr_id )\r
606         {\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
613                 break;\r
614 \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
621                 break;\r
622 \r
623         default:\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
627                 return;\r
628         }\r
629 \r
630         AL_EXIT( AL_DBG_CM );\r
631 }\r
632 \r
633 \r
634 static void\r
635 __reject_req(\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
639 {\r
640         mad_cm_req_t    *p_req;\r
641         mad_cm_rej_t    *p_rej;\r
642 \r
643         AL_ENTER( AL_DBG_CM );\r
644 \r
645         CL_ASSERT( p_port_cep );\r
646         CL_ASSERT( p_mad );\r
647         CL_ASSERT( reason != 0 );\r
648 \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
651 \r
652         /*\r
653          * Format the reject information, overwriting the REQ data and send\r
654          * the response.\r
655          */\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
664 \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
669 \r
670         __cep_send_mad( p_port_cep, p_mad );\r
671 \r
672         AL_EXIT( AL_DBG_CM );\r
673 }\r
674 \r
675 \r
676 static void\r
677 __format_req_av(\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
681 {\r
682         cep_agent_t                             *p_port_cep;\r
683         const req_path_info_t   *p_path;\r
684 \r
685         AL_ENTER( AL_DBG_CM );\r
686 \r
687         CL_ASSERT( p_cep );\r
688         CL_ASSERT( p_req );\r
689 \r
690         cl_memclr( &p_cep->av[idx], sizeof(kcep_av_t) );\r
691 \r
692         p_path = &((&p_req->primary_path)[idx]);\r
693 \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
696         if( !p_port_cep )\r
697         {\r
698                 if( !idx )\r
699                         p_cep->local_ca_guid = 0;\r
700                 AL_EXIT( AL_DBG_CM );\r
701                 return;\r
702         }\r
703 \r
704         if( !idx )\r
705                 p_cep->local_ca_guid = p_port_cep->h_ca->obj.p_ci_ca->verbs.guid;\r
706 \r
707         /* Check that CA GUIDs match if formatting the alternate path. */\r
708         if( idx &&\r
709                 p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )\r
710         {\r
711                 AL_EXIT( AL_DBG_CM );\r
712                 return;\r
713         }\r
714 \r
715         /*\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
718          */\r
719         if( idx &&\r
720                 p_cep->av[0].pkey_index != p_cep->av[1].pkey_index )\r
721         {\r
722                 AL_EXIT( AL_DBG_CM );\r
723                 return;\r
724         }\r
725 \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
728 \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
731 \r
732         if( !conn_req_path_get_subn_lcl( p_path ) )\r
733         {\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
740         }\r
741         else\r
742         {\r
743                 p_cep->av[idx].attr.grh_valid = FALSE;\r
744         }\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
748 \r
749         /*\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
752          */\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
760 \r
761         AL_EXIT( AL_DBG_CM );\r
762 }\r
763 \r
764 \r
765 /*\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
771  */\r
772 static void\r
773 __save_wire_req(\r
774         IN      OUT                     kcep_t*  const                          p_cep,\r
775         IN      OUT                     mad_cm_req_t* const                     p_req )\r
776 {\r
777         AL_ENTER( AL_DBG_CM );\r
778 \r
779         p_cep->state = CEP_STATE_REQ_RCVD;\r
780         p_cep->was_active = FALSE;\r
781 \r
782         p_cep->sid = p_req->sid;\r
783 \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
787 \r
788         p_cep->remote_qpn = conn_req_get_lcl_qpn( p_req );\r
789         p_cep->local_qpn = 0;\r
790 \r
791         p_cep->retry_timeout =\r
792                 __calc_mad_timeout( conn_req_get_lcl_resp_timeout( p_req ) );\r
793 \r
794         /* Store the retry count. */\r
795         p_cep->max_cm_retries = conn_req_get_max_cm_retries( p_req );\r
796 \r
797         /*\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
801          */\r
802         __format_req_av( p_cep, p_req, 0 );\r
803 \r
804         if( p_req->alternate_path.local_lid )\r
805                 __format_req_av( p_cep, p_req, 1 );\r
806         else\r
807                 cl_memclr( &p_cep->av[1], sizeof(kcep_av_t) );\r
808 \r
809         p_cep->idx_primary = 0;\r
810 \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
815 \r
816         /*\r
817          * Make sure the target ack delay is cleared - the above\r
818          * "packet life" includes it.\r
819          */\r
820         p_cep->target_ack_delay = 0;\r
821 \r
822         /* Store the requested initiator depth. */\r
823         p_cep->req_init_depth = conn_req_get_init_depth( p_req );\r
824 \r
825         /*\r
826          * Store the provided responder resources.  These turn into the local\r
827          * QP's initiator depth.\r
828          */\r
829         p_cep->init_depth = conn_req_get_resp_res( p_req );\r
830 \r
831         p_cep->sq_psn = conn_req_get_starting_psn( p_req );\r
832 \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
837 \r
838         AL_EXIT( AL_DBG_CM );\r
839 }\r
840 \r
841 \r
842 /* Must be called with the CEP lock held. */\r
843 static void\r
844 __repeat_mad(\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
848 {\r
849         AL_ENTER( AL_DBG_CM );\r
850 \r
851         CL_ASSERT( p_port_cep );\r
852         CL_ASSERT( p_cep );\r
853         CL_ASSERT( p_mad );\r
854 \r
855         /* Repeat the last mad sent for the connection. */\r
856         switch( p_cep->state )\r
857         {\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
867                 break;\r
868 \r
869         default:\r
870                 /* Return the MAD to the mad pool */\r
871                 ib_put_mad( p_mad );\r
872                 break;\r
873         }\r
874 \r
875         AL_EXIT( AL_DBG_CM );\r
876 }\r
877 \r
878 \r
879 static ib_api_status_t\r
880 __process_rej(\r
881         IN                              kcep_t* const                           p_cep,\r
882         IN                              ib_mad_element_t* const         p_mad )\r
883 {\r
884         ib_api_status_t         status;\r
885         mad_cm_rej_t            *p_rej;\r
886 \r
887         AL_ENTER( AL_DBG_CM );\r
888 \r
889         ASSERT( p_cep );\r
890         ASSERT( p_mad );\r
891         ASSERT( p_mad->p_mad_buf );\r
892 \r
893         p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;\r
894 \r
895         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
896                 ("Request rejected reason - %d.\n", p_rej->reason) );\r
897 \r
898         switch( p_cep->state )\r
899         {\r
900         case CEP_STATE_REQ_SENT:\r
901                 /*\r
902                  * Ignore rejects with the status set to IB_REJ_INVALID_SID.  We will\r
903                  * continue to retry (up to max_cm_retries) to connect to the remote\r
904                  * side.  This is required to support peer-to-peer connections.\r
905                  */\r
906                 if( p_cep->p2p && p_rej->reason == IB_REJ_INVALID_SID )\r
907                 {\r
908                         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
909                                 ("Request rejected (invalid SID) - retrying.\n") );\r
910                         goto err1;\r
911                 }\r
912 \r
913                 /* Fall through */\r
914         case CEP_STATE_REP_SENT:\r
915         case CEP_STATE_REQ_MRA_RCVD:\r
916         case CEP_STATE_REP_MRA_RCVD:\r
917                 /* Cancel any outstanding MAD. */\r
918                 if( p_cep->p_send_mad )\r
919                 {\r
920                         ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );\r
921                         p_cep->p_send_mad = NULL;\r
922                 }\r
923 \r
924                 /* Fall through */\r
925         case CEP_STATE_REQ_RCVD:\r
926         case CEP_STATE_REP_RCVD:\r
927         case CEP_STATE_REQ_MRA_SENT:\r
928         case CEP_STATE_REP_MRA_SENT:\r
929         case CEP_STATE_PRE_REP:\r
930         case CEP_STATE_PRE_REP_MRA_SENT:\r
931                 if( p_cep->state & CEP_STATE_PREP )\r
932                 {\r
933                         CL_ASSERT( p_cep->p_mad );\r
934                         ib_put_mad( p_cep->p_mad );\r
935                         p_cep->p_mad = NULL;\r
936                 }\r
937                 /* Abort connection establishment. No transition to timewait. */\r
938                 __remove_cep( p_cep );\r
939                 p_cep->state = CEP_STATE_IDLE;\r
940                 break;\r
941 \r
942         case CEP_STATE_ESTABLISHED:\r
943         case CEP_STATE_LAP_RCVD:\r
944         case CEP_STATE_LAP_SENT:\r
945         case CEP_STATE_LAP_MRA_RCVD:\r
946         case CEP_STATE_LAP_MRA_SENT:\r
947         case CEP_STATE_PRE_APR:\r
948         case CEP_STATE_PRE_APR_MRA_SENT:\r
949                 if( p_cep->state & CEP_STATE_PREP )\r
950                 {\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
954                 }\r
955                 p_cep->state = CEP_STATE_TIMEWAIT;\r
956                 __insert_timewait( p_cep );\r
957                 break;\r
958 \r
959         default:\r
960                 /* Ignore the REJ. */\r
961                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REJ received in invalid state.\n") );\r
962 err1:\r
963                 ib_put_mad( p_mad );\r
964                 AL_EXIT( AL_DBG_CM );\r
965                 return IB_NO_MATCH;\r
966         }\r
967 \r
968         status = __cep_queue_mad( p_cep, p_mad );\r
969 \r
970         AL_EXIT( AL_DBG_CM );\r
971         return status;\r
972 }\r
973 \r
974 \r
975 static ib_api_status_t\r
976 __process_stale(\r
977         IN                              kcep_t* const                           p_cep )\r
978 {\r
979         ib_api_status_t         status;\r
980         cep_agent_t                     *p_port_cep;\r
981         ib_mad_element_t        *p_mad;\r
982         mad_cm_rej_t            *p_rej;\r
983 \r
984         status = __cep_get_mad( p_cep, CM_REJ_ATTR_ID, &p_port_cep, &p_mad );\r
985         if( status != IB_SUCCESS )\r
986                 return status;\r
987 \r
988         p_rej = ib_get_mad_buf( p_mad );\r
989 \r
990         conn_rej_set_ari( NULL, 0, p_rej );\r
991         conn_rej_set_pdata( NULL, 0, p_rej );\r
992 \r
993         p_rej->local_comm_id = p_cep->remote_comm_id;\r
994         p_rej->remote_comm_id = p_cep->local_comm_id;\r
995         p_rej->reason = IB_REJ_STALE_CONN;\r
996 \r
997         switch( p_cep->state )\r
998         {\r
999         case CEP_STATE_REQ_RCVD:\r
1000         case CEP_STATE_REQ_MRA_SENT:\r
1001         case CEP_STATE_PRE_REP:\r
1002         case CEP_STATE_PRE_REP_MRA_SENT:\r
1003                 conn_rej_set_msg_rejected( 0, p_rej );\r
1004                 break;\r
1005 \r
1006         case CEP_STATE_REQ_SENT:\r
1007         case CEP_STATE_REP_RCVD:\r
1008         case CEP_STATE_REP_MRA_SENT:\r
1009                 conn_rej_set_msg_rejected( 1, p_rej );\r
1010                 break;\r
1011 \r
1012         default:\r
1013                 conn_rej_set_msg_rejected( 2, p_rej );\r
1014                 break;\r
1015         }\r
1016         conn_rej_clr_rsvd_fields( p_rej );\r
1017 \r
1018         return __process_rej( p_cep, p_mad );\r
1019 }\r
1020 \r
1021 \r
1022 static void\r
1023 __req_handler(\r
1024         IN                              cep_agent_t* const                      p_port_cep,\r
1025         IN                              ib_mad_element_t* const         p_mad )\r
1026 {\r
1027         ib_api_status_t         status = IB_SUCCESS;\r
1028         mad_cm_req_t            *p_req;\r
1029         kcep_t                          *p_cep, *p_new_cep, *p_stale_cep = NULL;\r
1030         KLOCK_QUEUE_HANDLE      hdl;\r
1031         ib_rej_status_t         reason;\r
1032 \r
1033         AL_ENTER( AL_DBG_CM );\r
1034 \r
1035         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1036 \r
1037         p_req = (mad_cm_req_t*)p_mad->p_mad_buf;\r
1038 \r
1039         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1040                 ("REQ: comm_id (x%x) qpn (x%x) received\n",\r
1041                 p_req->local_comm_id, conn_req_get_lcl_qpn( p_req )) );\r
1042 \r
1043         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1044 \r
1045         if( conn_req_get_qp_type( p_req ) > IB_QPT_UNRELIABLE_CONN )\r
1046         {\r
1047                 /* Reserved value.  Reject. */\r
1048                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid transport type received.\n") );\r
1049                 reason = IB_REJ_INVALID_XPORT;\r
1050                 goto reject;\r
1051         }\r
1052 \r
1053         /* Match against pending connections using remote comm ID and CA GUID. */\r
1054         p_cep = __lookup_by_id( p_req->local_comm_id, p_req->local_ca_guid );\r
1055         if( p_cep )\r
1056         {\r
1057                 /* Already received the REQ. */\r
1058                 switch( p_cep->state )\r
1059                 {\r
1060                 case CEP_STATE_REQ_MRA_SENT:\r
1061                         __repeat_mad( p_port_cep, p_cep, p_mad );\r
1062                         break;\r
1063 \r
1064                 case CEP_STATE_TIMEWAIT:\r
1065                 case CEP_STATE_DESTROY:\r
1066                         /* Send a reject. */\r
1067                         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1068                                 ("REQ received for connection in TIME_WAIT state.\n") );\r
1069                         __reject_req( p_port_cep, p_mad, IB_REJ_STALE_CONN );\r
1070                         break;\r
1071 \r
1072                 default:\r
1073                         /*\r
1074                          * Let regular retries repeat the MAD.  If our last message was\r
1075                          * dropped, resending only adds to the congestion.  If it wasn't\r
1076                          * dropped, then the remote CM will eventually process it, and\r
1077                          * we'd just be adding traffic.\r
1078                          */\r
1079                         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Duplicate REQ received.\n") );\r
1080                         ib_put_mad( p_mad );\r
1081                 }\r
1082                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1083                 AL_EXIT( AL_DBG_CM );\r
1084                 return;\r
1085         }\r
1086 \r
1087         /*\r
1088          * Allocate a new CEP for the new request.  This will\r
1089          * prevent multiple identical REQs from queueing up for processing.\r
1090          */\r
1091         p_new_cep = __create_cep();\r
1092         if( !p_new_cep )\r
1093         {\r
1094                 /* Reject the request for insufficient resources. */\r
1095                 reason = IB_REJ_INSUF_RESOURCES;\r
1096                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
1097                         ("al_create_cep failed\nREJ sent for insufficient resources.\n") );\r
1098                 goto reject;\r
1099         }\r
1100 \r
1101         __save_wire_req( p_new_cep, p_req );\r
1102 \r
1103         /*\r
1104          * Match against listens using SID and compare data, also provide the receiving\r
1105          * MAD service's port GUID so we can properly filter.\r
1106          */\r
1107         p_cep = __lookup_listen( p_req->sid, p_port_cep->port_guid, p_req->pdata );\r
1108         if( p_cep )\r
1109         {\r
1110                 __bind_cep( p_new_cep, p_cep->p_cid->h_al, p_cep->pfn_cb, NULL );\r
1111 \r
1112                 /* Add the new CEP to the map so that repeated REQs match up. */\r
1113                 p_stale_cep = __insert_cep( p_new_cep );\r
1114                 if( p_stale_cep != p_new_cep )\r
1115                 {\r
1116                         /* Duplicate - must be a stale connection. */\r
1117                         reason = IB_REJ_STALE_CONN;\r
1118                         /* Fail the local stale CEP. */\r
1119                         status = __process_stale( p_stale_cep );\r
1120                         goto unbind;\r
1121                 }\r
1122 \r
1123                 /*\r
1124                  * Queue the mad - the return value indicates whether we should\r
1125                  * invoke the callback.\r
1126                  */\r
1127                 status = __cep_queue_mad( p_cep, p_mad );\r
1128                 switch( status )\r
1129                 {\r
1130                 case IB_SUCCESS:\r
1131                 case IB_PENDING:\r
1132                         p_mad->send_context1 = p_new_cep;\r
1133                         break;\r
1134 \r
1135                 default:\r
1136                         reason = IB_REJ_INSUF_RESOURCES;\r
1137                         goto unbind;\r
1138                 }\r
1139         }\r
1140         else\r
1141         {\r
1142                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("No listens active!\n") );\r
1143 \r
1144                 /* Match against peer-to-peer requests using SID and compare data. */\r
1145                 //p_cep = __lookup_peer();\r
1146                 //if( p_cep )\r
1147                 //{\r
1148                 //      p_mad->send_context2 = NULL;\r
1149                 //      p_list_item = cl_qlist_find_from_head( &gp_cep_mgr->pending_list,\r
1150                 //              __match_peer, p_req );\r
1151                 //      if( p_list_item != cl_qlist_end( &gp_cep_mgr->pending_list ) )\r
1152                 //      {\r
1153                 //              KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1154                 //              p_conn = PARENT_STRUCT( p_list_item, kcep_t, map_item );\r
1155                 //              __peer_req( p_port_cep, p_conn, p_async_mad->p_mad );\r
1156                 //              cl_free( p_async_mad );\r
1157                 //              AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
1158                 //                      ("REQ matched a peer-to-peer request.\n") );\r
1159                 //              return;\r
1160                 //      }\r
1161                 //      reason = IB_REJ_INVALID_SID;\r
1162                 //      goto free;\r
1163                 //}\r
1164                 //else\r
1165                 {\r
1166                         /* No match found.  Reject. */\r
1167                         reason = IB_REJ_INVALID_SID;\r
1168                         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REQ received but no match found.\n") );\r
1169                         goto cleanup;\r
1170                 }\r
1171         }\r
1172 \r
1173         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1174 \r
1175         /* Process any queued MADs for the CEP. */\r
1176         if( status == IB_SUCCESS )\r
1177                 __process_cep( p_cep );\r
1178 \r
1179         AL_EXIT( AL_DBG_CM );\r
1180         return;\r
1181 \r
1182 unbind:\r
1183         __unbind_cep( p_new_cep );\r
1184 \r
1185 cleanup:\r
1186         /*\r
1187          * Move the CEP in the idle state so that we don't send a reject\r
1188          * for it when cleaning up.  Also clear the RQPN and RCID so that\r
1189          * we don't try to remove it from our maps (since it isn't inserted).\r
1190          */\r
1191         p_new_cep->state = CEP_STATE_IDLE;\r
1192         p_new_cep->remote_comm_id = 0;\r
1193         p_new_cep->remote_qpn = 0;\r
1194         __cleanup_cep( p_new_cep );\r
1195 \r
1196 reject:\r
1197         __reject_req( p_port_cep, p_mad, reason );\r
1198 \r
1199         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1200 \r
1201         if( reason == IB_REJ_STALE_CONN && status == IB_SUCCESS )\r
1202                 __process_cep( p_stale_cep );\r
1203 \r
1204         AL_EXIT( AL_DBG_CM );\r
1205 }\r
1206 \r
1207 \r
1208 static void\r
1209 __save_wire_rep(\r
1210         IN      OUT                     kcep_t*                         const   p_cep,\r
1211         IN              const   mad_cm_rep_t*           const   p_rep )\r
1212 {\r
1213         AL_ENTER( AL_DBG_CM );\r
1214 \r
1215         /* The send should have been cancelled during MRA processing. */\r
1216         p_cep->state = CEP_STATE_REP_RCVD;\r
1217 \r
1218         /* Store pertinent information in the connection. */\r
1219         p_cep->remote_comm_id = p_rep->local_comm_id;\r
1220         p_cep->remote_ca_guid = p_rep->local_ca_guid;\r
1221 \r
1222         p_cep->remote_qpn = conn_rep_get_lcl_qpn( p_rep );\r
1223 \r
1224         /* Store the remote endpoint's target ACK delay. */\r
1225         p_cep->target_ack_delay = conn_rep_get_target_ack_delay( p_rep );\r
1226 \r
1227         /* Update the local ACK delay stored in the AV's. */\r
1228         p_cep->av[0].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(\r
1229                 p_cep->av[0].attr.conn.local_ack_timeout, p_cep->target_ack_delay );\r
1230         p_cep->av[0].attr.conn.rnr_retry_cnt = conn_rep_get_rnr_retry_cnt( p_rep );\r
1231 \r
1232         if( p_cep->av[1].port_guid )\r
1233         {\r
1234                 p_cep->av[1].attr.conn.local_ack_timeout = calc_lcl_ack_timeout(\r
1235                         p_cep->av[1].attr.conn.local_ack_timeout,\r
1236                         p_cep->target_ack_delay );\r
1237                 p_cep->av[1].attr.conn.rnr_retry_cnt =\r
1238                         p_cep->av[0].attr.conn.rnr_retry_cnt;\r
1239         }\r
1240 \r
1241         p_cep->init_depth = p_rep->resp_resources;\r
1242         p_cep->resp_res = p_rep->initiator_depth;\r
1243 \r
1244         p_cep->sq_psn = conn_rep_get_starting_psn( p_rep );\r
1245 \r
1246         AL_EXIT( AL_DBG_CM );\r
1247 }\r
1248 \r
1249 \r
1250 static void\r
1251 __mra_handler(\r
1252         IN                              ib_mad_element_t* const         p_mad )\r
1253 {\r
1254         ib_api_status_t         status;\r
1255         mad_cm_mra_t            *p_mra;\r
1256         kcep_t                          *p_cep;\r
1257         KLOCK_QUEUE_HANDLE      hdl;\r
1258 \r
1259         AL_ENTER( AL_DBG_CM );\r
1260 \r
1261         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1262 \r
1263         p_mra = (mad_cm_mra_t*)p_mad->p_mad_buf;\r
1264 \r
1265         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1266         p_cep = __lookup_cep( NULL, p_mra->remote_comm_id );\r
1267         if( !p_cep )\r
1268         {\r
1269                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1270                         ("MRA received that could not be matched.\n") );\r
1271                 goto err;\r
1272         }\r
1273 \r
1274         if( p_cep->remote_comm_id )\r
1275         {\r
1276                 if( p_cep->remote_comm_id != p_mra->local_comm_id )\r
1277                 {\r
1278                         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1279                                 ("MRA received that could not be matched.\n") );\r
1280                         goto err;\r
1281                 }\r
1282         }\r
1283 \r
1284         /*\r
1285          * Note that we don't update the CEP's remote comm ID - it messes up REP\r
1286          * processing since a non-zero RCID implies the connection is in the RCID\r
1287          * map.  Adding it here requires checking there and conditionally adding\r
1288          * it.  Ignoring it is a valid thing to do.\r
1289          */\r
1290         if( !(p_cep->state & CEP_STATE_SENT) ||\r
1291                 (1 << conn_mra_get_msg_mraed( p_mra ) !=\r
1292                 (p_cep->state & CEP_MSG_MASK)) )\r
1293         {\r
1294                 /* Invalid state. */\r
1295                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("MRA received in invalid state.\n") );\r
1296                 goto err;\r
1297         }\r
1298 \r
1299         /* Delay the current send. */\r
1300         CL_ASSERT( p_cep->p_send_mad );\r
1301         ib_delay_mad( p_cep->h_mad_svc, p_cep->p_send_mad,\r
1302                 __calc_mad_timeout( conn_mra_get_svc_timeout( p_mra ) ) +\r
1303                 __calc_mad_timeout( p_cep->max_2pkt_life - 1 ) );\r
1304 \r
1305         /* We only invoke a single callback for MRA. */\r
1306         if( p_cep->state & CEP_STATE_MRA )\r
1307         {\r
1308                 /* Invalid state. */\r
1309                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("Already received MRA.\n") );\r
1310                 goto err;\r
1311         }\r
1312 \r
1313         p_cep->state |= CEP_STATE_MRA;\r
1314 \r
1315         status = __cep_queue_mad( p_cep, p_mad );\r
1316 \r
1317         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1318 \r
1319         if( status == IB_SUCCESS )\r
1320                 __process_cep( p_cep );\r
1321 \r
1322         AL_EXIT( AL_DBG_CM );\r
1323         return;\r
1324 \r
1325 err:\r
1326         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1327         ib_put_mad( p_mad );\r
1328         AL_EXIT( AL_DBG_CM );\r
1329 }\r
1330 \r
1331 \r
1332 static void\r
1333 __rej_handler(\r
1334         IN                              ib_mad_element_t* const         p_mad )\r
1335 {\r
1336         ib_api_status_t         status;\r
1337         mad_cm_rej_t            *p_rej;\r
1338         kcep_t                          *p_cep = NULL;\r
1339         KLOCK_QUEUE_HANDLE      hdl;\r
1340         net64_t                         ca_guid;\r
1341 \r
1342         AL_ENTER( AL_DBG_CM );\r
1343 \r
1344         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1345 \r
1346         p_rej = (mad_cm_rej_t*)p_mad->p_mad_buf;\r
1347 \r
1348         /* Either one of the communication IDs must be set. */\r
1349         if( !p_rej->remote_comm_id && !p_rej->local_comm_id )\r
1350                 goto err1;\r
1351 \r
1352         /* Check the pending list by the remote CA GUID and connection ID. */\r
1353         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1354         if( p_rej->remote_comm_id )\r
1355         {\r
1356                 p_cep = __lookup_cep( NULL, p_rej->remote_comm_id );\r
1357         }\r
1358         else if( p_rej->reason == IB_REJ_TIMEOUT &&\r
1359                 conn_rej_get_ari_len( p_rej ) == sizeof(net64_t) )\r
1360         {\r
1361                 cl_memcpy( &ca_guid, p_rej->ari, sizeof(net64_t) );\r
1362                 p_cep = __lookup_by_id( p_rej->local_comm_id, ca_guid );\r
1363         }\r
1364 \r
1365         if( !p_cep )\r
1366         {\r
1367                 goto err2;\r
1368         }\r
1369 \r
1370         if( p_cep->remote_comm_id &&\r
1371                 p_cep->remote_comm_id != p_rej->local_comm_id )\r
1372         {\r
1373         err2:\r
1374                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1375         err1:\r
1376                 ib_put_mad( p_mad );\r
1377                 AL_EXIT( AL_DBG_CM );\r
1378                 return;\r
1379         }\r
1380 \r
1381         status = __process_rej( p_cep, p_mad );\r
1382 \r
1383         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1384 \r
1385         if( status == IB_SUCCESS )\r
1386                 __process_cep( p_cep );\r
1387 \r
1388         AL_EXIT( AL_DBG_CM );\r
1389 }\r
1390 \r
1391 \r
1392 static void\r
1393 __rep_handler(\r
1394         IN                              cep_agent_t* const                      p_port_cep,\r
1395         IN                              ib_mad_element_t* const         p_mad )\r
1396 {\r
1397         ib_api_status_t         status;\r
1398         mad_cm_rep_t            *p_rep;\r
1399         kcep_t                          *p_cep;\r
1400         KLOCK_QUEUE_HANDLE      hdl;\r
1401         cep_state_t                     old_state;\r
1402 \r
1403         AL_ENTER( AL_DBG_CM );\r
1404 \r
1405         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1406 \r
1407         p_rep = (mad_cm_rep_t*)p_mad->p_mad_buf;\r
1408 \r
1409         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1410                 ("REP: comm_id (x%x) received\n", p_rep->local_comm_id ) );\r
1411 \r
1412         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1413         p_cep = __lookup_cep( NULL, p_rep->remote_comm_id );\r
1414         if( !p_cep )\r
1415         {\r
1416                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1417                 ib_put_mad( p_mad );\r
1418                 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1419                         ("REP received that could not be matched.\n") );\r
1420                 return;\r
1421         }\r
1422 \r
1423         switch( p_cep->state )\r
1424         {\r
1425         case CEP_STATE_REQ_MRA_RCVD:\r
1426         case CEP_STATE_REQ_SENT:\r
1427                 old_state = p_cep->state;\r
1428                 /* Save pertinent information and change state. */\r
1429                 __save_wire_rep( p_cep, p_rep );\r
1430 \r
1431                 if( __insert_cep( p_cep ) != p_cep )\r
1432                 {\r
1433                         /* Roll back the state change. */\r
1434                         __reject_mad( p_port_cep, p_cep, p_mad, IB_REJ_STALE_CONN );\r
1435                         p_cep->state = old_state;\r
1436                         status = __process_stale( p_cep );\r
1437                 }\r
1438                 else\r
1439                 {\r
1440                         /*\r
1441                          * Cancel any outstanding send.  Note that we do this only after\r
1442                          * inserting the CEP - if we failed, then the send will timeout\r
1443                          * and we'll finish our way through the state machine.\r
1444                          */\r
1445                         if( p_cep->p_send_mad )\r
1446                         {\r
1447                                 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );\r
1448                                 p_cep->p_send_mad = NULL;\r
1449                         }\r
1450 \r
1451                         status = __cep_queue_mad( p_cep, p_mad );\r
1452                 }\r
1453 \r
1454                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1455 \r
1456                 if( status == IB_SUCCESS )\r
1457                         __process_cep( p_cep );\r
1458 \r
1459                 AL_EXIT( AL_DBG_CM );\r
1460                 return;\r
1461 \r
1462         case CEP_STATE_ESTABLISHED:\r
1463         case CEP_STATE_LAP_RCVD:\r
1464         case CEP_STATE_LAP_SENT:\r
1465         case CEP_STATE_LAP_MRA_RCVD:\r
1466         case CEP_STATE_LAP_MRA_SENT:\r
1467         case CEP_STATE_REP_MRA_SENT:\r
1468                 /* Repeate the MRA or RTU. */\r
1469                 __repeat_mad( p_port_cep, p_cep, p_mad );\r
1470                 break;\r
1471 \r
1472         default:\r
1473                 ib_put_mad( p_mad );\r
1474                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("REP received in invalid state.\n") );\r
1475                 break;\r
1476         }\r
1477 \r
1478         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1479 \r
1480         AL_EXIT( AL_DBG_CM );\r
1481 }\r
1482 \r
1483 \r
1484 static void\r
1485 __rtu_handler(\r
1486         IN                              ib_mad_element_t* const         p_mad )\r
1487 {\r
1488         ib_api_status_t         status;\r
1489         mad_cm_rtu_t            *p_rtu;\r
1490         kcep_t                          *p_cep;\r
1491         KLOCK_QUEUE_HANDLE      hdl;\r
1492 \r
1493         AL_ENTER( AL_DBG_CM );\r
1494 \r
1495         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1496 \r
1497         p_rtu = (mad_cm_rtu_t*)p_mad->p_mad_buf;\r
1498 \r
1499         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1500                 ("RTU: comm_id (x%x) received\n", p_rtu->local_comm_id) );\r
1501 \r
1502         /* Find the connection by local connection ID. */\r
1503         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1504         p_cep = __lookup_cep( NULL, p_rtu->remote_comm_id );\r
1505         if( !p_cep || p_cep->remote_comm_id != p_rtu->local_comm_id )\r
1506         {\r
1507                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received that could not be matched.\n") );\r
1508                 goto done;\r
1509         }\r
1510 \r
1511         switch( p_cep->state )\r
1512         {\r
1513         case CEP_STATE_REP_SENT:\r
1514         case CEP_STATE_REP_MRA_RCVD:\r
1515                 /* Cancel any outstanding send. */\r
1516                 if( p_cep->p_send_mad )\r
1517                 {\r
1518                         ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );\r
1519                         p_cep->p_send_mad = NULL;\r
1520                 }\r
1521 \r
1522                 p_cep->state = CEP_STATE_ESTABLISHED;\r
1523 \r
1524                 status = __cep_queue_mad( p_cep, p_mad );\r
1525 \r
1526                 /* Update timewait time. */\r
1527                 __calc_timewait( p_cep );\r
1528 \r
1529                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1530 \r
1531                 if( status == IB_SUCCESS )\r
1532                         __process_cep( p_cep );\r
1533 \r
1534                 AL_EXIT( AL_DBG_CM );\r
1535                 return;\r
1536 \r
1537         default:\r
1538                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("RTU received in invalid state.\n") );\r
1539                 break;\r
1540         }\r
1541 \r
1542 done:\r
1543         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1544         ib_put_mad( p_mad );\r
1545         AL_EXIT( AL_DBG_CM );\r
1546 }\r
1547 \r
1548 \r
1549 static void\r
1550 __dreq_handler(\r
1551         IN                              cep_agent_t* const                      p_port_cep,\r
1552         IN                              ib_mad_element_t* const         p_mad )\r
1553 {\r
1554         ib_api_status_t         status;\r
1555         mad_cm_dreq_t           *p_dreq;\r
1556         kcep_t                          *p_cep;\r
1557         KLOCK_QUEUE_HANDLE      hdl;\r
1558 \r
1559         AL_ENTER( AL_DBG_CM );\r
1560 \r
1561         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1562 \r
1563         p_dreq = (mad_cm_dreq_t*)p_mad->p_mad_buf;\r
1564 \r
1565         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
1566                 ("DREQ: comm_id (x%x) qpn (x%x) received\n",\r
1567                 p_dreq->local_comm_id, conn_dreq_get_remote_qpn( p_dreq )) );\r
1568 \r
1569         /* Find the connection by connection IDs. */\r
1570         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1571         p_cep = __lookup_cep( NULL, p_dreq->remote_comm_id );\r
1572         if( !p_cep ||\r
1573                 p_cep->remote_comm_id != p_dreq->local_comm_id ||\r
1574                 p_cep->local_qpn != conn_dreq_get_remote_qpn( p_dreq ) )\r
1575         {\r
1576                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received that could not be matched.\n") );\r
1577                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1578                 ib_put_mad( p_mad );\r
1579                 AL_EXIT( AL_DBG_CM );\r
1580                 return;\r
1581         }\r
1582 \r
1583         switch( p_cep->state )\r
1584         {\r
1585         case CEP_STATE_REP_SENT:\r
1586         case CEP_STATE_REP_MRA_RCVD:\r
1587         case CEP_STATE_DREQ_SENT:\r
1588                 /* Cancel the outstanding MAD. */\r
1589                 if( p_cep->p_send_mad )\r
1590                 {\r
1591                         ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );\r
1592                         p_cep->p_send_mad = NULL;\r
1593                 }\r
1594 \r
1595                 /* Fall through and process as DREQ received case. */\r
1596         case CEP_STATE_ESTABLISHED:\r
1597         case CEP_STATE_LAP_RCVD:\r
1598         case CEP_STATE_LAP_SENT:\r
1599         case CEP_STATE_LAP_MRA_RCVD:\r
1600         case CEP_STATE_LAP_MRA_SENT:\r
1601                 p_cep->state = CEP_STATE_DREQ_RCVD;\r
1602 \r
1603                 status = __cep_queue_mad( p_cep, p_mad );\r
1604 \r
1605                 /* Store the TID for use in the reply DREP. */\r
1606                 p_cep->tid = p_dreq->hdr.trans_id;\r
1607 \r
1608                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1609 \r
1610                 if( status == IB_SUCCESS )\r
1611                         __process_cep( p_cep );\r
1612                 AL_EXIT( AL_DBG_CM );\r
1613                 return;\r
1614 \r
1615         case CEP_STATE_TIMEWAIT:\r
1616         case CEP_STATE_DESTROY:\r
1617                 /* Repeat the DREP. */\r
1618                 __repeat_mad( p_port_cep, p_cep, p_mad );\r
1619                 break;\r
1620 \r
1621         default:\r
1622                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREQ received in invalid state.\n") );\r
1623         case CEP_STATE_DREQ_RCVD:\r
1624                 ib_put_mad( p_mad );\r
1625                 break;\r
1626         }\r
1627 \r
1628         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1629         AL_EXIT( AL_DBG_CM );\r
1630 }\r
1631 \r
1632 \r
1633 static void\r
1634 __drep_handler(\r
1635         IN                              ib_mad_element_t* const         p_mad )\r
1636 {\r
1637         ib_api_status_t         status;\r
1638         mad_cm_drep_t           *p_drep;\r
1639         kcep_t                          *p_cep;\r
1640         KLOCK_QUEUE_HANDLE      hdl;\r
1641 \r
1642         AL_ENTER( AL_DBG_CM );\r
1643 \r
1644         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1645 \r
1646         p_drep = (mad_cm_drep_t*)p_mad->p_mad_buf;\r
1647 \r
1648         /* Find the connection by local connection ID. */\r
1649         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1650         p_cep = __lookup_cep( NULL, p_drep->remote_comm_id );\r
1651         if( !p_cep || p_cep->remote_comm_id != p_drep->local_comm_id )\r
1652         {\r
1653                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received that could not be matched.\n") );\r
1654                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1655                 ib_put_mad( p_mad );\r
1656                 AL_EXIT( AL_DBG_CM );\r
1657                 return;\r
1658         }\r
1659 \r
1660         if( p_cep->state != CEP_STATE_DREQ_SENT &&\r
1661                 p_cep->state != CEP_STATE_DREQ_DESTROY )\r
1662         {\r
1663                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("DREP received in invalid state.\n") );\r
1664 \r
1665                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1666                 ib_put_mad( p_mad );\r
1667                 AL_EXIT( AL_DBG_CM );\r
1668                 return;\r
1669         }\r
1670 \r
1671         /* Cancel the DREQ. */\r
1672         if( p_cep->p_send_mad )\r
1673         {\r
1674                 ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );\r
1675                 p_cep->p_send_mad = NULL;\r
1676         }\r
1677 \r
1678         if( p_cep->state == CEP_STATE_DREQ_SENT )\r
1679         {\r
1680                 p_cep->state = CEP_STATE_TIMEWAIT;\r
1681 \r
1682                 status = __cep_queue_mad( p_cep, p_mad );\r
1683         }\r
1684         else\r
1685         {\r
1686                 /* State is DREQ_DESTROY - move to DESTROY to allow cleanup. */\r
1687                 CL_ASSERT( p_cep->state == CEP_STATE_DREQ_DESTROY );\r
1688                 p_cep->state = CEP_STATE_DESTROY;\r
1689 \r
1690                 ib_put_mad( p_mad );\r
1691                 status = IB_INVALID_STATE;\r
1692         }\r
1693 \r
1694         __insert_timewait( p_cep );\r
1695 \r
1696         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1697 \r
1698         if( status == IB_SUCCESS )\r
1699                 __process_cep( p_cep );\r
1700 \r
1701         AL_EXIT( AL_DBG_CM );\r
1702 }\r
1703 \r
1704 \r
1705 static boolean_t\r
1706 __format_lap_av(\r
1707         IN                              kcep_t* const                           p_cep,\r
1708         IN              const   lap_path_info_t* const          p_path )\r
1709 {\r
1710         cep_agent_t             *p_port_cep;\r
1711 \r
1712         AL_ENTER( AL_DBG_CM );\r
1713 \r
1714         CL_ASSERT( p_cep );\r
1715         CL_ASSERT( p_path );\r
1716 \r
1717         cl_memclr( &p_cep->alt_av, sizeof(kcep_av_t) );\r
1718 \r
1719         p_port_cep = __find_port_cep( &p_path->remote_gid, p_path->remote_lid,\r
1720                 p_cep->pkey, &p_cep->alt_av.pkey_index );\r
1721         if( !p_port_cep )\r
1722         {\r
1723                 AL_EXIT( AL_DBG_CM );\r
1724                 return FALSE;\r
1725         }\r
1726 \r
1727         if( p_port_cep->h_ca->obj.p_ci_ca->verbs.guid != p_cep->local_ca_guid )\r
1728         {\r
1729                 AL_EXIT( AL_DBG_CM );\r
1730                 return FALSE;\r
1731         }\r
1732 \r
1733         p_cep->alt_av.port_guid = p_port_cep->port_guid;\r
1734         p_cep->alt_av.attr.port_num = p_port_cep->port_num;\r
1735 \r
1736         p_cep->alt_av.attr.sl = conn_lap_path_get_svc_lvl( p_path );\r
1737         p_cep->alt_av.attr.dlid = p_path->local_lid;\r
1738 \r
1739         if( !conn_lap_path_get_subn_lcl( p_path ) )\r
1740         {\r
1741                 p_cep->alt_av.attr.grh_valid = TRUE;\r
1742                 p_cep->alt_av.attr.grh.ver_class_flow = ib_grh_set_ver_class_flow(\r
1743                         1, conn_lap_path_get_tclass( p_path ),\r
1744                         conn_lap_path_get_flow_lbl( p_path ) );\r
1745                 p_cep->alt_av.attr.grh.hop_limit = p_path->hop_limit;\r
1746                 p_cep->alt_av.attr.grh.dest_gid = p_path->local_gid;\r
1747                 p_cep->alt_av.attr.grh.src_gid = p_path->remote_gid;\r
1748         }\r
1749         else\r
1750         {\r
1751                 p_cep->alt_av.attr.grh_valid = FALSE;\r
1752         }\r
1753         p_cep->alt_av.attr.static_rate = conn_lap_path_get_pkt_rate( p_path );\r
1754         p_cep->alt_av.attr.path_bits =\r
1755                 (uint8_t)(p_path->remote_lid - p_port_cep->base_lid);\r
1756 \r
1757         /*\r
1758          * Note that while we never use the connected AV attributes internally,\r
1759          * we store them so we can pass them back to users.  For the LAP, we\r
1760          * first copy the settings from the current primary - MTU and retry\r
1761          * counts are only specified in the REQ.\r
1762          */\r
1763         p_cep->alt_av.attr.conn = p_cep->av[p_cep->idx_primary].attr.conn;\r
1764         p_cep->alt_av.attr.conn.local_ack_timeout =\r
1765                 conn_lap_path_get_lcl_ack_timeout( p_path );\r
1766 \r
1767         AL_EXIT( AL_DBG_CM );\r
1768         return TRUE;\r
1769 }\r
1770 \r
1771 \r
1772 static void\r
1773 __lap_handler(\r
1774         IN                              cep_agent_t* const                      p_port_cep,\r
1775         IN                              ib_mad_element_t* const         p_mad )\r
1776 {\r
1777         ib_api_status_t         status;\r
1778         mad_cm_lap_t            *p_lap;\r
1779         kcep_t                          *p_cep;\r
1780         KLOCK_QUEUE_HANDLE      hdl;\r
1781 \r
1782         AL_ENTER( AL_DBG_CM );\r
1783 \r
1784         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1785 \r
1786         p_lap = (mad_cm_lap_t*)p_mad->p_mad_buf;\r
1787 \r
1788         /* Find the connection by local connection ID. */\r
1789         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1790         p_cep = __lookup_cep( NULL, p_lap->remote_comm_id );\r
1791         if( !p_cep || p_cep->remote_comm_id != p_lap->local_comm_id )\r
1792         {\r
1793                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1794                 ib_put_mad( p_mad );\r
1795                 AL_PRINT_EXIT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received that could not be matched.\n") );\r
1796                 return;\r
1797         }\r
1798 \r
1799         switch( p_cep->state )\r
1800         {\r
1801         case CEP_STATE_REP_SENT:\r
1802         case CEP_STATE_REP_MRA_RCVD:\r
1803                 /*\r
1804                  * These two cases handle the RTU being dropped.  Receipt of\r
1805                  * a LAP indicates that the connection is established.\r
1806                  */\r
1807         case CEP_STATE_ESTABLISHED:\r
1808                 /*\r
1809                  * We don't check for other "established" states related to\r
1810                  * alternate path management (CEP_STATE_LAP_RCVD, etc)\r
1811                  */\r
1812 \r
1813                 /* We only support receiving LAP if we took the passive role. */\r
1814                 if( p_cep->was_active )\r
1815                 {\r
1816                         ib_put_mad( p_mad );\r
1817                         break;\r
1818                 }\r
1819 \r
1820                 /* Store the transaction ID for use during the LAP exchange. */\r
1821                 p_cep->tid = p_lap->hdr.trans_id;\r
1822 \r
1823                 /*\r
1824                  * Copy the path record into the connection for use when\r
1825                  * sending the APR and loading the path.\r
1826                  */\r
1827                 if( !__format_lap_av( p_cep, &p_lap->alternate_path ) )\r
1828                 {\r
1829                         /* Trap an invalid path. */\r
1830                         ib_put_mad( p_mad );\r
1831                         break;\r
1832                 }\r
1833 \r
1834                 p_cep->state = CEP_STATE_LAP_RCVD;\r
1835 \r
1836                 status = __cep_queue_mad( p_cep, p_mad );\r
1837 \r
1838                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1839 \r
1840                 if( status == IB_SUCCESS )\r
1841                         __process_cep( p_cep );\r
1842 \r
1843                 AL_EXIT( AL_DBG_CM );\r
1844                 return;\r
1845 \r
1846         case CEP_STATE_LAP_MRA_SENT:\r
1847                 __repeat_mad( p_port_cep, p_cep, p_mad );\r
1848                 break;\r
1849 \r
1850         default:\r
1851                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("LAP received in invalid state.\n") );\r
1852                 ib_put_mad( p_mad );\r
1853                 break;\r
1854         }\r
1855 \r
1856         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1857         AL_EXIT( AL_DBG_CM );\r
1858 }\r
1859 \r
1860 \r
1861 static void\r
1862 __apr_handler(\r
1863         IN                              ib_mad_element_t* const         p_mad )\r
1864 {\r
1865         ib_api_status_t         status;\r
1866         mad_cm_apr_t            *p_apr;\r
1867         kcep_t                          *p_cep;\r
1868         KLOCK_QUEUE_HANDLE      hdl;\r
1869 \r
1870         AL_ENTER( AL_DBG_CM );\r
1871 \r
1872         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1873 \r
1874         p_apr = (mad_cm_apr_t*)p_mad->p_mad_buf;\r
1875 \r
1876         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
1877         p_cep = __lookup_cep( NULL, p_apr->remote_comm_id );\r
1878         if( !p_cep || p_cep->remote_comm_id != p_apr->local_comm_id )\r
1879         {\r
1880                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received that could not be matched.\n") );\r
1881                 goto done;\r
1882         }\r
1883 \r
1884         switch( p_cep->state )\r
1885         {\r
1886         case CEP_STATE_LAP_SENT:\r
1887         case CEP_STATE_LAP_MRA_RCVD:\r
1888                 /* Cancel sending the LAP. */\r
1889                 if( p_cep->p_send_mad )\r
1890                 {\r
1891                         ib_cancel_mad( p_cep->h_mad_svc, p_cep->p_send_mad );\r
1892                         p_cep->p_send_mad = NULL;\r
1893                 }\r
1894 \r
1895                 /* Copy the temporary alternate AV. */\r
1896                 p_cep->av[(p_cep->idx_primary + 1) & 0x1] = p_cep->alt_av;\r
1897 \r
1898                 /* Update the maximum packet lifetime. */\r
1899                 p_cep->max_2pkt_life = max( p_cep->max_2pkt_life, p_cep->alt_2pkt_life );\r
1900 \r
1901                 /* Update the timewait time. */\r
1902                 __calc_timewait( p_cep );\r
1903 \r
1904                 p_cep->state = CEP_STATE_ESTABLISHED;\r
1905 \r
1906                 status = __cep_queue_mad( p_cep, p_mad );\r
1907 \r
1908                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1909 \r
1910                 if( status == IB_SUCCESS )\r
1911                         __process_cep( p_cep );\r
1912 \r
1913                 AL_EXIT( AL_DBG_CM );\r
1914                 return;\r
1915 \r
1916         default:\r
1917                 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM, ("APR received in invalid state.\n") );\r
1918                 break;\r
1919         }\r
1920 \r
1921 done:\r
1922         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
1923         ib_put_mad( p_mad );\r
1924         AL_EXIT( AL_DBG_CM );\r
1925 }\r
1926 \r
1927 \r
1928 static void\r
1929 __cep_mad_recv_cb(\r
1930         IN                              ib_mad_svc_handle_t                     h_mad_svc,\r
1931         IN                              void                                            *context,\r
1932         IN                              ib_mad_element_t                        *p_mad )\r
1933 {\r
1934         cep_agent_t             *p_port_cep;\r
1935         ib_mad_t                *p_hdr;\r
1936 \r
1937         AL_ENTER( AL_DBG_CM );\r
1938 \r
1939         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
1940 \r
1941         UNUSED_PARAM( h_mad_svc );\r
1942         p_port_cep = (cep_agent_t*)context;\r
1943 \r
1944         CL_ASSERT( p_mad->p_next == NULL );\r
1945 \r
1946         p_hdr = (ib_mad_t*)p_mad->p_mad_buf;\r
1947 \r
1948         /*\r
1949          * TODO: Add filtering in all the handlers for unsupported class version.\r
1950          * See 12.6.7.2 Rejection Reason, code 31.\r
1951          */\r
1952 \r
1953         switch( p_hdr->attr_id )\r
1954         {\r
1955         case CM_REQ_ATTR_ID:\r
1956                 __req_handler( p_port_cep, p_mad );\r
1957                 break;\r
1958 \r
1959         case CM_MRA_ATTR_ID:\r
1960                 __mra_handler( p_mad );\r
1961                 break;\r
1962 \r
1963         case CM_REJ_ATTR_ID:\r
1964                 __rej_handler( p_mad );\r
1965                 break;\r
1966 \r
1967         case CM_REP_ATTR_ID:\r
1968                 __rep_handler( p_port_cep, p_mad );\r
1969                 break;\r
1970 \r
1971         case CM_RTU_ATTR_ID:\r
1972                 __rtu_handler( p_mad );\r
1973                 break;\r
1974 \r
1975         case CM_DREQ_ATTR_ID:\r
1976                 __dreq_handler( p_port_cep, p_mad );\r
1977                 break;\r
1978 \r
1979         case CM_DREP_ATTR_ID:\r
1980                 __drep_handler( p_mad );\r
1981                 break;\r
1982 \r
1983         case CM_LAP_ATTR_ID:\r
1984                 __lap_handler( p_port_cep, p_mad );\r
1985                 break;\r
1986 \r
1987         case CM_APR_ATTR_ID:\r
1988                 __apr_handler( p_mad );\r
1989                 break;\r
1990 \r
1991         case CM_SIDR_REQ_ATTR_ID:\r
1992 //              p_async_mad->item.pfn_callback = __process_cm_sidr_req;\r
1993 //              break;\r
1994 //\r
1995         case CM_SIDR_REP_ATTR_ID:\r
1996 //              p_async_mad->item.pfn_callback = __process_cm_sidr_rep;\r
1997 //              break;\r
1998 //\r
1999         default:\r
2000                 ib_put_mad( p_mad );\r
2001                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2002                         ("Invalid CM MAD attribute ID.\n") );\r
2003                 return;\r
2004         }\r
2005 \r
2006         AL_EXIT( AL_DBG_CM );\r
2007 }\r
2008 \r
2009 \r
2010 static inline cep_agent_t*\r
2011 __get_cep_agent(\r
2012         IN                              kcep_t* const                           p_cep )\r
2013 {\r
2014         cl_map_item_t           *p_item;\r
2015 \r
2016         CL_ASSERT( p_cep );\r
2017 \r
2018         /* Look up the primary CEP port agent */\r
2019         p_item = cl_qmap_get( &gp_cep_mgr->port_map,\r
2020                 p_cep->av[p_cep->idx_primary].port_guid );\r
2021         if( p_item == cl_qmap_end( &gp_cep_mgr->port_map ) )\r
2022                 return NULL;\r
2023 \r
2024         return PARENT_STRUCT( p_item, cep_agent_t, item );\r
2025 }\r
2026 \r
2027 \r
2028 static inline void\r
2029 __format_mad_av(\r
2030                 OUT                     ib_mad_element_t* const         p_mad,\r
2031         IN                              kcep_av_t* const                        p_av )\r
2032 {\r
2033         /* Set the addressing information in the MAD. */\r
2034         p_mad->grh_valid = p_av->attr.grh_valid;\r
2035         if( p_av->attr.grh_valid )\r
2036                 cl_memcpy( p_mad->p_grh, &p_av->attr.grh, sizeof(ib_grh_t) );\r
2037 \r
2038         p_mad->remote_sl = p_av->attr.sl;\r
2039         p_mad->remote_lid = p_av->attr.dlid;\r
2040         p_mad->path_bits = p_av->attr.path_bits;\r
2041         p_mad->pkey_index = p_av->pkey_index;\r
2042         p_mad->remote_qp = IB_QP1;\r
2043         p_mad->send_opt = IB_SEND_OPT_SIGNALED;\r
2044         p_mad->remote_qkey = IB_QP1_WELL_KNOWN_Q_KEY;\r
2045         /* Let the MAD service manage the AV for us. */\r
2046         p_mad->h_av = NULL;\r
2047 }\r
2048 \r
2049 \r
2050 static ib_api_status_t\r
2051 __cep_send_mad(\r
2052         IN                              cep_agent_t* const                      p_port_cep,\r
2053         IN                              ib_mad_element_t* const         p_mad )\r
2054 {\r
2055         ib_api_status_t         status;\r
2056 \r
2057         AL_ENTER( AL_DBG_CM );\r
2058 \r
2059         CL_ASSERT( p_port_cep );\r
2060         CL_ASSERT( p_mad );\r
2061 \r
2062         /* Use the mad's attributes already present */\r
2063         p_mad->resp_expected = FALSE;\r
2064         p_mad->retry_cnt = 0;\r
2065         p_mad->timeout_ms = 0;\r
2066 \r
2067         /* Clear the contexts since the send isn't associated with a CEP. */\r
2068         p_mad->context1 = NULL;\r
2069         p_mad->context2 = NULL;\r
2070 \r
2071         status = ib_send_mad( p_port_cep->h_mad_svc, p_mad, NULL );\r
2072         if( status != IB_SUCCESS )\r
2073         {\r
2074                 ib_put_mad( p_mad );\r
2075                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2076                         ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );\r
2077         }\r
2078 \r
2079         AL_EXIT( AL_DBG_CM );\r
2080         return status;\r
2081 }\r
2082 \r
2083 \r
2084 static ib_api_status_t\r
2085 __cep_send_retry(\r
2086         IN                              cep_agent_t* const                      p_port_cep,\r
2087         IN                              kcep_t* const                           p_cep,\r
2088         IN                              ib_mad_element_t* const         p_mad )\r
2089 {\r
2090         ib_api_status_t         status;\r
2091 \r
2092         AL_ENTER( AL_DBG_CM );\r
2093 \r
2094         CL_ASSERT( p_cep );\r
2095         CL_ASSERT( p_mad );\r
2096         CL_ASSERT( p_mad->p_mad_buf->attr_id == CM_REQ_ATTR_ID ||\r
2097                 p_mad->p_mad_buf->attr_id == CM_REP_ATTR_ID ||\r
2098                 p_mad->p_mad_buf->attr_id == CM_LAP_ATTR_ID ||\r
2099                 p_mad->p_mad_buf->attr_id == CM_DREQ_ATTR_ID );\r
2100 \r
2101         /*\r
2102                 * REQ, REP, and DREQ are retried until either a response is\r
2103                 * received or the operation times out.\r
2104                 */\r
2105         p_mad->resp_expected = TRUE;\r
2106         p_mad->retry_cnt = p_cep->max_cm_retries;\r
2107         p_mad->timeout_ms = p_cep->retry_timeout;\r
2108 \r
2109         CL_ASSERT( !p_cep->p_send_mad );\r
2110 \r
2111         /* Store the mad & mad service handle in the CEP for cancelling. */\r
2112         p_cep->h_mad_svc = p_port_cep->h_mad_svc;\r
2113         p_cep->p_send_mad = p_mad;\r
2114 \r
2115         /* reference the connection for which we are sending the MAD. */\r
2116         cl_atomic_inc( &p_cep->ref_cnt );\r
2117 \r
2118         /* Set the context. */\r
2119         p_mad->context1 = p_cep;\r
2120         p_mad->context2 = NULL;\r
2121 \r
2122         /* Fire in the hole! */\r
2123         status = ib_send_mad( p_cep->h_mad_svc, p_mad, NULL );\r
2124         if( status != IB_SUCCESS )\r
2125         {\r
2126                 /*\r
2127                  * Note that we don't need to check for destruction here since\r
2128                  * we're holding the global lock.\r
2129                  */\r
2130                 cl_atomic_dec( &p_cep->ref_cnt );\r
2131                 p_cep->p_send_mad = NULL;\r
2132                 ib_put_mad( p_mad );\r
2133                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2134                         ("ib_send_mad failed with status %s.\n", ib_get_err_str(status)) );\r
2135         }\r
2136 \r
2137         AL_EXIT( AL_DBG_CM );\r
2138         return status;\r
2139 }\r
2140 \r
2141 \r
2142 static void\r
2143 __cep_mad_send_cb(\r
2144         IN                              ib_mad_svc_handle_t                     h_mad_svc,\r
2145         IN                              void                                            *context,\r
2146         IN                              ib_mad_element_t                        *p_mad )\r
2147 {\r
2148         ib_api_status_t         status;\r
2149         cep_agent_t                     *p_port_cep;\r
2150         kcep_t                          *p_cep;\r
2151         KLOCK_QUEUE_HANDLE      hdl;\r
2152         ib_pfn_destroy_cb_t     pfn_destroy_cb;\r
2153         void                            *cep_context;\r
2154 \r
2155         AL_ENTER( AL_DBG_CM );\r
2156 \r
2157         UNUSED_PARAM( h_mad_svc );\r
2158         CL_ASSERT( p_mad->p_next == NULL );\r
2159         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
2160 \r
2161         p_port_cep = (cep_agent_t*)context;\r
2162 \r
2163         p_cep = (kcep_t* __ptr64)p_mad->context1;\r
2164 \r
2165         /*\r
2166          * The connection context is not set when performing immediate responses,\r
2167          * such as repeating MADS.\r
2168          */\r
2169         if( !p_cep )\r
2170         {\r
2171                 ib_put_mad( p_mad );\r
2172                 AL_EXIT( AL_DBG_CM );\r
2173                 return;\r
2174         }\r
2175 \r
2176         p_mad->context1 = NULL;\r
2177 \r
2178         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
2179         /* Clear the sent MAD pointer so that we don't try cancelling again. */\r
2180         if( p_cep->p_send_mad == p_mad )\r
2181                 p_cep->p_send_mad = NULL;\r
2182 \r
2183         switch( p_mad->status )\r
2184         {\r
2185         case IB_WCS_SUCCESS:\r
2186                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
2187                 ib_put_mad( p_mad );\r
2188                 break;\r
2189 \r
2190         case IB_WCS_CANCELED:\r
2191                 if( p_cep->state != CEP_STATE_REQ_SENT &&\r
2192                         p_cep->state != CEP_STATE_REQ_MRA_RCVD &&\r
2193                         p_cep->state != CEP_STATE_REP_SENT &&\r
2194                         p_cep->state != CEP_STATE_REP_MRA_RCVD &&\r
2195                         p_cep->state != CEP_STATE_LAP_SENT &&\r
2196                         p_cep->state != CEP_STATE_LAP_MRA_RCVD &&\r
2197                         p_cep->state != CEP_STATE_DREQ_SENT &&\r
2198                         p_cep->state != CEP_STATE_SREQ_SENT )\r
2199                 {\r
2200                         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
2201                         ib_put_mad( p_mad );\r
2202                         break;\r
2203                 }\r
2204                 /* Treat as a timeout so we don't stall the state machine. */\r
2205                 p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;\r
2206 \r
2207                 /* Fall through. */\r
2208         case IB_WCS_TIMEOUT_RETRY_ERR:\r
2209         default:\r
2210                 /* Timeout.  Reject the connection. */\r
2211                 switch( p_cep->state )\r
2212                 {\r
2213                 case CEP_STATE_REQ_SENT:\r
2214                 case CEP_STATE_REQ_MRA_RCVD:\r
2215                 case CEP_STATE_REP_SENT:\r
2216                 case CEP_STATE_REP_MRA_RCVD:\r
2217                         /* Send the REJ. */\r
2218                         __reject_timeout( p_port_cep, p_cep, p_mad );\r
2219                         __remove_cep( p_cep );\r
2220                         p_cep->state = CEP_STATE_IDLE;\r
2221                         break;\r
2222 \r
2223                 case CEP_STATE_DREQ_DESTROY:\r
2224                         p_cep->state = CEP_STATE_DESTROY;\r
2225                         __insert_timewait( p_cep );\r
2226                         /* Fall through. */\r
2227 \r
2228                 case CEP_STATE_DESTROY:\r
2229                         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
2230                         ib_put_mad( p_mad );\r
2231                         goto done;\r
2232 \r
2233                 case CEP_STATE_DREQ_SENT:\r
2234                         /*\r
2235                          * Make up a DREP mad so we can respond if we receive\r
2236                          * a DREQ while in timewait.\r
2237                          */\r
2238                         __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );\r
2239                         __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );\r
2240                         p_cep->state = CEP_STATE_TIMEWAIT;\r
2241                         __insert_timewait( p_cep );\r
2242 \r
2243                 default:\r
2244                         break;\r
2245                 }\r
2246 \r
2247                 status = __cep_queue_mad( p_cep, p_mad );\r
2248                 CL_ASSERT( status != IB_INVALID_STATE );\r
2249                 KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
2250 \r
2251                 if( status == IB_SUCCESS )\r
2252                         __process_cep( p_cep );\r
2253                 break;\r
2254         }\r
2255 \r
2256 done:\r
2257         pfn_destroy_cb = p_cep->pfn_destroy_cb;\r
2258         cep_context = p_cep->context;\r
2259 \r
2260         if( !cl_atomic_dec( &p_cep->ref_cnt ) && pfn_destroy_cb )\r
2261                 pfn_destroy_cb( cep_context );\r
2262         AL_EXIT( AL_DBG_CM );\r
2263 }\r
2264 \r
2265 \r
2266 static void\r
2267 __cep_qp_event_cb(\r
2268         IN                              ib_async_event_rec_t            *p_event_rec )\r
2269 {\r
2270         UNUSED_PARAM( p_event_rec );\r
2271 \r
2272         /*\r
2273          * Most of the QP events are trapped by the real owner of the QP.\r
2274          * For real events, the CM may not be able to do much anyways!\r
2275          */\r
2276 }\r
2277 \r
2278 \r
2279 static ib_api_status_t\r
2280 __init_data_svc(\r
2281         IN                              cep_agent_t* const                      p_port_cep,\r
2282         IN              const   ib_port_attr_t* const           p_port_attr )\r
2283 {\r
2284         ib_api_status_t         status;\r
2285         ib_qp_create_t          qp_create;\r
2286         ib_mad_svc_t            mad_svc;\r
2287 \r
2288         AL_ENTER( AL_DBG_CM );\r
2289 \r
2290         /*\r
2291          * Create the PD alias.  We use the port CM's al_obj_t as the context\r
2292          * to allow using deref_al_obj as the destroy callback.\r
2293          */\r
2294         status = ib_alloc_pd( p_port_cep->h_ca, IB_PDT_ALIAS, &p_port_cep->obj,\r
2295                 &p_port_cep->h_pd );\r
2296         if( status != IB_SUCCESS )\r
2297         {\r
2298                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2299                         ("ib_alloc_pd failed with status %s\n", ib_get_err_str(status)) );\r
2300                 return status;\r
2301         }\r
2302         /* Reference the port object on behalf of the PD. */\r
2303         ref_al_obj( &p_port_cep->obj );\r
2304 \r
2305         /* Create the MAD QP. */\r
2306         cl_memclr( &qp_create, sizeof( ib_qp_create_t ) );\r
2307         qp_create.qp_type = IB_QPT_QP1_ALIAS;\r
2308         qp_create.rq_depth = CEP_MAD_RQ_DEPTH;\r
2309         qp_create.sq_depth = CEP_MAD_SQ_DEPTH;\r
2310         qp_create.rq_sge = CEP_MAD_RQ_SGE;\r
2311         qp_create.sq_sge = CEP_MAD_SQ_SGE;\r
2312         qp_create.sq_signaled = TRUE;\r
2313         /*\r
2314          * We use the port CM's al_obj_t as the context to allow using\r
2315          * deref_al_obj as the destroy callback.\r
2316          */\r
2317         status = ib_get_spl_qp( p_port_cep->h_pd, p_port_attr->port_guid,\r
2318                 &qp_create, &p_port_cep->obj, __cep_qp_event_cb, &p_port_cep->pool_key,\r
2319                 &p_port_cep->h_qp );\r
2320         if( status != IB_SUCCESS )\r
2321         {\r
2322                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2323                         ("ib_get_spl_qp failed with status %s\n", ib_get_err_str(status)) );\r
2324                 return status;\r
2325         }\r
2326         /* Reference the port object on behalf of the QP. */\r
2327         ref_al_obj( &p_port_cep->obj );\r
2328 \r
2329         /* Create the MAD service. */\r
2330         cl_memclr( &mad_svc, sizeof(mad_svc) );\r
2331         mad_svc.mad_svc_context = p_port_cep;\r
2332         mad_svc.pfn_mad_recv_cb = __cep_mad_recv_cb;\r
2333         mad_svc.pfn_mad_send_cb = __cep_mad_send_cb;\r
2334         mad_svc.support_unsol = TRUE;\r
2335         mad_svc.mgmt_class = IB_MCLASS_COMM_MGMT;\r
2336         mad_svc.mgmt_version = IB_MCLASS_CM_VER_2;\r
2337         mad_svc.method_array[IB_MAD_METHOD_SEND] = TRUE;\r
2338         status =\r
2339                 ib_reg_mad_svc( p_port_cep->h_qp, &mad_svc, &p_port_cep->h_mad_svc );\r
2340         if( status != IB_SUCCESS )\r
2341         {\r
2342                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2343                         ("ib_reg_mad_svc failed with status %s\n", ib_get_err_str(status)) );\r
2344                 return status;\r
2345         }\r
2346 \r
2347         AL_EXIT( AL_DBG_CM );\r
2348         return IB_SUCCESS;\r
2349 }\r
2350 \r
2351 \r
2352 /*\r
2353  * Performs immediate cleanup of resources.\r
2354  */\r
2355 static void\r
2356 __destroying_port_cep(\r
2357         IN                              al_obj_t                                        *p_obj )\r
2358 {\r
2359         cep_agent_t                     *p_port_cep;\r
2360         KLOCK_QUEUE_HANDLE      hdl;\r
2361 \r
2362         AL_ENTER( AL_DBG_CM );\r
2363 \r
2364         p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );\r
2365 \r
2366         if( p_port_cep->port_guid )\r
2367         {\r
2368                 KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );\r
2369                 cl_qmap_remove_item( &gp_cep_mgr->port_map, &p_port_cep->item );\r
2370                 KeReleaseInStackQueuedSpinLock( &hdl );\r
2371         }\r
2372 \r
2373         if( p_port_cep->h_qp )\r
2374         {\r
2375                 ib_destroy_qp( p_port_cep->h_qp, (ib_pfn_destroy_cb_t)deref_al_obj );\r
2376                 p_port_cep->h_qp = NULL;\r
2377         }\r
2378 \r
2379         if( p_port_cep->h_pd )\r
2380         {\r
2381                 ib_dealloc_pd( p_port_cep->h_pd, (ib_pfn_destroy_cb_t)deref_al_obj );\r
2382                 p_port_cep->h_pd = NULL;\r
2383         }\r
2384 \r
2385         AL_EXIT( AL_DBG_CM );\r
2386 }\r
2387 \r
2388 \r
2389 \r
2390 /*\r
2391  * Release all resources allocated by a port CM agent.  Finishes any cleanup\r
2392  * for a port agent.\r
2393  */\r
2394 static void\r
2395 __free_port_cep(\r
2396         IN                              al_obj_t                                        *p_obj )\r
2397 {\r
2398         cep_agent_t                     *p_port_cep;\r
2399         ib_port_attr_mod_t      port_attr_mod;\r
2400 \r
2401         AL_ENTER( AL_DBG_CM );\r
2402 \r
2403         p_port_cep = PARENT_STRUCT( p_obj, cep_agent_t, obj );\r
2404 \r
2405         if( p_port_cep->h_ca )\r
2406         {\r
2407                 /* Update local port attributes */\r
2408                 port_attr_mod.cap.cm = FALSE;\r
2409                 ib_modify_ca( p_port_cep->h_ca, p_port_cep->port_num,\r
2410                         IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );\r
2411 \r
2412                 deref_al_obj( &p_port_cep->h_ca->obj );\r
2413         }\r
2414 \r
2415         destroy_al_obj( &p_port_cep->obj );\r
2416         cl_free( p_port_cep );\r
2417 \r
2418         AL_EXIT( AL_DBG_CM );\r
2419 }\r
2420 \r
2421 \r
2422 /*\r
2423  * Create a port agent for a given port.\r
2424  */\r
2425 static ib_api_status_t\r
2426 __create_port_cep(\r
2427         IN                              ib_pnp_port_rec_t                       *p_pnp_rec )\r
2428 {\r
2429         cep_agent_t                     *p_port_cep;\r
2430         ib_api_status_t         status;\r
2431         ib_port_attr_mod_t      port_attr_mod;\r
2432         KLOCK_QUEUE_HANDLE      hdl;\r
2433 \r
2434         AL_ENTER( AL_DBG_CM );\r
2435 \r
2436         /* calculate size of port_cm struct */\r
2437         p_port_cep = (cep_agent_t*)cl_zalloc( sizeof(cep_agent_t) );\r
2438         if( !p_port_cep )\r
2439         {\r
2440                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2441                         ("Failed to cl_zalloc port CM agent.\n") );\r
2442                 return IB_INSUFFICIENT_MEMORY;\r
2443         }\r
2444 \r
2445         construct_al_obj( &p_port_cep->obj, AL_OBJ_TYPE_CM );\r
2446 \r
2447         status = init_al_obj( &p_port_cep->obj, p_port_cep, TRUE,\r
2448                 __destroying_port_cep, NULL, __free_port_cep );\r
2449         if( status != IB_SUCCESS )\r
2450         {\r
2451                 __free_port_cep( &p_port_cep->obj );\r
2452                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2453                         ("init_al_obj failed with status %s.\n", ib_get_err_str(status)) );\r
2454                 return status;\r
2455         }\r
2456 \r
2457         /* Attach to the global CM object. */\r
2458         status = attach_al_obj( &gp_cep_mgr->obj, &p_port_cep->obj );\r
2459         if( status != IB_SUCCESS )\r
2460         {\r
2461                 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );\r
2462                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2463                         ("attach_al_obj returned %s.\n", ib_get_err_str(status)) );\r
2464                 return status;\r
2465         }\r
2466 \r
2467         p_port_cep->port_guid = p_pnp_rec->p_port_attr->port_guid;\r
2468         p_port_cep->port_num = p_pnp_rec->p_port_attr->port_num;\r
2469         p_port_cep->base_lid = p_pnp_rec->p_port_attr->lid;\r
2470 \r
2471         KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );\r
2472         cl_qmap_insert(\r
2473                 &gp_cep_mgr->port_map, p_port_cep->port_guid, &p_port_cep->item );\r
2474         KeReleaseInStackQueuedSpinLock( &hdl );\r
2475 \r
2476         /* Get a reference to the CA on which we are loading. */\r
2477         p_port_cep->h_ca = acquire_ca( p_pnp_rec->p_ca_attr->ca_guid );\r
2478         if( !p_port_cep->h_ca )\r
2479         {\r
2480                 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );\r
2481                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("acquire_ca failed.\n") );\r
2482                 return IB_INVALID_GUID; }\r
2483 \r
2484         status = __init_data_svc( p_port_cep, p_pnp_rec->p_port_attr );\r
2485         if( status != IB_SUCCESS )\r
2486         {\r
2487                 p_port_cep->obj.pfn_destroy( &p_port_cep->obj, NULL );\r
2488                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
2489                         ("__init_data_svc failed with status %s.\n",\r
2490                         ib_get_err_str(status)) );\r
2491                 return status;\r
2492         }\r
2493 \r
2494         /* Update local port attributes */\r
2495         cl_memclr( &port_attr_mod, sizeof(ib_port_attr_mod_t) );\r
2496         port_attr_mod.cap.cm = TRUE;\r
2497         status = ib_modify_ca( p_port_cep->h_ca, p_pnp_rec->p_port_attr->port_num,\r
2498                 IB_CA_MOD_IS_CM_SUPPORTED, &port_attr_mod );\r
2499 \r
2500         /* Update the PNP context to reference this port. */\r
2501         p_pnp_rec->pnp_rec.context = p_port_cep;\r
2502 \r
2503         /* Release the reference taken in init_al_obj. */\r
2504         deref_al_obj( &p_port_cep->obj );\r
2505 \r
2506         AL_EXIT( AL_DBG_CM );\r
2507         return IB_SUCCESS;\r
2508 }\r
2509 \r
2510 \r
2511 /******************************************************************************\r
2512 * Global CEP manager\r
2513 ******************************************************************************/\r
2514 \r
2515 static cep_cid_t*\r
2516 __get_lcid(\r
2517                 OUT                     net32_t* const                          p_cid )\r
2518 {\r
2519         cl_status_t                     status;\r
2520         uint32_t                        size, cid;\r
2521         cep_cid_t                       *p_cep_cid;\r
2522 \r
2523         AL_ENTER( AL_DBG_CM );\r
2524 \r
2525         size = (uint32_t)cl_vector_get_size( &gp_cep_mgr->cid_vector );\r
2526         cid = gp_cep_mgr->free_cid;\r
2527         if( gp_cep_mgr->free_cid == size )\r
2528         {\r
2529                 /* Grow the vector pool. */\r
2530                 status =\r
2531                         cl_vector_set_size( &gp_cep_mgr->cid_vector, size + CEP_CID_GROW );\r
2532                 if( status != CL_SUCCESS )\r
2533                 {\r
2534                         AL_EXIT( AL_DBG_CM );\r
2535                         return NULL;\r
2536                 }\r
2537                 /*\r
2538                  * Return the the start of the free list since the\r
2539                  * entry initializer incremented it.\r
2540                  */\r
2541                 gp_cep_mgr->free_cid = size;\r
2542         }\r
2543 \r
2544         /* Get the next free entry. */\r
2545         p_cep_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, cid );\r
2546 \r
2547         /* Update the next entry index. */\r
2548         gp_cep_mgr->free_cid = (uint32_t)(uintn_t)p_cep_cid->p_cep;\r
2549 \r
2550         *p_cid = cid;\r
2551 \r
2552         AL_EXIT( AL_DBG_CM );\r
2553         return p_cep_cid;\r
2554 }\r
2555 \r
2556 \r
2557 static inline kcep_t*\r
2558 __lookup_cep(\r
2559         IN                              ib_al_handle_t                          h_al OPTIONAL,\r
2560         IN                              net32_t                                         cid )\r
2561 {\r
2562         size_t                          idx;\r
2563         cep_cid_t                       *p_cid;\r
2564 \r
2565         /* Mask off the counter bits so we get the index in our vector. */\r
2566         idx = cid & CEP_MAX_CID_MASK;\r
2567 \r
2568         if( idx >= cl_vector_get_size( &gp_cep_mgr->cid_vector ) )\r
2569                 return NULL;\r
2570 \r
2571         p_cid = (cep_cid_t*)cl_vector_get_ptr( &gp_cep_mgr->cid_vector, idx );\r
2572         if( !p_cid->h_al )\r
2573                 return NULL;\r
2574 \r
2575         /*\r
2576          * h_al is NULL when processing MADs, so we need to match on\r
2577          * the actual local communication ID.  If h_al is non-NULL, we\r
2578          * are doing a lookup from a call to our API, and only need to match\r
2579          * on the index in the vector (without the modifier).\r
2580          */\r
2581         if( h_al )\r
2582         {\r
2583                 if( p_cid->h_al != h_al )\r
2584                         return NULL;\r
2585         }\r
2586         else if( p_cid->p_cep->local_comm_id != cid )\r
2587         {\r
2588                 return NULL;\r
2589         }\r
2590 \r
2591         return p_cid->p_cep;\r
2592 }\r
2593 \r
2594 \r
2595 /*\r
2596  * Lookup a CEP by remote comm ID and CA GUID.\r
2597  */\r
2598 static kcep_t*\r
2599 __lookup_by_id(\r
2600         IN                              net32_t                                         remote_comm_id,\r
2601         IN                              net64_t                                         remote_ca_guid )\r
2602 {\r
2603         cl_rbmap_item_t         *p_item;\r
2604         kcep_t                  *p_cep;\r
2605 \r
2606         AL_ENTER( AL_DBG_CM );\r
2607 \r
2608         /* Match against pending connections using remote comm ID and CA GUID. */\r
2609         p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );\r
2610         while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )\r
2611         {\r
2612                 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );\r
2613 \r
2614                 if( remote_comm_id < p_cep->remote_comm_id )\r
2615                         p_item = cl_rbmap_left( p_item );\r
2616                 else if( remote_comm_id > p_cep->remote_comm_id )\r
2617                         p_item = cl_rbmap_right( p_item );\r
2618                 else if( remote_ca_guid < p_cep->remote_ca_guid )\r
2619                         p_item = cl_rbmap_left( p_item );\r
2620                 else if( remote_ca_guid > p_cep->remote_ca_guid )\r
2621                         p_item = cl_rbmap_right( p_item );\r
2622                 else\r
2623                         return p_cep;\r
2624         }\r
2625 \r
2626         AL_EXIT( AL_DBG_CM );\r
2627         return NULL;\r
2628 }\r
2629 \r
2630 \r
2631 /*\r
2632  * Lookup a CEP by Service ID and private data.\r
2633  */\r
2634 static kcep_t*\r
2635 __lookup_listen(\r
2636         IN                              net64_t                                         sid,\r
2637         IN                              net64_t                                         port_guid,\r
2638         IN                              uint8_t                                         *p_pdata )\r
2639 {\r
2640         cl_rbmap_item_t         *p_item;\r
2641         kcep_t                          *p_cep;\r
2642         intn_t                          cmp;\r
2643 \r
2644         AL_ENTER( AL_DBG_CM );\r
2645 \r
2646         /* Match against pending connections using remote comm ID and CA GUID. */\r
2647         p_item = cl_rbmap_root( &gp_cep_mgr->listen_map );\r
2648         while( p_item != cl_rbmap_end( &gp_cep_mgr->listen_map ) )\r
2649         {\r
2650                 p_cep = PARENT_STRUCT( p_item, kcep_t, listen_item );\r
2651 \r
2652                 if( sid == p_cep->sid )\r
2653                         goto port_cmp;\r
2654                 else if( sid < p_cep->sid )\r
2655                         p_item = cl_rbmap_left( p_item );\r
2656                 else\r
2657                         p_item = cl_rbmap_right( p_item );\r
2658 \r
2659                 continue;\r
2660 \r
2661 port_cmp:\r
2662                 if( p_cep->port_guid != IB_ALL_PORTS )\r
2663                 {\r
2664                         if( port_guid == p_cep->port_guid )\r
2665                                 goto pdata_cmp;\r
2666                         else if( port_guid < p_cep->port_guid )\r
2667                                 p_item = cl_rbmap_left( p_item );\r
2668                         else\r
2669                                 p_item = cl_rbmap_right( p_item );\r
2670 \r
2671                         continue;\r
2672                 }\r
2673 \r
2674 pdata_cmp:\r
2675                 if( p_cep->p_cmp_buf && p_pdata )\r
2676                 {\r
2677                         cmp = cl_memcmp( &p_pdata[p_cep->cmp_offset],\r
2678                                 p_cep->p_cmp_buf, p_cep->cmp_len );\r
2679 \r
2680                         if( !cmp )\r
2681                                 goto match;\r
2682                         else if( cmp < 0 )\r
2683                                 p_item = cl_rbmap_left( p_item );\r
2684                         else\r
2685                                 p_item = cl_rbmap_right( p_item );\r
2686 \r
2687                         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_CM,\r
2688                                 ("Svc ID match but compare buffer mismatch.\n") );\r
2689                         continue;\r
2690                 }\r
2691 \r
2692 match:\r
2693                 /* Everything matched. */\r
2694                 AL_EXIT( AL_DBG_CM );\r
2695                 return p_cep;\r
2696         }\r
2697 \r
2698         AL_EXIT( AL_DBG_CM );\r
2699         return NULL;\r
2700 }\r
2701 \r
2702 \r
2703 static kcep_t*\r
2704 __insert_by_id(\r
2705         IN                              kcep_t* const                           p_new_cep )\r
2706 {\r
2707         kcep_t                          *p_cep;\r
2708         cl_rbmap_item_t         *p_item, *p_insert_at;\r
2709         boolean_t                       left = TRUE;\r
2710 \r
2711         AL_ENTER( AL_DBG_CM );\r
2712 \r
2713         p_item = cl_rbmap_root( &gp_cep_mgr->conn_id_map );\r
2714         p_insert_at = p_item;\r
2715         while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_id_map ) )\r
2716         {\r
2717                 p_insert_at = p_item;\r
2718                 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );\r
2719 \r
2720                 if( p_new_cep->remote_comm_id < p_cep->remote_comm_id )\r
2721                         p_item = cl_rbmap_left( p_item ), left = TRUE;\r
2722                 else if( p_new_cep->remote_comm_id > p_cep->remote_comm_id )\r
2723                         p_item = cl_rbmap_right( p_item ), left = FALSE;\r
2724                 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )\r
2725                         p_item = cl_rbmap_left( p_item ), left = TRUE;\r
2726                 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )\r
2727                         p_item = cl_rbmap_right( p_item ), left = FALSE;\r
2728                 else\r
2729                 {\r
2730                         AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
2731                                 ("WARNING: Duplicate remote CID and CA GUID.\n") );\r
2732                         goto done;\r
2733                 }\r
2734         }\r
2735 \r
2736         cl_rbmap_insert(\r
2737                 &gp_cep_mgr->conn_id_map, p_insert_at, &p_new_cep->rem_id_item, left );\r
2738         p_cep = p_new_cep;\r
2739 \r
2740 done:\r
2741         AL_EXIT( AL_DBG_CM );\r
2742         return p_cep;\r
2743 }\r
2744 \r
2745 \r
2746 static kcep_t*\r
2747 __insert_by_qpn(\r
2748         IN                              kcep_t* const                           p_new_cep )\r
2749 {\r
2750         kcep_t                          *p_cep;\r
2751         cl_rbmap_item_t         *p_item, *p_insert_at;\r
2752         boolean_t                       left = TRUE;\r
2753 \r
2754         AL_ENTER( AL_DBG_CM );\r
2755 \r
2756         p_item = cl_rbmap_root( &gp_cep_mgr->conn_qp_map );\r
2757         p_insert_at = p_item;\r
2758         while( p_item != cl_rbmap_end( &gp_cep_mgr->conn_qp_map ) )\r
2759         {\r
2760                 p_insert_at = p_item;\r
2761                 p_cep = PARENT_STRUCT( p_item, kcep_t, rem_id_item );\r
2762 \r
2763                 if( p_new_cep->remote_qpn < p_cep->remote_qpn )\r
2764                         p_item = cl_rbmap_left( p_item ), left = TRUE;\r
2765                 else if( p_new_cep->remote_qpn > p_cep->remote_qpn )\r
2766                         p_item = cl_rbmap_right( p_item ), left = FALSE;\r
2767                 else if( p_new_cep->remote_ca_guid < p_cep->remote_ca_guid )\r
2768                         p_item = cl_rbmap_left( p_item ), left = TRUE;\r
2769                 else if( p_new_cep->remote_ca_guid > p_cep->remote_ca_guid )\r
2770                         p_item = cl_rbmap_right( p_item ), left = FALSE;\r
2771                 else\r
2772                 {\r
2773                 AL_PRINT( TRACE_LEVEL_WARNING, AL_DBG_CM,\r
2774                         ("WARNING: Duplicate remote QPN and CA GUID.\n") );\r
2775                         goto done;\r
2776                 }\r
2777         }\r
2778 \r
2779         cl_rbmap_insert(\r
2780                 &gp_cep_mgr->conn_qp_map, p_insert_at, &p_new_cep->rem_qp_item, left );\r
2781         p_cep = p_new_cep;\r
2782 \r
2783 done:\r
2784         AL_EXIT( AL_DBG_CM );\r
2785         return p_cep;\r
2786 }\r
2787 \r
2788 \r
2789 static inline kcep_t*\r
2790 __insert_cep(\r
2791         IN                              kcep_t* const                           p_new_cep )\r
2792 {\r
2793         kcep_t                          *p_cep;\r
2794 \r
2795         AL_ENTER( AL_DBG_CM );\r
2796 \r
2797         p_cep = __insert_by_qpn( p_new_cep );\r
2798         if( p_cep != p_new_cep )\r
2799                 goto err;\r
2800 \r
2801         p_cep = __insert_by_id( p_new_cep );\r
2802         if( p_cep != p_new_cep )\r
2803         {\r
2804                 cl_rbmap_remove_item(\r
2805                         &gp_cep_mgr->conn_qp_map, &p_new_cep->rem_qp_item );\r
2806 err:\r
2807                 /*\r
2808                  * Clear the remote QPN and comm ID so that we don't try\r
2809                  * to remove the CEP from those maps.\r
2810                  */\r
2811                 p_new_cep->remote_qpn = 0;\r
2812                 p_new_cep->remote_comm_id = 0;\r
2813         }\r
2814 \r
2815         AL_EXIT( AL_DBG_CM );\r
2816         return p_cep;\r
2817 }\r
2818 \r
2819 \r
2820 static inline void\r
2821 __remove_cep(\r
2822         IN                              kcep_t* const                           p_cep )\r
2823 {\r
2824         AL_ENTER( AL_DBG_CM );\r
2825 \r
2826         if( p_cep->remote_comm_id )\r
2827         {\r
2828                 cl_rbmap_remove_item(\r
2829                         &gp_cep_mgr->conn_id_map, &p_cep->rem_id_item );\r
2830                 p_cep->remote_comm_id = 0;\r
2831         }\r
2832         if( p_cep->remote_qpn )\r
2833         {\r
2834                 cl_rbmap_remove_item(\r
2835                         &gp_cep_mgr->conn_qp_map, &p_cep->rem_qp_item );\r
2836                 p_cep->remote_qpn = 0;\r
2837         }\r
2838 \r
2839         AL_EXIT( AL_DBG_CM );\r
2840 }\r
2841 \r
2842 \r
2843 static boolean_t\r
2844 __is_lid_valid(\r
2845         IN                              ib_net16_t                                      lid,\r
2846         IN                              ib_net16_t                                      port_lid,\r
2847         IN                              uint8_t                                         lmc )\r
2848 {\r
2849         uint16_t                lid1;\r
2850         uint16_t                lid2;\r
2851         uint16_t                path_bits;\r
2852 \r
2853         if(lmc)\r
2854         {\r
2855                 lid1 = CL_NTOH16(lid);\r
2856                 lid2 = CL_NTOH16(port_lid);\r
2857                 path_bits = 0;\r
2858 \r
2859                 if( lid1 < lid2 )\r
2860                         return FALSE;\r
2861 \r
2862                 while( lmc-- )\r
2863                         path_bits = (uint16_t)( (path_bits << 1) | 1 );\r
2864 \r
2865                 lid2 |= path_bits;\r
2866 \r
2867                 if( lid1 > lid2)\r
2868                         return FALSE;\r
2869         }\r
2870         else\r
2871         {\r
2872                 if (lid != port_lid)\r
2873                         return FALSE;\r
2874         }\r
2875 \r
2876         return TRUE;\r
2877 }\r
2878 \r
2879 \r
2880 static inline boolean_t\r
2881 __is_gid_valid(\r
2882         IN              const   ib_port_attr_t* const           p_port_attr,\r
2883         IN              const   ib_gid_t* const                         p_gid )\r
2884 {\r
2885         uint16_t        idx;\r
2886 \r
2887         for( idx = 0; idx < p_port_attr->num_gids; idx++ )\r
2888         {\r
2889                 if( !cl_memcmp(\r
2890                         p_gid, &p_port_attr->p_gid_table[idx], sizeof(ib_gid_t) ) )\r
2891                 {\r
2892                         return TRUE;\r
2893                 }\r
2894         }\r
2895         return FALSE;\r
2896 }\r
2897 \r
2898 \r
2899 static inline boolean_t\r
2900 __get_pkey_index(\r
2901         IN              const   ib_port_attr_t* const           p_port_attr,\r
2902         IN              const   net16_t                                         pkey,\r
2903                 OUT                     uint16_t* const                         p_pkey_index )\r
2904 {\r
2905         uint16_t        idx;\r
2906 \r
2907         for( idx = 0; idx < p_port_attr->num_pkeys; idx++ )\r
2908         {\r
2909                 if( p_port_attr->p_pkey_table[idx] == pkey )\r
2910                 {\r
2911                         *p_pkey_index = idx;\r
2912                         return TRUE;\r
2913                 }\r
2914         }\r
2915 \r
2916         return FALSE;\r
2917 }\r
2918 \r
2919 \r
2920 /* Returns the 1-based port index of the CEP agent with the specified GID. */\r
2921 static cep_agent_t*\r
2922 __find_port_cep(\r
2923         IN              const   ib_gid_t* const                         p_gid,\r
2924         IN              const   net16_t                                         lid,\r
2925         IN              const   net16_t                                         pkey,\r
2926                 OUT                     uint16_t* const                         p_pkey_index )\r
2927 {\r
2928         cep_agent_t                             *p_port_cep;\r
2929         cl_list_item_t                  *p_item;\r
2930         const ib_port_attr_t    *p_port_attr;\r
2931 \r
2932         AL_ENTER( AL_DBG_CM );\r
2933 \r
2934         cl_spinlock_acquire( &gp_cep_mgr->obj.lock );\r
2935         for( p_item = cl_qlist_head( &gp_cep_mgr->obj.obj_list );\r
2936                 p_item != cl_qlist_end( &gp_cep_mgr->obj.obj_list );\r
2937                 p_item = cl_qlist_next( p_item ) )\r
2938         {\r
2939                 p_port_cep = PARENT_STRUCT( p_item, cep_agent_t, obj.pool_item );\r
2940 \r
2941                 CL_ASSERT( p_port_cep->port_num );\r
2942 \r
2943                 ci_ca_lock_attr( p_port_cep->h_ca->obj.p_ci_ca );\r
2944 \r
2945                 p_port_attr = p_port_cep->h_ca->obj.p_ci_ca->p_pnp_attr->p_port_attr;\r
2946                 p_port_attr += (p_port_cep->port_num - 1);\r
2947 \r
2948                 if( __is_lid_valid( lid, p_port_attr->lid, p_port_attr->lmc ) &&\r
2949                         __is_gid_valid( p_port_attr, p_gid ) &&\r
2950                         __get_pkey_index( p_port_attr, pkey, p_pkey_index ) )\r
2951                 {\r
2952                         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
2955                         return p_port_cep;\r
2956                 }\r
2957 \r
2958                 ci_ca_unlock_attr( p_port_cep->h_ca->obj.p_ci_ca );\r
2959         }\r
2960         cl_spinlock_release( &gp_cep_mgr->obj.lock );\r
2961         AL_EXIT( AL_DBG_CM );\r
2962         return NULL;\r
2963 }\r
2964 \r
2965 \r
2966 /*\r
2967  * PnP callback for port event notifications.\r
2968  */\r
2969 static ib_api_status_t\r
2970 __cep_pnp_cb(\r
2971         IN                              ib_pnp_rec_t                            *p_pnp_rec )\r
2972 {\r
2973         ib_api_status_t         status = IB_SUCCESS;\r
2974 \r
2975         AL_ENTER( AL_DBG_CM );\r
2976 \r
2977         switch( p_pnp_rec->pnp_event )\r
2978         {\r
2979         case IB_PNP_PORT_ADD:\r
2980                 /* Create the port agent. */\r
2981                 CL_ASSERT( !p_pnp_rec->context );\r
2982                 status = __create_port_cep( (ib_pnp_port_rec_t*)p_pnp_rec );\r
2983                 break;\r
2984 \r
2985         case IB_PNP_PORT_REMOVE:\r
2986                 CL_ASSERT( p_pnp_rec->context );\r
2987 \r
2988                 /* Destroy the port agent. */\r
2989                 ref_al_obj( &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj );\r
2990                 ((cep_agent_t* __ptr64)p_pnp_rec->context)->obj.pfn_destroy(\r
2991                         &((cep_agent_t* __ptr64)p_pnp_rec->context)->obj, NULL );\r
2992                 break;\r
2993 \r
2994         default:\r
2995                 break;  /* Ignore other PNP events. */\r
2996         }\r
2997 \r
2998         AL_EXIT( AL_DBG_CM );\r
2999         return status;\r
3000 }\r
3001 \r
3002 \r
3003 static inline int64_t\r
3004 __min_timewait(\r
3005         IN                              int64_t                                         current_min,\r
3006         IN                              kcep_t* const                           p_cep )\r
3007 {\r
3008         /*\r
3009          * The minimum timer interval is 50 milliseconds.  This means\r
3010          * 500000 100ns increments.  Since __process_timewait divides the\r
3011          * result in half (so that the worst cast timewait interval is 150%)\r
3012          * we compensate for this here.  Note that relative time values are\r
3013          * expressed as negative.\r
3014          */\r
3015 #define MIN_TIMEWAIT_100NS      -1000000\r
3016 \r
3017         /* Still in timewait - try again next time. */\r
3018         if( !current_min )\r
3019         {\r
3020                 return min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS );\r
3021         }\r
3022         else\r
3023         {\r
3024                 return max( current_min,\r
3025                         min( p_cep->timewait_time.QuadPart, MIN_TIMEWAIT_100NS ) );\r
3026         }\r
3027 }\r
3028 \r
3029 \r
3030 /*\r
3031  * Timer callback to process CEPs in timewait state.  Returns time in ms.\r
3032  */\r
3033 static uint32_t\r
3034 __process_timewait()\r
3035 {\r
3036         cl_list_item_t          *p_item;\r
3037         kcep_t                          *p_cep;\r
3038         LARGE_INTEGER           timeout;\r
3039         int64_t                         min_timewait = 0;\r
3040 \r
3041         AL_ENTER( AL_DBG_CM );\r
3042 \r
3043         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
3044 \r
3045         timeout.QuadPart = 0;\r
3046 \r
3047         p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );\r
3048         while( p_item != cl_qlist_end( &gp_cep_mgr->timewait_list ) )\r
3049         {\r
3050                 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );\r
3051                 p_item = cl_qlist_next( p_item );\r
3052 \r
3053                 CL_ASSERT( p_cep->state == CEP_STATE_DESTROY ||\r
3054                         p_cep->state == CEP_STATE_TIMEWAIT );\r
3055 \r
3056                 CL_ASSERT( !p_cep->p_mad );\r
3057 \r
3058                 if( KeWaitForSingleObject( &p_cep->timewait_timer, Executive,\r
3059                         KernelMode, FALSE, &timeout ) != STATUS_SUCCESS )\r
3060                 {\r
3061                         /* Still in timewait - try again next time. */\r
3062                         min_timewait = __min_timewait( min_timewait, p_cep );\r
3063                         continue;\r
3064                 }\r
3065 \r
3066                 if( p_cep->ref_cnt )\r
3067                 {\r
3068                         /* Send outstanding or destruction in progress. */\r
3069                         min_timewait = __min_timewait( min_timewait, p_cep );\r
3070                         continue;\r
3071                 }\r
3072 \r
3073                 /* Remove from the timewait list. */\r
3074                 cl_qlist_remove_item( &gp_cep_mgr->timewait_list, &p_cep->timewait_item );\r
3075 \r
3076                 /*\r
3077                  * Not in timewait.  Remove the CEP from the maps - it should\r
3078                  * no longer be matched against.\r
3079                  */\r
3080                 __remove_cep( p_cep );\r
3081 \r
3082                 if( p_cep->state == CEP_STATE_DESTROY )\r
3083                 {\r
3084                         __destroy_cep( p_cep );\r
3085                 }\r
3086                 else\r
3087                 {\r
3088                         /* Move the CEP to the IDLE state so that it can be used again. */\r
3089                         p_cep->state = CEP_STATE_IDLE;\r
3090                 }\r
3091         }\r
3092 \r
3093         AL_EXIT( AL_DBG_CM );\r
3094         return (uint32_t)(min_timewait / -20000);\r
3095 }\r
3096 \r
3097 \r
3098 /*\r
3099  * Timer callback to process CEPs in timewait state.\r
3100  */\r
3101 static void\r
3102 __cep_timewait_cb(\r
3103         IN                              void                                            *context )\r
3104 {\r
3105         KLOCK_QUEUE_HANDLE      hdl;\r
3106         uint32_t                        min_timewait;\r
3107 \r
3108         AL_ENTER( AL_DBG_CM );\r
3109 \r
3110         UNUSED_PARAM( context );\r
3111 \r
3112         CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
3113 \r
3114         KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
3115 \r
3116         min_timewait = __process_timewait();\r
3117 \r
3118         if( cl_qlist_count( &gp_cep_mgr->timewait_list ) )\r
3119         {\r
3120                 /*\r
3121                  * Reset the timer for half of the shortest timeout - this results\r
3122                  * in a worst case timeout of 150% of timewait.\r
3123                  */\r
3124                 cl_timer_trim( &gp_cep_mgr->timewait_timer, min_timewait );\r
3125         }\r
3126 \r
3127         KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
3128 \r
3129         AL_EXIT( AL_DBG_CM );\r
3130 }\r
3131 \r
3132 \r
3133 /*\r
3134  * Starts immediate cleanup of the CM.  Invoked during al_obj destruction.\r
3135  */\r
3136 static void\r
3137 __destroying_cep_mgr(\r
3138         IN                              al_obj_t*                                       p_obj )\r
3139 {\r
3140         ib_api_status_t         status;\r
3141         KLOCK_QUEUE_HANDLE      hdl;\r
3142         cl_list_item_t          *p_item;\r
3143         kcep_t                          *p_cep;\r
3144         LARGE_INTEGER           timeout;\r
3145 \r
3146         AL_ENTER( AL_DBG_CM );\r
3147 \r
3148         CL_ASSERT( &gp_cep_mgr->obj == p_obj );\r
3149         UNUSED_PARAM( p_obj );\r
3150 \r
3151         /* Deregister from PnP notifications. */\r
3152         if( gp_cep_mgr->h_pnp )\r
3153         {\r
3154                 status = ib_dereg_pnp(\r
3155                         gp_cep_mgr->h_pnp, (ib_pfn_destroy_cb_t)deref_al_obj );\r
3156                 if( status != IB_SUCCESS )\r
3157                 {\r
3158                         AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
3159                                 ("ib_dereg_pnp failed with status %s.\n",\r
3160                                 ib_get_err_str(status)) );\r
3161                         deref_al_obj( &gp_cep_mgr->obj );\r
3162                 }\r
3163         }\r
3164 \r
3165         /* Cancel all timewait timers. */\r
3166         timeout.QuadPart = 0;\r
3167         KeAcquireInStackQueuedSpinLock( &gp_cep_mgr->lock, &hdl );\r
3168         for( p_item = cl_qlist_head( &gp_cep_mgr->timewait_list );\r
3169                 p_item != cl_qlist_end( &gp_cep_mgr->timewait_list );\r
3170                 p_item = cl_qlist_next( p_item ) )\r
3171         {\r
3172                 p_cep = PARENT_STRUCT( p_item, kcep_t, timewait_item );\r
3173                 KeSetTimer( &p_cep->timewait_timer, timeout, NULL );\r
3174         }\r
3175         __process_timewait();\r
3176         KeReleaseInStackQueuedSpinLock( &hdl );\r
3177 \r
3178         AL_EXIT( AL_DBG_CM );\r
3179 }\r
3180 \r
3181 \r
3182 /*\r
3183  * Frees the global CEP agent.  Invoked during al_obj destruction.\r
3184  */\r
3185 static void\r
3186 __free_cep_mgr(\r
3187         IN                              al_obj_t*                                       p_obj )\r
3188 {\r
3189         AL_ENTER( AL_DBG_CM );\r
3190 \r
3191         CL_ASSERT( &gp_cep_mgr->obj == p_obj );\r
3192         /* All listen request should have been cleaned up by this point. */\r
3193         CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->listen_map ) );\r
3194         /* All connections should have been cancelled/disconnected by now. */\r
3195         CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_id_map ) );\r
3196         CL_ASSERT( cl_is_rbmap_empty( &gp_cep_mgr->conn_qp_map ) );\r
3197 \r
3198         cl_vector_destroy( &gp_cep_mgr->cid_vector );\r
3199 \r
3200         cl_timer_destroy( &gp_cep_mgr->timewait_timer );\r
3201 \r
3202         /*\r
3203          * All CM port agents should have been destroyed by now via the\r
3204          * standard child object destruction provided by the al_obj.\r
3205          */\r
3206         ExDeleteNPagedLookasideList( &gp_cep_mgr->cep_pool );\r
3207         destroy_al_obj( p_obj );\r
3208 \r
3209         cl_free( gp_cep_mgr );\r
3210         gp_cep_mgr = NULL;\r
3211 \r
3212         AL_EXIT( AL_DBG_CM );\r
3213 }\r
3214 \r
3215 \r
3216 static cl_status_t\r