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