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