aa4fe7aa9661d7b8b8e892dcdda23e5de87e0ba6
[mirror/winof/.git] / ulp / wsd / user / ib_cm.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 #include "ibspdll.h"\r
33 \r
34 static void AL_API cm_req_callback(IN ib_cm_req_rec_t * p_cm_req_rec);\r
35 static void AL_API cm_rep_callback(IN ib_cm_rep_rec_t * p_cm_rep_rec);\r
36 static void AL_API cm_rtu_callback(IN ib_cm_rtu_rec_t * p_cm_rtu_rec);\r
37 static void AL_API cm_rej_callback(IN ib_cm_rej_rec_t * p_cm_rej_rec);\r
38 static void AL_API cm_mra_callback(IN ib_cm_mra_rec_t * p_cm_mra_rec);\r
39 static void AL_API cm_dreq_callback(IN ib_cm_dreq_rec_t * p_cm_dreq_rec);\r
40 static void AL_API listen_err_callback(IN ib_listen_err_rec_t * p_listen_err_rec);\r
41 static void AL_API cm_apr_callback(IN ib_cm_apr_rec_t * p_cm_apr_rec);\r
42 \r
43 \r
44 /* Computes a service ID for a port. */\r
45 static inline ib_net64_t\r
46 get_service_id_for_port(\r
47                                         ib_net16_t                                      ip_port)\r
48 {\r
49         return BASE_LISTEN_ID | ip_port;\r
50 }\r
51 \r
52 \r
53 /* Signals a select event to the switch. */\r
54 void\r
55 ibsp_post_select_event(\r
56                                         struct ibsp_socket_info         *socket_info,\r
57                                         int                                                     event,\r
58                                         int                                                     error )\r
59 {\r
60         HANDLE          h_event;\r
61 \r
62         IBSP_ENTER( IBSP_DBG_NEV );\r
63 \r
64         CL_ASSERT( socket_info );\r
65         CL_ASSERT( event );\r
66 \r
67         switch( event )\r
68         {\r
69         case FD_CONNECT:\r
70                 IBSP_TRACE1( IBSP_DBG_NEV,\r
71                         ("socket %p FD_CONNECT\n", socket_info) );\r
72                 socket_info->errno_connect = error;\r
73                 break;\r
74 \r
75         case FD_ACCEPT:\r
76                 IBSP_TRACE1( IBSP_DBG_NEV,\r
77                         ("socket %p FD_ACCEPT\n", socket_info) );\r
78                 break;\r
79 \r
80         default:\r
81                 CL_ASSERT( 0 );\r
82                 break;\r
83         }\r
84 \r
85         _InterlockedOr( &socket_info->network_events, event );\r
86 \r
87         h_event = InterlockedCompareExchangePointer(\r
88                 &socket_info->event_select, NULL, NULL );\r
89         /* Check for event notification request and signal as needed. */\r
90         if( (socket_info->event_mask & event) && h_event )\r
91         {\r
92                 IBSP_TRACE2( IBSP_DBG_NEV,\r
93                         ("Signaling eventHandle %p at time %I64d.\n",\r
94                         h_event, cl_get_time_stamp() ) );\r
95                 SetEvent( h_event );\r
96         }\r
97 \r
98         IBSP_EXIT( IBSP_DBG_NEV );\r
99 }\r
100 \r
101 \r
102 /*\r
103  * A user-specified callback that is invoked after receiving a connection\r
104  * request message (REQ).\r
105  */\r
106 static void AL_API\r
107 cm_req_callback(\r
108         IN                              ib_cm_req_rec_t                         *p_cm_req_rec )\r
109 {\r
110         struct ibsp_socket_info *socket_info =\r
111                 (struct ibsp_socket_info * __ptr64)p_cm_req_rec->context;\r
112         struct listen_incoming *incoming;\r
113         ib_rej_status_t rej_status;\r
114         ib_cm_mra_t             mra;\r
115 \r
116         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
117 \r
118         CL_ASSERT( socket_info );\r
119 \r
120         if( !p_cm_req_rec->p_req_pdata )\r
121         {\r
122                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("No private data in CM REQ\n") );\r
123                 rej_status = IB_REJ_INSUF_RESOURCES;\r
124                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("rejecting\n") );\r
125                 ib_reject( p_cm_req_rec->h_cm_req, rej_status );\r
126                 return;\r
127         }\r
128 \r
129         incoming = HeapAlloc( g_ibsp.heap, 0, sizeof(struct listen_incoming) );\r
130         if( incoming == NULL )\r
131         {\r
132                 /* Low on memory. */\r
133                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("HeapAlloc failed\n") );\r
134                 rej_status = IB_REJ_INSUF_RESOURCES;\r
135                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("rejecting\n") );\r
136                 ib_reject( p_cm_req_rec->h_cm_req, rej_status );\r
137                 return;\r
138         }\r
139 \r
140         incoming->cm_req_received = *p_cm_req_rec;\r
141         cl_memcpy( &incoming->params, p_cm_req_rec->p_req_pdata,\r
142                 sizeof(struct cm_req_params) );\r
143         incoming->cm_req_received.p_req_pdata = (const uint8_t*)&incoming->params;\r
144 \r
145         cl_spinlock_acquire( &socket_info->mutex );\r
146 \r
147         switch( socket_info->socket_state )\r
148         {\r
149         case IBSP_LISTEN:\r
150                 if( cl_qlist_count( &socket_info->info.listen.list ) >= socket_info->info.listen.backlog )\r
151                 {\r
152                         /* Already too many connection requests are queued */\r
153                         cl_spinlock_release( &socket_info->mutex );\r
154                         CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("already too many incoming connections\n") );\r
155                         HeapFree( g_ibsp.heap, 0, incoming );\r
156                         rej_status = IB_REJ_INSUF_QP;   /* or IB_REJ_INSUF_RESOURCES */\r
157                         CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("rejecting\n") );\r
158                         ib_reject( p_cm_req_rec->h_cm_req, rej_status );\r
159                         return;\r
160                 }\r
161 \r
162                 /* Add to the waiting list */\r
163                 cl_qlist_insert_head( &socket_info->info.listen.list, &incoming->item );\r
164 \r
165                 /* Send MRA */\r
166                 mra.mra_length = 0;\r
167                 mra.p_mra_pdata = NULL;\r
168                 mra.svc_timeout = 0x1F;\r
169                 ib_cm_mra( p_cm_req_rec->h_cm_req, &mra );\r
170 \r
171                 ibsp_post_select_event( socket_info, FD_ACCEPT, 0 );\r
172                 break;\r
173 \r
174         case IBSP_DUPLICATING_REMOTE:\r
175                 {\r
176                         int error;\r
177                         int ret;\r
178 \r
179                         HeapFree( g_ibsp.heap, 0, incoming );\r
180 \r
181                         wait_cq_drain( socket_info );\r
182 \r
183                         ib_destroy_socket( socket_info );\r
184 \r
185 #if 0\r
186                         cl_spinlock_release( &socket_info->mutex );\r
187                         rej_status = IB_REJ_USER_DEFINED;\r
188                         CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("rejecting\n") );\r
189                         ib_reject( p_cm_req_rec->h_cm_req, rej_status );\r
190                         ibsp_dup_overlap_abort( socket_info );\r
191                         return;\r
192 #endif\r
193 \r
194                         ret = ib_create_socket( socket_info );\r
195                         if( ret )\r
196                         {\r
197                                 cl_spinlock_release( &socket_info->mutex );\r
198                                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl,\r
199                                         ("accept failed for duplicate socket (%d)\n", ret) );\r
200                                 rej_status = IB_REJ_USER_DEFINED;\r
201                                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("rejecting\n") );\r
202                                 ib_reject( p_cm_req_rec->h_cm_req, rej_status );\r
203                                 ibsp_dup_overlap_abort( socket_info );\r
204                                 return;\r
205                         }\r
206 \r
207                         ret = ib_accept( socket_info, p_cm_req_rec, &error );\r
208                         if( ret )\r
209                         {\r
210                                 cl_spinlock_release( &socket_info->mutex );\r
211                                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl,\r
212                                         ("accept failed for duplicate socket (%d, %d)\n", ret, error) );\r
213                                 rej_status = IB_REJ_USER_DEFINED;\r
214                                 /* Call ib_destroy_socket for above ib_create_socket() call */\r
215                                 ib_destroy_socket( socket_info );\r
216                                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("rejecting\n") );\r
217                                 ib_reject( p_cm_req_rec->h_cm_req, rej_status );\r
218                                 ibsp_dup_overlap_abort( socket_info );\r
219                                 return;\r
220 \r
221                         }\r
222                 }\r
223                 break;\r
224 \r
225         default:\r
226                 cl_spinlock_release( &socket_info->mutex );\r
227                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("socket is not listening anymore\n") );\r
228                 HeapFree( g_ibsp.heap, 0, incoming );\r
229                 rej_status = IB_REJ_INSUF_QP;   /* or IB_REJ_INSUF_RESOURCES */\r
230                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("rejecting\n") );\r
231                 ib_reject( p_cm_req_rec->h_cm_req, rej_status );\r
232                 return;\r
233         }\r
234 \r
235         cl_spinlock_release( &socket_info->mutex );\r
236 \r
237         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
238 }\r
239 \r
240 \r
241 /*\r
242  * A user-specified callback that is invoked after receiving a connection\r
243  * request reply message (REP).\r
244  */\r
245 static void AL_API\r
246 cm_rep_callback(\r
247         IN                              ib_cm_rep_rec_t                         *p_cm_rep_rec )\r
248 {\r
249         struct ibsp_socket_info *socket_info =\r
250                 (struct ibsp_socket_info * __ptr64)p_cm_rep_rec->qp_context;\r
251         ib_cm_rtu_t cm_rtu;\r
252         ib_api_status_t status;\r
253 \r
254         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
255 \r
256         memset( &cm_rtu, 0, sizeof(cm_rtu) );\r
257 \r
258         cm_rtu.access_ctrl = IB_AC_RDMA_READ | IB_AC_RDMA_WRITE | IB_AC_LOCAL_WRITE;\r
259 #if 0\r
260         // Bug in TAVOR\r
261         cm_rtu.sq_depth = QP_ATTRIB_SQ_DEPTH;\r
262         cm_rtu.rq_depth = QP_ATTRIB_RQ_DEPTH;\r
263 #endif\r
264         cm_rtu.pfn_cm_apr_cb = cm_apr_callback;\r
265         cm_rtu.pfn_cm_dreq_cb = cm_dreq_callback;\r
266 \r
267         cl_spinlock_acquire( &socket_info->mutex );\r
268 \r
269         if( socket_info->socket_state == IBSP_CONNECT )\r
270         {\r
271                 status = ib_cm_rtu( p_cm_rep_rec->h_cm_rep, &cm_rtu );\r
272                 if( status != IB_SUCCESS )\r
273                 {\r
274                         /* Note: a REJ has been automatically sent. */\r
275                         CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_rtu failed (0x%d)\n", status) );\r
276                         IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_BIND );\r
277                         ibsp_post_select_event( socket_info, FD_CONNECT, WSAENOBUFS );\r
278                 }\r
279                 else\r
280                 {\r
281                         IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_CONNECTED );\r
282                         ibsp_post_select_event( socket_info, FD_CONNECT, 0 );\r
283                 }\r
284         }\r
285         else if( socket_info->socket_state == IBSP_DUPLICATING_NEW )\r
286         {\r
287                 status = ib_cm_rtu( p_cm_rep_rec->h_cm_rep, &cm_rtu );\r
288                 if( status != IB_SUCCESS )\r
289                 {\r
290                         /* Note: a REJ has been automatically sent. */\r
291                         CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_rtu failed (0x%d)\n", status) );\r
292                 }\r
293                 else\r
294                 {\r
295                         IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_CONNECTED );\r
296                 }\r
297                 SetEvent( socket_info->info.connect.event );\r
298         }\r
299         else\r
300         {\r
301                 /* The socket might be closing */\r
302                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("socket %x not in connecting state (%s)\n",\r
303                         socket_info, IBSP_SOCKET_STATE_STR( socket_info->socket_state )) );\r
304 \r
305                 ib_reject( p_cm_rep_rec->h_cm_rep, IB_REJ_USER_DEFINED );\r
306         }\r
307 \r
308         cl_spinlock_release( &socket_info->mutex );\r
309 \r
310         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
311 }\r
312 \r
313 \r
314 /*\r
315  * A user-specified callback that is invoked after receiving a connection\r
316  * ready to use message (RTU).\r
317  */\r
318 static void AL_API\r
319 cm_rtu_callback(\r
320         IN                              ib_cm_rtu_rec_t                         *p_cm_rtu_rec )\r
321 {\r
322         struct ibsp_socket_info *socket_info =\r
323                 (struct ibsp_socket_info * __ptr64)p_cm_rtu_rec->qp_context;\r
324 \r
325         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
326 \r
327         cl_spinlock_acquire( &socket_info->mutex );\r
328 \r
329         if( socket_info->socket_state == IBSP_ACCEPT )\r
330         {\r
331                 IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_CONNECTED );\r
332                 SetEvent( socket_info->info.accept.event );\r
333         }\r
334         else if( socket_info->socket_state == IBSP_DUPLICATING_REMOTE )\r
335         {\r
336                 struct _recv_wr         *wr;\r
337                 ib_api_status_t         status;\r
338                 uint8_t                         idx;\r
339 \r
340                 /* Repost all the WR to the new QP */\r
341                 cl_spinlock_acquire( &socket_info->recv_lock );\r
342 \r
343                 while( socket_info->dup_cnt )\r
344                 {\r
345                         if( (socket_info->recv_cnt + socket_info->dup_cnt) >\r
346                                 QP_ATTRIB_RQ_DEPTH )\r
347                         {\r
348                                 CL_ASSERT( (socket_info->recv_cnt + socket_info->dup_cnt) <=\r
349                                         QP_ATTRIB_RQ_DEPTH );\r
350                                 /* TODO: Flag the socket as having failed. */\r
351                                 break;\r
352                         }\r
353 \r
354 \r
355                         /* Figure out the starting index in the duplicate array. */\r
356                         idx = socket_info->dup_idx - (uint8_t)socket_info->dup_cnt;\r
357                         if( idx >= QP_ATTRIB_RQ_DEPTH )\r
358                         {\r
359                                 /* The duplicates wrap over the end of the array. */\r
360                                 idx += QP_ATTRIB_RQ_DEPTH;\r
361                         }\r
362 \r
363                         /*\r
364                          * Copy the duplicate work request from the duplicate array\r
365                          * to the receive array.\r
366                          */\r
367                         socket_info->recv_wr[socket_info->recv_idx] =\r
368                                 socket_info->dup_wr[idx];\r
369 \r
370                         wr = &socket_info->recv_wr[socket_info->recv_idx];\r
371 \r
372                         /* Update the work request ID. */\r
373                         wr->recv.wr_id = (uint64_t)(void* __ptr64)wr;\r
374 \r
375                         /*\r
376                          * Increment the count before posting so it doesn't go\r
377                          * negative in the completion path.\r
378                          */\r
379                         cl_atomic_inc( &socket_info->recv_cnt );\r
380 \r
381                         status = ib_post_recv( socket_info->qp, &wr->recv, NULL );\r
382 \r
383 \r
384                         if( status == IB_SUCCESS )\r
385                         {\r
386                                 /* Update the index and wrap as needed */\r
387 #if QP_ATTRIB_RQ_DEPTH == 256 || QP_ATTRIB_RQ_DEPTH == 128 || \\r
388         QP_ATTRIB_RQ_DEPTH == 64 || QP_ATTRIB_RQ_DEPTH == 32 || \\r
389         QP_ATTRIB_RQ_DEPTH == 16 || QP_ATTRIB_RQ_DEPTH == 8\r
390                                 socket_info->recv_idx++;\r
391                                 socket_info->recv_idx &= (QP_ATTRIB_RQ_DEPTH - 1);\r
392 #else\r
393                                 if( ++socket_info->recv_idx == QP_ATTRIB_RQ_DEPTH )\r
394                                         socket_info->recv_idx = 0;\r
395 #endif\r
396 \r
397                                 cl_atomic_dec( &socket_info->dup_cnt );\r
398                         }\r
399                         else\r
400                         {\r
401                                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl,\r
402                                         ("ib_post_recv returned %s for reposted buffer\n",\r
403                                         ib_get_err_str( status )) );\r
404 \r
405                                 cl_atomic_dec( &socket_info->recv_cnt );\r
406                                 CL_ASSERT( status == IB_SUCCESS );\r
407                                 /* TODO: Flag the socket as having failed. */\r
408                                 break;\r
409                         }\r
410                 }\r
411 \r
412                 cl_spinlock_release( &socket_info->recv_lock );\r
413 \r
414                 socket_info->qp_error = 0;\r
415                 IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_CONNECTED );\r
416         }\r
417         else\r
418         {\r
419                 /* The Socket might be closing */\r
420                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl,\r
421                         ("Got RTU while in socket_state %s - ignoring\n",\r
422                         IBSP_SOCKET_STATE_STR( socket_info->socket_state )) );\r
423         }\r
424 \r
425         cl_spinlock_release( &socket_info->mutex );\r
426 \r
427         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
428 }\r
429 \r
430 \r
431 /*\r
432  * A user-specified callback that is invoked after receiving a connection\r
433  * rejection message (REJ).\r
434  */\r
435 static void AL_API\r
436 cm_rej_callback(\r
437         IN                              ib_cm_rej_rec_t                         *p_cm_rej_rec )\r
438 {\r
439         struct ibsp_socket_info *socket_info =\r
440                 (struct ibsp_socket_info * __ptr64)p_cm_rej_rec->qp_context;\r
441 \r
442         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
443 \r
444         fzprint(("%s():%d:0x%x:0x%x: socket=0x%p \n", __FUNCTION__,\r
445                          __LINE__, GetCurrentProcessId(), GetCurrentThreadId(), socket_info));\r
446 \r
447         CL_TRACE( IBSP_DBG_CONN, gdbg_lvl, ("connect reject, reason=%d\n",\r
448                                                                            p_cm_rej_rec->rej_status));\r
449 \r
450         cl_spinlock_acquire( &socket_info->mutex );\r
451 \r
452         switch( socket_info->socket_state )\r
453         {\r
454         case IBSP_CONNECT:\r
455                 IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_BIND );\r
456                 ibsp_post_select_event( socket_info, FD_CONNECT, WSAECONNREFUSED );\r
457                 break;\r
458 \r
459         case IBSP_ACCEPT:\r
460                 IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_BIND );\r
461                 SetEvent( socket_info->info.accept.event );\r
462                 break;\r
463 \r
464         case IBSP_DUPLICATING_NEW:\r
465                 /* Leave in that state. IBSPSocket will eventually return \r
466                  * an error becaus the socket is not connected. */\r
467                 SetEvent( socket_info->info.connect.event );\r
468                 break;\r
469 \r
470         default:\r
471                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("Got an unexpected REJ\n") );\r
472                 break;\r
473         }\r
474 \r
475         cl_spinlock_release( &socket_info->mutex );\r
476 \r
477         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
478 }\r
479 \r
480 \r
481 /*\r
482  * A user-specified callback that is invoked after receiving a message\r
483  * received acknowledgement.\r
484  */\r
485 static void AL_API\r
486 cm_mra_callback(\r
487         IN                              ib_cm_mra_rec_t                         *p_cm_mra_rec )\r
488 {\r
489         /* TODO */\r
490         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
491 \r
492         UNUSED_PARAM( p_cm_mra_rec );\r
493 \r
494         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
495 }\r
496 \r
497 \r
498 /*\r
499  * A user-specified callback that is invoked after receiving a disconnect\r
500  * request message (DREQ).\r
501  */\r
502 static void AL_API\r
503 cm_dreq_callback(\r
504         IN                              ib_cm_dreq_rec_t                        *p_cm_dreq_rec )\r
505 {\r
506         ib_api_status_t status;\r
507         ib_cm_drep_t cm_drep;\r
508         struct disconnect_reason *reason;\r
509         struct ibsp_socket_info *socket_info =\r
510                 (struct ibsp_socket_info * __ptr64)p_cm_dreq_rec->qp_context;\r
511 \r
512         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
513         CL_TRACE( IBSP_DBG_CM, gdbg_lvl,\r
514                 ("cm_dreq_callback: enter, socket_state=%s, socket=%p\n",\r
515                 IBSP_SOCKET_STATE_STR( socket_info->socket_state ), socket_info) );\r
516 \r
517         reason = (struct disconnect_reason * __ptr64)p_cm_dreq_rec->p_dreq_pdata;\r
518 \r
519         if( reason->type == DISC_DUPLICATING )\r
520         {\r
521                 cl_spinlock_acquire( &socket_info->mutex );\r
522 \r
523                 if( socket_info->socket_state == IBSP_CONNECTED )\r
524                 {\r
525                         int ret;\r
526                         int error;\r
527 \r
528                         IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_DUPLICATING_REMOTE );\r
529                         socket_info->qp_error = -1;\r
530                         socket_info->duplicate.identifier = reason->duplicating.identifier;\r
531                         socket_info->duplicate.dwProcessId = reason->duplicating.dwProcessId;\r
532 \r
533                         /* Now, setup our listening callback. */\r
534                         socket_info->info.listen.listen_req_param.dwProcessId =\r
535                                 reason->duplicating.dwProcessId;\r
536                         socket_info->info.listen.listen_req_param.identifier =\r
537                                 reason->duplicating.identifier;\r
538 \r
539                         ret = ib_listen( socket_info, &error );\r
540                         if( ret )\r
541                         {\r
542                                 CL_EXIT_ERROR( IBSP_DBG_CONN, gdbg_lvl,\r
543                                         ("ib_listen failed with %d\n", error) );\r
544                                 IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_DISCONNECTED );\r
545                                 socket_info->qp_error = WSAECONNRESET;\r
546                         }\r
547                 }\r
548 \r
549                 cl_spinlock_release( &socket_info->mutex );\r
550         }\r
551         else\r
552         {\r
553                 /* Right now, treat anything as a normal disconnect. */\r
554                 cl_spinlock_acquire( &socket_info->mutex );\r
555 \r
556                 if( socket_info->socket_state == IBSP_CONNECTED )\r
557                 {\r
558                         IBSP_CHANGE_SOCKET_STATE( socket_info, IBSP_DISCONNECTED );\r
559                         socket_info->qp_error = WSAECONNRESET;\r
560                 }\r
561 \r
562                 cl_spinlock_release( &socket_info->mutex );\r
563         }\r
564 \r
565         memset( &cm_drep, 0, sizeof(cm_drep) );\r
566 \r
567         status = ib_cm_drep( p_cm_dreq_rec->h_cm_dreq, &cm_drep );\r
568         if( status != IB_SUCCESS )\r
569         {\r
570                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_drep failed (0x%d)\n", status) );\r
571         }\r
572 \r
573         SetEvent( socket_info->info.disconnect.event );\r
574 \r
575         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
576 }\r
577 \r
578 \r
579 /*\r
580  * A user-specified callback that is invoked after receiving a disconnect\r
581  *      reply message.\r
582  */\r
583 static void AL_API\r
584 cm_drep_callback(\r
585         IN                              ib_cm_drep_rec_t                        *p_cm_drep_rec )\r
586 {\r
587         struct ibsp_socket_info *socket_info =\r
588                 (struct ibsp_socket_info * __ptr64)p_cm_drep_rec->qp_context;\r
589 \r
590         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
591 \r
592         CL_TRACE( IBSP_DBG_CM, gdbg_lvl, ("posting DREP event from drep_callback\n") );\r
593 \r
594         SetEvent( socket_info->info.disconnect.event );\r
595 \r
596         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
597 }\r
598 \r
599 \r
600 /*\r
601  * A user-specified callback that is invoked after an error has occurred on\r
602  * a listen request.\r
603  */\r
604 static void AL_API\r
605 listen_err_callback(\r
606         IN                              ib_listen_err_rec_t                     *p_listen_err_rec )\r
607 {\r
608         /* TODO */\r
609         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
610 \r
611         UNUSED_PARAM( p_listen_err_rec );\r
612 \r
613         CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("not implemented") );\r
614 \r
615         CL_ASSERT( 0 );\r
616 \r
617         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
618 }\r
619 \r
620 \r
621 /*\r
622  * A user-specified callback that is invoked after receiving a load\r
623  * alternate path response message.\r
624  */\r
625 static void AL_API\r
626 cm_apr_callback(\r
627         IN                              ib_cm_apr_rec_t                         *p_cm_apr_rec )\r
628 {\r
629         /* TODO */\r
630         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
631 \r
632         UNUSED_PARAM( p_cm_apr_rec );\r
633 \r
634         CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("not implemented"));\r
635 \r
636         CL_ASSERT( 0 );\r
637 \r
638         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
639 }\r
640 \r
641 \r
642 /*\r
643  * A user-specified callback that is invoked after receiving a load\r
644  * alternate path message.\r
645  *\r
646  * SYNOPSIS\r
647  */\r
648 static void AL_API\r
649 cm_lap_callback(\r
650         IN                              ib_cm_lap_rec_t                         *p_cm_lap_rec )\r
651 {\r
652         /* TODO */\r
653         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
654 \r
655         UNUSED_PARAM( p_cm_lap_rec );\r
656 \r
657         CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("not implemented"));\r
658 \r
659         CL_ASSERT( 0 );\r
660 \r
661         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
662 }\r
663 \r
664 \r
665 /* Listen for an incoming connection. */\r
666 int\r
667 ib_listen(\r
668         IN                              struct ibsp_socket_info         *socket_info,\r
669                 OUT                     LPINT                                           lpErrno )\r
670 {\r
671         ib_cm_listen_t param;\r
672         ib_api_status_t status;\r
673 \r
674         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
675 \r
676         memset( &param, 0, sizeof(param) );\r
677 \r
678         param.svc_id = get_service_id_for_port( socket_info->local_addr.sin_port );\r
679         if( socket_info->port )\r
680         {\r
681                 /* The socket is bound to an IP address */\r
682                 param.ca_guid = socket_info->port->hca->guid;\r
683                 param.port_guid = socket_info->port->guid;\r
684         }\r
685         else\r
686         {\r
687                 /* The socket is bound to INADDR_ANY */\r
688                 param.ca_guid = IB_ALL_CAS;\r
689                 param.port_guid = IB_ALL_PORTS;\r
690         }\r
691         param.lid = IB_ALL_LIDS;\r
692 \r
693         param.p_compare_buffer = (uint8_t *) & socket_info->info.listen.listen_req_param;\r
694         param.compare_length = sizeof(struct listen_req_param);\r
695         param.compare_offset = offsetof(struct cm_req_params, listen_req_param);\r
696 \r
697         fzprint(("%s():%d:0x%x:0x%x: socket=0x%p params: %x %x\n", __FUNCTION__,\r
698                          __LINE__, GetCurrentProcessId(),\r
699                          GetCurrentThreadId(), socket_info,\r
700                          socket_info->info.listen.listen_req_param.dwProcessId,\r
701                          socket_info->info.listen.listen_req_param.identifier));\r
702 \r
703         param.pfn_cm_req_cb = cm_req_callback;\r
704 \r
705         param.qp_type = IB_QPT_RELIABLE_CONN;\r
706 \r
707         status = ib_cm_listen( g_ibsp.al_handle, &param, listen_err_callback, socket_info,      /* context */\r
708                 &socket_info->info.listen.handle );\r
709 \r
710         if( status != IB_SUCCESS )\r
711         {\r
712                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_listen failed (0x%d)\n", status) );\r
713                 *lpErrno = ibal_to_wsa_error( status );\r
714                 return SOCKET_ERROR;\r
715         }\r
716 \r
717         STAT_INC( listen_num );\r
718 \r
719         CL_TRACE_EXIT( IBSP_DBG_CM, gdbg_lvl,\r
720                 ("started listening for port %d\n",\r
721                 CL_HTON16( socket_info->local_addr.sin_port )) );\r
722 \r
723         *lpErrno = 0;\r
724         return 0;\r
725 }\r
726 \r
727 \r
728 /* Reject all the queued incoming connection requests. */\r
729 static void\r
730 empty_listen_queue(\r
731         IN                              struct ibsp_socket_info         *socket_info )\r
732 {\r
733         cl_list_item_t *item;\r
734         struct listen_incoming *incoming;\r
735 \r
736         // TODO: we don't need to lock anymore, do we?\r
737         cl_spinlock_acquire( &socket_info->mutex );\r
738 \r
739         for( item = cl_qlist_head( &socket_info->info.listen.list );\r
740                 item != cl_qlist_end( &socket_info->info.listen.list );\r
741                 item = cl_qlist_head( &socket_info->info.listen.list ) )\r
742         {\r
743                 incoming = PARENT_STRUCT(item, struct listen_incoming, item);\r
744 \r
745                 cl_qlist_remove_item( &socket_info->info.listen.list, &incoming->item );\r
746 \r
747                 ib_reject( incoming->cm_req_received.h_cm_req, IB_REJ_USER_DEFINED );\r
748 \r
749                 HeapFree( g_ibsp.heap, 0, incoming );\r
750         }\r
751 \r
752         cl_spinlock_release( &socket_info->mutex );\r
753 }\r
754 \r
755 \r
756 /* Stop listening on the socket. */\r
757 void\r
758 ib_listen_cancel(\r
759         IN                              struct ibsp_socket_info         *socket_info )\r
760 {\r
761         ib_api_status_t status;\r
762 \r
763         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
764 \r
765         /* We should be in the CLOSING state */\r
766         CL_ASSERT( socket_info->socket_state == IBSP_CLOSING );\r
767 \r
768         status = ib_cm_cancel( socket_info->info.listen.handle, ib_sync_destroy );\r
769         if( status )\r
770         {\r
771                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl,\r
772                         ("ib_cm_cancel returned %s\n", ib_get_err_str( status )) );\r
773         }\r
774         else\r
775         {\r
776                 STAT_DEC( listen_num );\r
777         }\r
778 \r
779         /* We can empty the queue now. Since we are closing, \r
780          * no new entry will be added. */\r
781         empty_listen_queue( socket_info );\r
782 \r
783         socket_info->info.listen.handle = NULL;\r
784 \r
785         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
786 }\r
787 \r
788 \r
789 int\r
790 ib_connect(\r
791         IN                              struct ibsp_socket_info         *socket_info,\r
792         IN                              ib_path_rec_t                           *path_rec,\r
793                 OUT                     LPINT                                           lpErrno )\r
794 {\r
795         ib_cm_req_t cm_req;\r
796         ib_api_status_t status;\r
797         struct cm_req_params params;\r
798 \r
799         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
800 \r
801         fzprint(("%s():%d:0x%x:0x%x: socket=0x%p \n", __FUNCTION__,\r
802                          __LINE__, GetCurrentProcessId(), GetCurrentThreadId(), socket_info));\r
803 \r
804 \r
805         memset( &cm_req, 0, sizeof(cm_req) );\r
806 \r
807         cm_req.svc_id = get_service_id_for_port( socket_info->peer_addr.sin_port );\r
808         cm_req.max_cm_retries = CM_RETRIES;\r
809         cm_req.p_primary_path = path_rec;\r
810         cm_req.pfn_cm_rep_cb = cm_rep_callback;\r
811 \r
812         cm_req.p_req_pdata = (uint8_t *) & params;\r
813         params.source = socket_info->local_addr;\r
814         params.dest = socket_info->peer_addr;\r
815         params.listen_req_param.dwProcessId = socket_info->duplicate.dwProcessId;\r
816         params.listen_req_param.identifier = socket_info->duplicate.identifier;\r
817 \r
818         CL_TRACE( IBSP_DBG_CM, gdbg_lvl,\r
819                 ("ib_connect listen params: %x %x\n", params.listen_req_param.dwProcessId,\r
820                 params.listen_req_param.identifier) );\r
821         CL_TRACE( IBSP_DBG_CM, gdbg_lvl,\r
822                 ("connecting to port %d, SID=%016I64x\n", socket_info->peer_addr.sin_port,\r
823                 cm_req.svc_id) );\r
824         CL_TRACE( IBSP_DBG_CM, gdbg_lvl, ("From:\n") );\r
825         DebugPrintSockAddr( IBSP_DBG_CM, gdbg_lvl, &socket_info->local_addr );\r
826         CL_TRACE( IBSP_DBG_CM, gdbg_lvl, ("To:\n") );\r
827         DebugPrintSockAddr( IBSP_DBG_CM, gdbg_lvl, &socket_info->peer_addr );\r
828 \r
829         cm_req.req_length = sizeof(struct cm_req_params);\r
830 \r
831         cm_req.qp_type = IB_QPT_RELIABLE_CONN;\r
832         cm_req.h_qp = socket_info->qp;\r
833         cm_req.resp_res = QP_ATTRIB_RESPONDER_RESOURCES;\r
834         cm_req.init_depth = QP_ATTRIB_INITIATOR_DEPTH;\r
835 \r
836         cm_req.remote_resp_timeout =\r
837                 ib_path_rec_pkt_life( path_rec ) + CM_REMOTE_TIMEOUT;\r
838         if( cm_req.remote_resp_timeout > 0x1F )\r
839                 cm_req.remote_resp_timeout = 0x1F;\r
840         else if( cm_req.remote_resp_timeout < CM_MIN_REMOTE_TIMEOUT )\r
841                 cm_req.remote_resp_timeout = CM_MIN_REMOTE_TIMEOUT;\r
842 \r
843         cm_req.flow_ctrl = TRUE;        /* HCAs must support end-to-end flow control. */\r
844 \r
845         cm_req.local_resp_timeout =\r
846                 ib_path_rec_pkt_life( path_rec ) + CM_LOCAL_TIMEOUT;\r
847         if( cm_req.local_resp_timeout > 0x1F )\r
848                 cm_req.local_resp_timeout = 0x1F;\r
849         else if( cm_req.local_resp_timeout < CM_MIN_LOCAL_TIMEOUT )\r
850                 cm_req.local_resp_timeout = CM_MIN_LOCAL_TIMEOUT;\r
851 \r
852         cm_req.rnr_nak_timeout = QP_ATTRIB_RNR_NAK_TIMEOUT;\r
853         cm_req.rnr_retry_cnt = QP_ATTRIB_RNR_RETRY;\r
854         cm_req.retry_cnt = QP_ATTRIB_RETRY_COUNT;\r
855         cm_req.p_alt_path = NULL;\r
856         cm_req.pfn_cm_mra_cb = cm_mra_callback;\r
857         cm_req.pfn_cm_rej_cb = cm_rej_callback;\r
858 \r
859         status = ib_cm_req( &cm_req );\r
860         if( status != IB_SUCCESS )\r
861         {\r
862                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_req failed (0x%d)\n", status) );\r
863                 *lpErrno = ibal_to_wsa_error( status );\r
864                 return SOCKET_ERROR;\r
865         }\r
866 \r
867         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
868 \r
869         *lpErrno = 0;\r
870         return 0;\r
871 }\r
872 \r
873 void\r
874 ib_reject(\r
875         IN                              const ib_cm_handle_t            h_cm,\r
876         IN                              const ib_rej_status_t           rej_status )\r
877 {\r
878         ib_cm_rej_t cm_rej;\r
879         ib_api_status_t status;\r
880 \r
881         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
882 \r
883         UNUSED_PARAM( rej_status );\r
884 \r
885         memset( &cm_rej, 0, sizeof(cm_rej) );\r
886 \r
887         status = ib_cm_rej( h_cm, &cm_rej );\r
888         if( status != IB_SUCCESS )\r
889         {\r
890                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_rej failed (0x%d)\n", status) );\r
891         }\r
892 \r
893         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
894 }\r
895 \r
896 \r
897 int\r
898 ib_accept(\r
899         IN                              struct ibsp_socket_info         *socket_info,\r
900         IN                              ib_cm_req_rec_t                         *cm_req_received,\r
901                 OUT                     LPINT                                           lpErrno )\r
902 {\r
903         ib_cm_rep_t cm_rep;\r
904         ib_api_status_t status;\r
905 \r
906         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
907 \r
908         memset( &cm_rep, 0, sizeof(cm_rep) );\r
909 \r
910         cm_rep.qp_type = IB_QPT_RELIABLE_CONN;\r
911         cm_rep.h_qp = socket_info->qp;\r
912         cm_rep.access_ctrl = IB_AC_RDMA_READ | IB_AC_RDMA_WRITE | IB_AC_LOCAL_WRITE;\r
913 #if 0\r
914         // Bug in TAVOR\r
915         cm_rep.sq_depth = QP_ATTRIB_SQ_DEPTH;\r
916         cm_rep.rq_depth = QP_ATTRIB_RQ_DEPTH;\r
917 #endif\r
918         cm_rep.init_depth = QP_ATTRIB_INITIATOR_DEPTH;\r
919         cm_rep.target_ack_delay = 10;\r
920         cm_rep.failover_accepted = IB_FAILOVER_ACCEPT_UNSUPPORTED;\r
921         cm_rep.flow_ctrl = cm_req_received->flow_ctrl;\r
922         cm_rep.rnr_nak_timeout = QP_ATTRIB_RNR_NAK_TIMEOUT;\r
923         cm_rep.rnr_retry_cnt = cm_req_received->rnr_retry_cnt;\r
924         cm_rep.pfn_cm_mra_cb = cm_mra_callback;\r
925         cm_rep.pfn_cm_rej_cb = cm_rej_callback;\r
926         cm_rep.pfn_cm_rtu_cb = cm_rtu_callback;\r
927         cm_rep.pfn_cm_lap_cb = cm_lap_callback;\r
928         cm_rep.pfn_cm_dreq_cb = cm_dreq_callback;\r
929 \r
930         fzprint(("%s():%d:0x%x:0x%x: flow_ctrl=%d rnr_retry_cnt=%d\n", __FUNCTION__,\r
931                          __LINE__, GetCurrentProcessId(),\r
932                          GetCurrentThreadId(), cm_rep.flow_ctrl, cm_rep.rnr_retry_cnt));\r
933 \r
934         status = ib_cm_rep( cm_req_received->h_cm_req, &cm_rep );\r
935         if( status != IB_SUCCESS )\r
936         {\r
937                 CL_EXIT_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_rep failed (0x%d)\n", status) );\r
938                 *lpErrno = ibal_to_wsa_error( status );\r
939                 return SOCKET_ERROR;\r
940         }\r
941 \r
942         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
943 \r
944         *lpErrno = 0;\r
945         return 0;\r
946 }\r
947 \r
948 \r
949 void\r
950 ib_disconnect(\r
951         IN                              struct ibsp_socket_info         *socket_info,\r
952         IN                              struct disconnect_reason        *reason )\r
953 {\r
954         ib_api_status_t         status;\r
955         ib_cm_dreq_t            cm_dreq;\r
956         ib_qp_attr_t            qp_attr;\r
957 \r
958         CL_ENTER( IBSP_DBG_CM, gdbg_lvl );\r
959 \r
960         socket_info->info.disconnect.event = CreateEvent( NULL, FALSE, FALSE, NULL );\r
961 \r
962         memset( &cm_dreq, 0, sizeof(cm_dreq) );\r
963 \r
964         cm_dreq.qp_type = IB_QPT_RELIABLE_CONN;\r
965         cm_dreq.h_qp = socket_info->qp;\r
966         cm_dreq.pfn_cm_drep_cb = cm_drep_callback;\r
967 \r
968         cm_dreq.p_dreq_pdata = (uint8_t *) reason;\r
969         cm_dreq.dreq_length = sizeof(struct disconnect_reason);\r
970 \r
971         status = ib_cm_dreq( &cm_dreq );\r
972 \r
973         if( status != IB_SUCCESS )\r
974         {\r
975                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("ib_cm_dreq failed (0x%d)\n", status) );\r
976                 goto done;\r
977         }\r
978 \r
979         CL_TRACE( IBSP_DBG_CM, gdbg_lvl, ("wait for DREP event\n") );\r
980 \r
981         if( WaitForSingleObject( socket_info->info.disconnect.event, INFINITE ) != WAIT_OBJECT_0 )\r
982         {\r
983                 CL_ERROR( IBSP_DBG_CM, gdbg_lvl, ("WaitForSingleObject failed\n") );\r
984         }\r
985 \r
986         memset( &qp_attr, 0, sizeof(qp_attr) );\r
987         status = ib_query_qp( socket_info->qp, &qp_attr );\r
988         if( status != IB_SUCCESS )\r
989         {\r
990                 CL_ERROR( IBSP_DBG_EP, gdbg_lvl, ("ib_query_qp failed (%d)\n", status));\r
991                 goto done;\r
992         }\r
993 \r
994         if( qp_attr.state != IB_QPS_ERROR )\r
995         {\r
996                 ib_qp_mod_t qp_mod;\r
997 \r
998                 fzprint(("%s():%d:0x%x:0x%x: socket=0x%p qp state=%d\n", __FUNCTION__,\r
999                                         __LINE__, GetCurrentProcessId(),\r
1000                                         GetCurrentThreadId(), socket_info, qp_attr.state));\r
1001                 memset( &qp_mod, 0, sizeof(qp_mod) );\r
1002                 qp_mod.req_state = IB_QPS_ERROR;\r
1003                 ib_modify_qp( socket_info->qp, &qp_mod );\r
1004                 if( status != IB_SUCCESS )\r
1005                 {\r
1006                         CL_ERROR( IBSP_DBG_CM, gdbg_lvl,\r
1007                                 ("ib_mod_qp failed (0x%d)\n", status) );\r
1008                 }\r
1009                 Sleep( 100 );\r
1010         }\r
1011 \r
1012 done:\r
1013         CloseHandle( socket_info->info.disconnect.event );\r
1014 \r
1015         CL_EXIT( IBSP_DBG_CM, gdbg_lvl );\r
1016 }\r