9a3350cb95af140f18759b776368e8f55b742dfb
[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) {\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 (pEndpoint->pIbCmId != NULL) {\r
189                 IbCmInterface.CM.destroy_id(pEndpoint->pIbCmId);\r
190         }\r
191 \r
192         if (InterlockedDecrement(&pEndpoint->Ref) > 0) {\r
193                 KeWaitForSingleObject(&pEndpoint->Event, Executive, KernelMode, FALSE, NULL);\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 /*\r
377  * The QP transition to error may be done from an async worker thread.\r
378  * Synchronize against application exit.\r
379  */\r
380 static NTSTATUS WvEpModifyQpErr(WV_QUEUE_PAIR *pQp,\r
381                                                                 UINT8 *pVerbsData, UINT32 VerbsSize)\r
382 {\r
383         ib_qp_mod_t                     attr;\r
384         ib_api_status_t         ib_status;\r
385         NTSTATUS                        status;\r
386 \r
387         KeAcquireGuardedMutex(&pQp->Lock);\r
388         if (pQp->hVerbsQp == NULL) {\r
389                 status = STATUS_NOT_FOUND;\r
390                 goto out;\r
391         }\r
392 \r
393         attr.req_state = IB_QPS_ERROR;\r
394         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
395                                                                                    VerbsSize, pVerbsData);\r
396         if (ib_status == IB_SUCCESS) {\r
397                 status = STATUS_SUCCESS;\r
398         } else {        \r
399                 status = STATUS_UNSUCCESSFUL;\r
400         }\r
401 \r
402 out:\r
403         KeReleaseGuardedMutex(&pQp->Lock);\r
404         return status;\r
405 }\r
406 \r
407 static NTSTATUS WvEpDisconnectQp(WV_PROVIDER *pProvider, UINT64 QpId,\r
408                                                                  UINT8 *pVerbsData, UINT32 VerbsSize)\r
409 {\r
410         WV_QUEUE_PAIR   *qp;\r
411         NTSTATUS                status;\r
412 \r
413         if (QpId == 0) {\r
414                 return STATUS_SUCCESS;\r
415         }\r
416 \r
417         qp = WvQpAcquire(pProvider, QpId);\r
418         if (qp == NULL) {\r
419                 return STATUS_NOT_FOUND;\r
420         }\r
421 \r
422         status = WvEpModifyQpErr(qp, pVerbsData, VerbsSize);\r
423         WvQpRelease(qp);\r
424 \r
425         return status;\r
426 }\r
427 \r
428 static void WvEpDisconnectHandler(WORK_ENTRY *pWork)\r
429 {\r
430         WV_PROVIDER                     *prov;\r
431         WDFREQUEST                      request;\r
432         WV_IO_EP_DISCONNECT     *pattr;\r
433         UINT8                           *out;\r
434         size_t                          outlen = 0;\r
435         NTSTATUS                        status;\r
436 \r
437         request = (WDFREQUEST) pWork->Context;\r
438         prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
439 \r
440         status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_DISCONNECT),\r
441                                                                                    &pattr, NULL);\r
442         if (!NT_SUCCESS(status)) {\r
443                 goto complete;\r
444         }\r
445 \r
446         status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
447         if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
448                 goto complete;\r
449         }\r
450 \r
451         status = (NTSTATUS) WdfRequestGetInformation(request);\r
452         if (NT_SUCCESS(status)) {\r
453                 status = WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
454         } else {\r
455                 WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
456         }\r
457 \r
458 complete:\r
459         WdfRequestCompleteWithInformation(request, status, outlen);\r
460         WvProviderPut(prov);\r
461 }\r
462 \r
463 // We use IRP DriverContext to queue the request for further processing,\r
464 // but the request/IRP are no longer owned by the framework.\r
465 static void WvEpCompleteDisconnect(WV_ENDPOINT *pEndpoint, NTSTATUS DiscStatus)\r
466 {\r
467         WDFREQUEST                              request;\r
468         WDF_REQUEST_PARAMETERS  param;\r
469         WORK_ENTRY                              *work;\r
470         NTSTATUS                                status;\r
471 \r
472         WdfObjectAcquireLock(pEndpoint->Queue);\r
473         if (pEndpoint->State == WvEpDestroying) {\r
474                 goto release;\r
475         }\r
476         pEndpoint->State = WvEpDisconnected;\r
477 \r
478         status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
479         while (NT_SUCCESS(status)) {\r
480                 WdfObjectReleaseLock(pEndpoint->Queue);\r
481 \r
482                 WDF_REQUEST_PARAMETERS_INIT(&param);\r
483                 WdfRequestGetParameters(request, &param);\r
484                 if (param.Parameters.DeviceIoControl.IoControlCode == WV_IOCTL_EP_DISCONNECT) {\r
485                         work = WorkEntryFromIrp(WdfRequestWdmGetIrp(request));\r
486                         WdfRequestSetInformation(request, DiscStatus);\r
487                         WorkEntryInit(work, WvEpDisconnectHandler, request);\r
488                         WvProviderGet(pEndpoint->pProvider);\r
489                         WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, work);\r
490                 } else {\r
491                         WdfRequestComplete(request, DiscStatus);\r
492                 }\r
493 \r
494                 WdfObjectAcquireLock(pEndpoint->Queue);\r
495                 status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
496         }\r
497 release:\r
498         WdfObjectReleaseLock(pEndpoint->Queue);\r
499 }\r
500 \r
501 static NTSTATUS WvEpIbCmHandler(iba_cm_id *pId, iba_cm_event *pEvent)\r
502 {\r
503         WV_ENDPOINT     *ep;\r
504 \r
505         ep = pId->context;\r
506         switch (pEvent->type) {\r
507         case iba_cm_req_error:\r
508         case iba_cm_rep_error:\r
509                 WdfObjectAcquireLock(ep->Queue);\r
510                 ep->State = WvEpDisconnected;\r
511                 WvCompleteRequests(ep->Queue, STATUS_IO_TIMEOUT);\r
512                 WdfObjectReleaseLock(ep->Queue);\r
513                 break;\r
514         case iba_cm_dreq_error:\r
515                 WvEpCompleteDisconnect(ep, STATUS_IO_TIMEOUT);\r
516                 break;\r
517         case iba_cm_rep_received:\r
518                 WdfObjectAcquireLock(ep->Queue);\r
519                 if (ep->State == WvEpActiveConnect) {\r
520                         WvEpSaveReply(ep, &pEvent->data.rep);\r
521                         WvCompleteRequests(ep->Queue, STATUS_SUCCESS);\r
522                 }\r
523                 WdfObjectReleaseLock(ep->Queue);\r
524                 break;\r
525         case iba_cm_rtu_received:\r
526                 WdfObjectAcquireLock(ep->Queue);\r
527                 if (ep->State == WvEpPassiveConnect) {\r
528                         ep->State = WvEpConnected;\r
529                         WvCompleteRequestsWithInformation(ep->Queue, STATUS_SUCCESS);\r
530                 }\r
531                 WdfObjectReleaseLock(ep->Queue);\r
532                 break;\r
533         case iba_cm_dreq_received:\r
534                 WdfObjectAcquireLock(ep->Queue);\r
535                 if (ep->State == WvEpConnected) {\r
536                         ep->State = WvEpPassiveDisconnect;\r
537                         WvCompleteRequests(ep->Queue, STATUS_SUCCESS);\r
538                         WdfObjectReleaseLock(ep->Queue);\r
539                 } else {\r
540                         WdfObjectReleaseLock(ep->Queue);\r
541                         WvEpCompleteDisconnect(ep, STATUS_SUCCESS);\r
542                 }\r
543                 break;\r
544         case iba_cm_drep_received:\r
545                 WvEpCompleteDisconnect(ep, STATUS_SUCCESS);\r
546                 break;\r
547         case iba_cm_rej_received:\r
548                 WdfObjectAcquireLock(ep->Queue);\r
549                 if (ep->State == WvEpPassiveConnect || ep->State == WvEpActiveConnect) {\r
550                         ep->State = WvEpDisconnected;\r
551                         WvEpSaveReject(ep, &pEvent->data.rej);\r
552                         WvCompleteRequests(ep->Queue, STATUS_CONNECTION_REFUSED);\r
553                 }\r
554                 WdfObjectReleaseLock(ep->Queue);\r
555                 break;\r
556         case iba_cm_mra_received:\r
557                 break;\r
558         default:\r
559                 WdfObjectAcquireLock(ep->Queue);\r
560                 ep->State = WvEpDisconnected;\r
561                 WvCompleteRequests(ep->Queue, STATUS_NOT_IMPLEMENTED);\r
562                 WdfObjectReleaseLock(ep->Queue);\r
563                 break;\r
564         }\r
565 \r
566         return STATUS_SUCCESS;\r
567 }\r
568 \r
569 void WvEpConnectHandler(WORK_ENTRY *pWork)\r
570 {\r
571         WV_PROVIDER                     *prov;\r
572         WDFREQUEST                      request;\r
573         WV_IO_EP_CONNECT        *pattr;\r
574         WV_ENDPOINT                     *ep;\r
575         WV_QUEUE_PAIR           *qp;\r
576         iba_cm_req                      req;\r
577         NTSTATUS                        status;\r
578         UINT8                           data[IB_REQ_PDATA_SIZE];\r
579 \r
580         request = (WDFREQUEST) pWork->Context;\r
581         prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
582 \r
583         status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_CONNECT),\r
584                                                                                    &pattr, NULL);\r
585         if (!NT_SUCCESS(status)) {\r
586                 goto complete;\r
587         }\r
588 \r
589         if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
590                 status = STATUS_INVALID_BUFFER_SIZE;\r
591                 goto complete;\r
592         }\r
593 \r
594         ep = WvEpAcquire(prov, pattr->Id);\r
595         if (ep == NULL) {\r
596                 status = STATUS_NOT_FOUND;\r
597                 goto complete;\r
598         }\r
599 \r
600         qp = WvQpAcquire(prov, pattr->QpId);\r
601         if (qp == NULL) {\r
602                 status = STATUS_NOT_FOUND;\r
603                 goto release;\r
604         }\r
605 \r
606         ep->Attributes.PeerAddress = pattr->PeerAddress;\r
607         WvFormatCmaHeader((IB_CMA_HEADER *) data, &ep->Attributes.LocalAddress,\r
608                                           &ep->Attributes.PeerAddress);\r
609 \r
610         req.service_id = WvGetServiceId(ep->EpType, &ep->Attributes.PeerAddress);\r
611         req.p_primary_path = &ep->Route;\r
612         req.p_alt_path = NULL;\r
613         req.qpn = qp->Qpn;\r
614         req.qp_type = IB_QPT_RELIABLE_CONN;\r
615         req.starting_psn = (net32_t) RtlRandomEx(&RandomSeed);\r
616         req.p_pdata = data;\r
617         RtlCopyMemory(data + sizeof(IB_CMA_HEADER), pattr->Param.Data,\r
618                                   pattr->Param.DataLength);\r
619         req.pdata_len = sizeof(IB_CMA_HEADER) + pattr->Param.DataLength;\r
620         req.max_cm_retries = IB_CMA_MAX_CM_RETRIES;\r
621         req.resp_res = (UINT8) pattr->Param.ResponderResources;\r
622         req.init_depth = (UINT8) pattr->Param.InitiatorDepth;\r
623         req.remote_resp_timeout = IB_CMA_CM_RESPONSE_TIMEOUT;\r
624         req.flow_ctrl = 1;\r
625         req.local_resp_timeout = IB_CMA_CM_RESPONSE_TIMEOUT;\r
626         req.rnr_retry_cnt = pattr->Param.RnrRetryCount;\r
627         req.retry_cnt = pattr->Param.RetryCount;\r
628         req.srq = (qp->pSrq != NULL);\r
629 \r
630         WvQpRelease(qp);\r
631         RtlCopyMemory(&ep->Attributes.Param.Connect, &pattr->Param,\r
632                                   sizeof(pattr->Param));\r
633 \r
634         WdfObjectAcquireLock(ep->Queue);\r
635         if (ep->State != WvEpRouteResolved) {\r
636                 status = STATUS_NOT_SUPPORTED;\r
637                 goto unlock;\r
638         }\r
639 \r
640         status = IbCmInterface.CM.create_id(WvEpIbCmHandler, ep, &ep->pIbCmId);\r
641         if (!NT_SUCCESS(status)) {\r
642                 goto unlock;\r
643         }\r
644 \r
645         ep->State = WvEpActiveConnect;\r
646         status = IbCmInterface.CM.send_req(ep->pIbCmId, &req);\r
647         if (NT_SUCCESS(status)) {\r
648                 status = WdfRequestForwardToIoQueue(request, ep->Queue);\r
649         }\r
650 \r
651         if (!NT_SUCCESS(status)) {\r
652                 ep->State = WvEpDisconnected;\r
653         }\r
654 unlock:\r
655         WdfObjectReleaseLock(ep->Queue);\r
656 release:\r
657         WvEpRelease(ep);\r
658 complete:\r
659         if (!NT_SUCCESS(status)) {\r
660                 WdfRequestComplete(request, status);\r
661         }\r
662         WvProviderPut(prov);\r
663 }\r
664 \r
665 static void WvEpProcessAsync(WV_PROVIDER *pProvider, WDFREQUEST Request,\r
666                                                          void (*AsyncHandler)(struct _WORK_ENTRY *Work))\r
667 {\r
668         WORK_ENTRY      *work;\r
669 \r
670         work = WorkEntryFromIrp(WdfRequestWdmGetIrp(Request));\r
671         WorkEntryInit(work, AsyncHandler, Request);\r
672         WvProviderGet(pProvider);\r
673         WorkQueueInsert(&pProvider->WorkQueue, work);\r
674 }\r
675 \r
676 void WvEpConnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
677 {\r
678         WvEpProcessAsync(pProvider, Request, WvEpConnectHandler);\r
679 }\r
680 \r
681 static NTSTATUS WvEpModifyQpRtr(WV_ENDPOINT *pEndpoint, WV_QUEUE_PAIR *pQp,\r
682                                                                 UINT64 ResponderResources, UINT32 Psn,\r
683                                                                 UINT8 *pVerbsData, UINT32 VerbsSize)\r
684 {\r
685         ib_qp_mod_t                     attr;\r
686         ib_api_status_t         ib_status;\r
687         NTSTATUS                        status;\r
688 \r
689         status =IbCmInterface.CM.get_qp_attr(pEndpoint->pIbCmId, IB_QPS_INIT, &attr);\r
690         if (!NT_SUCCESS(status)) {\r
691                 return status;\r
692         }\r
693         \r
694         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
695                                                                                    VerbsSize, pVerbsData);\r
696         if (ib_status != IB_SUCCESS) {\r
697                 return STATUS_UNSUCCESSFUL;\r
698         }\r
699 \r
700         status = IbCmInterface.CM.get_qp_attr(pEndpoint->pIbCmId, IB_QPS_RTR, &attr);\r
701         if (!NT_SUCCESS(status)) {\r
702                 return status;\r
703         }\r
704         \r
705         if (pEndpoint->State == WvEpPassiveConnect) {\r
706                 attr.state.rtr.resp_res = (UINT8) ResponderResources;\r
707                 attr.state.rtr.rq_psn = Psn;\r
708         }\r
709 \r
710         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
711                                                                                    VerbsSize, pVerbsData);\r
712         if (ib_status != IB_SUCCESS) {\r
713                 return STATUS_UNSUCCESSFUL;\r
714         }\r
715 \r
716         return STATUS_SUCCESS;\r
717 }\r
718 \r
719 static NTSTATUS WvEpModifyQpRts(WV_ENDPOINT *pEndpoint, WV_QUEUE_PAIR *pQp,\r
720                                                                 UINT64 InitiatorDepth,\r
721                                                                 UINT8 *pVerbsData, UINT32 VerbsSize)\r
722 {\r
723         ib_qp_mod_t                     attr;\r
724         ib_api_status_t         ib_status;\r
725         NTSTATUS                        status;\r
726 \r
727         status = IbCmInterface.CM.get_qp_attr(pEndpoint->pIbCmId, IB_QPS_RTS, &attr);\r
728         if (!NT_SUCCESS(status)) {\r
729                 return status;\r
730         }\r
731         \r
732         if (pEndpoint->State == WvEpPassiveConnect) {\r
733                 attr.state.rts.init_depth = (UINT8) InitiatorDepth;\r
734         }\r
735 \r
736         ib_status = pQp->pVerbs->ndi_modify_qp(pQp->hVerbsQp, &attr, NULL,\r
737                                                                                    VerbsSize, pVerbsData);\r
738         if (ib_status != IB_SUCCESS) {\r
739                 return STATUS_UNSUCCESSFUL;\r
740         }\r
741 \r
742         return STATUS_SUCCESS;\r
743 }\r
744 \r
745 static NTSTATUS WvEpAcceptActive(WDFREQUEST Request, UINT8 *pVerbsData, size_t VerbsSize,\r
746                                                                  WV_ENDPOINT *pEndpoint, WV_IO_EP_ACCEPT *pAttr)\r
747 {\r
748         WV_QUEUE_PAIR           *qp;\r
749         NTSTATUS                        status;\r
750 \r
751         qp = WvQpAcquire(pEndpoint->pProvider, pAttr->QpId);\r
752         if (qp == NULL) {\r
753                 return STATUS_NOT_FOUND;\r
754         }\r
755 \r
756         status = WvEpModifyQpRtr(pEndpoint, qp, 0, 0, pVerbsData, VerbsSize);\r
757         if (NT_SUCCESS(status)) {\r
758                 status = WvEpModifyQpRts(pEndpoint, qp, 0, pVerbsData, VerbsSize);\r
759         }\r
760 \r
761         WvQpRelease(qp);\r
762 \r
763         WdfObjectAcquireLock(pEndpoint->Queue);\r
764         if (pEndpoint->State != WvEpActiveConnect) {\r
765                 status = STATUS_NOT_SUPPORTED;\r
766                 goto release;\r
767         }\r
768 \r
769         pEndpoint->State = WvEpConnected;\r
770         status = IbCmInterface.CM.send_rtu(pEndpoint->pIbCmId, pAttr->Param.Data,\r
771                                                                            pAttr->Param.DataLength);\r
772         if (NT_SUCCESS(status)) {\r
773                 WdfRequestCompleteWithInformation(Request, status, VerbsSize);\r
774         } else {\r
775                 pEndpoint->State = WvEpDisconnected;\r
776         }\r
777 \r
778 release:\r
779         WdfObjectReleaseLock(pEndpoint->Queue);\r
780         return status;\r
781 }\r
782 \r
783 static NTSTATUS WvEpAcceptPassive(WDFREQUEST Request, UINT8 *pVerbsData, size_t VerbsSize,\r
784                                                                   WV_ENDPOINT *pEndpoint, WV_IO_EP_ACCEPT *pAttr)\r
785 {\r
786         WV_QUEUE_PAIR           *qp;\r
787         iba_cm_rep                      rep;\r
788         NTSTATUS                        status;\r
789 \r
790         qp = WvQpAcquire(pEndpoint->pProvider, pAttr->QpId);\r
791         if (qp == NULL) {\r
792                 return STATUS_NOT_FOUND;\r
793         }\r
794 \r
795         rep.qpn = qp->Qpn;\r
796         rep.starting_psn = (net32_t) RtlRandomEx(&RandomSeed);\r
797         rep.p_pdata = pAttr->Param.Data;\r
798         rep.pdata_len = pAttr->Param.DataLength;\r
799         rep.failover_accepted = IB_FAILOVER_ACCEPT_UNSUPPORTED;\r
800         rep.resp_res = (UINT8) pAttr->Param.ResponderResources;\r
801         rep.init_depth = (UINT8) pAttr->Param.InitiatorDepth;\r
802         rep.flow_ctrl = 1;\r
803         rep.rnr_retry_cnt = pAttr->Param.RnrRetryCount;\r
804         rep.srq = (qp->pSrq != NULL);\r
805 \r
806         status = WvEpModifyQpRtr(pEndpoint, qp, pAttr->Param.ResponderResources,\r
807                                                          rep.starting_psn, pVerbsData, VerbsSize);\r
808         if (NT_SUCCESS(status)) {\r
809                 status = WvEpModifyQpRts(pEndpoint, qp, pAttr->Param.InitiatorDepth,\r
810                                                                  pVerbsData, VerbsSize);\r
811         }\r
812 \r
813         WvQpRelease(qp);\r
814 \r
815         if (!NT_SUCCESS(status)) {\r
816                 goto out;\r
817         }\r
818 \r
819         WdfObjectAcquireLock(pEndpoint->Queue);\r
820         if (pEndpoint->State != WvEpPassiveConnect) {\r
821                 status = STATUS_NOT_SUPPORTED;\r
822                 goto release;\r
823         }\r
824 \r
825         status = IbCmInterface.CM.send_rep(pEndpoint->pIbCmId, &rep);\r
826         if (NT_SUCCESS(status)) {\r
827                 status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
828         }\r
829         \r
830         if (!NT_SUCCESS(status)) {\r
831                 pEndpoint->State = WvEpDisconnected;\r
832         }\r
833 \r
834 release:\r
835         WdfObjectReleaseLock(pEndpoint->Queue);\r
836 out:\r
837         return status;\r
838 }\r
839 \r
840 void WvEpAcceptHandler(WORK_ENTRY *pWork)\r
841 {\r
842         WV_PROVIDER                     *prov;\r
843         WDFREQUEST                      request;\r
844         WV_IO_EP_ACCEPT         *pattr;\r
845         WV_ENDPOINT                     *ep;\r
846         NTSTATUS                        status;\r
847         UINT8                           *out;\r
848         size_t                          outlen;\r
849 \r
850         request = (WDFREQUEST) pWork->Context;\r
851         prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
852 \r
853         status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_ACCEPT),\r
854                                                                                    &pattr, NULL);\r
855         if (!NT_SUCCESS(status)) {\r
856                 goto complete;\r
857         }\r
858 \r
859         status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
860         if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
861                 goto complete;\r
862         }\r
863 \r
864         if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
865                 status = STATUS_INVALID_BUFFER_SIZE;\r
866                 goto complete;\r
867         }\r
868 \r
869         ep = WvEpAcquire(prov, pattr->Id);\r
870         if (ep == NULL) {\r
871                 status = STATUS_NOT_FOUND;\r
872                 goto complete;\r
873         }\r
874 \r
875         /* EP state is re-checked under lock in WvEpAccept* calls */\r
876         switch (ep->State) {\r
877         case WvEpActiveConnect:\r
878                 status = WvEpAcceptActive(request, out, outlen, ep, pattr);\r
879                 break;\r
880         case WvEpPassiveConnect:\r
881                 status = WvEpAcceptPassive(request, out, outlen, ep, pattr);\r
882                 break;\r
883         default:\r
884                 status = STATUS_NOT_SUPPORTED;\r
885                 break;\r
886         }\r
887 \r
888         WvEpRelease(ep);\r
889 complete:\r
890         if (!NT_SUCCESS(status)) {\r
891                 WdfRequestComplete(request, status);\r
892         }\r
893         WvProviderPut(prov);\r
894 }\r
895 \r
896 void WvEpAccept(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
897 {\r
898         WvEpProcessAsync(pProvider, Request, WvEpAcceptHandler);\r
899 }\r
900 \r
901 void WvEpReject(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
902 {\r
903         WV_IO_ID                        *id;\r
904         WV_ENDPOINT                     *ep;\r
905         NTSTATUS                        status;\r
906         size_t                          len;\r
907 \r
908         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_ID), &id, &len);\r
909         if (!NT_SUCCESS(status)) {\r
910                 goto complete;\r
911         }\r
912 \r
913         ep = WvEpAcquire(pProvider, id->Id);\r
914         if (ep == NULL) {\r
915                 status = STATUS_NOT_FOUND;\r
916                 goto complete;\r
917         }\r
918 \r
919         WdfObjectAcquireLock(ep->Queue);\r
920         if (ep->State != WvEpActiveConnect && ep->State != WvEpPassiveConnect) {\r
921                 status = STATUS_NOT_SUPPORTED;\r
922                 goto release;\r
923         }\r
924 \r
925         ep->State = WvEpDisconnected;\r
926         status = IbCmInterface.CM.send_rej(ep->pIbCmId, IB_REJ_USER_DEFINED,\r
927                                                                            NULL, 0, id + 1, len - sizeof(WV_IO_ID));\r
928 \r
929 release:\r
930         WdfObjectReleaseLock(ep->Queue);\r
931         WvEpRelease(ep);\r
932 complete:\r
933         WdfRequestComplete(Request, status);\r
934 }\r
935 \r
936 static NTSTATUS WvEpDisconnectActive(WDFREQUEST Request,\r
937                                                                          UINT8 *pVerbsData, size_t VerbsSize,\r
938                                                                          WV_ENDPOINT *pEndpoint,\r
939                                                                          WV_IO_EP_DISCONNECT *pAttr)\r
940 {\r
941         NTSTATUS status, failure;\r
942 \r
943         WdfObjectAcquireLock(pEndpoint->Queue);\r
944         if (pEndpoint->State != WvEpConnected) {\r
945                 status = STATUS_NOT_SUPPORTED;\r
946                 goto release;\r
947         }\r
948 \r
949         pEndpoint->State = WvEpActiveDisconnect;\r
950         IbCmInterface.CM.send_dreq(pEndpoint->pIbCmId, NULL, 0);\r
951 \r
952         status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
953         if (!NT_SUCCESS(status)) {\r
954                 pEndpoint->State = WvEpDisconnected;\r
955                 WvCompleteRequests(pEndpoint->Queue, STATUS_UNSUCCESSFUL);\r
956                 WdfObjectReleaseLock(pEndpoint->Queue);\r
957 \r
958                 failure = status;\r
959                 status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
960                                                                   pVerbsData, VerbsSize);\r
961                 if (NT_SUCCESS(status)) {\r
962                         WdfRequestCompleteWithInformation(Request, failure, VerbsSize);\r
963                 }\r
964                 return status;\r
965         }\r
966 \r
967 release:\r
968         WdfObjectReleaseLock(pEndpoint->Queue);\r
969         return status;\r
970 }\r
971 \r
972 static NTSTATUS WvEpDisconnectPassive(WDFREQUEST Request,\r
973                                                                           UINT8 *pVerbsData, size_t VerbsSize,\r
974                                                                           WV_ENDPOINT *pEndpoint,\r
975                                                                           WV_IO_EP_DISCONNECT *pAttr)\r
976 {\r
977         NTSTATUS status;\r
978 \r
979         WdfObjectAcquireLock(pEndpoint->Queue);\r
980         if (pEndpoint->State != WvEpPassiveDisconnect) {\r
981                 WdfObjectReleaseLock(pEndpoint->Queue);\r
982                 return STATUS_NOT_SUPPORTED;\r
983         }\r
984 \r
985         pEndpoint->State = WvEpDisconnected;\r
986         WdfObjectReleaseLock(pEndpoint->Queue);\r
987 \r
988         IbCmInterface.CM.send_drep(pEndpoint->pIbCmId, NULL, 0);\r
989 \r
990         status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
991                                                           pVerbsData, VerbsSize);\r
992         if (NT_SUCCESS(status)) {\r
993                 WdfRequestCompleteWithInformation(Request, status, VerbsSize);\r
994         }\r
995 \r
996         return status;\r
997 }\r
998 \r
999 void WvEpDisconnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1000 {\r
1001         WV_IO_EP_DISCONNECT     *pattr;\r
1002         WV_ENDPOINT                     *ep;\r
1003         NTSTATUS                        status;\r
1004         UINT8                           *out;\r
1005         size_t                          outlen;\r
1006 \r
1007         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_DISCONNECT),\r
1008                                                                                    &pattr, NULL);\r
1009         if (!NT_SUCCESS(status)) {\r
1010                 goto complete;\r
1011         }\r
1012 \r
1013         status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
1014         if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
1015                 goto complete;\r
1016         }\r
1017 \r
1018         ep = WvEpAcquire(pProvider, pattr->Id);\r
1019         if (ep == NULL) {\r
1020                 status = STATUS_NOT_FOUND;\r
1021                 goto complete;\r
1022         }\r
1023 \r
1024         /* EP state is re-checked under lock in WvEpDisconnect* calls */\r
1025         switch (ep->State) {\r
1026         case WvEpConnected:\r
1027                 status = WvEpDisconnectActive(Request, out, outlen, ep, pattr);\r
1028                 break;\r
1029         case WvEpPassiveDisconnect:\r
1030                 status = WvEpDisconnectPassive(Request, out, outlen, ep, pattr);\r
1031                 break;\r
1032         default:\r
1033                 status = STATUS_NOT_SUPPORTED;\r
1034                 break;\r
1035         }\r
1036 \r
1037         WvEpRelease(ep);\r
1038 complete:\r
1039         if (!NT_SUCCESS(status)) {\r
1040                 WdfRequestComplete(Request, status);\r
1041         }\r
1042 }\r
1043 \r
1044 void WvEpDisconnectNotify(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1045 {\r
1046         UINT64                          *id;\r
1047         WV_ENDPOINT                     *ep;\r
1048         NTSTATUS                        status;\r
1049 \r
1050         status = WdfRequestRetrieveInputBuffer(Request, sizeof(UINT64), &id, NULL);\r
1051         if (!NT_SUCCESS(status)) {\r
1052                 goto complete;\r
1053         }\r
1054 \r
1055         ep = WvEpAcquire(pProvider, *id);\r
1056         if (ep == NULL) {\r
1057                 status = STATUS_NOT_FOUND;\r
1058                 goto complete;\r
1059         }\r
1060 \r
1061         WdfObjectAcquireLock(ep->Queue);\r
1062         switch (ep->State) {\r
1063         case WvEpConnected:\r
1064         case WvEpActiveDisconnect:\r
1065                 status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
1066                 if (NT_SUCCESS(status)) {\r
1067                         WdfObjectReleaseLock(ep->Queue);\r
1068                         WvEpRelease(ep);\r
1069                         return;\r
1070                 }\r
1071                 break;\r
1072         case WvEpPassiveDisconnect:\r
1073         case WvEpDisconnected:\r
1074                 status = STATUS_SUCCESS;\r
1075                 break;\r
1076         default:\r
1077                 status = STATUS_NOT_SUPPORTED;\r
1078                 break;\r
1079         }\r
1080         WdfObjectReleaseLock(ep->Queue);\r
1081 \r
1082         WvEpRelease(ep);\r
1083 complete:\r
1084         WdfRequestComplete(Request, status);\r
1085 }\r
1086 \r
1087 static NTSTATUS WvEpIbListenHandler(iba_cm_id *pId, iba_cm_event *pEvent)\r
1088 {\r
1089         WV_ENDPOINT             *listen, *ep;\r
1090         WDFREQUEST              request;\r
1091         NTSTATUS                status;\r
1092         IB_CMA_HEADER   *hdr;\r
1093 \r
1094         listen = ((iba_cm_id *) pId->context)->context;\r
1095 \r
1096         WdfObjectAcquireLock(listen->Queue);\r
1097         status = WdfIoQueueRetrieveNextRequest(listen->Queue, &request);\r
1098         if (!NT_SUCCESS(status)) {\r
1099                 goto release;\r
1100         }\r
1101 \r
1102         ASSERT(!IsListEmpty(&listen->Entry));\r
1103         ep = CONTAINING_RECORD(RemoveHeadList(&listen->Entry), WV_ENDPOINT, Entry);\r
1104         ep->pIbCmId = pId;\r
1105         pId->callback = WvEpIbCmHandler;\r
1106         pId->context = ep;\r
1107 \r
1108         hdr = pEvent->data.req.req.p_pdata;\r
1109         if ((hdr->IpVersion >> 4) == 4) {\r
1110                 ep->Attributes.LocalAddress.SockAddr.In.SinFamily = WV_AF_INET;\r
1111                 ep->Attributes.LocalAddress.SockAddr.In.SinAddr = hdr->DstAddress.Ip4.Address;\r
1112                 ep->Attributes.PeerAddress.SockAddr.In.SinFamily = WV_AF_INET;\r
1113                 ep->Attributes.PeerAddress.SockAddr.In.SinAddr = hdr->SrcAddress.Ip4.Address;\r
1114         } else {\r
1115                 ep->Attributes.LocalAddress.SockAddr.In6.Sin6Family = WV_AF_INET6; \r
1116                 RtlCopyMemory(ep->Attributes.LocalAddress.SockAddr.In6.Sin6Addr,\r
1117                                           hdr->DstAddress.Ip6Address, 16);\r
1118                 ep->Attributes.PeerAddress.SockAddr.In6.Sin6Family = WV_AF_INET6;\r
1119                 RtlCopyMemory(ep->Attributes.PeerAddress.SockAddr.In6.Sin6Addr,\r
1120                                           hdr->SrcAddress.Ip6Address, 16);\r
1121         }\r
1122         ep->Attributes.Device.DeviceGuid = pEvent->data.req.local_ca_guid;\r
1123         ep->Attributes.Device.Pkey = pEvent->data.req.req.p_primary_path->pkey;\r
1124         ep->Attributes.Device.PortNumber = pEvent->data.req.port_num;\r
1125         ep->Attributes.Param.Connect.ResponderResources = pEvent->data.req.req.resp_res;\r
1126         ep->Attributes.Param.Connect.InitiatorDepth = pEvent->data.req.req.init_depth;\r
1127         ep->Attributes.Param.Connect.RetryCount = pEvent->data.req.req.retry_cnt;\r
1128         ep->Attributes.Param.Connect.RnrRetryCount = pEvent->data.req.req.rnr_retry_cnt;\r
1129         ep->Attributes.Param.Connect.DataLength = sizeof(ep->Attributes.Param.Connect.Data);\r
1130         RtlCopyMemory(ep->Attributes.Param.Connect.Data, hdr + 1,\r
1131                                   sizeof(ep->Attributes.Param.Connect.Data));\r
1132         ep->Route = *pEvent->data.req.req.p_primary_path;\r
1133 \r
1134         ep->State = WvEpPassiveConnect;\r
1135         WvEpPut(ep);\r
1136 \r
1137         WdfRequestComplete(request, STATUS_SUCCESS);\r
1138 release:\r
1139         WdfObjectReleaseLock(listen->Queue);\r
1140         return status;\r
1141 }\r
1142 \r
1143 void WvEpListen(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1144 {\r
1145         WV_ENDPOINT                     *ep;\r
1146         WV_IO_EP_LISTEN         *pattr;\r
1147         NTSTATUS                        status;\r
1148         void                            *buf;\r
1149         UINT8                           offset, len;\r
1150         UINT64                          sid;\r
1151 \r
1152         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_LISTEN),\r
1153                                                                                    &pattr, NULL);\r
1154         if (!NT_SUCCESS(status)) {\r
1155                 goto complete;\r
1156         }\r
1157 \r
1158         ep = WvEpAcquire(pProvider, pattr->Id);\r
1159         if (ep == NULL) {\r
1160                 status = STATUS_NOT_FOUND;\r
1161                 goto complete;\r
1162         }\r
1163 \r
1164         if (WvAnyAddress(&ep->Attributes.LocalAddress)) {\r
1165                 buf = NULL;\r
1166                 offset = 0;\r
1167                 len = 0;\r
1168         } else {\r
1169                 if (ep->Attributes.LocalAddress.SockAddr.Sa.SaFamily == WV_AF_INET) {\r
1170                         buf = &ep->Attributes.LocalAddress.SockAddr.In.SinAddr;\r
1171                         len = sizeof ep->Attributes.LocalAddress.SockAddr.In.SinAddr;\r
1172                         offset = FIELD_OFFSET(IB_CMA_HEADER, DstAddress.Ip4.Address);\r
1173                 } else {\r
1174                         buf = ep->Attributes.LocalAddress.SockAddr.In6.Sin6Addr;\r
1175                         len = sizeof ep->Attributes.LocalAddress.SockAddr.In6.Sin6Addr;\r
1176                         offset = FIELD_OFFSET(IB_CMA_HEADER, DstAddress.Ip6Address);\r
1177                 }\r
1178         }\r
1179 \r
1180         WdfObjectAcquireLock(ep->Queue);\r
1181         if (ep->State != WvEpAddressBound) {\r
1182                 status = STATUS_NOT_SUPPORTED;\r
1183                 goto release;\r
1184         }\r
1185 \r
1186         status = IbCmInterface.CM.create_id(WvEpIbListenHandler, ep, &ep->pIbCmId);\r
1187         if (!NT_SUCCESS(status)) {\r
1188                 goto release;\r
1189         }\r
1190 \r
1191         ep->Attributes.Param.Backlog = pattr->Backlog;\r
1192         ep->State = WvEpListening;\r
1193         sid = WvGetServiceId(ep->EpType, &ep->Attributes.LocalAddress);\r
1194         status = IbCmInterface.CM.listen(ep->pIbCmId, sid, buf, len, offset);\r
1195 \r
1196 release:\r
1197         WdfObjectReleaseLock(ep->Queue);\r
1198         WvEpRelease(ep);\r
1199 complete:\r
1200         WdfRequestComplete(Request, status);\r
1201 }\r
1202 \r
1203 void WvEpGetRequest(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1204 {\r
1205         WV_ENDPOINT                             *listen, *ep;\r
1206         WV_IO_EP_GET_REQUEST    *req;\r
1207         NTSTATUS                                status;\r
1208 \r
1209         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_GET_REQUEST),\r
1210                                                                                    &req, NULL);\r
1211         if (!NT_SUCCESS(status)) {\r
1212                 goto complete;\r
1213         }\r
1214 \r
1215         listen = WvEpAcquire(pProvider, req->Id);\r
1216         if (listen == NULL) {\r
1217                 status = STATUS_NOT_FOUND;\r
1218                 goto complete;\r
1219         }\r
1220 \r
1221         if (listen->State != WvEpListening) {\r
1222                 status = STATUS_NOT_SUPPORTED;\r
1223                 goto release1;\r
1224         }\r
1225 \r
1226         ep = WvEpAcquire(pProvider, req->EpId);\r
1227         if (ep == NULL) {\r
1228                 status = STATUS_NOT_FOUND;\r
1229                 goto release1;\r
1230         }\r
1231 \r
1232         WdfObjectAcquireLock(ep->Queue);\r
1233         if (ep->State == WvEpIdle) {\r
1234                 ep->State = WvEpQueued;\r
1235         } else {\r
1236                 status = STATUS_CONNECTION_IN_USE;\r
1237         }\r
1238         WdfObjectReleaseLock(ep->Queue);\r
1239 \r
1240         if (!NT_SUCCESS(status)) {\r
1241                 goto release2;\r
1242         }\r
1243 \r
1244         WdfObjectAcquireLock(listen->Queue);\r
1245         status = WdfRequestForwardToIoQueue(Request, listen->Queue);\r
1246         if (NT_SUCCESS(status)) {\r
1247                 InsertTailList(&listen->Entry, &ep->Entry);\r
1248                 WvEpGet(ep);\r
1249         }\r
1250         WdfObjectReleaseLock(listen->Queue);\r
1251 \r
1252 release2:\r
1253         WvEpRelease(ep);\r
1254 release1:\r
1255         WvEpRelease(listen);\r
1256 complete:\r
1257         if (!NT_SUCCESS(status)) {\r
1258                 WdfRequestComplete(Request, status);\r
1259         }\r
1260 }\r
1261 \r
1262 void WvEpLookup(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1263 {\r
1264         UNUSED_PARAM(pProvider);\r
1265         WdfRequestComplete(Request, STATUS_NOT_IMPLEMENTED);\r
1266 }\r
1267 \r
1268 void WvEpMulticastJoin(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1269 {\r
1270         UNUSED_PARAM(pProvider);\r
1271         WdfRequestComplete(Request, STATUS_NOT_IMPLEMENTED);\r
1272 }\r
1273 \r
1274 void WvEpMulticastLeave(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1275 {\r
1276         UNUSED_PARAM(pProvider);\r
1277         WdfRequestComplete(Request, STATUS_NOT_IMPLEMENTED);\r
1278 }\r
1279 \r
1280 //\r
1281 // Note that the framework may have already canceled outstanding requests.\r
1282 //\r
1283 void WvEpCancelListen(WV_ENDPOINT *pListen)\r
1284 {\r
1285         WV_ENDPOINT                     *ep;\r
1286 \r
1287         WdfObjectAcquireLock(pListen->Queue);\r
1288         WvCompleteRequests(pListen->Queue, STATUS_CANCELLED);\r
1289 \r
1290         while (!IsListEmpty(&pListen->Entry)) {\r
1291                 ep = CONTAINING_RECORD(RemoveHeadList(&pListen->Entry), WV_ENDPOINT, Entry);\r
1292                 ep->State = WvEpIdle;\r
1293                 WvEpPut(ep);\r
1294         }\r
1295         WdfObjectReleaseLock(pListen->Queue);\r
1296 }\r
1297 \r
1298 void WvEpCancel(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
1299 {\r
1300         UINT64                          *id;\r
1301         WV_ENDPOINT                     *ep;\r
1302         NTSTATUS                        status;\r
1303 \r
1304         status = WdfRequestRetrieveInputBuffer(Request, sizeof(UINT64), &id, NULL);\r
1305         if (!NT_SUCCESS(status)) {\r
1306                 goto out;\r
1307         }\r
1308 \r
1309         ep = WvEpAcquire(pProvider, *id);\r
1310         if (ep == NULL) {\r
1311                 status = STATUS_NOT_FOUND;\r
1312                 goto out;\r
1313         }\r
1314 \r
1315         if (ep->State == WvEpListening) {\r
1316                 WvEpCancelListen(ep);\r
1317         } else {\r
1318                 WvFlushQueue(ep->Queue, STATUS_CANCELLED);\r
1319         }\r
1320         WvEpRelease(ep);\r
1321 \r
1322 out:\r
1323         WdfRequestComplete(Request, status);\r
1324 }\r