netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / datapath-windows / ovsext / User.c
index f6b1157..e97f2b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 VMware, Inc.
+ * Copyright (c) 2014, 2016 VMware, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include "precomp.h"
 
-#include "Switch.h"
-#include "Vport.h"
-#include "Event.h"
-#include "User.h"
 #include "Datapath.h"
-#include "PacketIO.h"
-#include "Checksum.h"
-#include "NetProto.h"
+#include "Debug.h"
+#include "Event.h"
 #include "Flow.h"
-#include "TunnelIntf.h"
 #include "Jhash.h"
+#include "NetProto.h"
+#include "Offload.h"
+#include "PacketIO.h"
+#include "Switch.h"
+#include "TunnelIntf.h"
+#include "User.h"
+#include "Vport.h"
 
 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
 #endif
 #define OVS_DBG_MOD OVS_DBG_USER
-#include "Debug.h"
 
 POVS_PACKET_QUEUE_ELEM OvsGetNextPacket(POVS_OPEN_INSTANCE instance);
 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
@@ -48,6 +48,20 @@ OVS_USER_STATS ovsUserStats;
 static VOID _MapNlAttrToOvsPktExec(PNL_ATTR *nlAttrs, PNL_ATTR *keyAttrs,
                                    OvsPacketExecute  *execute);
 extern NL_POLICY nlFlowKeyPolicy[];
+extern UINT32 nlFlowKeyPolicyLen;
+
+static __inline VOID
+OvsAcquirePidHashLock()
+{
+    NdisAcquireSpinLock(&(gOvsSwitchContext->pidHashLock));
+}
+
+static __inline VOID
+OvsReleasePidHashLock()
+{
+    NdisReleaseSpinLock(&(gOvsSwitchContext->pidHashLock));
+}
+
 
 static VOID
 OvsPurgePacketQueue(POVS_USER_PACKET_QUEUE queue,
@@ -72,7 +86,7 @@ OvsPurgePacketQueue(POVS_USER_PACKET_QUEUE queue,
     LIST_FORALL_SAFE(&tmp, link, next) {
         RemoveEntryList(link);
         elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
-        OvsFreeMemory(elem);
+        OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
     }
 }
 
@@ -85,6 +99,7 @@ OvsCleanupPacketQueue(POVS_OPEN_INSTANCE instance)
     LIST_ENTRY tmp;
     PIRP irp = NULL;
 
+    ASSERT(instance);
     InitializeListHead(&tmp);
     queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue;
     if (queue) {
@@ -118,13 +133,21 @@ OvsCleanupPacketQueue(POVS_OPEN_INSTANCE instance)
     LIST_FORALL_SAFE(&tmp, link, next) {
         RemoveEntryList(link);
         elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
-        OvsFreeMemory(elem);
+        OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
     }
     if (irp) {
         OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
     }
     if (queue) {
-        OvsFreeMemory(queue);
+        OvsFreeMemoryWithTag(queue, OVS_USER_POOL_TAG);
+    }
+
+    /* Verify if gOvsSwitchContext exists. */
+    if (gOvsSwitchContext) {
+        /* Remove the instance from pidHashArray */
+        OvsAcquirePidHashLock();
+        OvsDelPidInstance(gOvsSwitchContext, instance->pid);
+        OvsReleasePidHashLock();
     }
 }
 
@@ -136,24 +159,12 @@ OvsSubscribeDpIoctl(PVOID instanceP,
     POVS_USER_PACKET_QUEUE queue;
     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)instanceP;
 
-    OvsAcquireCtrlLock();
-    if (!gOvsSwitchContext) {
-        OvsReleaseCtrlLock();
-        return STATUS_INVALID_PARAMETER;
-    }
-    OvsReleaseCtrlLock();
-
     if (instance->packetQueue && !join) {
         /* unsubscribe */
         OvsCleanupPacketQueue(instance);
-
-        OvsAcquireCtrlLock();
-        /* Remove the instance from pidHashArray */
-        OvsDelPidInstance(gOvsSwitchContext, pid);
-        OvsReleaseCtrlLock();
-
     } else if (instance->packetQueue == NULL && join) {
-        queue = (POVS_USER_PACKET_QUEUE) OvsAllocateMemory(sizeof *queue);
+        queue = (POVS_USER_PACKET_QUEUE) OvsAllocateMemoryWithTag(
+            sizeof *queue, OVS_USER_POOL_TAG);
         if (queue == NULL) {
             return STATUS_NO_MEMORY;
         }
@@ -168,10 +179,10 @@ OvsSubscribeDpIoctl(PVOID instanceP,
         instance->packetQueue = queue;
         NdisReleaseSpinLock(&queue->queueLock);
 
-        OvsAcquireCtrlLock();
+        OvsAcquirePidHashLock();
         /* Insert the instance to pidHashArray */
         OvsAddPidInstance(gOvsSwitchContext, pid, instance);
-        OvsReleaseCtrlLock();
+        OvsReleasePidHashLock();
 
     } else {
         /* user mode should call only once for subscribe */
@@ -231,7 +242,7 @@ OvsReadDpIoctl(PFILE_OBJECT fileObject,
         }
 
         *replyLen = len;
-        OvsFreeMemory(elem);
+        OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
     }
     return STATUS_SUCCESS;
 }
@@ -246,50 +257,6 @@ OvsAllocateForwardingContextForNBL(POVS_SWITCH_CONTEXT switchContext,
             switchContext->NdisSwitchContext, nbl);
 }
 
-/*
- * --------------------------------------------------------------------------
- * This function allocates all the stuff necessary for creating an NBL from the
- * input buffer of specified length, namely, a nonpaged data buffer of size
- * length, an MDL from it, and a NB and NBL from it. It does not allocate an NBL
- * context yet. It also copies data from the specified buffer to the NBL.
- * --------------------------------------------------------------------------
- */
-PNET_BUFFER_LIST
-OvsAllocateNBLForUserBuffer(POVS_SWITCH_CONTEXT switchContext,
-                            PVOID userBuffer,
-                            ULONG length)
-{
-    UINT8 *data = NULL;
-    PNET_BUFFER_LIST nbl = NULL;
-    PNET_BUFFER nb;
-    PMDL mdl;
-
-    if (length > OVS_DEFAULT_DATA_SIZE) {
-        nbl = OvsAllocateVariableSizeNBL(switchContext, length,
-                                         OVS_DEFAULT_HEADROOM_SIZE);
-
-    } else {
-        nbl = OvsAllocateFixSizeNBL(switchContext, length,
-                                    OVS_DEFAULT_HEADROOM_SIZE);
-    }
-    if (nbl == NULL) {
-        return NULL;
-    }
-
-    nb = NET_BUFFER_LIST_FIRST_NB(nbl);
-    mdl = NET_BUFFER_CURRENT_MDL(nb);
-    data = (PUINT8)MmGetSystemAddressForMdlSafe(mdl, LowPagePriority) +
-                    NET_BUFFER_CURRENT_MDL_OFFSET(nb);
-    if (!data) {
-        OvsCompleteNBL(switchContext, nbl, TRUE);
-        return NULL;
-    }
-
-    NdisMoveMemory(data, userBuffer, length);
-
-    return nbl;
-}
-
 /*
  *----------------------------------------------------------------------------
  *  OvsNlExecuteCmdHandler --
@@ -329,7 +296,8 @@ OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
 
     /* Get all the top level Flow attributes */
     if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr),
-                     nlPktExecPolicy, nlAttrs, ARRAY_SIZE(nlAttrs)))
+                     nlPktExecPolicy, ARRAY_SIZE(nlPktExecPolicy),
+                     nlAttrs, ARRAY_SIZE(nlAttrs)))
                      != TRUE) {
         OVS_LOG_ERROR("Attr Parsing failed for msg: %p",
                        nlMsgHdr);
@@ -343,8 +311,8 @@ OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     /* Get flow keys attributes */
     if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset,
                            NlAttrLen(nlAttrs[OVS_PACKET_ATTR_KEY]),
-                           nlFlowKeyPolicy, keyAttrs,
-                           ARRAY_SIZE(keyAttrs))) != TRUE) {
+                           nlFlowKeyPolicy, nlFlowKeyPolicyLen,
+                           keyAttrs, ARRAY_SIZE(keyAttrs))) != TRUE) {
         OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p", nlMsgHdr);
         status = STATUS_UNSUCCESSFUL;
         goto done;
@@ -358,17 +326,21 @@ OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
 
     /* Default reply that we want to send */
     if (status == STATUS_SUCCESS) {
+        BOOLEAN ok;
+
         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
                   usrParamsCtx->outputLength);
 
         /* Prepare nl Msg headers */
-        status = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
+        ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
                  nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
                  genlMsgHdr->cmd, OVS_PACKET_VERSION,
                  ovsHdr->dp_ifindex);
 
-        if (status == STATUS_SUCCESS) {
+        if (ok) {
             *replyLen = msgOut->nlMsg.nlmsgLen;
+        } else {
+            status = STATUS_INVALID_BUFFER_SIZE;
         }
     } else {
         /* Map NTSTATUS to NL_ERROR */
@@ -382,7 +354,7 @@ OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
 
             POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
                                            usrParamsCtx->outputBuffer;
-            BuildErrorMsg(msgIn, msgError, nlError);
+            NlBuildErrorMsg(msgIn, msgError, nlError);
             *replyLen = msgError->nlMsg.nlmsgLen;
             status = STATUS_SUCCESS;
             goto done;
@@ -425,15 +397,9 @@ OvsExecuteDpIoctl(OvsPacketExecute *execute)
     OVS_PACKET_HDR_INFO layers;
     POVS_VPORT_ENTRY vport;
 
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL) {
-        status = STATUS_INVALID_PARAMETER;
-        goto unlock;
-    }
-
     if (execute->packetLen == 0) {
         status = STATUS_INVALID_PARAMETER;
-        goto unlock;
+        goto exit;
     }
 
     actions = execute->actions;
@@ -444,11 +410,11 @@ OvsExecuteDpIoctl(OvsPacketExecute *execute)
      * Allocate the NBL, copy the data from the userspace buffer. Allocate
      * also, the forwarding context for the packet.
      */
-    pNbl = OvsAllocateNBLForUserBuffer(gOvsSwitchContext, execute->packetBuf,
-                                       execute->packetLen);
+    pNbl = OvsAllocateNBLFromBuffer(gOvsSwitchContext, execute->packetBuf,
+                                    execute->packetLen);
     if (pNbl == NULL) {
         status = STATUS_NO_MEMORY;
-        goto unlock;
+        goto exit;
     }
 
     fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl);
@@ -463,14 +429,12 @@ OvsExecuteDpIoctl(OvsPacketExecute *execute)
     // XXX: Figure out if any of the other members of fwdDetail need to be set.
 
     ndisStatus = OvsExtractFlow(pNbl, fwdDetail->SourcePortId, &key, &layers,
-                              NULL);
+                                NULL);
     if (ndisStatus == NDIS_STATUS_SUCCESS) {
-        ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-        NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
-                              NDIS_RWL_AT_DISPATCH_LEVEL);
+        NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
         ndisStatus = OvsActionsExecute(gOvsSwitchContext, NULL, pNbl,
                                        vport ? vport->portNo :
-                                               OVS_DEFAULT_PORT_NO,
+                                               OVS_DPPORT_NUMBER_INVALID,
                                        NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP,
                                        &key, NULL, &layers, actions,
                                        execute->actionsLen);
@@ -488,8 +452,7 @@ OvsExecuteDpIoctl(OvsPacketExecute *execute)
     if (pNbl) {
         OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE);
     }
-unlock:
-    NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
     return status;
 }
 
@@ -612,7 +575,6 @@ OvsGetNextPacket(POVS_OPEN_INSTANCE instance)
 /*
  * ---------------------------------------------------------------------------
  * Given a pid, returns the corresponding USER_PACKET_QUEUE.
- * gOvsCtrlLock must be acquired before calling this API.
  * ---------------------------------------------------------------------------
  */
 POVS_USER_PACKET_QUEUE
@@ -633,7 +595,7 @@ OvsGetQueue(UINT32 pid)
 /*
  * ---------------------------------------------------------------------------
  * Given a pid, returns the corresponding instance.
- * gOvsCtrlLock must be acquired before calling this API.
+ * pidHashLock must be acquired before calling this API.
  * ---------------------------------------------------------------------------
  */
 POVS_OPEN_INSTANCE
@@ -656,7 +618,7 @@ OvsGetPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid)
 /*
  * ---------------------------------------------------------------------------
  * Given a pid and an instance. This API adds instance to pidHashArray.
- * gOvsCtrlLock must be acquired before calling this API.
+ * pidHashLock must be acquired before calling this API.
  * ---------------------------------------------------------------------------
  */
 VOID
@@ -673,7 +635,7 @@ OvsAddPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid,
 /*
  * ---------------------------------------------------------------------------
  * Given a pid and an instance. This API removes instance from pidHashArray.
- * gOvsCtrlLock must be acquired before calling this API.
+ * pidHashLock must be acquired before calling this API.
  * ---------------------------------------------------------------------------
  */
 VOID
@@ -687,55 +649,64 @@ OvsDelPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid)
 }
 
 VOID
-OvsQueuePackets(UINT32 queueId,
-                PLIST_ENTRY packetList,
+OvsQueuePackets(PLIST_ENTRY packetList,
                 UINT32 numElems)
 {
-    POVS_USER_PACKET_QUEUE queue = OvsGetQueue(queueId);
+    POVS_USER_PACKET_QUEUE upcallQueue = NULL;
     POVS_PACKET_QUEUE_ELEM elem;
-    PIRP irp = NULL;
     PLIST_ENTRY  link;
     UINT32 num = 0;
+    LIST_ENTRY dropPackets;
 
-    OVS_LOG_LOUD("Enter: queueId %u, numELems: %u",
-                  queueId, numElems);
-    if (queue == NULL) {
-        goto cleanup;
-    }
+    OVS_LOG_LOUD("Enter: numELems: %u", numElems);
 
-    NdisAcquireSpinLock(&queue->queueLock);
-    if (queue->instance == NULL) {
-        NdisReleaseSpinLock(&queue->queueLock);
-        goto cleanup;
-    } else {
-        OvsAppendList(&queue->packetList, packetList);
-        queue->numPackets += numElems;
-    }
-    if (queue->pendingIrp) {
-        PDRIVER_CANCEL cancelRoutine;
-        irp = queue->pendingIrp;
-        queue->pendingIrp = NULL;
-        cancelRoutine = IoSetCancelRoutine(irp, NULL);
-        if (cancelRoutine == NULL) {
-            irp = NULL;
-        }
-    }
-    NdisReleaseSpinLock(&queue->queueLock);
-    if (irp) {
-        OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
-    }
+    InitializeListHead(&dropPackets);
 
-cleanup:
     while (!IsListEmpty(packetList)) {
         link = RemoveHeadList(packetList);
         elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
-        OvsFreeMemory(elem);
+
+        ASSERT(elem);
+
+        OvsAcquirePidHashLock();
+
+        upcallQueue = OvsGetQueue(elem->upcallPid);
+        if (!upcallQueue) {
+            /* No upcall queue found, drop this packet. */
+            InsertTailList(&dropPackets, &elem->link);
+        } else {
+            NdisAcquireSpinLock(&upcallQueue->queueLock);
+
+            if (upcallQueue->instance == NULL) {
+                InsertTailList(&dropPackets, &elem->link);
+            } else {
+                InsertTailList(&upcallQueue->packetList, &elem->link);
+                upcallQueue->numPackets++;
+                if (upcallQueue->pendingIrp) {
+                    PIRP irp = upcallQueue->pendingIrp;
+                    PDRIVER_CANCEL cancelRoutine;
+                    upcallQueue->pendingIrp = NULL;
+                    cancelRoutine = IoSetCancelRoutine(irp, NULL);
+                    if (cancelRoutine != NULL) {
+                        OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
+                    }
+                }
+            }
+            NdisReleaseSpinLock(&upcallQueue->queueLock);
+        }
+        OvsReleasePidHashLock();
+    }
+
+    while (!IsListEmpty(&dropPackets)) {
+        link = RemoveHeadList(&dropPackets);
+        elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
+        OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
         num++;
     }
+
     OVS_LOG_LOUD("Exit: drop %u packets", num);
 }
 
-
 /*
  *----------------------------------------------------------------------------
  * OvsCreateAndAddPackets --
@@ -750,7 +721,7 @@ NTSTATUS
 OvsCreateAndAddPackets(PVOID userData,
                        UINT32 userDataLen,
                        UINT32 cmd,
-                       UINT32 inPort,
+                       POVS_VPORT_ENTRY vport,
                        OvsFlowKey *key,
                        PNET_BUFFER_LIST nbl,
                        BOOLEAN isRecv,
@@ -787,7 +758,7 @@ OvsCreateAndAddPackets(PVOID userData,
     nb = NET_BUFFER_LIST_FIRST_NB(nbl);
     while (nb) {
         elem = OvsCreateQueueNlPacket(userData, userDataLen,
-                                    cmd, inPort, key, nbl, nb,
+                                    cmd, vport, key, nbl, nb,
                                     isRecv, hdrInfo);
         if (elem) {
             InsertTailList(list, &elem->link);
@@ -816,7 +787,7 @@ OvsGetUpcallMsgSize(PVOID userData,
         size += NlAttrTotalSize(userDataLen);
     }
     /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */
-    /* Is it included in the the flwo key attr XXX */
+    /* Is it included in the flow key attr XXX */
     if (tunnelKey) {
         size += NlAttrTotalSize(OvsTunKeyAttrSize());
     }
@@ -932,6 +903,8 @@ OvsGetPid(POVS_VPORT_ENTRY vport, PNET_BUFFER nb, UINT32 *pid)
 {
     UNREFERENCED_PARAMETER(nb);
 
+    ASSERT(vport);
+
     /* XXX select a pid from an array of pids using a flow based hash */
     *pid = vport->upcallPid;
     return STATUS_SUCCESS;
@@ -964,7 +937,7 @@ POVS_PACKET_QUEUE_ELEM
 OvsCreateQueueNlPacket(PVOID userData,
                        UINT32 userDataLen,
                        UINT32 cmd,
-                       UINT32 inPort,
+                       POVS_VPORT_ENTRY vport,
                        OvsFlowKey *key,
                        PNET_BUFFER_LIST nbl,
                        PNET_BUFFER nb,
@@ -983,10 +956,6 @@ OvsCreateQueueNlPacket(PVOID userData,
     NL_BUFFER nlBuf;
     PNL_MSG_HDR nlMsg;
 
-    /* XXX pass vport in the stack rather than portNo */
-    POVS_VPORT_ENTRY vport =
-        OvsFindVportByPortNo(gOvsSwitchContext, inPort);
-
     if (vport == NULL){
         /* No vport is not fatal. */
         return NULL;
@@ -1025,18 +994,20 @@ OvsCreateQueueNlPacket(PVOID userData,
                                     dataLen + extraLen);
 
     allocLen = sizeof (OVS_PACKET_QUEUE_ELEM) + nlMsgSize;
-    elem = (POVS_PACKET_QUEUE_ELEM)OvsAllocateMemory(allocLen);
+    elem = (POVS_PACKET_QUEUE_ELEM)OvsAllocateMemoryWithTag(allocLen,
+                                                            OVS_USER_POOL_TAG);
     if (elem == NULL) {
         ovsUserStats.dropDuetoResource++;
         return NULL;
     }
     elem->hdrInfo.value = hdrInfo->value;
+    elem->upcallPid = pid;
     elem->packet.totalLen = nlMsgSize;
     /* XXX remove queueid */
     elem->packet.queue = 0;
     /* XXX  no need as the length is already in the NL attrib */
     elem->packet.userDataLen = userDataLen;
-    elem->packet.inPort = inPort;
+    elem->packet.inPort = vport->portNo;
     elem->packet.cmd = cmd;
     if (cmd == (UINT32)OVS_PACKET_CMD_MISS) {
         ovsUserStats.miss++;
@@ -1056,9 +1027,9 @@ OvsCreateQueueNlPacket(PVOID userData,
      * Since we are pre allocating memory for the NL buffer
      * the attribute settings should not fail
      */
-    if (NlFillOvsMsg(&nlBuf, OVS_WIN_NL_PACKET_FAMILY_ID, 0,
+    if (!NlFillOvsMsg(&nlBuf, OVS_WIN_NL_PACKET_FAMILY_ID, 0,
                       0, pid, (UINT8)cmd, OVS_PACKET_VERSION,
-                      gOvsSwitchContext->dpNo) != STATUS_SUCCESS) {
+                      gOvsSwitchContext->dpNo)) {
         goto fail;
     }
 
@@ -1127,6 +1098,114 @@ OvsCreateQueueNlPacket(PVOID userData,
 
     return elem;
 fail:
-    OvsFreeMemory(elem);
+    OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
     return NULL;
 }
+
+/*
+ * --------------------------------------------------------------------------
+ *  Handler for the subscription for a packet queue
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                             UINT32 *replyLen)
+{
+    NDIS_STATUS status;
+    BOOLEAN rc;
+    UINT8 join;
+    UINT32 pid;
+    const NL_POLICY policy[] =  {
+        [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 },
+        [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 }
+        };
+    PNL_ATTR attrs[ARRAY_SIZE(policy)];
+
+    UNREFERENCED_PARAMETER(replyLen);
+
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+
+    rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
+         NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, ARRAY_SIZE(policy),
+                       attrs, ARRAY_SIZE(attrs));
+    if (!rc) {
+        status = STATUS_INVALID_PARAMETER;
+        goto done;
+    }
+
+    join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_SUBSCRIBE]);
+    pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]);
+
+    /* The socket subscribed with must be the same socket we perform receive*/
+    ASSERT(pid == instance->pid);
+
+    status = OvsSubscribeDpIoctl(instance, pid, join);
+
+    /*
+     * XXX Need to add this instance to a global data structure
+     * which hold all packet based instances. The data structure (hash)
+     * should be searched through the pid field of the instance for
+     * placing the missed packet into the correct queue
+     */
+done:
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Handler for queueing an IRP used for missed packet notification. The IRP is
+ * completed when a packet received and mismatched. STATUS_PENDING is returned
+ * on success. User mode keep a pending IRP at all times.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                       UINT32 *replyLen)
+{
+    UNREFERENCED_PARAMETER(replyLen);
+
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+
+    /*
+     * XXX access to packet queue must be through acquiring a lock as user mode
+     * could unsubscribe and the instnace will be freed.
+     */
+    return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Handler for reading missed pacckets from the driver event queue. This
+ * handler is executed when user modes issues a socket receive on a socket
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                       UINT32 *replyLen)
+{
+#ifdef DBG
+    POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+#endif
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+    NTSTATUS status;
+
+    ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
+
+    /* Should never read events with a dump socket */
+    ASSERT(instance->dumpState.ovsMsg == NULL);
+
+    /* Must have an packet queue */
+    ASSERT(instance->packetQueue != NULL);
+
+    /* Output buffer has been validated while validating read dev op. */
+    ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+    /* Read a packet from the instance queue */
+    status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
+                            usrParamsCtx->outputLength, replyLen);
+    return status;
+}