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