--- /dev/null
+Bottom: ed7908e04b3bbaa4e2413f01b2efe982eb5349d8
+Top: 7edcb31c25e21f961a1e6f9af11bf03fa5d98c65
+Author: E~1\svn\LOCALS~1\Temp/report.7.tmp <E~1\svn\LOCALS~1\Temp/report.7.tmp@ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86>
+Date: 2010-01-04 19:01:09 +0000
+
+ib/cm: fix handling failed send completions
+
+__cep_mad_send_cb() assumes that the mad being processed is
+associated with the current state of the CEP. This may not be
+the case.
+
+For example, for a short lived connection, it was observed that
+a REP mad completed with status canceled. This is normal. However,
+the user already attempted to disconnect the connection by sending
+a DREQ. This left the cep in the DREQ_SENT state by the time that
+the REP mad completed. Since the REP failed, but the state was
+DREQ_SENT, the code assumed that the DREQ had failed and transitioned
+the cep into TIMEWAIT. The result is that the DREQ is never
+matched with a DREP or canceled, but holds a reference on the CEP.
+
+Until the DREQ times out (time depends on connection, but easily
+up to a minute), attempts to destroy the CEP are blocked.
+
+Fix this by simply discarding any completed sends that were not
+sent from the current state of the cep when the completion handler
+is invoked.
+
+Signed-off-by: Sean Hefty <sean.hefty@intel.com>
+
+git-svn-id: svn://openib.tc.cornell.edu/gen1@2650 ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86
+
+
+---
+
+diff --git a/trunk/core/al/kernel/al_cm.c b/trunk/core/al/kernel/al_cm.c
+index 48b0cb5..955985a 100644
+--- a/trunk/core/al/kernel/al_cm.c
++++ b/trunk/core/al/kernel/al_cm.c
+@@ -37,7 +37,7 @@
+ typedef struct _iba_cm_id_priv\r
+ {\r
+ iba_cm_id id;\r
+- KEVENT destroy_event; \r
++ KEVENT destroy_event;\r
+ \r
+ } iba_cm_id_priv;\r
+ \r
+diff --git a/trunk/core/al/kernel/al_cm_cep.c b/trunk/core/al/kernel/al_cm_cep.c
+index 49fa417..4987207 100644
+--- a/trunk/core/al/kernel/al_cm_cep.c
++++ b/trunk/core/al/kernel/al_cm_cep.c
+@@ -27,7 +27,7 @@
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ *\r
+- * $Id$\r
++ * $Id: al_cm_cep.c 2540 2009-11-03 17:23:09Z shefty $\r
+ */\r
+ \r
+ \r
+@@ -2213,10 +2213,7 @@ __cep_mad_send_cb(
+ \r
+ p_cep = (kcep_t*)p_mad->context1;\r
+ \r
+- /*\r
+- * The connection context is not set when performing immediate responses,\r
+- * such as repeating MADS.\r
+- */\r
++ /* The cep context is only set for MADs that are retried. */\r
+ if( !p_cep )\r
+ {\r
+ ib_put_mad( p_mad );\r
+@@ -2224,94 +2221,71 @@ __cep_mad_send_cb(
+ return;\r
+ }\r
+ \r
++ CL_ASSERT( p_mad->status != IB_WCS_SUCCESS );\r
+ p_mad->context1 = NULL;\r
+ \r
+ KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
+- /* Clear the sent MAD pointer so that we don't try cancelling again. */\r
+- if( p_cep->p_send_mad == p_mad )\r
+- p_cep->p_send_mad = NULL;\r
+-\r
+- switch( p_mad->status )\r
++ if( p_cep->p_send_mad != p_mad )\r
+ {\r
+- case IB_WCS_SUCCESS:\r
+ KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
+ ib_put_mad( p_mad );\r
+- break;\r
+-\r
+- case IB_WCS_CANCELED:\r
+- if( p_cep->state != CEP_STATE_REQ_SENT &&\r
+- p_cep->state != CEP_STATE_REQ_MRA_RCVD &&\r
+- p_cep->state != CEP_STATE_REP_SENT &&\r
+- p_cep->state != CEP_STATE_REP_MRA_RCVD &&\r
+- p_cep->state != CEP_STATE_LAP_SENT &&\r
+- p_cep->state != CEP_STATE_LAP_MRA_RCVD &&\r
+- p_cep->state != CEP_STATE_DREQ_SENT &&\r
+- p_cep->state != CEP_STATE_SREQ_SENT )\r
+- {\r
+- KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
+- ib_put_mad( p_mad );\r
+- break;\r
+- }\r
+- /* Treat as a timeout so we don't stall the state machine. */\r
+- p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;\r
+-\r
+- /* Fall through. */\r
+- case IB_WCS_TIMEOUT_RETRY_ERR:\r
+- default:\r
+- /* Timeout. Reject the connection. */\r
+- switch( p_cep->state )\r
+- {\r
+- case CEP_STATE_REQ_SENT:\r
+- case CEP_STATE_REQ_MRA_RCVD:\r
+- case CEP_STATE_REP_SENT:\r
+- case CEP_STATE_REP_MRA_RCVD:\r
+- /* Send the REJ. */\r
+- __reject_timeout( p_port_cep, p_cep, p_mad );\r
+- __remove_cep( p_cep );\r
+- p_cep->state = CEP_STATE_IDLE;\r
+- break;\r
+-\r
+- case CEP_STATE_DREQ_DESTROY:\r
+- p_cep->state = CEP_STATE_DESTROY;\r
+- __insert_timewait( p_cep );\r
+- /* Fall through. */\r
++ goto done;\r
++ }\r
+ \r
+- case CEP_STATE_DESTROY:\r
+- KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
+- ib_put_mad( p_mad );\r
+- goto done;\r
++ /* Clear the sent MAD pointer so that we don't try cancelling again. */\r
++ p_cep->p_send_mad = NULL;\r
+ \r
+- case CEP_STATE_DREQ_SENT:\r
+- /*\r
+- * Make up a DREP mad so we can respond if we receive\r
+- * a DREQ while in timewait.\r
+- */\r
+- __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );\r
+- __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );\r
+- p_cep->state = CEP_STATE_TIMEWAIT;\r
+- __insert_timewait( p_cep );\r
+- break;\r
++ switch( p_cep->state )\r
++ {\r
++ case CEP_STATE_REQ_SENT:\r
++ case CEP_STATE_REQ_MRA_RCVD:\r
++ case CEP_STATE_REP_SENT:\r
++ case CEP_STATE_REP_MRA_RCVD:\r
++ /* Send the REJ. */\r
++ __reject_timeout( p_port_cep, p_cep, p_mad );\r
++ __remove_cep( p_cep );\r
++ p_cep->state = CEP_STATE_IDLE;\r
++ break;\r
+ \r
+- case CEP_STATE_LAP_SENT:\r
+- /*\r
+- * Before CEP was sent, we have been in CEP_STATE_ESTABLISHED as we\r
+- * failed to send, we return to that state.\r
+- */\r
+- p_cep->state = CEP_STATE_ESTABLISHED;\r
+- break;\r
+- default:\r
+- break;\r
+- }\r
++ case CEP_STATE_DREQ_DESTROY:\r
++ p_cep->state = CEP_STATE_DESTROY;\r
++ __insert_timewait( p_cep );\r
++ /* Fall through. */\r
+ \r
+- status = __cep_queue_mad( p_cep, p_mad );\r
+- CL_ASSERT( status != IB_INVALID_STATE );\r
++ case CEP_STATE_DESTROY:\r
+ KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
++ ib_put_mad( p_mad );\r
++ goto done;\r
+ \r
+- if( status == IB_SUCCESS )\r
+- __process_cep( p_cep );\r
++ case CEP_STATE_DREQ_SENT:\r
++ /*\r
++ * Make up a DREP mad so we can respond if we receive\r
++ * a DREQ while in timewait.\r
++ */\r
++ __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );\r
++ __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );\r
++ p_cep->state = CEP_STATE_TIMEWAIT;\r
++ __insert_timewait( p_cep );\r
++ break;\r
++\r
++ case CEP_STATE_LAP_SENT:\r
++ /*\r
++ * Before CEP was sent, we have been in CEP_STATE_ESTABLISHED as we\r
++ * failed to send, we return to that state.\r
++ */\r
++ p_cep->state = CEP_STATE_ESTABLISHED;\r
++ break;\r
++ default:\r
+ break;\r
+ }\r
+ \r
++ status = __cep_queue_mad( p_cep, p_mad );\r
++ CL_ASSERT( status != IB_INVALID_STATE );\r
++ KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
++\r
++ if( status == IB_SUCCESS )\r
++ __process_cep( p_cep );\r
++\r
+ done:\r
+ pfn_destroy_cb = p_cep->pfn_destroy_cb;\r
+ cep_context = p_cep->context;\r
+@@ -3938,12 +3912,8 @@ __cleanup_cep(
+ CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
+ \r
+ /* If we've already come through here, we're done. */\r
+- if( p_cep->state == CEP_STATE_DESTROY ||\r
+- p_cep->state == CEP_STATE_DREQ_DESTROY )\r
+- {\r
+- AL_EXIT( AL_DBG_CM );\r
+- return -1;\r
+- }\r
++ CL_ASSERT( p_cep->state != CEP_STATE_DESTROY &&\r
++ p_cep->state != CEP_STATE_DREQ_DESTROY );\r
+ \r
+ /* Cleanup the pending MAD list. */\r
+ while( p_cep->p_mad_head )
--- /dev/null
+Bottom: 8c81b098194a73a6b885161efe4ac769111b5c88
+Top: 8ebcb054bec1d85cae3b87aaad82a6488d5fc842
+Author: stansmith <stansmith@ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86>
+Date: 2010-01-05 18:59:21 +0000
+
+[IBAL] ib/cm: fix handling failed send completions
+__cep_mad_send_cb() assumes that the mad being processed is
+associated with the current state of the CEP. This may not be
+the case.
+For example, for a short lived connection, it was observed that
+a REP mad completed with status canceled. This is normal. However,
+the user already attempted to disconnect the connection by sending
+a DREQ. This left the cep in the DREQ_SENT state by the time that
+the REP mad completed. Since the REP status was not success, but the
+state was DREQ_SENT, the code assumed that the DREQ had failed and
+transitioned the cep into TIMEWAIT. The result is that the DREQ is never
+>matched with a DREP or canceled, but holds a reference on the CEP.
+Until the DREQ times out (time depends on connection, but easily
+up to a minute), attempts to destroy the CEP are blocked.
+Fix this by simply discarding any completed sends that were not
+sent from the current state of the cep when the completion handler
+is invoked.
+Signed-off-by: Sean Hefty <sean.hefty@intel.com>
+
+git-svn-id: svn://openib.tc.cornell.edu/gen1@2652 ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86
+
+
+---
+
+diff --git a/branches/WOF2-2/core/al/kernel/al_cm.c b/branches/WOF2-2/core/al/kernel/al_cm.c
+index 48b0cb5..955985a 100644
+--- a/branches/WOF2-2/core/al/kernel/al_cm.c
++++ b/branches/WOF2-2/core/al/kernel/al_cm.c
+@@ -37,7 +37,7 @@
+ typedef struct _iba_cm_id_priv\r
+ {\r
+ iba_cm_id id;\r
+- KEVENT destroy_event; \r
++ KEVENT destroy_event;\r
+ \r
+ } iba_cm_id_priv;\r
+ \r
+diff --git a/branches/WOF2-2/core/al/kernel/al_cm_cep.c b/branches/WOF2-2/core/al/kernel/al_cm_cep.c
+index 49fa417..89ffe12 100644
+--- a/branches/WOF2-2/core/al/kernel/al_cm_cep.c
++++ b/branches/WOF2-2/core/al/kernel/al_cm_cep.c
+@@ -2213,10 +2213,7 @@ __cep_mad_send_cb(
+ \r
+ p_cep = (kcep_t*)p_mad->context1;\r
+ \r
+- /*\r
+- * The connection context is not set when performing immediate responses,\r
+- * such as repeating MADS.\r
+- */\r
++ /* The cep context is only set for MADs that are retried. */\r
+ if( !p_cep )\r
+ {\r
+ ib_put_mad( p_mad );\r
+@@ -2224,94 +2221,71 @@ __cep_mad_send_cb(
+ return;\r
+ }\r
+ \r
++ CL_ASSERT( p_mad->status != IB_WCS_SUCCESS );\r
+ p_mad->context1 = NULL;\r
+ \r
+ KeAcquireInStackQueuedSpinLockAtDpcLevel( &gp_cep_mgr->lock, &hdl );\r
+- /* Clear the sent MAD pointer so that we don't try cancelling again. */\r
+- if( p_cep->p_send_mad == p_mad )\r
+- p_cep->p_send_mad = NULL;\r
+-\r
+- switch( p_mad->status )\r
++ if( p_cep->p_send_mad != p_mad )\r
+ {\r
+- case IB_WCS_SUCCESS:\r
+ KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
+ ib_put_mad( p_mad );\r
+- break;\r
+-\r
+- case IB_WCS_CANCELED:\r
+- if( p_cep->state != CEP_STATE_REQ_SENT &&\r
+- p_cep->state != CEP_STATE_REQ_MRA_RCVD &&\r
+- p_cep->state != CEP_STATE_REP_SENT &&\r
+- p_cep->state != CEP_STATE_REP_MRA_RCVD &&\r
+- p_cep->state != CEP_STATE_LAP_SENT &&\r
+- p_cep->state != CEP_STATE_LAP_MRA_RCVD &&\r
+- p_cep->state != CEP_STATE_DREQ_SENT &&\r
+- p_cep->state != CEP_STATE_SREQ_SENT )\r
+- {\r
+- KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
+- ib_put_mad( p_mad );\r
+- break;\r
+- }\r
+- /* Treat as a timeout so we don't stall the state machine. */\r
+- p_mad->status = IB_WCS_TIMEOUT_RETRY_ERR;\r
+-\r
+- /* Fall through. */\r
+- case IB_WCS_TIMEOUT_RETRY_ERR:\r
+- default:\r
+- /* Timeout. Reject the connection. */\r
+- switch( p_cep->state )\r
+- {\r
+- case CEP_STATE_REQ_SENT:\r
+- case CEP_STATE_REQ_MRA_RCVD:\r
+- case CEP_STATE_REP_SENT:\r
+- case CEP_STATE_REP_MRA_RCVD:\r
+- /* Send the REJ. */\r
+- __reject_timeout( p_port_cep, p_cep, p_mad );\r
+- __remove_cep( p_cep );\r
+- p_cep->state = CEP_STATE_IDLE;\r
+- break;\r
+-\r
+- case CEP_STATE_DREQ_DESTROY:\r
+- p_cep->state = CEP_STATE_DESTROY;\r
+- __insert_timewait( p_cep );\r
+- /* Fall through. */\r
++ goto done;\r
++ }\r
+ \r
+- case CEP_STATE_DESTROY:\r
+- KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
+- ib_put_mad( p_mad );\r
+- goto done;\r
++ /* Clear the sent MAD pointer so that we don't try cancelling again. */\r
++ p_cep->p_send_mad = NULL;\r
+ \r
+- case CEP_STATE_DREQ_SENT:\r
+- /*\r
+- * Make up a DREP mad so we can respond if we receive\r
+- * a DREQ while in timewait.\r
+- */\r
+- __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );\r
+- __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );\r
+- p_cep->state = CEP_STATE_TIMEWAIT;\r
+- __insert_timewait( p_cep );\r
+- break;\r
++ switch( p_cep->state )\r
++ {\r
++ case CEP_STATE_REQ_SENT:\r
++ case CEP_STATE_REQ_MRA_RCVD:\r
++ case CEP_STATE_REP_SENT:\r
++ case CEP_STATE_REP_MRA_RCVD:\r
++ /* Send the REJ. */\r
++ __reject_timeout( p_port_cep, p_cep, p_mad );\r
++ __remove_cep( p_cep );\r
++ p_cep->state = CEP_STATE_IDLE;\r
++ break;\r
+ \r
+- case CEP_STATE_LAP_SENT:\r
+- /*\r
+- * Before CEP was sent, we have been in CEP_STATE_ESTABLISHED as we\r
+- * failed to send, we return to that state.\r
+- */\r
+- p_cep->state = CEP_STATE_ESTABLISHED;\r
+- break;\r
+- default:\r
+- break;\r
+- }\r
++ case CEP_STATE_DREQ_DESTROY:\r
++ p_cep->state = CEP_STATE_DESTROY;\r
++ __insert_timewait( p_cep );\r
++ /* Fall through. */\r
+ \r
+- status = __cep_queue_mad( p_cep, p_mad );\r
+- CL_ASSERT( status != IB_INVALID_STATE );\r
++ case CEP_STATE_DESTROY:\r
+ KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
++ ib_put_mad( p_mad );\r
++ goto done;\r
+ \r
+- if( status == IB_SUCCESS )\r
+- __process_cep( p_cep );\r
++ case CEP_STATE_DREQ_SENT:\r
++ /*\r
++ * Make up a DREP mad so we can respond if we receive\r
++ * a DREQ while in timewait.\r
++ */\r
++ __format_mad_hdr( &p_cep->mads.drep.hdr, p_cep, CM_DREP_ATTR_ID );\r
++ __format_drep( p_cep, NULL, 0, &p_cep->mads.drep );\r
++ p_cep->state = CEP_STATE_TIMEWAIT;\r
++ __insert_timewait( p_cep );\r
++ break;\r
++\r
++ case CEP_STATE_LAP_SENT:\r
++ /*\r
++ * Before CEP was sent, we have been in CEP_STATE_ESTABLISHED as we\r
++ * failed to send, we return to that state.\r
++ */\r
++ p_cep->state = CEP_STATE_ESTABLISHED;\r
++ break;\r
++ default:\r
+ break;\r
+ }\r
+ \r
++ status = __cep_queue_mad( p_cep, p_mad );\r
++ CL_ASSERT( status != IB_INVALID_STATE );\r
++ KeReleaseInStackQueuedSpinLockFromDpcLevel( &hdl );\r
++\r
++ if( status == IB_SUCCESS )\r
++ __process_cep( p_cep );\r
++\r
+ done:\r
+ pfn_destroy_cb = p_cep->pfn_destroy_cb;\r
+ cep_context = p_cep->context;\r
+@@ -3938,12 +3912,8 @@ __cleanup_cep(
+ CL_ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );\r
+ \r
+ /* If we've already come through here, we're done. */\r
+- if( p_cep->state == CEP_STATE_DESTROY ||\r
+- p_cep->state == CEP_STATE_DREQ_DESTROY )\r
+- {\r
+- AL_EXIT( AL_DBG_CM );\r
+- return -1;\r
+- }\r
++ CL_ASSERT( p_cep->state != CEP_STATE_DESTROY &&\r
++ p_cep->state != CEP_STATE_DREQ_DESTROY );\r
+ \r
+ /* Cleanup the pending MAD list. */\r
+ while( p_cep->p_mad_head )
--- /dev/null
+Bottom: 6a0d87b4622bc7319d396436b319ad51c6ae3636
+Top: ed7908e04b3bbaa4e2413f01b2efe982eb5349d8
+Author: oushandling code to queue a separate data structure, so that all requestscan remain on the IO queues.Signed-off-by: Sean Hefty sean.hefty@intel.com <oushandling code to queue a separate data structure, so that all requestscan remain on the IO queues.Signed-off-by: Sean Hefty sean.hefty@intel.com@ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86>
+Date: 2010-01-04 19:00:54 +0000
+
+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>
+
+git-svn-id: svn://openib.tc.cornell.edu/gen1@2649 ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86
+
+
+---
+
+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;