Version: 1
-Previous: 5c704a79dcedec434c2aa0697f8cbdb3f4bd066e
+Previous: 1397d161d2dc8fa13f1645c7340b79f3f83398b1
Head: d6d2302f2d258fa2c07e55a2e124a9bdd75367a0
Applied:
rm-build: d6d2302f2d258fa2c07e55a2e124a9bdd75367a0
Unapplied:
old-bld-32: bd1bd1d366e3927b7646da33e243213a5d59c235
- apphang: 7a6f7ff02c6035e54fc262414eb9484ea98018b9
+ old-apphang: 7a6f7ff02c6035e54fc262414eb9484ea98018b9
cm_send_cb: 70dceed7518c8429e4c9e0646c7ec035f1cf53cb
queue_mads: a9c572c34ae693764a33e87173125d3065a6d1c5
epdisc: 4c931f6af5e37a502f7a10b78a902747f9738623
+++ /dev/null
-Bottom: 6a0d87b4622bc7319d396436b319ad51c6ae3636
-Top: ed7908e04b3bbaa4e2413f01b2efe982eb5349d8
-Author: U-AMR\MSHEFTY <MSHEFTY@mshefty-MOBL2.amr.corp.intel.com>
-Date: 2009-12-09 17:26:01 -0800
-
-KMDF tracks all requests that pass through an IO queue. Even after a
-request has been removed from the queue, the request still maintains a
-reference on the queue. Any attempt to delete the queue will block
-until all requests holding references on the queue have completed.
-
-To avoid deadlock conditions during cleanup, we need to ensure that
-all requests can complete during cleanup. Modify the asynchronous
-handling code to queue a separate data structure, so that all requests
-can remain on the IO queues.
-
-Signed-off-by: Sean Hefty <sean.hefty@intel.com>
-
-
----
-
-diff --git a/trunk/core/winverbs/kernel/wv_ep.c b/trunk/core/winverbs/kernel/wv_ep.c
-index 2fbb5b3..3d5c6ce 100644
---- a/trunk/core/winverbs/kernel/wv_ep.c
-+++ b/trunk/core/winverbs/kernel/wv_ep.c
-@@ -38,6 +38,8 @@
- #define WV_AF_INET 2\r
- #define WV_AF_INET6 23\r
- \r
-+static void WvEpWorkHandler(WORK_ENTRY *pWork);\r
-+\r
- static void WvEpGet(WV_ENDPOINT *pEndpoint)\r
- {\r
- InterlockedIncrement(&pEndpoint->Ref);\r
-@@ -87,6 +89,12 @@ static NTSTATUS WvEpAllocate(WV_PROVIDER *pProvider, UINT16 EpType,
- }\r
- \r
- RtlZeroMemory(ep, sizeof(WV_ENDPOINT));\r
-+ ep->pWork = ExAllocatePoolWithTag(NonPagedPool, sizeof(WV_WORK_ENTRY), 'wevw');\r
-+ if (ep->pWork == NULL) {\r
-+ status = STATUS_NO_MEMORY;\r
-+ goto err1;\r
-+ }\r
-+\r
- ep->Ref = 1;\r
- ep->pProvider = pProvider;\r
- ep->EpType = EpType;\r
-@@ -97,13 +105,15 @@ static NTSTATUS WvEpAllocate(WV_PROVIDER *pProvider, UINT16 EpType,
- status = WdfIoQueueCreate(ControlDevice, &config,\r
- WDF_NO_OBJECT_ATTRIBUTES, &ep->Queue);\r
- if (!NT_SUCCESS(status)) {\r
-- goto err;\r
-+ goto err2;\r
- }\r
- \r
- *ppEndpoint = ep;\r
- return STATUS_SUCCESS;\r
- \r
--err:\r
-+err2:\r
-+ ExFreePoolWithTag(ep->pWork, 'wevw');\r
-+err1:\r
- ExFreePoolWithTag(ep, 'pevw');\r
- return status;\r
- }\r
-@@ -137,6 +147,7 @@ void WvEpCreate(WV_PROVIDER *pProvider, WDFREQUEST Request)
- }\r
- KeReleaseGuardedMutex(&pProvider->Lock);\r
- \r
-+ WvWorkEntryInit(ep->pWork, *pId, WvEpWorkHandler, pProvider);\r
- WdfRequestCompleteWithInformation(Request, status, sizeof(UINT64));\r
- return;\r
- \r
-@@ -195,6 +206,9 @@ void WvEpFree(WV_ENDPOINT *pEndpoint)
- \r
- WdfIoQueuePurgeSynchronously(pEndpoint->Queue);\r
- WdfObjectDelete(pEndpoint->Queue);\r
-+ if (pEndpoint->pWork != NULL) {\r
-+ ExFreePoolWithTag(pEndpoint->pWork, 'wevw');\r
-+ }\r
- ExFreePoolWithTag(pEndpoint, 'pevw');\r
- }\r
- \r
-@@ -413,77 +427,72 @@ static NTSTATUS WvEpDisconnectQp(WV_PROVIDER *pProvider, UINT64 QpId,
- return status;\r
- }\r
- \r
--static void WvEpDisconnectHandler(WORK_ENTRY *pWork)\r
-+static NTSTATUS WvEpAsyncDisconnect(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
- {\r
-- WV_PROVIDER *prov;\r
-- WDFREQUEST request;\r
- WV_IO_EP_DISCONNECT *pattr;\r
- UINT8 *out;\r
- size_t outlen = 0;\r
- NTSTATUS status;\r
- \r
-- request = (WDFREQUEST) pWork->Context;\r
-- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
--\r
-- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_DISCONNECT),\r
-+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_DISCONNECT),\r
- &pattr, NULL);\r
- if (!NT_SUCCESS(status)) {\r
-- goto complete;\r
-+ return status;\r
- }\r
- \r
-- status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
-+ status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
- if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
-- goto complete;\r
-+ return status;\r
- }\r
- \r
-- status = (NTSTATUS) WdfRequestGetInformation(request);\r
-+ status = (NTSTATUS) WdfRequestGetInformation(Request);\r
- if (NT_SUCCESS(status)) {\r
-- status = WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
-+ status = WvEpDisconnectQp(pEndpoint->pProvider, pattr->QpId, out, outlen);\r
- } else {\r
-- WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
-+ WvEpDisconnectQp(pEndpoint->pProvider, pattr->QpId, out, outlen);\r
- }\r
- \r
--complete:\r
-- WdfRequestCompleteWithInformation(request, status, outlen);\r
-- WvProviderPut(prov);\r
-+ WdfRequestCompleteWithInformation(Request, status, outlen);\r
-+ return STATUS_SUCCESS;\r
- }\r
- \r
--// We use IRP DriverContext to queue the request for further processing,\r
--// but the request/IRP are no longer owned by the framework.\r
- static void WvEpCompleteDisconnect(WV_ENDPOINT *pEndpoint, NTSTATUS DiscStatus)\r
- {\r
- WDFREQUEST request;\r
-+ WDFREQUEST disc_req = NULL;\r
- WDF_REQUEST_PARAMETERS param;\r
-- WORK_ENTRY *work;\r
- NTSTATUS status;\r
- \r
- WdfObjectAcquireLock(pEndpoint->Queue);\r
-- if (pEndpoint->State == WvEpDestroying) {\r
-+ if (pEndpoint->State == WvEpDestroying || !pEndpoint->pWork) {\r
- goto release;\r
- }\r
- pEndpoint->State = WvEpDisconnected;\r
- \r
- status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
- while (NT_SUCCESS(status)) {\r
-- WdfObjectReleaseLock(pEndpoint->Queue);\r
- \r
- WDF_REQUEST_PARAMETERS_INIT(¶m);\r
- WdfRequestGetParameters(request, ¶m);\r
- if (param.Parameters.DeviceIoControl.IoControlCode == WV_IOCTL_EP_DISCONNECT) {\r
-- work = WorkEntryFromIrp(WdfRequestWdmGetIrp(request));\r
- WdfRequestSetInformation(request, DiscStatus);\r
-- WorkEntryInit(work, WvEpDisconnectHandler, request);\r
- WvProviderGet(pEndpoint->pProvider);\r
-- WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, work);\r
-+ WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, &pEndpoint->pWork->Work);\r
-+ pEndpoint->pWork = NULL;\r
-+ disc_req = request;\r
- } else {\r
- WdfRequestComplete(request, DiscStatus);\r
- }\r
- \r
-- WdfObjectAcquireLock(pEndpoint->Queue);\r
- status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
- }\r
-+\r
-+ if (disc_req != NULL) {\r
-+ WdfRequestRequeue(disc_req);\r
-+ }\r
- release:\r
- WdfObjectReleaseLock(pEndpoint->Queue);\r
-+\r
- }\r
- \r
- static NTSTATUS WvEpIbCmHandler(iba_cm_id *pId, iba_cm_event *pEvent)\r
-@@ -565,49 +574,31 @@ static NTSTATUS WvEpIbCmHandler(iba_cm_id *pId, iba_cm_event *pEvent)
- return STATUS_SUCCESS;\r
- }\r
- \r
--void WvEpConnectHandler(WORK_ENTRY *pWork)\r
-+static NTSTATUS WvEpAsyncConnect(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
- {\r
-- WV_PROVIDER *prov;\r
-- WDFREQUEST request;\r
- WV_IO_EP_CONNECT *pattr;\r
-- WV_ENDPOINT *ep;\r
- WV_QUEUE_PAIR *qp;\r
- iba_cm_req req;\r
- NTSTATUS status;\r
- UINT8 data[IB_REQ_PDATA_SIZE];\r
- \r
-- request = (WDFREQUEST) pWork->Context;\r
-- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
--\r
-- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_CONNECT),\r
-+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_CONNECT),\r
- &pattr, NULL);\r
- if (!NT_SUCCESS(status)) {\r
-- goto complete;\r
-- }\r
--\r
-- if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
-- status = STATUS_INVALID_BUFFER_SIZE;\r
-- goto complete;\r
-- }\r
--\r
-- ep = WvEpAcquire(prov, pattr->Id);\r
-- if (ep == NULL) {\r
-- status = STATUS_NOT_FOUND;\r
-- goto complete;\r
-+ return status;\r
- }\r
- \r
-- qp = WvQpAcquire(prov, pattr->QpId);\r
-+ qp = WvQpAcquire(pEndpoint->pProvider, pattr->QpId);\r
- if (qp == NULL) {\r
-- status = STATUS_NOT_FOUND;\r
-- goto release;\r
-+ return STATUS_NOT_FOUND;\r
- }\r
- \r
-- ep->Attributes.PeerAddress = pattr->PeerAddress;\r
-- WvFormatCmaHeader((IB_CMA_HEADER *) data, &ep->Attributes.LocalAddress,\r
-- &ep->Attributes.PeerAddress);\r
-+ pEndpoint->Attributes.PeerAddress = pattr->PeerAddress;\r
-+ WvFormatCmaHeader((IB_CMA_HEADER *) data, &pEndpoint->Attributes.LocalAddress,\r
-+ &pEndpoint->Attributes.PeerAddress);\r
- \r
-- req.service_id = WvGetServiceId(ep->EpType, &ep->Attributes.PeerAddress);\r
-- req.p_primary_path = &ep->Route;\r
-+ req.service_id = WvGetServiceId(pEndpoint->EpType, &pEndpoint->Attributes.PeerAddress);\r
-+ req.p_primary_path = &pEndpoint->Route;\r
- req.p_alt_path = NULL;\r
- req.qpn = qp->Qpn;\r
- req.qp_type = IB_QPT_RELIABLE_CONN;\r
-@@ -627,54 +618,86 @@ void WvEpConnectHandler(WORK_ENTRY *pWork)
- req.srq = (qp->pSrq != NULL);\r
- \r
- WvQpRelease(qp);\r
-- RtlCopyMemory(&ep->Attributes.Param.Connect, &pattr->Param,\r
-+ RtlCopyMemory(&pEndpoint->Attributes.Param.Connect, &pattr->Param,\r
- sizeof(pattr->Param));\r
- \r
-- WdfObjectAcquireLock(ep->Queue);\r
-- if (ep->State != WvEpRouteResolved) {\r
-+ WdfObjectAcquireLock(pEndpoint->Queue);\r
-+ if (pEndpoint->State != WvEpRouteResolved) {\r
- status = STATUS_NOT_SUPPORTED;\r
-- goto unlock;\r
-+ goto out;\r
- }\r
- \r
-- status = IbCmInterface.CM.create_id(WvEpIbCmHandler, ep, &ep->pIbCmId);\r
-+ status = IbCmInterface.CM.create_id(WvEpIbCmHandler, pEndpoint, &pEndpoint->pIbCmId);\r
- if (!NT_SUCCESS(status)) {\r
-- goto unlock;\r
-+ goto out;\r
- }\r
- \r
-- ep->State = WvEpActiveConnect;\r
-- status = IbCmInterface.CM.send_req(ep->pIbCmId, &req);\r
-+ pEndpoint->State = WvEpActiveConnect;\r
-+ status = IbCmInterface.CM.send_req(pEndpoint->pIbCmId, &req);\r
- if (NT_SUCCESS(status)) {\r
-- status = WdfRequestForwardToIoQueue(request, ep->Queue);\r
-+ status = WdfRequestRequeue(Request);\r
- }\r
- \r
- if (!NT_SUCCESS(status)) {\r
-- ep->State = WvEpDisconnected;\r
-- }\r
--unlock:\r
-- WdfObjectReleaseLock(ep->Queue);\r
--release:\r
-- WvEpRelease(ep);\r
--complete:\r
-- if (!NT_SUCCESS(status)) {\r
-- WdfRequestComplete(request, status);\r
-+ pEndpoint->State = WvEpDisconnected;\r
- }\r
-- WvProviderPut(prov);\r
-+\r
-+out:\r
-+ WdfObjectReleaseLock(pEndpoint->Queue);\r
-+ return status;\r
- }\r
- \r
--static void WvEpProcessAsync(WV_PROVIDER *pProvider, WDFREQUEST Request,\r
-- void (*AsyncHandler)(struct _WORK_ENTRY *Work))\r
-+static NTSTATUS WvEpProcessAsync(WV_PROVIDER *pProvider, UINT64 Id, WDFREQUEST Request)\r
- {\r
-- WORK_ENTRY *work;\r
-+ WV_ENDPOINT *ep;\r
-+ NTSTATUS status;\r
-+\r
-+ ep = WvEpAcquire(pProvider, Id);\r
-+ if (ep == NULL) {\r
-+ return STATUS_NOT_FOUND;\r
-+ }\r
-+\r
-+ WdfObjectAcquireLock(ep->Queue);\r
-+ if (!ep->pWork) {\r
-+ status = STATUS_TOO_MANY_COMMANDS;\r
-+ goto out;\r
-+ }\r
- \r
-- work = WorkEntryFromIrp(WdfRequestWdmGetIrp(Request));\r
-- WorkEntryInit(work, AsyncHandler, Request);\r
-- WvProviderGet(pProvider);\r
-- WorkQueueInsert(&pProvider->WorkQueue, work);\r
-+ status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
-+ if (NT_SUCCESS(status)) {\r
-+ WvProviderGet(pProvider);\r
-+ WorkQueueInsert(&pProvider->WorkQueue, &ep->pWork->Work);\r
-+ ep->pWork = NULL;\r
-+ }\r
-+\r
-+out:\r
-+ WdfObjectReleaseLock(ep->Queue);\r
-+ WvEpRelease(ep);\r
-+ return status;\r
- }\r
- \r
- void WvEpConnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
- {\r
-- WvEpProcessAsync(pProvider, Request, WvEpConnectHandler);\r
-+ WV_IO_EP_CONNECT *pattr;\r
-+ NTSTATUS status;\r
-+\r
-+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_CONNECT),\r
-+ &pattr, NULL);\r
-+ if (!NT_SUCCESS(status)) {\r
-+ goto out;\r
-+ }\r
-+\r
-+ if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
-+ status = STATUS_INVALID_BUFFER_SIZE;\r
-+ goto out;\r
-+ }\r
-+\r
-+ status = WvEpProcessAsync(pProvider, pattr->Id, Request);\r
-+\r
-+out:\r
-+ if (!NT_SUCCESS(status)) {\r
-+ WdfRequestComplete(Request, status);\r
-+ }\r
- }\r
- \r
- static NTSTATUS WvEpModifyQpRtr(WV_ENDPOINT *pEndpoint, WV_QUEUE_PAIR *pQp,\r
-@@ -823,7 +846,7 @@ static NTSTATUS WvEpAcceptPassive(WDFREQUEST Request, UINT8 *pVerbsData, size_t
- \r
- status = IbCmInterface.CM.send_rep(pEndpoint->pIbCmId, &rep);\r
- if (NT_SUCCESS(status)) {\r
-- status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
-+ status = WdfRequestRequeue(Request);\r
- }\r
- \r
- if (!NT_SUCCESS(status)) {\r
-@@ -836,65 +859,114 @@ out:
- return status;\r
- }\r
- \r
--void WvEpAcceptHandler(WORK_ENTRY *pWork)\r
-+static NTSTATUS WvEpAsyncAccept(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
- {\r
-- WV_PROVIDER *prov;\r
-- WDFREQUEST request;\r
- WV_IO_EP_ACCEPT *pattr;\r
-- WV_ENDPOINT *ep;\r
- NTSTATUS status;\r
- UINT8 *out;\r
- size_t outlen;\r
- \r
-- request = (WDFREQUEST) pWork->Context;\r
-- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
--\r
-- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_ACCEPT),\r
-+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_ACCEPT),\r
- &pattr, NULL);\r
- if (!NT_SUCCESS(status)) {\r
-- goto complete;\r
-+ return status;\r
- }\r
- \r
-- status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
-+ status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
- if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
-- goto complete;\r
-- }\r
--\r
-- if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
-- status = STATUS_INVALID_BUFFER_SIZE;\r
-- goto complete;\r
-- }\r
--\r
-- ep = WvEpAcquire(prov, pattr->Id);\r
-- if (ep == NULL) {\r
-- status = STATUS_NOT_FOUND;\r
-- goto complete;\r
-+ return status;\r
- }\r
- \r
- /* EP state is re-checked under lock in WvEpAccept* calls */\r
-- switch (ep->State) {\r
-+ switch (pEndpoint->State) {\r
- case WvEpActiveConnect:\r
-- status = WvEpAcceptActive(request, out, outlen, ep, pattr);\r
-+ status = WvEpAcceptActive(Request, out, outlen, pEndpoint, pattr);\r
- break;\r
- case WvEpPassiveConnect:\r
-- status = WvEpAcceptPassive(request, out, outlen, ep, pattr);\r
-+ status = WvEpAcceptPassive(Request, out, outlen, pEndpoint, pattr);\r
- break;\r
- default:\r
- status = STATUS_NOT_SUPPORTED;\r
- break;\r
- }\r
- \r
-- WvEpRelease(ep);\r
--complete:\r
-+ return status;\r
-+}\r
-+\r
-+static void WvEpWorkHandler(WORK_ENTRY *pWork)\r
-+{\r
-+ WV_PROVIDER *prov;\r
-+ WV_ENDPOINT *ep;\r
-+ WV_WORK_ENTRY *work;\r
-+ WDFREQUEST request;\r
-+ WDF_REQUEST_PARAMETERS param;\r
-+ NTSTATUS status;\r
-+\r
-+ work = CONTAINING_RECORD(pWork, WV_WORK_ENTRY, Work);\r
-+ prov = (WV_PROVIDER *) pWork->Context;\r
-+\r
-+ ep = WvEpAcquire(prov, work->Id);\r
-+ if (ep == NULL) {\r
-+ ExFreePoolWithTag(work, 'wevw');\r
-+ goto out;\r
-+ }\r
-+\r
-+ WdfObjectAcquireLock(ep->Queue);\r
-+ ep->pWork = work;\r
-+ status = WdfIoQueueRetrieveNextRequest(ep->Queue, &request);\r
-+ WdfObjectReleaseLock(ep->Queue);\r
-+\r
-+ if (!NT_SUCCESS(status)) {\r
-+ goto put;\r
-+ }\r
-+\r
-+ WDF_REQUEST_PARAMETERS_INIT(¶m);\r
-+ WdfRequestGetParameters(request, ¶m);\r
-+ switch (param.Parameters.DeviceIoControl.IoControlCode) {\r
-+ case WV_IOCTL_EP_CONNECT:\r
-+ status = WvEpAsyncConnect(ep, request);\r
-+ break;\r
-+ case WV_IOCTL_EP_ACCEPT:\r
-+ status = WvEpAsyncAccept(ep, request);\r
-+ break;\r
-+ case WV_IOCTL_EP_DISCONNECT:\r
-+ status = WvEpAsyncDisconnect(ep, request);\r
-+ break;\r
-+ default:\r
-+ status = STATUS_NOT_IMPLEMENTED;\r
-+ }\r
-+\r
- if (!NT_SUCCESS(status)) {\r
- WdfRequestComplete(request, status);\r
- }\r
-+put:\r
-+ WvEpRelease(ep);\r
-+out:\r
- WvProviderPut(prov);\r
- }\r
- \r
- void WvEpAccept(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
- {\r
-- WvEpProcessAsync(pProvider, Request, WvEpAcceptHandler);\r
-+ WV_IO_EP_ACCEPT *pattr;\r
-+ NTSTATUS status;\r
-+\r
-+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_ACCEPT),\r
-+ &pattr, NULL);\r
-+ if (!NT_SUCCESS(status)) {\r
-+ goto out;\r
-+ }\r
-+\r
-+ if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
-+ status = STATUS_INVALID_BUFFER_SIZE;\r
-+ goto out;\r
-+ }\r
-+\r
-+ status = WvEpProcessAsync(pProvider, pattr->Id, Request);\r
-+\r
-+out:\r
-+ if (!NT_SUCCESS(status)) {\r
-+ WdfRequestComplete(Request, status);\r
-+ }\r
- }\r
- \r
- void WvEpReject(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
-@@ -932,69 +1004,7 @@ complete:
- WdfRequestComplete(Request, status);\r
- }\r
- \r
--static NTSTATUS WvEpDisconnectActive(WDFREQUEST Request,\r
-- UINT8 *pVerbsData, size_t VerbsSize,\r
-- WV_ENDPOINT *pEndpoint,\r
-- WV_IO_EP_DISCONNECT *pAttr)\r
--{\r
-- NTSTATUS status, failure;\r
--\r
-- WdfObjectAcquireLock(pEndpoint->Queue);\r
-- if (pEndpoint->State != WvEpConnected) {\r
-- status = STATUS_NOT_SUPPORTED;\r
-- goto release;\r
-- }\r
--\r
-- pEndpoint->State = WvEpActiveDisconnect;\r
-- IbCmInterface.CM.send_dreq(pEndpoint->pIbCmId, NULL, 0);\r
--\r
-- status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
-- if (!NT_SUCCESS(status)) {\r
-- pEndpoint->State = WvEpDisconnected;\r
-- WvCompleteRequests(pEndpoint->Queue, STATUS_UNSUCCESSFUL);\r
-- WdfObjectReleaseLock(pEndpoint->Queue);\r
--\r
-- failure = status;\r
-- status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
-- pVerbsData, VerbsSize);\r
-- if (NT_SUCCESS(status)) {\r
-- WdfRequestCompleteWithInformation(Request, failure, VerbsSize);\r
-- }\r
-- return status;\r
-- }\r
--\r
--release:\r
-- WdfObjectReleaseLock(pEndpoint->Queue);\r
-- return status;\r
--}\r
--\r
--static NTSTATUS WvEpDisconnectPassive(WDFREQUEST Request,\r
-- UINT8 *pVerbsData, size_t VerbsSize,\r
-- WV_ENDPOINT *pEndpoint,\r
-- WV_IO_EP_DISCONNECT *pAttr)\r
--{\r
-- NTSTATUS status;\r
--\r
-- WdfObjectAcquireLock(pEndpoint->Queue);\r
-- if (pEndpoint->State != WvEpPassiveDisconnect) {\r
-- WdfObjectReleaseLock(pEndpoint->Queue);\r
-- return STATUS_NOT_SUPPORTED;\r
-- }\r
--\r
-- pEndpoint->State = WvEpDisconnected;\r
-- WdfObjectReleaseLock(pEndpoint->Queue);\r
--\r
-- IbCmInterface.CM.send_drep(pEndpoint->pIbCmId, NULL, 0);\r
--\r
-- status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
-- pVerbsData, VerbsSize);\r
-- if (NT_SUCCESS(status)) {\r
-- WdfRequestCompleteWithInformation(Request, status, VerbsSize);\r
-- }\r
--\r
-- return status;\r
--}\r
--\r
-+// The IB CM could have received and processed a DREQ that we haven't seen yet.\r
- void WvEpDisconnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
- {\r
- WV_IO_EP_DISCONNECT *pattr;\r
-@@ -1020,19 +1030,36 @@ void WvEpDisconnect(WV_PROVIDER *pProvider, WDFREQUEST Request)
- goto complete;\r
- }\r
- \r
-- /* EP state is re-checked under lock in WvEpDisconnect* calls */\r
-+ WdfObjectAcquireLock(ep->Queue);\r
- switch (ep->State) {\r
- case WvEpConnected:\r
-- status = WvEpDisconnectActive(Request, out, outlen, ep, pattr);\r
-- break;\r
-+ status = IbCmInterface.CM.send_dreq(ep->pIbCmId, NULL, 0);\r
-+ if (NT_SUCCESS(status)) {\r
-+ status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
-+ if (NT_SUCCESS(status)) {\r
-+ ep->State = WvEpActiveDisconnect;\r
-+ break;\r
-+ }\r
-+ }\r
-+ /* Fall through to passive disconnect case on failure */\r
- case WvEpPassiveDisconnect:\r
-- status = WvEpDisconnectPassive(Request, out, outlen, ep, pattr);\r
-- break;\r
-+ ep->State = WvEpDisconnected;\r
-+ WdfObjectReleaseLock(ep->Queue);\r
-+\r
-+ IbCmInterface.CM.send_drep(ep->pIbCmId, NULL, 0);\r
-+\r
-+ status = WvEpDisconnectQp(ep->pProvider, pattr->QpId, out, outlen);\r
-+ if (NT_SUCCESS(status)) {\r
-+ WdfRequestCompleteWithInformation(Request, status, outlen);\r
-+ }\r
-+ goto release;\r
- default:\r
-- status = STATUS_NOT_SUPPORTED;\r
-+ status = STATUS_INVALID_DEVICE_STATE;\r
- break;\r
- }\r
-+ WdfObjectReleaseLock(ep->Queue);\r
- \r
-+release:\r
- WvEpRelease(ep);\r
- complete:\r
- if (!NT_SUCCESS(status)) {\r
-diff --git a/trunk/core/winverbs/kernel/wv_ep.h b/trunk/core/winverbs/kernel/wv_ep.h
-index f6d48d3..650b23c 100644
---- a/trunk/core/winverbs/kernel/wv_ep.h
-+++ b/trunk/core/winverbs/kernel/wv_ep.h
-@@ -72,6 +72,7 @@ typedef struct _WV_ENDPOINT
- KEVENT Event;\r
- LONG Ref;\r
- WDFQUEUE Queue;\r
-+ WV_WORK_ENTRY *pWork;\r
- \r
- } WV_ENDPOINT;\r
- \r
-diff --git a/trunk/core/winverbs/kernel/wv_provider.h b/trunk/core/winverbs/kernel/wv_provider.h
-index 329145f..bd430fb 100644
---- a/trunk/core/winverbs/kernel/wv_provider.h
-+++ b/trunk/core/winverbs/kernel/wv_provider.h
-@@ -44,6 +44,20 @@
- struct _WV_DEVICE;\r
- struct _WV_PROTECTION_DOMAIN;\r
- \r
-+typedef struct _WV_WORK_ENTRY\r
-+{\r
-+ WORK_ENTRY Work;\r
-+ UINT64 Id;\r
-+\r
-+} WV_WORK_ENTRY;\r
-+\r
-+static void WvWorkEntryInit(WV_WORK_ENTRY *pWork, UINT64 Id,\r
-+ void (*WorkHandler)(WORK_ENTRY *Work), void *Context)\r
-+{\r
-+ pWork->Id = Id;\r
-+ WorkEntryInit(&pWork->Work, WorkHandler, Context);\r
-+}\r
-+\r
- typedef struct _WV_PROVIDER\r
- {\r
- LIST_ENTRY Entry;
--- /dev/null
+Bottom: 6a0d87b4622bc7319d396436b319ad51c6ae3636
+Top: ed7908e04b3bbaa4e2413f01b2efe982eb5349d8
+Author: U-AMR\MSHEFTY <MSHEFTY@mshefty-MOBL2.amr.corp.intel.com>
+Date: 2009-12-09 17:26:01 -0800
+
+KMDF tracks all requests that pass through an IO queue. Even after a
+request has been removed from the queue, the request still maintains a
+reference on the queue. Any attempt to delete the queue will block
+until all requests holding references on the queue have completed.
+
+To avoid deadlock conditions during cleanup, we need to ensure that
+all requests can complete during cleanup. Modify the asynchronous
+handling code to queue a separate data structure, so that all requests
+can remain on the IO queues.
+
+Signed-off-by: Sean Hefty <sean.hefty@intel.com>
+
+
+---
+
+diff --git a/trunk/core/winverbs/kernel/wv_ep.c b/trunk/core/winverbs/kernel/wv_ep.c
+index 2fbb5b3..3d5c6ce 100644
+--- a/trunk/core/winverbs/kernel/wv_ep.c
++++ b/trunk/core/winverbs/kernel/wv_ep.c
+@@ -38,6 +38,8 @@
+ #define WV_AF_INET 2\r
+ #define WV_AF_INET6 23\r
+ \r
++static void WvEpWorkHandler(WORK_ENTRY *pWork);\r
++\r
+ static void WvEpGet(WV_ENDPOINT *pEndpoint)\r
+ {\r
+ InterlockedIncrement(&pEndpoint->Ref);\r
+@@ -87,6 +89,12 @@ static NTSTATUS WvEpAllocate(WV_PROVIDER *pProvider, UINT16 EpType,
+ }\r
+ \r
+ RtlZeroMemory(ep, sizeof(WV_ENDPOINT));\r
++ ep->pWork = ExAllocatePoolWithTag(NonPagedPool, sizeof(WV_WORK_ENTRY), 'wevw');\r
++ if (ep->pWork == NULL) {\r
++ status = STATUS_NO_MEMORY;\r
++ goto err1;\r
++ }\r
++\r
+ ep->Ref = 1;\r
+ ep->pProvider = pProvider;\r
+ ep->EpType = EpType;\r
+@@ -97,13 +105,15 @@ static NTSTATUS WvEpAllocate(WV_PROVIDER *pProvider, UINT16 EpType,
+ status = WdfIoQueueCreate(ControlDevice, &config,\r
+ WDF_NO_OBJECT_ATTRIBUTES, &ep->Queue);\r
+ if (!NT_SUCCESS(status)) {\r
+- goto err;\r
++ goto err2;\r
+ }\r
+ \r
+ *ppEndpoint = ep;\r
+ return STATUS_SUCCESS;\r
+ \r
+-err:\r
++err2:\r
++ ExFreePoolWithTag(ep->pWork, 'wevw');\r
++err1:\r
+ ExFreePoolWithTag(ep, 'pevw');\r
+ return status;\r
+ }\r
+@@ -137,6 +147,7 @@ void WvEpCreate(WV_PROVIDER *pProvider, WDFREQUEST Request)
+ }\r
+ KeReleaseGuardedMutex(&pProvider->Lock);\r
+ \r
++ WvWorkEntryInit(ep->pWork, *pId, WvEpWorkHandler, pProvider);\r
+ WdfRequestCompleteWithInformation(Request, status, sizeof(UINT64));\r
+ return;\r
+ \r
+@@ -195,6 +206,9 @@ void WvEpFree(WV_ENDPOINT *pEndpoint)
+ \r
+ WdfIoQueuePurgeSynchronously(pEndpoint->Queue);\r
+ WdfObjectDelete(pEndpoint->Queue);\r
++ if (pEndpoint->pWork != NULL) {\r
++ ExFreePoolWithTag(pEndpoint->pWork, 'wevw');\r
++ }\r
+ ExFreePoolWithTag(pEndpoint, 'pevw');\r
+ }\r
+ \r
+@@ -413,77 +427,72 @@ static NTSTATUS WvEpDisconnectQp(WV_PROVIDER *pProvider, UINT64 QpId,
+ return status;\r
+ }\r
+ \r
+-static void WvEpDisconnectHandler(WORK_ENTRY *pWork)\r
++static NTSTATUS WvEpAsyncDisconnect(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
+ {\r
+- WV_PROVIDER *prov;\r
+- WDFREQUEST request;\r
+ WV_IO_EP_DISCONNECT *pattr;\r
+ UINT8 *out;\r
+ size_t outlen = 0;\r
+ NTSTATUS status;\r
+ \r
+- request = (WDFREQUEST) pWork->Context;\r
+- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
+-\r
+- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_DISCONNECT),\r
++ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_DISCONNECT),\r
+ &pattr, NULL);\r
+ if (!NT_SUCCESS(status)) {\r
+- goto complete;\r
++ return status;\r
+ }\r
+ \r
+- status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
++ status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
+ if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
+- goto complete;\r
++ return status;\r
+ }\r
+ \r
+- status = (NTSTATUS) WdfRequestGetInformation(request);\r
++ status = (NTSTATUS) WdfRequestGetInformation(Request);\r
+ if (NT_SUCCESS(status)) {\r
+- status = WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
++ status = WvEpDisconnectQp(pEndpoint->pProvider, pattr->QpId, out, outlen);\r
+ } else {\r
+- WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
++ WvEpDisconnectQp(pEndpoint->pProvider, pattr->QpId, out, outlen);\r
+ }\r
+ \r
+-complete:\r
+- WdfRequestCompleteWithInformation(request, status, outlen);\r
+- WvProviderPut(prov);\r
++ WdfRequestCompleteWithInformation(Request, status, outlen);\r
++ return STATUS_SUCCESS;\r
+ }\r
+ \r
+-// We use IRP DriverContext to queue the request for further processing,\r
+-// but the request/IRP are no longer owned by the framework.\r
+ static void WvEpCompleteDisconnect(WV_ENDPOINT *pEndpoint, NTSTATUS DiscStatus)\r
+ {\r
+ WDFREQUEST request;\r
++ WDFREQUEST disc_req = NULL;\r
+ WDF_REQUEST_PARAMETERS param;\r
+- WORK_ENTRY *work;\r
+ NTSTATUS status;\r
+ \r
+ WdfObjectAcquireLock(pEndpoint->Queue);\r
+- if (pEndpoint->State == WvEpDestroying) {\r
++ if (pEndpoint->State == WvEpDestroying || !pEndpoint->pWork) {\r
+ goto release;\r
+ }\r
+ pEndpoint->State = WvEpDisconnected;\r
+ \r
+ status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
+ while (NT_SUCCESS(status)) {\r
+- WdfObjectReleaseLock(pEndpoint->Queue);\r
+ \r
+ WDF_REQUEST_PARAMETERS_INIT(¶m);\r
+ WdfRequestGetParameters(request, ¶m);\r
+ if (param.Parameters.DeviceIoControl.IoControlCode == WV_IOCTL_EP_DISCONNECT) {\r
+- work = WorkEntryFromIrp(WdfRequestWdmGetIrp(request));\r
+ WdfRequestSetInformation(request, DiscStatus);\r
+- WorkEntryInit(work, WvEpDisconnectHandler, request);\r
+ WvProviderGet(pEndpoint->pProvider);\r
+- WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, work);\r
++ WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, &pEndpoint->pWork->Work);\r
++ pEndpoint->pWork = NULL;\r
++ disc_req = request;\r
+ } else {\r
+ WdfRequestComplete(request, DiscStatus);\r
+ }\r
+ \r
+- WdfObjectAcquireLock(pEndpoint->Queue);\r
+ status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
+ }\r
++\r
++ if (disc_req != NULL) {\r
++ WdfRequestRequeue(disc_req);\r
++ }\r
+ release:\r
+ WdfObjectReleaseLock(pEndpoint->Queue);\r
++\r
+ }\r
+ \r
+ static NTSTATUS WvEpIbCmHandler(iba_cm_id *pId, iba_cm_event *pEvent)\r
+@@ -565,49 +574,31 @@ static NTSTATUS WvEpIbCmHandler(iba_cm_id *pId, iba_cm_event *pEvent)
+ return STATUS_SUCCESS;\r
+ }\r
+ \r
+-void WvEpConnectHandler(WORK_ENTRY *pWork)\r
++static NTSTATUS WvEpAsyncConnect(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
+ {\r
+- WV_PROVIDER *prov;\r
+- WDFREQUEST request;\r
+ WV_IO_EP_CONNECT *pattr;\r
+- WV_ENDPOINT *ep;\r
+ WV_QUEUE_PAIR *qp;\r
+ iba_cm_req req;\r
+ NTSTATUS status;\r
+ UINT8 data[IB_REQ_PDATA_SIZE];\r
+ \r
+- request = (WDFREQUEST) pWork->Context;\r
+- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
+-\r
+- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_CONNECT),\r
++ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_CONNECT),\r
+ &pattr, NULL);\r
+ if (!NT_SUCCESS(status)) {\r
+- goto complete;\r
+- }\r
+-\r
+- if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
+- status = STATUS_INVALID_BUFFER_SIZE;\r
+- goto complete;\r
+- }\r
+-\r
+- ep = WvEpAcquire(prov, pattr->Id);\r
+- if (ep == NULL) {\r
+- status = STATUS_NOT_FOUND;\r
+- goto complete;\r
++ return status;\r
+ }\r
+ \r
+- qp = WvQpAcquire(prov, pattr->QpId);\r
++ qp = WvQpAcquire(pEndpoint->pProvider, pattr->QpId);\r
+ if (qp == NULL) {\r
+- status = STATUS_NOT_FOUND;\r
+- goto release;\r
++ return STATUS_NOT_FOUND;\r
+ }\r
+ \r
+- ep->Attributes.PeerAddress = pattr->PeerAddress;\r
+- WvFormatCmaHeader((IB_CMA_HEADER *) data, &ep->Attributes.LocalAddress,\r
+- &ep->Attributes.PeerAddress);\r
++ pEndpoint->Attributes.PeerAddress = pattr->PeerAddress;\r
++ WvFormatCmaHeader((IB_CMA_HEADER *) data, &pEndpoint->Attributes.LocalAddress,\r
++ &pEndpoint->Attributes.PeerAddress);\r
+ \r
+- req.service_id = WvGetServiceId(ep->EpType, &ep->Attributes.PeerAddress);\r
+- req.p_primary_path = &ep->Route;\r
++ req.service_id = WvGetServiceId(pEndpoint->EpType, &pEndpoint->Attributes.PeerAddress);\r
++ req.p_primary_path = &pEndpoint->Route;\r
+ req.p_alt_path = NULL;\r
+ req.qpn = qp->Qpn;\r
+ req.qp_type = IB_QPT_RELIABLE_CONN;\r
+@@ -627,54 +618,86 @@ void WvEpConnectHandler(WORK_ENTRY *pWork)
+ req.srq = (qp->pSrq != NULL);\r
+ \r
+ WvQpRelease(qp);\r
+- RtlCopyMemory(&ep->Attributes.Param.Connect, &pattr->Param,\r
++ RtlCopyMemory(&pEndpoint->Attributes.Param.Connect, &pattr->Param,\r
+ sizeof(pattr->Param));\r
+ \r
+- WdfObjectAcquireLock(ep->Queue);\r
+- if (ep->State != WvEpRouteResolved) {\r
++ WdfObjectAcquireLock(pEndpoint->Queue);\r
++ if (pEndpoint->State != WvEpRouteResolved) {\r
+ status = STATUS_NOT_SUPPORTED;\r
+- goto unlock;\r
++ goto out;\r
+ }\r
+ \r
+- status = IbCmInterface.CM.create_id(WvEpIbCmHandler, ep, &ep->pIbCmId);\r
++ status = IbCmInterface.CM.create_id(WvEpIbCmHandler, pEndpoint, &pEndpoint->pIbCmId);\r
+ if (!NT_SUCCESS(status)) {\r
+- goto unlock;\r
++ goto out;\r
+ }\r
+ \r
+- ep->State = WvEpActiveConnect;\r
+- status = IbCmInterface.CM.send_req(ep->pIbCmId, &req);\r
++ pEndpoint->State = WvEpActiveConnect;\r
++ status = IbCmInterface.CM.send_req(pEndpoint->pIbCmId, &req);\r
+ if (NT_SUCCESS(status)) {\r
+- status = WdfRequestForwardToIoQueue(request, ep->Queue);\r
++ status = WdfRequestRequeue(Request);\r
+ }\r
+ \r
+ if (!NT_SUCCESS(status)) {\r
+- ep->State = WvEpDisconnected;\r
+- }\r
+-unlock:\r
+- WdfObjectReleaseLock(ep->Queue);\r
+-release:\r
+- WvEpRelease(ep);\r
+-complete:\r
+- if (!NT_SUCCESS(status)) {\r
+- WdfRequestComplete(request, status);\r
++ pEndpoint->State = WvEpDisconnected;\r
+ }\r
+- WvProviderPut(prov);\r
++\r
++out:\r
++ WdfObjectReleaseLock(pEndpoint->Queue);\r
++ return status;\r
+ }\r
+ \r
+-static void WvEpProcessAsync(WV_PROVIDER *pProvider, WDFREQUEST Request,\r
+- void (*AsyncHandler)(struct _WORK_ENTRY *Work))\r
++static NTSTATUS WvEpProcessAsync(WV_PROVIDER *pProvider, UINT64 Id, WDFREQUEST Request)\r
+ {\r
+- WORK_ENTRY *work;\r
++ WV_ENDPOINT *ep;\r
++ NTSTATUS status;\r
++\r
++ ep = WvEpAcquire(pProvider, Id);\r
++ if (ep == NULL) {\r
++ return STATUS_NOT_FOUND;\r
++ }\r
++\r
++ WdfObjectAcquireLock(ep->Queue);\r
++ if (!ep->pWork) {\r
++ status = STATUS_TOO_MANY_COMMANDS;\r
++ goto out;\r
++ }\r
+ \r
+- work = WorkEntryFromIrp(WdfRequestWdmGetIrp(Request));\r
+- WorkEntryInit(work, AsyncHandler, Request);\r
+- WvProviderGet(pProvider);\r
+- WorkQueueInsert(&pProvider->WorkQueue, work);\r
++ status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
++ if (NT_SUCCESS(status)) {\r
++ WvProviderGet(pProvider);\r
++ WorkQueueInsert(&pProvider->WorkQueue, &ep->pWork->Work);\r
++ ep->pWork = NULL;\r
++ }\r
++\r
++out:\r
++ WdfObjectReleaseLock(ep->Queue);\r
++ WvEpRelease(ep);\r
++ return status;\r
+ }\r
+ \r
+ void WvEpConnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
+ {\r
+- WvEpProcessAsync(pProvider, Request, WvEpConnectHandler);\r
++ WV_IO_EP_CONNECT *pattr;\r
++ NTSTATUS status;\r
++\r
++ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_CONNECT),\r
++ &pattr, NULL);\r
++ if (!NT_SUCCESS(status)) {\r
++ goto out;\r
++ }\r
++\r
++ if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
++ status = STATUS_INVALID_BUFFER_SIZE;\r
++ goto out;\r
++ }\r
++\r
++ status = WvEpProcessAsync(pProvider, pattr->Id, Request);\r
++\r
++out:\r
++ if (!NT_SUCCESS(status)) {\r
++ WdfRequestComplete(Request, status);\r
++ }\r
+ }\r
+ \r
+ static NTSTATUS WvEpModifyQpRtr(WV_ENDPOINT *pEndpoint, WV_QUEUE_PAIR *pQp,\r
+@@ -823,7 +846,7 @@ static NTSTATUS WvEpAcceptPassive(WDFREQUEST Request, UINT8 *pVerbsData, size_t
+ \r
+ status = IbCmInterface.CM.send_rep(pEndpoint->pIbCmId, &rep);\r
+ if (NT_SUCCESS(status)) {\r
+- status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
++ status = WdfRequestRequeue(Request);\r
+ }\r
+ \r
+ if (!NT_SUCCESS(status)) {\r
+@@ -836,65 +859,114 @@ out:
+ return status;\r
+ }\r
+ \r
+-void WvEpAcceptHandler(WORK_ENTRY *pWork)\r
++static NTSTATUS WvEpAsyncAccept(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
+ {\r
+- WV_PROVIDER *prov;\r
+- WDFREQUEST request;\r
+ WV_IO_EP_ACCEPT *pattr;\r
+- WV_ENDPOINT *ep;\r
+ NTSTATUS status;\r
+ UINT8 *out;\r
+ size_t outlen;\r
+ \r
+- request = (WDFREQUEST) pWork->Context;\r
+- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
+-\r
+- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_ACCEPT),\r
++ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_ACCEPT),\r
+ &pattr, NULL);\r
+ if (!NT_SUCCESS(status)) {\r
+- goto complete;\r
++ return status;\r
+ }\r
+ \r
+- status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
++ status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
+ if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
+- goto complete;\r
+- }\r
+-\r
+- if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
+- status = STATUS_INVALID_BUFFER_SIZE;\r
+- goto complete;\r
+- }\r
+-\r
+- ep = WvEpAcquire(prov, pattr->Id);\r
+- if (ep == NULL) {\r
+- status = STATUS_NOT_FOUND;\r
+- goto complete;\r
++ return status;\r
+ }\r
+ \r
+ /* EP state is re-checked under lock in WvEpAccept* calls */\r
+- switch (ep->State) {\r
++ switch (pEndpoint->State) {\r
+ case WvEpActiveConnect:\r
+- status = WvEpAcceptActive(request, out, outlen, ep, pattr);\r
++ status = WvEpAcceptActive(Request, out, outlen, pEndpoint, pattr);\r
+ break;\r
+ case WvEpPassiveConnect:\r
+- status = WvEpAcceptPassive(request, out, outlen, ep, pattr);\r
++ status = WvEpAcceptPassive(Request, out, outlen, pEndpoint, pattr);\r
+ break;\r
+ default:\r
+ status = STATUS_NOT_SUPPORTED;\r
+ break;\r
+ }\r
+ \r
+- WvEpRelease(ep);\r
+-complete:\r
++ return status;\r
++}\r
++\r
++static void WvEpWorkHandler(WORK_ENTRY *pWork)\r
++{\r
++ WV_PROVIDER *prov;\r
++ WV_ENDPOINT *ep;\r
++ WV_WORK_ENTRY *work;\r
++ WDFREQUEST request;\r
++ WDF_REQUEST_PARAMETERS param;\r
++ NTSTATUS status;\r
++\r
++ work = CONTAINING_RECORD(pWork, WV_WORK_ENTRY, Work);\r
++ prov = (WV_PROVIDER *) pWork->Context;\r
++\r
++ ep = WvEpAcquire(prov, work->Id);\r
++ if (ep == NULL) {\r
++ ExFreePoolWithTag(work, 'wevw');\r
++ goto out;\r
++ }\r
++\r
++ WdfObjectAcquireLock(ep->Queue);\r
++ ep->pWork = work;\r
++ status = WdfIoQueueRetrieveNextRequest(ep->Queue, &request);\r
++ WdfObjectReleaseLock(ep->Queue);\r
++\r
++ if (!NT_SUCCESS(status)) {\r
++ goto put;\r
++ }\r
++\r
++ WDF_REQUEST_PARAMETERS_INIT(¶m);\r
++ WdfRequestGetParameters(request, ¶m);\r
++ switch (param.Parameters.DeviceIoControl.IoControlCode) {\r
++ case WV_IOCTL_EP_CONNECT:\r
++ status = WvEpAsyncConnect(ep, request);\r
++ break;\r
++ case WV_IOCTL_EP_ACCEPT:\r
++ status = WvEpAsyncAccept(ep, request);\r
++ break;\r
++ case WV_IOCTL_EP_DISCONNECT:\r
++ status = WvEpAsyncDisconnect(ep, request);\r
++ break;\r
++ default:\r
++ status = STATUS_NOT_IMPLEMENTED;\r
++ }\r
++\r
+ if (!NT_SUCCESS(status)) {\r
+ WdfRequestComplete(request, status);\r
+ }\r
++put:\r
++ WvEpRelease(ep);\r
++out:\r
+ WvProviderPut(prov);\r
+ }\r
+ \r
+ void WvEpAccept(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
+ {\r
+- WvEpProcessAsync(pProvider, Request, WvEpAcceptHandler);\r
++ WV_IO_EP_ACCEPT *pattr;\r
++ NTSTATUS status;\r
++\r
++ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_ACCEPT),\r
++ &pattr, NULL);\r
++ if (!NT_SUCCESS(status)) {\r
++ goto out;\r
++ }\r
++\r
++ if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
++ status = STATUS_INVALID_BUFFER_SIZE;\r
++ goto out;\r
++ }\r
++\r
++ status = WvEpProcessAsync(pProvider, pattr->Id, Request);\r
++\r
++out:\r
++ if (!NT_SUCCESS(status)) {\r
++ WdfRequestComplete(Request, status);\r
++ }\r
+ }\r
+ \r
+ void WvEpReject(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
+@@ -932,69 +1004,7 @@ complete:
+ WdfRequestComplete(Request, status);\r
+ }\r
+ \r
+-static NTSTATUS WvEpDisconnectActive(WDFREQUEST Request,\r
+- UINT8 *pVerbsData, size_t VerbsSize,\r
+- WV_ENDPOINT *pEndpoint,\r
+- WV_IO_EP_DISCONNECT *pAttr)\r
+-{\r
+- NTSTATUS status, failure;\r
+-\r
+- WdfObjectAcquireLock(pEndpoint->Queue);\r
+- if (pEndpoint->State != WvEpConnected) {\r
+- status = STATUS_NOT_SUPPORTED;\r
+- goto release;\r
+- }\r
+-\r
+- pEndpoint->State = WvEpActiveDisconnect;\r
+- IbCmInterface.CM.send_dreq(pEndpoint->pIbCmId, NULL, 0);\r
+-\r
+- status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
+- if (!NT_SUCCESS(status)) {\r
+- pEndpoint->State = WvEpDisconnected;\r
+- WvCompleteRequests(pEndpoint->Queue, STATUS_UNSUCCESSFUL);\r
+- WdfObjectReleaseLock(pEndpoint->Queue);\r
+-\r
+- failure = status;\r
+- status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
+- pVerbsData, VerbsSize);\r
+- if (NT_SUCCESS(status)) {\r
+- WdfRequestCompleteWithInformation(Request, failure, VerbsSize);\r
+- }\r
+- return status;\r
+- }\r
+-\r
+-release:\r
+- WdfObjectReleaseLock(pEndpoint->Queue);\r
+- return status;\r
+-}\r
+-\r
+-static NTSTATUS WvEpDisconnectPassive(WDFREQUEST Request,\r
+- UINT8 *pVerbsData, size_t VerbsSize,\r
+- WV_ENDPOINT *pEndpoint,\r
+- WV_IO_EP_DISCONNECT *pAttr)\r
+-{\r
+- NTSTATUS status;\r
+-\r
+- WdfObjectAcquireLock(pEndpoint->Queue);\r
+- if (pEndpoint->State != WvEpPassiveDisconnect) {\r
+- WdfObjectReleaseLock(pEndpoint->Queue);\r
+- return STATUS_NOT_SUPPORTED;\r
+- }\r
+-\r
+- pEndpoint->State = WvEpDisconnected;\r
+- WdfObjectReleaseLock(pEndpoint->Queue);\r
+-\r
+- IbCmInterface.CM.send_drep(pEndpoint->pIbCmId, NULL, 0);\r
+-\r
+- status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
+- pVerbsData, VerbsSize);\r
+- if (NT_SUCCESS(status)) {\r
+- WdfRequestCompleteWithInformation(Request, status, VerbsSize);\r
+- }\r
+-\r
+- return status;\r
+-}\r
+-\r
++// The IB CM could have received and processed a DREQ that we haven't seen yet.\r
+ void WvEpDisconnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
+ {\r
+ WV_IO_EP_DISCONNECT *pattr;\r
+@@ -1020,19 +1030,36 @@ void WvEpDisconnect(WV_PROVIDER *pProvider, WDFREQUEST Request)
+ goto complete;\r
+ }\r
+ \r
+- /* EP state is re-checked under lock in WvEpDisconnect* calls */\r
++ WdfObjectAcquireLock(ep->Queue);\r
+ switch (ep->State) {\r
+ case WvEpConnected:\r
+- status = WvEpDisconnectActive(Request, out, outlen, ep, pattr);\r
+- break;\r
++ status = IbCmInterface.CM.send_dreq(ep->pIbCmId, NULL, 0);\r
++ if (NT_SUCCESS(status)) {\r
++ status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
++ if (NT_SUCCESS(status)) {\r
++ ep->State = WvEpActiveDisconnect;\r
++ break;\r
++ }\r
++ }\r
++ /* Fall through to passive disconnect case on failure */\r
+ case WvEpPassiveDisconnect:\r
+- status = WvEpDisconnectPassive(Request, out, outlen, ep, pattr);\r
+- break;\r
++ ep->State = WvEpDisconnected;\r
++ WdfObjectReleaseLock(ep->Queue);\r
++\r
++ IbCmInterface.CM.send_drep(ep->pIbCmId, NULL, 0);\r
++\r
++ status = WvEpDisconnectQp(ep->pProvider, pattr->QpId, out, outlen);\r
++ if (NT_SUCCESS(status)) {\r
++ WdfRequestCompleteWithInformation(Request, status, outlen);\r
++ }\r
++ goto release;\r
+ default:\r
+- status = STATUS_NOT_SUPPORTED;\r
++ status = STATUS_INVALID_DEVICE_STATE;\r
+ break;\r
+ }\r
++ WdfObjectReleaseLock(ep->Queue);\r
+ \r
++release:\r
+ WvEpRelease(ep);\r
+ complete:\r
+ if (!NT_SUCCESS(status)) {\r
+diff --git a/trunk/core/winverbs/kernel/wv_ep.h b/trunk/core/winverbs/kernel/wv_ep.h
+index f6d48d3..650b23c 100644
+--- a/trunk/core/winverbs/kernel/wv_ep.h
++++ b/trunk/core/winverbs/kernel/wv_ep.h
+@@ -72,6 +72,7 @@ typedef struct _WV_ENDPOINT
+ KEVENT Event;\r
+ LONG Ref;\r
+ WDFQUEUE Queue;\r
++ WV_WORK_ENTRY *pWork;\r
+ \r
+ } WV_ENDPOINT;\r
+ \r
+diff --git a/trunk/core/winverbs/kernel/wv_provider.h b/trunk/core/winverbs/kernel/wv_provider.h
+index 329145f..bd430fb 100644
+--- a/trunk/core/winverbs/kernel/wv_provider.h
++++ b/trunk/core/winverbs/kernel/wv_provider.h
+@@ -44,6 +44,20 @@
+ struct _WV_DEVICE;\r
+ struct _WV_PROTECTION_DOMAIN;\r
+ \r
++typedef struct _WV_WORK_ENTRY\r
++{\r
++ WORK_ENTRY Work;\r
++ UINT64 Id;\r
++\r
++} WV_WORK_ENTRY;\r
++\r
++static void WvWorkEntryInit(WV_WORK_ENTRY *pWork, UINT64 Id,\r
++ void (*WorkHandler)(WORK_ENTRY *Work), void *Context)\r
++{\r
++ pWork->Id = Id;\r
++ WorkEntryInit(&pWork->Work, WorkHandler, Context);\r
++}\r
++\r
+ typedef struct _WV_PROVIDER\r
+ {\r
+ LIST_ENTRY Entry;