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