/*
- * 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 "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;
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,
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);
}
}
LIST_ENTRY tmp;
PIRP irp = NULL;
+ ASSERT(instance);
InitializeListHead(&tmp);
queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue;
if (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);
}
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();
}
}
/* unsubscribe */
OvsCleanupPacketQueue(instance);
} 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;
}
queue->instance = instance;
instance->packetQueue = queue;
NdisReleaseSpinLock(&queue->queueLock);
+
+ OvsAcquirePidHashLock();
+ /* Insert the instance to pidHashArray */
+ OvsAddPidInstance(gOvsSwitchContext, pid, instance);
+ OvsReleasePidHashLock();
+
} else {
/* user mode should call only once for subscribe */
return STATUS_INVALID_PARAMETER;
}
+
return STATUS_SUCCESS;
}
}
*replyLen = len;
- OvsFreeMemory(elem);
+ OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
}
return STATUS_SUCCESS;
}
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 --
/* 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);
/* 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;
/* 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 */
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;
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;
* 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);
// 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);
if (pNbl) {
OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE);
}
-unlock:
- NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
return status;
}
return CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
}
-
+/*
+ * ---------------------------------------------------------------------------
+ * Given a pid, returns the corresponding USER_PACKET_QUEUE.
+ * ---------------------------------------------------------------------------
+ */
POVS_USER_PACKET_QUEUE
OvsGetQueue(UINT32 pid)
{
- /* XXX To be implemented. Return the queue assoiated with the pid*/
- UNREFERENCED_PARAMETER(pid);
- ASSERT(FALSE);
+ POVS_OPEN_INSTANCE instance;
+ POVS_USER_PACKET_QUEUE ret = NULL;
+
+ instance = OvsGetPidInstance(gOvsSwitchContext, pid);
+
+ if (instance) {
+ ret = instance->packetQueue;
+ }
+
+ return ret;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * Given a pid, returns the corresponding instance.
+ * pidHashLock must be acquired before calling this API.
+ * ---------------------------------------------------------------------------
+ */
+POVS_OPEN_INSTANCE
+OvsGetPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid)
+{
+ POVS_OPEN_INSTANCE instance;
+ PLIST_ENTRY head, link;
+ UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid),
+ OVS_HASH_BASIS);
+ head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]);
+ LIST_FORALL(head, link) {
+ instance = CONTAINING_RECORD(link, OVS_OPEN_INSTANCE, pidLink);
+ if (instance->pid == pid) {
+ return instance;
+ }
+ }
return NULL;
}
+/*
+ * ---------------------------------------------------------------------------
+ * Given a pid and an instance. This API adds instance to pidHashArray.
+ * pidHashLock must be acquired before calling this API.
+ * ---------------------------------------------------------------------------
+ */
VOID
-OvsQueuePackets(UINT32 queueId,
- PLIST_ENTRY packetList,
+OvsAddPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid,
+ POVS_OPEN_INSTANCE instance)
+{
+ PLIST_ENTRY head;
+ UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid),
+ OVS_HASH_BASIS);
+ head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]);
+ InsertHeadList(head, &(instance->pidLink));
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * Given a pid and an instance. This API removes instance from pidHashArray.
+ * pidHashLock must be acquired before calling this API.
+ * ---------------------------------------------------------------------------
+ */
+VOID
+OvsDelPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid)
+{
+ POVS_OPEN_INSTANCE instance = OvsGetPidInstance(switchContext, pid);
+
+ if (instance) {
+ RemoveEntryList(&(instance->pidLink));
+ }
+}
+
+VOID
+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 --
OvsCreateAndAddPackets(PVOID userData,
UINT32 userDataLen,
UINT32 cmd,
- UINT32 inPort,
+ POVS_VPORT_ENTRY vport,
OvsFlowKey *key,
PNET_BUFFER_LIST nbl,
BOOLEAN isRecv,
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);
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());
}
{
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;
OvsCreateQueueNlPacket(PVOID userData,
UINT32 userDataLen,
UINT32 cmd,
- UINT32 inPort,
+ POVS_VPORT_ENTRY vport,
OvsFlowKey *key,
PNET_BUFFER_LIST nbl,
PNET_BUFFER nb,
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;
}
- if (!OvsGetPid(vport, nb, &pid)) {
+ OvsGetPid(vport, nb, &pid);
+
+ if (!pid) {
/*
* There is no userspace queue created yet, so there is no point for
* creating a new packet to be queued.
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++;
* 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;
}
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;
+}