winverbs: fix crash accessing freed memory from async thread
[mirror/winof/.git] / core / winverbs / kernel / wv_ep.c
1 /*\r
2  * Copyright (c) 2008 Intel Corporation. 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 AWV\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 \r
30 #include <ntifs.h>\r
31 #include <iba/ib_rdma_cm.h>\r
32 \r
33 #include "wv_ep.h"\r
34 #include "wv_qp.h"\r
35 #include "wv_ioctl.h"\r
36 #include "wv_driver.h"\r
37 \r
38 #define WV_AF_INET      2\r
39 #define WV_AF_INET6     23\r
40 \r
41 static void WvEpGet(WV_ENDPOINT *pEndpoint)\r
42 {\r
43         InterlockedIncrement(&pEndpoint->Ref);\r
44 }\r
45 \r
46 static void WvEpPut(WV_ENDPOINT *pEndpoint)\r
47 {\r
48         if (InterlockedDecrement(&pEndpoint->Ref) == 0) {\r
49                 KeSetEvent(&pEndpoint->Event, 0, FALSE);\r
50         }\r
51 }\r
52 \r
53 WV_ENDPOINT *WvEpAcquire(WV_PROVIDER *pProvider, UINT64 Id)\r
54 {\r
55         WV_ENDPOINT *ep;\r
56 \r
57         KeAcquireGuardedMutex(&pProvider->Lock);\r
58         WvProviderDisableRemove(pProvider);\r
59         ep = IndexListAt(&pProvider->EpIndex, (SIZE_T) Id);\r
60         if (ep != NULL && ep->State != WvEpDestroying) {\r
61                 WvEpGet(ep);\r
62         } else {\r
63                 ep = NULL;\r
64                 WvProviderEnableRemove(pProvider);\r
65         }\r
66         KeReleaseGuardedMutex(&pProvider->Lock);\r
67 \r
68         return ep;\r
69 }\r
70 \r
71 void WvEpRelease(WV_ENDPOINT *pEndpoint)\r
72 {\r
73         WvProviderEnableRemove(pEndpoint->pProvider);\r
74         WvEpPut(pEndpoint);\r
75 }\r
76 \r
77 static NTSTATUS WvEpAllocate(WV_PROVIDER *pProvider, UINT16 EpType,\r
78                                                          WV_ENDPOINT **ppEndpoint)\r
79 {\r
80         WV_ENDPOINT                     *ep;\r
81         NTSTATUS                        status;\r
82         WDF_IO_QUEUE_CONFIG     config;\r
83 \r
84         ep = ExAllocatePoolWithTag(NonPagedPool, sizeof(WV_ENDPOINT), 'pevw');\r
85         if (ep == NULL) {\r
86                 return STATUS_NO_MEMORY;\r
87         }\r
88 \r
89         RtlZeroMemory(ep, sizeof(WV_ENDPOINT));\r
90         ep->Ref = 1;\r
91         ep->pProvider = pProvider;\r
92         ep->EpType = EpType;\r
93         KeInitializeEvent(&ep->Event, NotificationEvent, FALSE);\r
94         InitializeListHead(&ep->Entry);\r
95 \r
96         WDF_IO_QUEUE_CONFIG_INIT(&config, WdfIoQueueDispatchManual);\r
97         status = WdfIoQueueCreate(ControlDevice, &config,\r
98                                                           WDF_NO_OBJECT_ATTRIBUTES, &ep->Queue);\r
99         if (!NT_SUCCESS(status)) {\r
100                 goto err;\r
101         }\r
102 \r
103         *ppEndpoint = ep;\r
104         return STATUS_SUCCESS;\r
105 \r
106 err:\r
107         ExFreePoolWithTag(ep, 'pevw');\r
108         return status;\r
109 }\r
110 \r
111 void WvEpCreate(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
112 {\r
113         UINT64                          *pId;\r
114         UINT64                          *type;\r
115         WV_ENDPOINT                     *ep;\r
116         NTSTATUS                        status;\r
117 \r
118         status = WdfRequestRetrieveInputBuffer(Request, sizeof(UINT64), &type, NULL);\r
119         if (!NT_SUCCESS(status)) {\r
120                 goto err1;\r
121         }\r
122         status = WdfRequestRetrieveOutputBuffer(Request, sizeof(UINT64), &pId, NULL);\r
123         if (!NT_SUCCESS(status)) {\r
124                 goto err1;\r
125         }\r
126 \r
127         status = WvEpAllocate(pProvider, (UINT16) *type, &ep);\r
128         if (!NT_SUCCESS(status)) {\r
129                 goto err1;\r
130         }\r
131 \r
132         KeAcquireGuardedMutex(&pProvider->Lock);\r
133         *pId = IndexListInsertHead(&pProvider->EpIndex, ep);\r
134         if (*pId == 0) {\r
135                 status = STATUS_NO_MEMORY;\r
136                 goto err2;\r
137         }\r
138         KeReleaseGuardedMutex(&pProvider->Lock);\r
139 \r
140         WdfRequestCompleteWithInformation(Request, status, sizeof(UINT64));\r
141         return;\r
142 \r
143 err2:\r
144         KeReleaseGuardedMutex(&pProvider->Lock);\r
145         WvEpFree(ep);\r
146 err1:\r
147         WdfRequestComplete(Request, status);\r
148 }\r
149 \r
150 void WvEpDestroy(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
151 {\r
152         WV_ENDPOINT             *ep;\r
153         UINT64                  *id;\r
154         NTSTATUS                status;\r
155 \r
156         status = WdfRequestRetrieveInputBuffer(Request, sizeof(UINT64), &id, NULL);\r
157         if (!NT_SUCCESS(status)) {\r
158                 goto out;\r
159         }\r
160 \r
161         KeAcquireGuardedMutex(&pProvider->Lock);\r
162         WvProviderDisableRemove(pProvider);\r
163         ep = IndexListAt(&pProvider->EpIndex, (SIZE_T) *id);\r
164         if (ep == NULL) {\r
165                 status = STATUS_NOT_FOUND;\r
166         } else if (ep->Ref > 1) {\r
167                 status = STATUS_ACCESS_DENIED;\r
168         } else {\r
169                 IndexListRemove(&pProvider->EpIndex, (SIZE_T) *id);\r
170                 status = STATUS_SUCCESS;\r
171         }\r
172         KeReleaseGuardedMutex(&pProvider->Lock);\r
173 \r
174         if (NT_SUCCESS(status)) {\r
175                 WvEpFree(ep);\r
176         }\r
177         WvProviderEnableRemove(pProvider);\r
178 out:\r
179         WdfRequestComplete(Request, status);\r
180 }\r
181 \r
182 void WvEpFree(WV_ENDPOINT *pEndpoint)\r
183 {\r
184         WdfObjectAcquireLock(pEndpoint->Queue);\r
185         pEndpoint->State = WvEpDestroying;\r
186         WdfObjectReleaseLock(pEndpoint->Queue);\r
187 \r
188         if (InterlockedDecrement(&pEndpoint->Ref) > 0) {\r
189                 KeWaitForSingleObject(&pEndpoint->Event, Executive, KernelMode, FALSE, NULL);\r
190         }\r
191 \r
192         if (pEndpoint->pIbCmId != NULL) {\r
193                 IbCmInterface.CM.destroy_id(pEndpoint->pIbCmId);\r
194         }\r
195 \r
196         WdfIoQueuePurgeSynchronously(pEndpoint->Queue);\r
197         WdfObjectDelete(pEndpoint->Queue);\r
198         ExFreePoolWithTag(pEndpoint, 'pevw');\r
199 }\r
200 \r
201 void WvEpQuery(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
202 {\r
203         UINT64                          *id;\r
204         WV_IO_EP_ATTRIBUTES     *pattr;\r
205         WV_ENDPOINT                     *ep;\r
206         size_t                          len = 0;\r
207         NTSTATUS                        status;\r
208 \r
209         status = WdfRequestRetrieveInputBuffer(Request, sizeof(UINT64), &id, NULL);\r
210         if (!NT_SUCCESS(status)) {\r
211                 goto complete;\r
212         }\r
213         status = WdfRequestRetrieveOutputBuffer(Request, sizeof(WV_IO_EP_ATTRIBUTES),\r
214                                                                                         &pattr, NULL);\r
215         if (!NT_SUCCESS(status)) {\r
216                 goto complete;\r
217         }\r
218 \r
219         ep = WvEpAcquire(pProvider, *id);\r
220         if (ep == NULL) {\r
221                 status = STATUS_NOT_FOUND;\r
222                 goto complete;\r
223         }\r
224 \r
225         *pattr = ep->Attributes;\r
226         WvEpRelease(ep);\r
227         len = sizeof(WV_IO_EP_ATTRIBUTES);\r
228 \r
229 complete:\r
230         WdfRequestCompleteWithInformation(Request, status, len);\r
231 }\r
232 \r
233 void WvEpModify(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
234 {\r
235         WV_IO_ID                                *pId;\r
236         size_t                                  inlen;\r
237         WV_ENDPOINT                             *ep;\r
238         NTSTATUS                                status;\r
239 \r
240         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_ID), &pId, &inlen);\r
241         if (!NT_SUCCESS(status)) {\r
242                 goto complete;\r
243         }\r
244 \r
245         if (pId->Data != WV_IO_EP_OPTION_ROUTE) {\r
246                 status = STATUS_INVALID_PARAMETER;\r
247                 goto complete;\r
248         }\r
249 \r
250         if (inlen < sizeof(WV_IO_ID) + sizeof(ib_path_rec_t)) {\r
251                 status = STATUS_BUFFER_TOO_SMALL;\r
252                 goto complete;\r
253         }\r
254 \r
255         ep = WvEpAcquire(pProvider, pId->Id);\r
256         if (ep == NULL) {\r
257                 status = STATUS_NOT_FOUND;\r
258                 goto complete;\r
259         }\r
260 \r
261         WdfObjectAcquireLock(ep->Queue);\r
262         if (ep->State != WvEpAddressBound) {\r
263                 status = STATUS_NOT_SUPPORTED;\r
264                 goto release;\r
265         }\r
266 \r
267         RtlCopyMemory(&ep->Route, pId + 1, sizeof ep->Route);\r
268         ep->State = WvEpRouteResolved;\r
269 \r
270 release:\r
271         WdfObjectReleaseLock(ep->Queue);\r
272         WvEpRelease(ep);\r
273 complete:\r
274         WdfRequestComplete(Request, status);\r
275 }\r
276 \r
277 void WvEpBind(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
278 {\r
279         WV_IO_EP_BIND           *pattr;\r
280         WV_ENDPOINT                     *ep;\r
281         size_t                          len = 0;\r
282         NTSTATUS                        status;\r
283 \r
284         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_BIND),\r
285                                                                                    &pattr, NULL);\r
286         if (!NT_SUCCESS(status)) {\r
287                 goto complete;\r
288         }\r
289         status = WdfRequestRetrieveOutputBuffer(Request, sizeof(WV_IO_EP_BIND),\r
290                                                                                         &pattr, NULL);\r
291         if (!NT_SUCCESS(status)) {\r
292                 goto complete;\r
293         }\r
294 \r
295         ep = WvEpAcquire(pProvider, pattr->Id);\r
296         if (ep == NULL) {\r
297                 status = STATUS_NOT_FOUND;\r
298                 goto complete;\r
299         }\r
300 \r
301         WdfObjectAcquireLock(ep->Queue);\r
302         if (ep->State != WvEpIdle) {\r
303                 status = STATUS_NOT_SUPPORTED;\r
304                 goto release;\r
305         }\r
306 \r
307         ep->Attributes.LocalAddress = pattr->Address;\r
308         ep->Attributes.Device = pattr->Device;\r
309         len = sizeof(WV_IO_EP_BIND);\r
310         ep->State = WvEpAddressBound;\r
311 \r
312 release:\r
313         WdfObjectReleaseLock(ep->Queue);\r
314         WvEpRelease(ep);\r
315 complete:\r
316         WdfRequestCompleteWithInformation(Request, status, len);\r
317 }\r
318 \r
319 static UINT64 WvGetServiceId(UINT16 EpType, WV_IO_SOCKADDR_DATA *pAddress)\r
320 {\r
321         return RtlUlonglongByteSwap(((UINT64)EpType << 16) +\r
322                    RtlUshortByteSwap(pAddress->SockAddr.In.SinPort));\r
323 }\r
324 \r
325 static int WvAnyAddress(WV_IO_SOCKADDR_DATA *pAddress)\r
326 {\r
327         if (pAddress->SockAddr.Sa.SaFamily == WV_AF_INET) {\r
328                 return (pAddress->SockAddr.In.SinAddr == 0) ||\r
329                            ((pAddress->SockAddr.In.SinAddr & 0xff) == 0x7f);\r
330         } else {\r
331                 return (RtlCompareMemoryUlong(pAddress->SockAddr.In6.Sin6Addr, 16, 0) == 16);\r
332         }\r
333 }\r
334 \r
335 static void WvFormatCmaHeader(IB_CMA_HEADER *pHeader,\r
336                                                           WV_IO_SOCKADDR_DATA *pLocalAddress,\r
337                                                           WV_IO_SOCKADDR_DATA *pPeerAddress)\r
338 {\r
339         pHeader->CmaVersion = IB_CMA_VERSION;\r
340         if (pLocalAddress->SockAddr.Sa.SaFamily == WV_AF_INET) {\r
341                 pHeader->IpVersion = 4 << 4;\r
342                 pHeader->SrcAddress.Ip4.Address = pLocalAddress->SockAddr.In.SinAddr;\r
343                 pHeader->DstAddress.Ip4.Address = pPeerAddress->SockAddr.In.SinAddr;\r
344                 pHeader->Port = pLocalAddress->SockAddr.In.SinPort;\r
345         } else {\r
346                 pHeader->IpVersion = 6 << 4;\r
347                 RtlCopyMemory(pHeader->SrcAddress.Ip6Address,\r
348                                           pLocalAddress->SockAddr.In6.Sin6Addr, 16);\r
349                 RtlCopyMemory(pHeader->DstAddress.Ip6Address,\r
350                                           pPeerAddress->SockAddr.In6.Sin6Addr, 16);\r
351                 pHeader->Port = pLocalAddress->SockAddr.In6.Sin6Port;\r
352         }\r
353 }\r
354 \r
355 static void WvEpSaveReply(WV_ENDPOINT *pEndpoint, iba_cm_rep_event *pReply)\r
356 {\r
357         UINT8   len;\r
358 \r
359         len = sizeof(pEndpoint->Attributes.Param.Connect.Data);\r
360         RtlCopyMemory(pEndpoint->Attributes.Param.Connect.Data, pReply->rep.p_pdata, len);\r
361         pEndpoint->Attributes.Param.Connect.DataLength = len;\r
362         pEndpoint->Attributes.Param.Connect.InitiatorDepth = pReply->rep.resp_res;\r
363         pEndpoint->Attributes.Param.Connect.ResponderResources = pReply->rep.init_depth;\r
364         pEndpoint->Attributes.Param.Connect.RnrRetryCount = pReply->rep.rnr_retry_cnt;\r
365 }\r
366 \r
367 static void WvEpSaveReject(WV_ENDPOINT *pEndpoint, iba_cm_rej_event *pReject)\r
368 {\r
369         UINT8   len;\r
370 \r
371         len = sizeof(pEndpoint->Attributes.Param.Connect.Data);\r
372         RtlCopyMemory(pEndpoint->Attributes.Param.Connect.Data, pReject->p_pdata, len);\r
373         pEndpoint->Attributes.Param.Connect.DataLength = len;\r
374 }\r
375 \r
376 static NTSTATUS WvEpModifyQpErr(WV_QUEUE_PAIR *pQp,\r
377                                                                 UINT8 *pVerbsData, UINT32 VerbsSize)\r
378 {\r
379         ib_qp_mod_t                     attr;\r
380         ib_api_status_t         ib_status;\r
381         NTSTATUS                        status;\r
382 \r
383         attr.req_state = IB_QPS_ERROR;\r
384         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
385                                                                                    VerbsSize, pVerbsData);\r
386         if (ib_status == IB_SUCCESS) {\r
387                 status = STATUS_SUCCESS;\r
388         } else {        \r
389                 status = STATUS_UNSUCCESSFUL;\r
390         }\r
391 \r
392         return status;\r
393 }\r
394 \r
395 static NTSTATUS WvEpDisconnectQp(WV_PROVIDER *pProvider, UINT64 QpId,\r
396                                                                  UINT8 *pVerbsData, UINT32 VerbsSize)\r
397 {\r
398         WV_QUEUE_PAIR   *qp;\r
399         NTSTATUS                status;\r
400 \r
401         if (QpId == 0) {\r
402                 return STATUS_SUCCESS;\r
403         }\r
404 \r
405         qp = WvQpAcquire(pProvider, QpId);\r
406         if (qp == NULL) {\r
407                 return STATUS_NOT_FOUND;\r
408         }\r
409 \r
410         status = WvEpModifyQpErr(qp, pVerbsData, VerbsSize);\r
411         WvQpRelease(qp);\r
412 \r
413         return status;\r
414 }\r
415 \r
416 static void WvEpDisconnectHandler(WORK_ENTRY *pWork)\r
417 {\r
418         WV_PROVIDER                     *prov;\r
419         WDFREQUEST                      request;\r
420         WV_IO_EP_DISCONNECT     *pattr;\r
421         UINT8                           *out;\r
422         size_t                          outlen = 0;\r
423         NTSTATUS                        status;\r
424 \r
425         request = (WDFREQUEST) pWork->Context;\r
426         prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
427 \r
428         status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_DISCONNECT),\r
429                                                                                    &pattr, NULL);\r
430         if (!NT_SUCCESS(status)) {\r
431                 goto complete;\r
432         }\r
433 \r
434         status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
435         if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
436                 goto complete;\r
437         }\r
438 \r
439         status = (NTSTATUS) WdfRequestGetInformation(request);\r
440         if (NT_SUCCESS(status)) {\r
441                 status = WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
442         } else {\r
443                 WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
444         }\r
445 \r
446 complete:\r
447         WdfRequestCompleteWithInformation(request, status, outlen);\r
448         WvProviderPut(prov);\r
449 }\r
450 \r
451 // We use IRP DriverContext to queue the request for further processing,\r
452 // but the request/IRP are no longer owned by the framework.\r
453 static void WvEpCompleteDisconnect(WV_ENDPOINT *pEndpoint, NTSTATUS DiscStatus)\r
454 {\r
455         WDFREQUEST                              request;\r
456         WDF_REQUEST_PARAMETERS  param;\r
457         WORK_ENTRY                              *work;\r
458         NTSTATUS                                status;\r
459 \r
460         WdfObjectAcquireLock(pEndpoint->Queue);\r
461         if (pEndpoint->State == WvEpDestroying) {\r
462                 goto release;\r
463         }\r
464         pEndpoint->State = WvEpDisconnected;\r
465 \r
466         status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
467         while (NT_SUCCESS(status)) {\r
468                 WdfObjectReleaseLock(pEndpoint->Queue);\r
469 \r
470                 WDF_REQUEST_PARAMETERS_INIT(&param);\r
471                 WdfRequestGetParameters(request, &param);\r
472                 if (param.Parameters.DeviceIoControl.IoControlCode == WV_IOCTL_EP_DISCONNECT) {\r
473                         work = WorkEntryFromIrp(WdfRequestWdmGetIrp(request));\r
474                         WdfRequestSetInformation(request, DiscStatus);\r
475                         WorkEntryInit(work, WvEpDisconnectHandler, request);\r
476                         WvProviderGet(pEndpoint->pProvider);\r
477                         WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, work);\r
478                 } else {\r
479                         WdfRequestComplete(request, DiscStatus);\r
480                 }\r
481 \r
482                 WdfObjectAcquireLock(pEndpoint->Queue);\r
483                 status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
484         }\r
485 release:\r
486         WdfObjectReleaseLock(pEndpoint->Queue);\r
487 }\r
488 \r
489 static NTSTATUS WvEpIbCmHandler(iba_cm_id *pId, iba_cm_event *pEvent)\r
490 {\r
491         WV_ENDPOINT     *ep;\r
492 \r
493         ep = pId->context;\r
494         switch (pEvent->type) {\r
495         case iba_cm_req_error:\r
496                 WdfObjectAcquireLock(ep->Queue);\r
497                 if (ep->State == WvEpActiveConnect) {\r
498                         ep->State = WvEpDisconnected;\r
499                         WvCompleteRequests(ep->Queue, STATUS_IO_TIMEOUT);\r
500                 }\r
501                 WdfObjectReleaseLock(ep->Queue);\r
502                 break;\r
503         case iba_cm_rep_error:\r
504                 WdfObjectAcquireLock(ep->Queue);\r
505                 if (ep->State == WvEpPassiveConnect) {\r
506                         ep->State = WvEpDisconnected;\r
507                         WvCompleteRequests(ep->Queue, STATUS_IO_TIMEOUT);\r
508                 }\r
509                 WdfObjectReleaseLock(ep->Queue);\r
510                 break;\r
511         case iba_cm_dreq_error:\r
512                 WvEpCompleteDisconnect(ep, STATUS_IO_TIMEOUT);\r
513                 break;\r
514         case iba_cm_rep_received:\r
515                 WdfObjectAcquireLock(ep->Queue);\r
516                 if (ep->State == WvEpActiveConnect) {\r
517                         WvEpSaveReply(ep, &pEvent->data.rep);\r
518                         WvCompleteRequests(ep->Queue, STATUS_SUCCESS);\r
519                 }\r
520                 WdfObjectReleaseLock(ep->Queue);\r
521                 break;\r
522         case iba_cm_rtu_received:\r
523                 WdfObjectAcquireLock(ep->Queue);\r
524                 if (ep->State == WvEpPassiveConnect) {\r
525                         ep->State = WvEpConnected;\r
526                         WvCompleteRequestsWithInformation(ep->Queue, STATUS_SUCCESS);\r
527                 }\r
528                 WdfObjectReleaseLock(ep->Queue);\r
529                 break;\r
530         case iba_cm_dreq_received:\r
531                 WdfObjectAcquireLock(ep->Queue);\r
532                 if (ep->State == WvEpConnected) {\r
533                         ep->State = WvEpPassiveDisconnect;\r
534                         WvCompleteRequests(ep->Queue, STATUS_SUCCESS);\r
535                         WdfObjectReleaseLock(ep->Queue);\r
536                 } else {\r
537                         WdfObjectReleaseLock(ep->Queue);\r
538                         WvEpCompleteDisconnect(ep, STATUS_SUCCESS);\r
539                 }\r
540                 break;\r
541         case iba_cm_drep_received:\r
542                 WvEpCompleteDisconnect(ep, STATUS_SUCCESS);\r
543                 break;\r
544         case iba_cm_rej_received:\r
545                 WdfObjectAcquireLock(ep->Queue);\r
546                 if (ep->State == WvEpPassiveConnect || ep->State == WvEpActiveConnect) {\r
547                         ep->State = WvEpDisconnected;\r
548                         WvEpSaveReject(ep, &pEvent->data.rej);\r
549                         WvCompleteRequests(ep->Queue, STATUS_CONNECTION_REFUSED);\r
550                 }\r
551                 WdfObjectReleaseLock(ep->Queue);\r
552                 break;\r
553         case iba_cm_mra_received:\r
554                 break;\r
555         default:\r
556                 WdfObjectAcquireLock(ep->Queue);\r
557                 if (ep->State != WvEpDestroying) {\r
558                         ep->State = WvEpDisconnected;\r
559                         WvCompleteRequests(ep->Queue, STATUS_NOT_IMPLEMENTED);\r
560                 }\r
561                 WdfObjectReleaseLock(ep->Queue);\r
562                 break;\r
563         }\r
564 \r
565         return STATUS_SUCCESS;\r
566 }\r
567 \r
568 void WvEpConnectHandler(WORK_ENTRY *pWork)\r
569 {\r
570         WV_PROVIDER                     *prov;\r
571         WDFREQUEST                      request;\r
572         WV_IO_EP_CONNECT        *pattr;\r
573         WV_ENDPOINT                     *ep;\r
574         WV_QUEUE_PAIR           *qp;\r
575         iba_cm_req                      req;\r
576         NTSTATUS                        status;\r
577         UINT8                           data[IB_REQ_PDATA_SIZE];\r
578 \r
579         request = (WDFREQUEST) pWork->Context;\r
580         prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
581 \r
582         status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_CONNECT),\r
583                                                                                    &pattr, NULL);\r
584         if (!NT_SUCCESS(status)) {\r
585                 goto complete;\r
586         }\r
587 \r
588         if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
589                 status = STATUS_INVALID_BUFFER_SIZE;\r
590                 goto complete;\r
591         }\r
592 \r
593         ep = WvEpAcquire(prov, pattr->Id);\r
594         if (ep == NULL) {\r
595                 status = STATUS_NOT_FOUND;\r
596                 goto complete;\r
597         }\r
598 \r
599         qp = WvQpAcquire(prov, pattr->QpId);\r
600         if (qp == NULL) {\r
601                 status = STATUS_NOT_FOUND;\r
602                 goto release;\r
603         }\r
604 \r
605         ep->Attributes.PeerAddress = pattr->PeerAddress;\r
606         WvFormatCmaHeader((IB_CMA_HEADER *) data, &ep->Attributes.LocalAddress,\r
607                                           &ep->Attributes.PeerAddress);\r
608 \r
609         req.service_id = WvGetServiceId(ep->EpType, &ep->Attributes.PeerAddress);\r
610         req.p_primary_path = &ep->Route;\r
611         req.p_alt_path = NULL;\r
612         req.qpn = qp->Qpn;\r
613         req.qp_type = IB_QPT_RELIABLE_CONN;\r
614         req.starting_psn = (net32_t) RtlRandomEx(&RandomSeed);\r
615         req.p_pdata = data;\r
616         RtlCopyMemory(data + sizeof(IB_CMA_HEADER), pattr->Param.Data,\r
617                                   pattr->Param.DataLength);\r
618         req.pdata_len = sizeof(IB_CMA_HEADER) + pattr->Param.DataLength;\r
619         req.max_cm_retries = IB_CMA_MAX_CM_RETRIES;\r
620         req.resp_res = (UINT8) pattr->Param.ResponderResources;\r
621         req.init_depth = (UINT8) pattr->Param.InitiatorDepth;\r
622         req.remote_resp_timeout = IB_CMA_CM_RESPONSE_TIMEOUT;\r
623         req.flow_ctrl = 1;\r
624         req.local_resp_timeout = IB_CMA_CM_RESPONSE_TIMEOUT;\r
625         req.rnr_retry_cnt = pattr->Param.RnrRetryCount;\r
626         req.retry_cnt = pattr->Param.RetryCount;\r
627         req.srq = (qp->pSrq != NULL);\r
628 \r
629         WvQpRelease(qp);\r
630         RtlCopyMemory(&ep->Attributes.Param.Connect, &pattr->Param,\r
631                                   sizeof(pattr->Param));\r
632 \r
633         WdfObjectAcquireLock(ep->Queue);\r
634         if (ep->State != WvEpRouteResolved) {\r
635                 status = STATUS_NOT_SUPPORTED;\r
636                 goto unlock;\r
637         }\r
638 \r
639         status = IbCmInterface.CM.create_id(WvEpIbCmHandler, ep, &ep->pIbCmId);\r
640         if (!NT_SUCCESS(status)) {\r
641                 goto unlock;\r
642         }\r
643 \r
644         ep->State = WvEpActiveConnect;\r
645         status = IbCmInterface.CM.send_req(ep->pIbCmId, &req);\r
646         if (NT_SUCCESS(status)) {\r
647                 status = WdfRequestForwardToIoQueue(request, ep->Queue);\r
648         }\r
649 \r
650         if (!NT_SUCCESS(status)) {\r
651                 ep->State = WvEpDisconnected;\r
652         }\r
653 unlock:\r
654         WdfObjectReleaseLock(ep->Queue);\r
655 release:\r
656         WvEpRelease(ep);\r
657 complete:\r
658         if (!NT_SUCCESS(status)) {\r
659                 WdfRequestComplete(request, status);\r
660         }\r
661         WvProviderPut(prov);\r
662 }\r
663 \r
664 static void WvEpProcessAsync(WV_PROVIDER *pProvider, WDFREQUEST Request,\r
665                                                          void (*AsyncHandler)(struct _WORK_ENTRY *Work))\r
666 {\r
667         WORK_ENTRY      *work;\r
668 \r
669         work = WorkEntryFromIrp(WdfRequestWdmGetIrp(Request));\r
670         WorkEntryInit(work, AsyncHandler, Request);\r
671         WvProviderGet(pProvider);\r
672         WorkQueueInsert(&pProvider->WorkQueue, work);\r
673 }\r
674 \r
675 void WvEpConnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
676 {\r
677         WvEpProcessAsync(pProvider, Request, WvEpConnectHandler);\r
678 }\r
679 \r
680 static NTSTATUS WvEpModifyQpRtr(WV_ENDPOINT *pEndpoint, WV_QUEUE_PAIR *pQp,\r
681                                                                 UINT64 ResponderResources, UINT32 Psn,\r
682                                                                 UINT8 *pVerbsData, UINT32 VerbsSize)\r
683 {\r
684         ib_qp_mod_t                     attr;\r
685         ib_api_status_t         ib_status;\r
686         NTSTATUS                        status;\r
687 \r
688         status =IbCmInterface.CM.get_qp_attr(pEndpoint->pIbCmId, IB_QPS_INIT, &attr);\r
689         if (!NT_SUCCESS(status)) {\r
690                 return status;\r
691         }\r
692         \r
693         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
694                                                                                    VerbsSize, pVerbsData);\r
695         if (ib_status != IB_SUCCESS) {\r
696                 return STATUS_UNSUCCESSFUL;\r
697         }\r
698 \r
699         status = IbCmInterface.CM.get_qp_attr(pEndpoint->pIbCmId, IB_QPS_RTR, &attr);\r
700         if (!NT_SUCCESS(status)) {\r
701                 return status;\r
702         }\r
703         \r
704         if (pEndpoint->State == WvEpPassiveConnect) {\r
705                 attr.state.rtr.resp_res = (UINT8) ResponderResources;\r
706                 attr.state.rtr.rq_psn = Psn;\r
707         }\r
708 \r
709         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
710                                                                                    VerbsSize, pVerbsData);\r
711         if (ib_status != IB_SUCCESS) {\r
712                 return STATUS_UNSUCCESSFUL;\r
713         }\r
714 \r
715         return STATUS_SUCCESS;\r
716 }\r
717 \r
718 static NTSTATUS WvEpModifyQpRts(WV_ENDPOINT *pEndpoint, WV_QUEUE_PAIR *pQp,\r
719                                                                 UINT64 InitiatorDepth,\r
720                                                                 UINT8 *pVerbsData, UINT32 VerbsSize)\r
721 {\r
722         ib_qp_mod_t                     attr;\r
723         ib_api_status_t         ib_status;\r
724         NTSTATUS                        status;\r
725 \r
726         status = IbCmInterface.CM.get_qp_attr(pEndpoint->pIbCmId, IB_QPS_RTS, &attr);\r
727         if (!NT_SUCCESS(status)) {\r
728                 return status;\r
729         }\r
730         \r
731         if (pEndpoint->State == WvEpPassiveConnect) {\r
732                 attr.state.rts.init_depth = (UINT8) InitiatorDepth;\r
733         }\r
734 \r
735         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
736                                                                                    VerbsSize, pVerbsData);\r
737         if (ib_status != IB_SUCCESS) {\r
738                 return STATUS_UNSUCCESSFUL;\r
739         }\r
740 \r
741         return STATUS_SUCCESS;\r
742 }\r
743 \r
744 static NTSTATUS WvEpAcceptActive(WDFREQUEST Request, UINT8 *pVerbsData, size_t VerbsSize,\r
745                                                                  WV_ENDPOINT *pEndpoint, WV_IO_EP_ACCEPT *pAttr)\r
746 {\r
747         WV_QUEUE_PAIR           *qp;\r
748         NTSTATUS                        status;\r
749 \r
750         qp = WvQpAcquire(pEndpoint->pProvider, pAttr->QpId);\r
751         if (qp == NULL) {\r
752                 return STATUS_NOT_FOUND;\r
753         }\r
754 \r
755         status = WvEpModifyQpRtr(pEndpoint, qp, 0, 0, pVerbsData, VerbsSize);\r
756         if (NT_SUCCESS(status)) {\r
757                 status = WvEpModifyQpRts(pEndpoint, qp, 0, pVerbsData, VerbsSize);\r
758         }\r
759 \r
760         WvQpRelease(qp);\r
761 \r
762         WdfObjectAcquireLock(pEndpoint->Queue);\r
763         if (pEndpoint->State != WvEpActiveConnect) {\r
764                 status = STATUS_NOT_SUPPORTED;\r
765                 goto release;\r
766         }\r
767 \r
768         pEndpoint->State = WvEpConnected;\r
769         status = IbCmInterface.CM.send_rtu(pEndpoint->pIbCmId, pAttr->Param.Data,\r
770                                                                            pAttr->Param.DataLength);\r
771         if (NT_SUCCESS(status)) {\r
772                 WdfRequestCompleteWithInformation(Request, status, VerbsSize);\r
773         } else {\r
774                 pEndpoint->State = WvEpDisconnected;\r
775         }\r
776 \r
777 release:\r
778         WdfObjectReleaseLock(pEndpoint->Queue);\r
779         return status;\r
780 }\r
781 \r
782 static NTSTATUS WvEpAcceptPassive(WDFREQUEST Request, UINT8 *pVerbsData, size_t VerbsSize,\r
783                                                                   WV_ENDPOINT *pEndpoint, WV_IO_EP_ACCEPT *pAttr)\r
784 {\r
785         WV_QUEUE_PAIR           *qp;\r
786         iba_cm_rep                      rep;\r
787         NTSTATUS                        status;\r
788 \r
789         qp = WvQpAcquire(pEndpoint->pProvider, pAttr->QpId);\r
790         if (qp == NULL) {\r
791                 return STATUS_NOT_FOUND;\r
792         }\r
793 \r
794         rep.qpn = qp->Qpn;\r
795         rep.starting_psn = (net32_t) RtlRandomEx(&RandomSeed);\r
796         rep.p_pdata = pAttr->Param.Data;\r
797         rep.pdata_len = pAttr->Param.DataLength;\r
798         rep.failover_accepted = IB_FAILOVER_ACCEPT_UNSUPPORTED;\r
799         rep.resp_res = (UINT8) pAttr->Param.ResponderResources;\r
800         rep.init_depth = (UINT8) pAttr->Param.InitiatorDepth;\r
801         rep.flow_ctrl = 1;\r
802         rep.rnr_retry_cnt = pAttr->Param.RnrRetryCount;\r
803         rep.srq = (qp->pSrq != NULL);\r
804 \r
805         status = WvEpModifyQpRtr(pEndpoint, qp, pAttr->Param.ResponderResources,\r
806                                                          rep.starting_psn, pVerbsData, VerbsSize);\r
807         if (NT_SUCCESS(status)) {\r
808                 status = WvEpModifyQpRts(pEndpoint, qp, pAttr->Param.InitiatorDepth,\r
809                                                                  pVerbsData, VerbsSize);\r
810         }\r
811 \r
812         WvQpRelease(qp);\r
813 \r
814         if (!NT_SUCCESS(status)) {\r
815                 goto out;\r
816         }\r
817 \r
818         WdfObjectAcquireLock(pEndpoint->Queue);\r
819         if (pEndpoint->State != WvEpPassiveConnect) {\r
820                 status = STATUS_NOT_SUPPORTED;\r
821                 goto release;\r
822         }\r
823 \r
824         status = IbCmInterface.CM.send_rep(pEndpoint->pIbCmId, &rep);\r
825         if (NT_SUCCESS(status)) {\r
826                 status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
827         }\r
828         \r
829         if (!NT_SUCCESS(status)) {\r
830                 pEndpoint->State = WvEpDisconnected;\r
831         }\r
832 \r
833 release:\r
834         WdfObjectReleaseLock(pEndpoint->Queue);\r
835 out:\r
836         return status;\r
837 }\r
838 \r
839 void WvEpAcceptHandler(WORK_ENTRY *pWork)\r
840 {\r
841         WV_PROVIDER                     *prov;\r
842         WDFREQUEST                      request;\r
843         WV_IO_EP_ACCEPT         *pattr;\r
844         WV_ENDPOINT                     *ep;\r
845         NTSTATUS                        status;\r
846         UINT8                           *out;\r
847         size_t                          outlen;\r
848 \r
849         request = (WDFREQUEST) pWork->Context;\r
850         prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
851 \r
852         status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_ACCEPT),\r
853                                                                                    &pattr, NULL);\r
854         if (!NT_SUCCESS(status)) {\r
855                 goto complete;\r
856         }\r
857 \r
858         status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
859         if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
860                 goto complete;\r
861         }\r
862 \r
863         if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
864                 status = STATUS_INVALID_BUFFER_SIZE;\r
865                 goto complete;\r
866         }\r
867 \r
868         ep = WvEpAcquire(prov, pattr->Id);\r
869         if (ep == NULL) {\r
870                 status = STATUS_NOT_FOUND;\r
871                 goto complete;\r
872         }\r
873 \r
874         /* EP state is re-checked under lock in WvEpAccept* calls */\r
875         switch (ep->State) {\r
876         case WvEpActiveConnect:\r
877                 status = WvEpAcceptActive(request, out, outlen, ep, pattr);\r
878                 break;\r
879         case WvEpPassiveConnect:\r
880                 status = WvEpAcceptPassive(request, out, outlen, ep, pattr);\r
881                 break;\r
882         default:\r
883                 status = STATUS_NOT_SUPPORTED;\r
884                 break;\r
885         }\r
886 \r
887         WvEpRelease(ep);\r
888 complete:\r
889         if (!NT_SUCCESS(status)) {\r
890                 WdfRequestComplete(request, status);\r
891         }\r
892         WvProviderPut(prov);\r
893 }\r
894 \r
895 void WvEpAccept(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
896 {\r
897         WvEpProcessAsync(pProvider, Request, WvEpAcceptHandler);\r
898 }\r
899 \r
900 void WvEpReject(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
901 {\r
902         WV_IO_ID                        *id;\r
903         WV_ENDPOINT                     *ep;\r
904         NTSTATUS                        status;\r
905         size_t                          len;\r
906 \r
907         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_ID), &id, &len);\r
908         if (!NT_SUCCESS(status)) {\r
909                 goto complete;\r
910         }\r
911 \r
912         ep = WvEpAcquire(pProvider, id->Id);\r
913         if (ep == NULL) {\r
914                 status = STATUS_NOT_FOUND;\r
915                 goto complete;\r
916         }\r
917 \r
918         WdfObjectAcquireLock(ep->Queue);\r
919         if (ep->State != WvEpActiveConnect && ep->State != WvEpPassiveConnect) {\r
920                 status = STATUS_NOT_SUPPORTED;\r
921                 goto release;\r
922         }\r
923 \r
924         ep->State = WvEpDisconnected;\r
925         status = IbCmInterface.CM.send_rej(ep->pIbCmId, IB_REJ_USER_DEFINED,\r
926                                                                            NULL, 0, id + 1, len - sizeof(WV_IO_ID));\r
927 \r
928 release:\r
929         WdfObjectReleaseLock(ep->Queue);\r
930         WvEpRelease(ep);\r
931 complete:\r
932         WdfRequestComplete(Request, status);\r
933 }\r
934 \r
935 static NTSTATUS WvEpDisconnectActive(WDFREQUEST Request,\r
936                                                                          UINT8 *pVerbsData, size_t VerbsSize,\r
937                                                                          WV_ENDPOINT *pEndpoint,\r
938                                                                          WV_IO_EP_DISCONNECT *pAttr)\r
939 {\r
940         NTSTATUS status, failure;\r
941 \r
942         WdfObjectAcquireLock(pEndpoint->Queue);\r
943         if (pEndpoint->State != WvEpConnected) {\r
944                 status = STATUS_NOT_SUPPORTED;\r
945                 goto release;\r
946         }\r
947 \r
948         pEndpoint->State = WvEpActiveDisconnect;\r
949         IbCmInterface.CM.send_dreq(pEndpoint->pIbCmId, NULL, 0);\r
950 \r
951         status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
952         if (!NT_SUCCESS(status)) {\r
953                 pEndpoint->State = WvEpDisconnected;\r
954                 WvCompleteRequests(pEndpoint->Queue, STATUS_UNSUCCESSFUL);\r
955                 WdfObjectReleaseLock(pEndpoint->Queue);\r
956 \r
957                 failure = status;\r
958                 status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
959                                                                   pVerbsData, VerbsSize);\r
960                 if (NT_SUCCESS(status)) {\r
961                         WdfRequestCompleteWithInformation(Request, failure, VerbsSize);\r
962                 }\r
963                 return status;\r
964         }\r
965 \r
966 release:\r
967         WdfObjectReleaseLock(pEndpoint->Queue);\r
968         return status;\r
969 }\r
970 \r
971 static NTSTATUS WvEpDisconnectPassive(WDFREQUEST Request,\r
972                                                                           UINT8 *pVerbsData, size_t VerbsSize,\r
973                                                                           WV_ENDPOINT *pEndpoint,\r
974                                                                           WV_IO_EP_DISCONNECT *pAttr)\r
975 {\r
976         NTSTATUS status;\r
977 \r
978         WdfObjectAcquireLock(pEndpoint->Queue);\r
979         if (pEndpoint->State != WvEpPassiveDisconnect) {\r
980                 WdfObjectReleaseLock(pEndpoint->Queue);\r
981                 return STATUS_NOT_SUPPORTED;\r
982         }\r
983 \r
984         pEndpoint->State = WvEpDisconnected;\r
985         WdfObjectReleaseLock(pEndpoint->Queue);\r
986 \r
987         IbCmInterface.CM.send_drep(pEndpoint->pIbCmId, NULL, 0);\r
988 \r
989         status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
990                                                           pVerbsData, VerbsSize);\r
991         if (NT_SUCCESS(status)) {\r
992                 WdfRequestCompleteWithInformation(Request, status, VerbsSize);\r
993         }\r
994 \r
995         return status;\r
996 }\r
997 \r
998 void WvEpDisconnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
999 {\r
1000         WV_IO_EP_DISCONNECT     *pattr;\r
1001         WV_ENDPOINT                     *ep;\r
1002         NTSTATUS                        status;\r
1003         UINT8                           *out;\r
1004         size_t                          outlen;\r
1005 \r
1006         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_DISCONNECT),\r
1007                                                                                    &pattr, NULL);\r
1008         if (!NT_SUCCESS(status)) {\r
1009                 goto complete;\r
1010         }\r
1011 \r
1012         status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
1013         if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
1014                 goto complete;\r
1015         }\r
1016 \r
1017         ep = WvEpAcquire(pProvider, pattr->Id);\r
1018         if (ep == NULL) {\r
1019                 status = STATUS_NOT_FOUND;\r
1020                 goto complete;\r
1021         }\r
1022 \r
1023         /* EP state is re-checked under lock in WvEpDisconnect* calls */\r
1024         switch (ep->State) {\r
1025         case WvEpConnected:\r
1026                 status = WvEpDisconnectActive(Request, out, outlen, ep, pattr);\r
1027                 break;\r
1028         case WvEpPassiveDisconnect:\r
1029                 status = WvEpDisconnectPassive(Request, out, outlen, ep, pattr);\r
1030                 break;\r
1031         default:\r
1032                 status = STATUS_NOT_SUPPORTED;\r
1033                 break;\r
1034         }\r
1035 \r
1036         WvEpRelease(ep);\r
1037 complete:\r
1038         if (!NT_SUCCESS(status)) {\r
1039                 WdfRequestComplete(Request, status);\r
1040         }\r
1041 }\r
1042 \r
1043 void WvEpDisconnectNotify(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1044 {\r
1045         UINT64                          *id;\r
1046         WV_ENDPOINT                     *ep;\r
1047         NTSTATUS                        status;\r
1048 \r
1049         status = WdfRequestRetrieveInputBuffer(Request, sizeof(UINT64), &id, NULL);\r
1050         if (!NT_SUCCESS(status)) {\r
1051                 goto complete;\r
1052         }\r
1053 \r
1054         ep = WvEpAcquire(pProvider, *id);\r
1055         if (ep == NULL) {\r
1056                 status = STATUS_NOT_FOUND;\r
1057                 goto complete;\r
1058         }\r
1059 \r
1060         WdfObjectAcquireLock(ep->Queue);\r
1061         switch (ep->State) {\r
1062         case WvEpConnected:\r
1063         case WvEpActiveDisconnect:\r
1064                 status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
1065                 if (NT_SUCCESS(status)) {\r
1066                         WdfObjectReleaseLock(ep->Queue);\r
1067                         WvEpRelease(ep);\r
1068                         return;\r
1069                 }\r
1070                 break;\r
1071         case WvEpPassiveDisconnect:\r
1072         case WvEpDisconnected:\r
1073                 status = STATUS_SUCCESS;\r
1074                 break;\r
1075         default:\r
1076                 status = STATUS_NOT_SUPPORTED;\r
1077                 break;\r
1078         }\r
1079         WdfObjectReleaseLock(ep->Queue);\r
1080 \r
1081         WvEpRelease(ep);\r
1082 complete:\r
1083         WdfRequestComplete(Request, status);\r
1084 }\r
1085 \r
1086 static NTSTATUS WvEpIbListenHandler(iba_cm_id *pId, iba_cm_event *pEvent)\r
1087 {\r
1088         WV_ENDPOINT             *listen, *ep;\r
1089         WDFREQUEST              request;\r
1090         NTSTATUS                status;\r
1091         IB_CMA_HEADER   *hdr;\r
1092 \r
1093         listen = ((iba_cm_id *) pId->context)->context;\r
1094 \r
1095         WdfObjectAcquireLock(listen->Queue);\r
1096         status = WdfIoQueueRetrieveNextRequest(listen->Queue, &request);\r
1097         if (!NT_SUCCESS(status)) {\r
1098                 goto release;\r
1099         }\r
1100 \r
1101         ASSERT(!IsListEmpty(&listen->Entry));\r
1102         ep = CONTAINING_RECORD(RemoveHeadList(&listen->Entry), WV_ENDPOINT, Entry);\r
1103         ep->pIbCmId = pId;\r
1104         pId->callback = WvEpIbCmHandler;\r
1105         pId->context = ep;\r
1106 \r
1107         hdr = pEvent->data.req.req.p_pdata;\r
1108         if ((hdr->IpVersion >> 4) == 4) {\r
1109                 ep->Attributes.LocalAddress.SockAddr.In.SinFamily = WV_AF_INET;\r
1110                 ep->Attributes.LocalAddress.SockAddr.In.SinAddr = hdr->DstAddress.Ip4.Address;\r
1111                 ep->Attributes.PeerAddress.SockAddr.In.SinFamily = WV_AF_INET;\r
1112                 ep->Attributes.PeerAddress.SockAddr.In.SinAddr = hdr->SrcAddress.Ip4.Address;\r
1113         } else {\r
1114                 ep->Attributes.LocalAddress.SockAddr.In6.Sin6Family = WV_AF_INET6; \r
1115                 RtlCopyMemory(ep->Attributes.LocalAddress.SockAddr.In6.Sin6Addr,\r
1116                                           hdr->DstAddress.Ip6Address, 16);\r
1117                 ep->Attributes.PeerAddress.SockAddr.In6.Sin6Family = WV_AF_INET6;\r
1118                 RtlCopyMemory(ep->Attributes.PeerAddress.SockAddr.In6.Sin6Addr,\r
1119                                           hdr->SrcAddress.Ip6Address, 16);\r
1120         }\r
1121         ep->Attributes.Device.DeviceGuid = pEvent->data.req.local_ca_guid;\r
1122         ep->Attributes.Device.Pkey = pEvent->data.req.req.p_primary_path->pkey;\r
1123         ep->Attributes.Device.PortNumber = pEvent->data.req.port_num;\r
1124         ep->Attributes.Param.Connect.ResponderResources = pEvent->data.req.req.resp_res;\r
1125         ep->Attributes.Param.Connect.InitiatorDepth = pEvent->data.req.req.init_depth;\r
1126         ep->Attributes.Param.Connect.RetryCount = pEvent->data.req.req.retry_cnt;\r
1127         ep->Attributes.Param.Connect.RnrRetryCount = pEvent->data.req.req.rnr_retry_cnt;\r
1128         ep->Attributes.Param.Connect.DataLength = sizeof(ep->Attributes.Param.Connect.Data);\r
1129         RtlCopyMemory(ep->Attributes.Param.Connect.Data, hdr + 1,\r
1130                                   sizeof(ep->Attributes.Param.Connect.Data));\r
1131         ep->Route = *pEvent->data.req.req.p_primary_path;\r
1132 \r
1133         ep->State = WvEpPassiveConnect;\r
1134         WvEpPut(ep);\r
1135 \r
1136         WdfRequestComplete(request, STATUS_SUCCESS);\r
1137 release:\r
1138         WdfObjectReleaseLock(listen->Queue);\r
1139         return status;\r
1140 }\r
1141 \r
1142 void WvEpListen(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1143 {\r
1144         WV_ENDPOINT                     *ep;\r
1145         WV_IO_EP_LISTEN         *pattr;\r
1146         NTSTATUS                        status;\r
1147         void                            *buf;\r
1148         UINT8                           offset, len;\r
1149         UINT64                          sid;\r
1150 \r
1151         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_LISTEN),\r
1152                                                                                    &pattr, NULL);\r
1153         if (!NT_SUCCESS(status)) {\r
1154                 goto complete;\r
1155         }\r
1156 \r
1157         ep = WvEpAcquire(pProvider, pattr->Id);\r
1158         if (ep == NULL) {\r
1159                 status = STATUS_NOT_FOUND;\r
1160                 goto complete;\r
1161         }\r
1162 \r
1163         if (WvAnyAddress(&ep->Attributes.LocalAddress)) {\r
1164                 buf = NULL;\r
1165                 offset = 0;\r
1166                 len = 0;\r
1167         } else {\r
1168                 if (ep->Attributes.LocalAddress.SockAddr.Sa.SaFamily == WV_AF_INET) {\r
1169                         buf = &ep->Attributes.LocalAddress.SockAddr.In.SinAddr;\r
1170                         len = sizeof ep->Attributes.LocalAddress.SockAddr.In.SinAddr;\r
1171                         offset = FIELD_OFFSET(IB_CMA_HEADER, DstAddress.Ip4.Address);\r
1172                 } else {\r
1173                         buf = ep->Attributes.LocalAddress.SockAddr.In6.Sin6Addr;\r
1174                         len = sizeof ep->Attributes.LocalAddress.SockAddr.In6.Sin6Addr;\r
1175                         offset = FIELD_OFFSET(IB_CMA_HEADER, DstAddress.Ip6Address);\r
1176                 }\r
1177         }\r
1178 \r
1179         WdfObjectAcquireLock(ep->Queue);\r
1180         if (ep->State != WvEpAddressBound) {\r
1181                 status = STATUS_NOT_SUPPORTED;\r
1182                 goto release;\r
1183         }\r
1184 \r
1185         status = IbCmInterface.CM.create_id(WvEpIbListenHandler, ep, &ep->pIbCmId);\r
1186         if (!NT_SUCCESS(status)) {\r
1187                 goto release;\r
1188         }\r
1189 \r
1190         ep->Attributes.Param.Backlog = pattr->Backlog;\r
1191         ep->State = WvEpListening;\r
1192         sid = WvGetServiceId(ep->EpType, &ep->Attributes.LocalAddress);\r
1193         status = IbCmInterface.CM.listen(ep->pIbCmId, sid, buf, len, offset);\r
1194 \r
1195 release:\r
1196         WdfObjectReleaseLock(ep->Queue);\r
1197         WvEpRelease(ep);\r
1198 complete:\r
1199         WdfRequestComplete(Request, status);\r
1200 }\r
1201 \r
1202 void WvEpGetRequest(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1203 {\r
1204         WV_ENDPOINT                             *listen, *ep;\r
1205         WV_IO_EP_GET_REQUEST    *req;\r
1206         NTSTATUS                                status;\r
1207 \r
1208         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_GET_REQUEST),\r
1209                                                                                    &req, NULL);\r
1210         if (!NT_SUCCESS(status)) {\r
1211                 goto complete;\r
1212         }\r
1213 \r
1214         listen = WvEpAcquire(pProvider, req->Id);\r
1215         if (listen == NULL) {\r
1216                 status = STATUS_NOT_FOUND;\r
1217                 goto complete;\r
1218         }\r
1219 \r
1220         if (listen->State != WvEpListening) {\r
1221                 status = STATUS_NOT_SUPPORTED;\r
1222                 goto release1;\r
1223         }\r
1224 \r
1225         ep = WvEpAcquire(pProvider, req->EpId);\r
1226         if (ep == NULL) {\r
1227                 status = STATUS_NOT_FOUND;\r
1228                 goto release1;\r
1229         }\r
1230 \r
1231         WdfObjectAcquireLock(ep->Queue);\r
1232         if (ep->State == WvEpIdle) {\r
1233                 ep->State = WvEpQueued;\r
1234         } else {\r
1235                 status = STATUS_CONNECTION_IN_USE;\r
1236         }\r
1237         WdfObjectReleaseLock(ep->Queue);\r
1238 \r
1239         if (!NT_SUCCESS(status)) {\r
1240                 goto release2;\r
1241         }\r
1242 \r
1243         WdfObjectAcquireLock(listen->Queue);\r
1244         status = WdfRequestForwardToIoQueue(Request, listen->Queue);\r
1245         if (NT_SUCCESS(status)) {\r
1246                 InsertTailList(&listen->Entry, &ep->Entry);\r
1247                 WvEpGet(ep);\r
1248         }\r
1249         WdfObjectReleaseLock(listen->Queue);\r
1250 \r
1251 release2:\r
1252         WvEpRelease(ep);\r
1253 release1:\r
1254         WvEpRelease(listen);\r
1255 complete:\r
1256         if (!NT_SUCCESS(status)) {\r
1257                 WdfRequestComplete(Request, status);\r
1258         }\r
1259 }\r
1260 \r
1261 void WvEpLookup(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1262 {\r
1263         UNUSED_PARAM(pProvider);\r
1264         WdfRequestComplete(Request, STATUS_NOT_IMPLEMENTED);\r
1265 }\r
1266 \r
1267 void WvEpMulticastJoin(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1268 {\r
1269         UNUSED_PARAM(pProvider);\r
1270         WdfRequestComplete(Request, STATUS_NOT_IMPLEMENTED);\r
1271 }\r
1272 \r
1273 void WvEpMulticastLeave(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1274 {\r
1275         UNUSED_PARAM(pProvider);\r
1276         WdfRequestComplete(Request, STATUS_NOT_IMPLEMENTED);\r
1277 }\r
1278 \r
1279 //\r
1280 // Note that the framework may have already canceled outstanding requests.\r
1281 //\r
1282 void WvEpCancelListen(WV_ENDPOINT *pListen)\r
1283 {\r
1284         WV_ENDPOINT                     *ep;\r
1285 \r
1286         WdfObjectAcquireLock(pListen->Queue);\r
1287         WvCompleteRequests(pListen->Queue, STATUS_CANCELLED);\r
1288 \r
1289         while (!IsListEmpty(&pListen->Entry)) {\r
1290                 ep = CONTAINING_RECORD(RemoveHeadList(&pListen->Entry), WV_ENDPOINT, Entry);\r
1291                 ep->State = WvEpIdle;\r
1292                 WvEpPut(ep);\r
1293         }\r
1294         WdfObjectReleaseLock(pListen->Queue);\r
1295 }\r
1296 \r
1297 void WvEpCancel(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1298 {\r
1299         UINT64                          *id;\r
1300         WV_ENDPOINT                     *ep;\r
1301         NTSTATUS                        status;\r
1302 \r
1303         status = WdfRequestRetrieveInputBuffer(Request, sizeof(UINT64), &id, NULL);\r
1304         if (!NT_SUCCESS(status)) {\r
1305                 goto out;\r
1306         }\r
1307 \r
1308         ep = WvEpAcquire(pProvider, *id);\r
1309         if (ep == NULL) {\r
1310                 status = STATUS_NOT_FOUND;\r
1311                 goto out;\r
1312         }\r
1313 \r
1314         if (ep->State == WvEpListening) {\r
1315                 WvEpCancelListen(ep);\r
1316         } else {\r
1317                 WvFlushQueue(ep->Queue, STATUS_CANCELLED);\r
1318         }\r
1319         WvEpRelease(ep);\r
1320 \r
1321 out:\r
1322         WdfRequestComplete(Request, status);\r
1323 }\r