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