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