datapath-windows: move packet read code to User.c
[cascardo/ovs.git] / datapath-windows / ovsext / User.c
1 /*
2  * Copyright (c) 2014 VMware, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * OvsUser.c
19  *      Manage packet queue for packet miss for userAction.
20  */
21
22
23 #include "precomp.h"
24
25 #include "Switch.h"
26 #include "Vport.h"
27 #include "Event.h"
28 #include "User.h"
29 #include "Datapath.h"
30 #include "PacketIO.h"
31 #include "Checksum.h"
32 #include "NetProto.h"
33 #include "Flow.h"
34 #include "TunnelIntf.h"
35 #include "Jhash.h"
36
37 #ifdef OVS_DBG_MOD
38 #undef OVS_DBG_MOD
39 #endif
40 #define OVS_DBG_MOD OVS_DBG_USER
41 #include "Debug.h"
42
43 POVS_PACKET_QUEUE_ELEM OvsGetNextPacket(POVS_OPEN_INSTANCE instance);
44 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
45 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
46 OVS_USER_STATS ovsUserStats;
47
48 static VOID _MapNlAttrToOvsPktExec(PNL_ATTR *nlAttrs, PNL_ATTR *keyAttrs,
49                                    OvsPacketExecute  *execute);
50 extern NL_POLICY nlFlowKeyPolicy[];
51 extern UINT32 nlFlowKeyPolicyLen;
52
53 static __inline VOID
54 OvsAcquirePidHashLock()
55 {
56     NdisAcquireSpinLock(&(gOvsSwitchContext->pidHashLock));
57 }
58
59 static __inline VOID
60 OvsReleasePidHashLock()
61 {
62     NdisReleaseSpinLock(&(gOvsSwitchContext->pidHashLock));
63 }
64
65
66 static VOID
67 OvsPurgePacketQueue(POVS_USER_PACKET_QUEUE queue,
68                     POVS_OPEN_INSTANCE instance)
69 {
70     PLIST_ENTRY link, next;
71     LIST_ENTRY tmp;
72     POVS_PACKET_QUEUE_ELEM elem;
73
74     InitializeListHead(&tmp);
75     NdisAcquireSpinLock(&queue->queueLock);
76     if (queue->instance != instance) {
77         NdisReleaseSpinLock(&queue->queueLock);
78         return;
79     }
80
81     if (queue->numPackets) {
82         OvsAppendList(&tmp, &queue->packetList);
83         queue->numPackets = 0;
84     }
85     NdisReleaseSpinLock(&queue->queueLock);
86     LIST_FORALL_SAFE(&tmp, link, next) {
87         RemoveEntryList(link);
88         elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
89         OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
90     }
91 }
92
93 VOID
94 OvsCleanupPacketQueue(POVS_OPEN_INSTANCE instance)
95 {
96     POVS_USER_PACKET_QUEUE queue;
97     POVS_PACKET_QUEUE_ELEM elem;
98     PLIST_ENTRY link, next;
99     LIST_ENTRY tmp;
100     PIRP irp = NULL;
101
102     ASSERT(instance);
103     InitializeListHead(&tmp);
104     queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue;
105     if (queue) {
106         PDRIVER_CANCEL cancelRoutine;
107         NdisAcquireSpinLock(&queue->queueLock);
108         ASSERT(queue->instance == instance);
109         /* XXX Should not happen */
110         if (queue->instance != instance) {
111             NdisReleaseSpinLock(&queue->queueLock);
112             NdisFreeSpinLock(&queue->queueLock);
113             return;
114         }
115
116         if (queue->numPackets) {
117             OvsAppendList(&tmp, &queue->packetList);
118             queue->numPackets = 0;
119         }
120         queue->instance = NULL;
121         instance->packetQueue = NULL;
122         irp = queue->pendingIrp;
123         queue->pendingIrp = NULL;
124         if (irp) {
125             cancelRoutine = IoSetCancelRoutine(irp, NULL);
126             if (cancelRoutine == NULL) {
127                 irp = NULL;
128             }
129         }
130         NdisReleaseSpinLock(&queue->queueLock);
131         NdisFreeSpinLock(&queue->queueLock);
132     }
133     LIST_FORALL_SAFE(&tmp, link, next) {
134         RemoveEntryList(link);
135         elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
136         OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
137     }
138     if (irp) {
139         OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
140     }
141     if (queue) {
142         OvsFreeMemoryWithTag(queue, OVS_USER_POOL_TAG);
143     }
144
145     /* Verify if gOvsSwitchContext exists. */
146     if (gOvsSwitchContext) {
147         /* Remove the instance from pidHashArray */
148         OvsAcquirePidHashLock();
149         OvsDelPidInstance(gOvsSwitchContext, instance->pid);
150         OvsReleasePidHashLock();
151     }
152 }
153
154 NTSTATUS
155 OvsSubscribeDpIoctl(PVOID instanceP,
156                     UINT32 pid,
157                     UINT8 join)
158 {
159     POVS_USER_PACKET_QUEUE queue;
160     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)instanceP;
161
162     if (instance->packetQueue && !join) {
163         /* unsubscribe */
164         OvsCleanupPacketQueue(instance);
165     } else if (instance->packetQueue == NULL && join) {
166         queue = (POVS_USER_PACKET_QUEUE) OvsAllocateMemoryWithTag(
167             sizeof *queue, OVS_USER_POOL_TAG);
168         if (queue == NULL) {
169             return STATUS_NO_MEMORY;
170         }
171         InitializeListHead(&(instance->pidLink));
172         instance->packetQueue = queue;
173         RtlZeroMemory(queue, sizeof (*queue));
174         NdisAllocateSpinLock(&queue->queueLock);
175         NdisAcquireSpinLock(&queue->queueLock);
176         InitializeListHead(&queue->packetList);
177         queue->pid = pid;
178         queue->instance = instance;
179         instance->packetQueue = queue;
180         NdisReleaseSpinLock(&queue->queueLock);
181
182         OvsAcquirePidHashLock();
183         /* Insert the instance to pidHashArray */
184         OvsAddPidInstance(gOvsSwitchContext, pid, instance);
185         OvsReleasePidHashLock();
186
187     } else {
188         /* user mode should call only once for subscribe */
189         return STATUS_INVALID_PARAMETER;
190     }
191
192     return STATUS_SUCCESS;
193 }
194
195
196 NTSTATUS
197 OvsReadDpIoctl(PFILE_OBJECT fileObject,
198                PVOID outputBuffer,
199                UINT32 outputLength,
200                UINT32 *replyLen)
201 {
202     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
203     POVS_PACKET_QUEUE_ELEM elem;
204     UINT32 len;
205
206 #define TCP_CSUM_OFFSET  16
207 #define UDP_CSUM_OFFSET  6
208     ASSERT(instance);
209
210     if (instance->packetQueue == NULL) {
211         return STATUS_INVALID_PARAMETER;
212     }
213     if (outputLength < (sizeof (OVS_PACKET_INFO) + OVS_MIN_PACKET_SIZE)) {
214         return STATUS_BUFFER_TOO_SMALL;
215     }
216
217     elem = OvsGetNextPacket(instance);
218     if (elem) {
219         /*
220          * XXX revisit this later
221          */
222         len = elem->packet.totalLen > outputLength ? outputLength :
223                  elem->packet.totalLen;
224
225         if ((elem->hdrInfo.tcpCsumNeeded || elem->hdrInfo.udpCsumNeeded) &&
226             len == elem->packet.totalLen) {
227             UINT16 sum, *ptr;
228             UINT16 size = (UINT16)(elem->packet.payload - elem->packet.data +
229                                   elem->hdrInfo.l4Offset);
230             RtlCopyMemory(outputBuffer, &elem->packet.data, size);
231             ASSERT(len - size >= elem->hdrInfo.l4PayLoad);
232             sum = CopyAndCalculateChecksum((UINT8 *)outputBuffer + size,
233                                            (UINT8 *)&elem->packet.data + size,
234                                            elem->hdrInfo.l4PayLoad, 0);
235             ptr =(UINT16 *)((UINT8 *)outputBuffer + size +
236                             (elem->hdrInfo.tcpCsumNeeded ?
237                              TCP_CSUM_OFFSET : UDP_CSUM_OFFSET));
238             *ptr = sum;
239             ovsUserStats.l4Csum++;
240         } else {
241             RtlCopyMemory(outputBuffer, &elem->packet.data, len);
242         }
243
244         *replyLen = len;
245         OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
246     }
247     return STATUS_SUCCESS;
248 }
249
250 /* Helper function to allocate a Forwarding Context for an NBL */
251 NTSTATUS
252 OvsAllocateForwardingContextForNBL(POVS_SWITCH_CONTEXT switchContext,
253                                    PNET_BUFFER_LIST nbl)
254 {
255     return switchContext->NdisSwitchHandlers.
256         AllocateNetBufferListForwardingContext(
257             switchContext->NdisSwitchContext, nbl);
258 }
259
260 /*
261  * --------------------------------------------------------------------------
262  * This function allocates all the stuff necessary for creating an NBL from the
263  * input buffer of specified length, namely, a nonpaged data buffer of size
264  * length, an MDL from it, and a NB and NBL from it. It does not allocate an NBL
265  * context yet. It also copies data from the specified buffer to the NBL.
266  * --------------------------------------------------------------------------
267  */
268 PNET_BUFFER_LIST
269 OvsAllocateNBLForUserBuffer(POVS_SWITCH_CONTEXT switchContext,
270                             PVOID userBuffer,
271                             ULONG length)
272 {
273     UINT8 *data = NULL;
274     PNET_BUFFER_LIST nbl = NULL;
275     PNET_BUFFER nb;
276     PMDL mdl;
277
278     if (length > OVS_DEFAULT_DATA_SIZE) {
279         nbl = OvsAllocateVariableSizeNBL(switchContext, length,
280                                          OVS_DEFAULT_HEADROOM_SIZE);
281
282     } else {
283         nbl = OvsAllocateFixSizeNBL(switchContext, length,
284                                     OVS_DEFAULT_HEADROOM_SIZE);
285     }
286     if (nbl == NULL) {
287         return NULL;
288     }
289
290     nb = NET_BUFFER_LIST_FIRST_NB(nbl);
291     mdl = NET_BUFFER_CURRENT_MDL(nb);
292     data = (PUINT8)MmGetSystemAddressForMdlSafe(mdl, LowPagePriority) +
293                     NET_BUFFER_CURRENT_MDL_OFFSET(nb);
294     if (!data) {
295         OvsCompleteNBL(switchContext, nbl, TRUE);
296         return NULL;
297     }
298
299     NdisMoveMemory(data, userBuffer, length);
300
301     return nbl;
302 }
303
304 /*
305  *----------------------------------------------------------------------------
306  *  OvsNlExecuteCmdHandler --
307  *    Handler for OVS_PACKET_CMD_EXECUTE command.
308  *----------------------------------------------------------------------------
309  */
310 NTSTATUS
311 OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
312                        UINT32 *replyLen)
313 {
314     NTSTATUS status = STATUS_SUCCESS;
315     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
316     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
317     PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
318     PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
319     POVS_HDR ovsHdr = &(msgIn->ovsHdr);
320
321     PNL_ATTR nlAttrs[__OVS_PACKET_ATTR_MAX];
322     PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX] = {NULL};
323
324     UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN;
325     UINT32 keyAttrOffset = 0;
326     OvsPacketExecute execute;
327     NL_ERROR nlError = NL_ERROR_SUCCESS;
328     NL_BUFFER nlBuf;
329
330     static const NL_POLICY nlPktExecPolicy[] = {
331         [OVS_PACKET_ATTR_PACKET] = {.type = NL_A_UNSPEC, .optional = FALSE},
332         [OVS_PACKET_ATTR_KEY] = {.type = NL_A_UNSPEC, .optional = FALSE},
333         [OVS_PACKET_ATTR_ACTIONS] = {.type = NL_A_UNSPEC, .optional = FALSE},
334         [OVS_PACKET_ATTR_USERDATA] = {.type = NL_A_UNSPEC, .optional = TRUE},
335         [OVS_PACKET_ATTR_EGRESS_TUN_KEY] = {.type = NL_A_UNSPEC,
336                                             .optional = TRUE}
337     };
338
339     RtlZeroMemory(&execute, sizeof(OvsPacketExecute));
340
341     /* Get all the top level Flow attributes */
342     if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr),
343                      nlPktExecPolicy, ARRAY_SIZE(nlPktExecPolicy),
344                      nlAttrs, ARRAY_SIZE(nlAttrs)))
345                      != TRUE) {
346         OVS_LOG_ERROR("Attr Parsing failed for msg: %p",
347                        nlMsgHdr);
348         status = STATUS_UNSUCCESSFUL;
349         goto done;
350     }
351
352     keyAttrOffset = (UINT32)((PCHAR)nlAttrs[OVS_PACKET_ATTR_KEY] -
353                     (PCHAR)nlMsgHdr);
354
355     /* Get flow keys attributes */
356     if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset,
357                            NlAttrLen(nlAttrs[OVS_PACKET_ATTR_KEY]),
358                            nlFlowKeyPolicy, nlFlowKeyPolicyLen,
359                            keyAttrs, ARRAY_SIZE(keyAttrs))) != TRUE) {
360         OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p", nlMsgHdr);
361         status = STATUS_UNSUCCESSFUL;
362         goto done;
363     }
364
365     execute.dpNo = ovsHdr->dp_ifindex;
366
367     _MapNlAttrToOvsPktExec(nlAttrs, keyAttrs, &execute);
368
369     status = OvsExecuteDpIoctl(&execute);
370
371     /* Default reply that we want to send */
372     if (status == STATUS_SUCCESS) {
373         BOOLEAN ok;
374
375         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
376                   usrParamsCtx->outputLength);
377
378         /* Prepare nl Msg headers */
379         ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
380                  nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
381                  genlMsgHdr->cmd, OVS_PACKET_VERSION,
382                  ovsHdr->dp_ifindex);
383
384         if (ok) {
385             *replyLen = msgOut->nlMsg.nlmsgLen;
386         } else {
387             status = STATUS_INVALID_BUFFER_SIZE;
388         }
389     } else {
390         /* Map NTSTATUS to NL_ERROR */
391         nlError = NlMapStatusToNlErr(status);
392
393         /* As of now there are no transactional errors in the implementation.
394          * Once we have them then we need to map status to correct
395          * nlError value, so that below mentioned code gets hit. */
396         if ((nlError != NL_ERROR_SUCCESS) &&
397             (usrParamsCtx->outputBuffer)) {
398
399             POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
400                                            usrParamsCtx->outputBuffer;
401             NlBuildErrorMsg(msgIn, msgError, nlError);
402             *replyLen = msgError->nlMsg.nlmsgLen;
403             status = STATUS_SUCCESS;
404             goto done;
405         }
406     }
407
408 done:
409     return status;
410 }
411
412 /*
413  *----------------------------------------------------------------------------
414  *  _MapNlAttrToOvsPktExec --
415  *    Maps input Netlink attributes to OvsPacketExecute.
416  *----------------------------------------------------------------------------
417  */
418 static VOID
419 _MapNlAttrToOvsPktExec(PNL_ATTR *nlAttrs, PNL_ATTR *keyAttrs,
420                        OvsPacketExecute *execute)
421 {
422     execute->packetBuf = NlAttrGet(nlAttrs[OVS_PACKET_ATTR_PACKET]);
423     execute->packetLen = NlAttrGetSize(nlAttrs[OVS_PACKET_ATTR_PACKET]);
424
425     execute->actions = NlAttrGet(nlAttrs[OVS_PACKET_ATTR_ACTIONS]);
426     execute->actionsLen = NlAttrGetSize(nlAttrs[OVS_PACKET_ATTR_ACTIONS]);
427
428     execute->inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]);
429 }
430
431 NTSTATUS
432 OvsExecuteDpIoctl(OvsPacketExecute *execute)
433 {
434     NTSTATUS                    status = STATUS_SUCCESS;
435     NTSTATUS                    ndisStatus;
436     LOCK_STATE_EX               lockState;
437     PNET_BUFFER_LIST pNbl;
438     PNL_ATTR actions;
439     PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
440     OvsFlowKey key;
441     OVS_PACKET_HDR_INFO layers;
442     POVS_VPORT_ENTRY vport;
443
444     if (execute->packetLen == 0) {
445         status = STATUS_INVALID_PARAMETER;
446         goto exit;
447     }
448
449     actions = execute->actions;
450
451     ASSERT(actions);
452
453     /*
454      * Allocate the NBL, copy the data from the userspace buffer. Allocate
455      * also, the forwarding context for the packet.
456      */
457     pNbl = OvsAllocateNBLForUserBuffer(gOvsSwitchContext, execute->packetBuf,
458                                        execute->packetLen);
459     if (pNbl == NULL) {
460         status = STATUS_NO_MEMORY;
461         goto exit;
462     }
463
464     fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl);
465     vport = OvsFindVportByPortNo(gOvsSwitchContext, execute->inPort);
466     if (vport) {
467         fwdDetail->SourcePortId = vport->portId;
468         fwdDetail->SourceNicIndex = vport->nicIndex;
469     } else {
470         fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
471         fwdDetail->SourceNicIndex = 0;
472     }
473     // XXX: Figure out if any of the other members of fwdDetail need to be set.
474
475     ndisStatus = OvsExtractFlow(pNbl, fwdDetail->SourcePortId, &key, &layers,
476                                 NULL);
477     if (ndisStatus == NDIS_STATUS_SUCCESS) {
478         NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
479         ndisStatus = OvsActionsExecute(gOvsSwitchContext, NULL, pNbl,
480                                        vport ? vport->portNo :
481                                                OVS_DEFAULT_PORT_NO,
482                                        NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP,
483                                        &key, NULL, &layers, actions,
484                                        execute->actionsLen);
485         pNbl = NULL;
486         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
487     }
488     if (ndisStatus != NDIS_STATUS_SUCCESS) {
489         if (ndisStatus == NDIS_STATUS_NOT_SUPPORTED) {
490             status = STATUS_NOT_SUPPORTED;
491         } else {
492             status = STATUS_UNSUCCESSFUL;
493         }
494     }
495
496     if (pNbl) {
497         OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE);
498     }
499 exit:
500     return status;
501 }
502
503
504 NTSTATUS
505 OvsPurgeDpIoctl(PFILE_OBJECT fileObject)
506 {
507     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
508     POVS_USER_PACKET_QUEUE queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue;
509
510     if (queue == NULL) {
511         return STATUS_INVALID_PARAMETER;
512     }
513     OvsPurgePacketQueue(queue, instance);
514     return STATUS_SUCCESS;
515 }
516
517 VOID
518 OvsCancelIrpDatapath(PDEVICE_OBJECT deviceObject,
519                      PIRP irp)
520 {
521     PIO_STACK_LOCATION irpSp;
522     PFILE_OBJECT fileObject;
523     POVS_OPEN_INSTANCE instance;
524     POVS_USER_PACKET_QUEUE queue = NULL;
525
526     UNREFERENCED_PARAMETER(deviceObject);
527
528     IoReleaseCancelSpinLock(irp->CancelIrql);
529     irpSp = IoGetCurrentIrpStackLocation(irp);
530     fileObject = irpSp->FileObject;
531
532     if (fileObject == NULL) {
533         goto done;
534     }
535     NdisAcquireSpinLock(gOvsCtrlLock);
536     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
537     if (instance) {
538         queue = instance->packetQueue;
539     }
540     if (instance == NULL || queue == NULL) {
541         NdisReleaseSpinLock(gOvsCtrlLock);
542         goto done;
543     }
544     NdisReleaseSpinLock(gOvsCtrlLock);
545     NdisAcquireSpinLock(&queue->queueLock);
546     if (queue->pendingIrp == irp) {
547         queue->pendingIrp = NULL;
548     }
549     NdisReleaseSpinLock(&queue->queueLock);
550 done:
551     OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
552 }
553
554
555 NTSTATUS
556 OvsWaitDpIoctl(PIRP irp, PFILE_OBJECT fileObject)
557 {
558     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
559     POVS_USER_PACKET_QUEUE queue =
560                (POVS_USER_PACKET_QUEUE)instance->packetQueue;
561     NTSTATUS status = STATUS_SUCCESS;
562     BOOLEAN cancelled = FALSE;
563
564     if (queue == NULL) {
565         return STATUS_INVALID_PARAMETER;
566     }
567     NdisAcquireSpinLock(&queue->queueLock);
568     if (queue->instance != instance) {
569         NdisReleaseSpinLock(&queue->queueLock);
570         return STATUS_INVALID_PARAMETER;
571     }
572     if (queue->pendingIrp) {
573         NdisReleaseSpinLock(&queue->queueLock);
574         return STATUS_DEVICE_BUSY;
575     }
576     if (queue->numPackets == 0) {
577         PDRIVER_CANCEL cancelRoutine;
578         IoMarkIrpPending(irp);
579         IoSetCancelRoutine(irp, OvsCancelIrpDatapath);
580         if (irp->Cancel) {
581             cancelRoutine = IoSetCancelRoutine(irp, NULL);
582             if (cancelRoutine) {
583                 cancelled = TRUE;
584             }
585         } else {
586             queue->pendingIrp = irp;
587         }
588         status = STATUS_PENDING;
589     }
590     NdisReleaseSpinLock(&queue->queueLock);
591     if (cancelled) {
592         OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
593         OVS_LOG_INFO("Datapath IRP cancelled: %p", irp);
594     }
595     return status;
596 }
597
598
599 POVS_PACKET_QUEUE_ELEM
600 OvsGetNextPacket(POVS_OPEN_INSTANCE instance)
601 {
602     POVS_USER_PACKET_QUEUE queue;
603     PLIST_ENTRY link;
604     queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue;
605     if (queue == NULL) {
606         return NULL;
607     }
608     NdisAcquireSpinLock(&queue->queueLock);
609     if (queue->instance != instance || queue->numPackets == 0) {
610         NdisReleaseSpinLock(&queue->queueLock);
611         return NULL;
612     }
613     link = RemoveHeadList(&queue->packetList);
614     queue->numPackets--;
615     NdisReleaseSpinLock(&queue->queueLock);
616     return CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
617 }
618
619 /*
620  * ---------------------------------------------------------------------------
621  * Given a pid, returns the corresponding USER_PACKET_QUEUE.
622  * ---------------------------------------------------------------------------
623  */
624 POVS_USER_PACKET_QUEUE
625 OvsGetQueue(UINT32 pid)
626 {
627     POVS_OPEN_INSTANCE instance;
628     POVS_USER_PACKET_QUEUE ret = NULL;
629
630     instance = OvsGetPidInstance(gOvsSwitchContext, pid);
631
632     if (instance) {
633         ret = instance->packetQueue;
634     }
635
636     return ret;
637 }
638
639 /*
640  * ---------------------------------------------------------------------------
641  * Given a pid, returns the corresponding instance.
642  * pidHashLock must be acquired before calling this API.
643  * ---------------------------------------------------------------------------
644  */
645 POVS_OPEN_INSTANCE
646 OvsGetPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid)
647 {
648     POVS_OPEN_INSTANCE instance;
649     PLIST_ENTRY head, link;
650     UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid),
651                                 OVS_HASH_BASIS);
652     head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]);
653     LIST_FORALL(head, link) {
654         instance = CONTAINING_RECORD(link, OVS_OPEN_INSTANCE, pidLink);
655         if (instance->pid == pid) {
656             return instance;
657         }
658     }
659     return NULL;
660 }
661
662 /*
663  * ---------------------------------------------------------------------------
664  * Given a pid and an instance. This API adds instance to pidHashArray.
665  * pidHashLock must be acquired before calling this API.
666  * ---------------------------------------------------------------------------
667  */
668 VOID
669 OvsAddPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid,
670                   POVS_OPEN_INSTANCE instance)
671 {
672     PLIST_ENTRY head;
673     UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid),
674                                 OVS_HASH_BASIS);
675     head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]);
676     InsertHeadList(head, &(instance->pidLink));
677 }
678
679 /*
680  * ---------------------------------------------------------------------------
681  * Given a pid and an instance. This API removes instance from pidHashArray.
682  * pidHashLock must be acquired before calling this API.
683  * ---------------------------------------------------------------------------
684  */
685 VOID
686 OvsDelPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid)
687 {
688     POVS_OPEN_INSTANCE instance = OvsGetPidInstance(switchContext, pid);
689
690     if (instance) {
691         RemoveEntryList(&(instance->pidLink));
692     }
693 }
694
695 VOID
696 OvsQueuePackets(PLIST_ENTRY packetList,
697                 UINT32 numElems)
698 {
699     POVS_USER_PACKET_QUEUE upcallQueue = NULL;
700     POVS_PACKET_QUEUE_ELEM elem;
701     PIRP irp = NULL;
702     PLIST_ENTRY  link;
703     UINT32 num = 0;
704     LIST_ENTRY dropPackets;
705
706     OVS_LOG_LOUD("Enter: numELems: %u", numElems);
707
708     InitializeListHead(&dropPackets);
709
710     while (!IsListEmpty(packetList)) {
711         link = RemoveHeadList(packetList);
712         elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
713
714         ASSERT(elem);
715
716         OvsAcquirePidHashLock();
717
718         upcallQueue = OvsGetQueue(elem->upcallPid);
719         if (!upcallQueue) {
720             /* No upcall queue found, drop this packet. */
721             InsertTailList(&dropPackets, &elem->link);
722         } else {
723             NdisAcquireSpinLock(&upcallQueue->queueLock);
724
725             if (upcallQueue->instance == NULL) {
726                 InsertTailList(&dropPackets, &elem->link);
727             } else {
728                 InsertTailList(&upcallQueue->packetList, &elem->link);
729                 upcallQueue->numPackets++;
730                 if (upcallQueue->pendingIrp) {
731                     PDRIVER_CANCEL cancelRoutine;
732                     irp = upcallQueue->pendingIrp;
733                     upcallQueue->pendingIrp = NULL;
734                     cancelRoutine = IoSetCancelRoutine(irp, NULL);
735                     if (cancelRoutine == NULL) {
736                         irp = NULL;
737                     }
738                 }
739             }
740
741             if (irp) {
742                 OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
743             }
744
745             NdisReleaseSpinLock(&upcallQueue->queueLock);
746         }
747
748         OvsReleasePidHashLock();
749     }
750
751     while (!IsListEmpty(&dropPackets)) {
752         link = RemoveHeadList(&dropPackets);
753         elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link);
754         OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
755         num++;
756     }
757
758     OVS_LOG_LOUD("Exit: drop %u packets", num);
759 }
760
761 /*
762  *----------------------------------------------------------------------------
763  * OvsCreateAndAddPackets --
764  *
765  *  Create a packet and forwarded to user space.
766  *
767  *  This function would fragment packet if needed, and queue
768  *  each segment to user space.
769  *----------------------------------------------------------------------------
770  */
771 NTSTATUS
772 OvsCreateAndAddPackets(PVOID userData,
773                        UINT32 userDataLen,
774                        UINT32 cmd,
775                        POVS_VPORT_ENTRY vport,
776                        OvsFlowKey *key,
777                        PNET_BUFFER_LIST nbl,
778                        BOOLEAN isRecv,
779                        POVS_PACKET_HDR_INFO hdrInfo,
780                        POVS_SWITCH_CONTEXT switchContext,
781                        LIST_ENTRY *list,
782                        UINT32 *num)
783 {
784     POVS_PACKET_QUEUE_ELEM elem;
785     PNET_BUFFER_LIST newNbl = NULL;
786     PNET_BUFFER nb;
787
788     if (hdrInfo->isTcp) {
789         NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo;
790         UINT32 packetLength;
791
792         tsoInfo.Value = NET_BUFFER_LIST_INFO(nbl, TcpLargeSendNetBufferListInfo);
793         nb = NET_BUFFER_LIST_FIRST_NB(nbl);
794         packetLength = NET_BUFFER_DATA_LENGTH(nb);
795
796         OVS_LOG_TRACE("MSS %u packet len %u",
797                 tsoInfo.LsoV1Transmit.MSS, packetLength);
798         if (tsoInfo.LsoV1Transmit.MSS) {
799             OVS_LOG_TRACE("l4Offset %d", hdrInfo->l4Offset);
800             newNbl = OvsTcpSegmentNBL(switchContext, nbl, hdrInfo,
801                     tsoInfo.LsoV1Transmit.MSS , 0);
802             if (newNbl == NULL) {
803                 return NDIS_STATUS_FAILURE;
804             }
805             nbl = newNbl;
806         }
807     }
808
809     nb = NET_BUFFER_LIST_FIRST_NB(nbl);
810     while (nb) {
811         elem = OvsCreateQueueNlPacket(userData, userDataLen,
812                                     cmd, vport, key, nbl, nb,
813                                     isRecv, hdrInfo);
814         if (elem) {
815             InsertTailList(list, &elem->link);
816             (*num)++;
817         }
818         nb = NET_BUFFER_NEXT_NB(nb);
819     }
820     if (newNbl) {
821         OvsCompleteNBL(switchContext, newNbl, TRUE);
822     }
823     return NDIS_STATUS_SUCCESS;
824 }
825
826 static __inline UINT32
827 OvsGetUpcallMsgSize(PVOID userData,
828                     UINT32 userDataLen,
829                     OvsIPv4TunnelKey *tunnelKey,
830                     UINT32 payload)
831 {
832     UINT32 size = NLMSG_ALIGN(sizeof(struct ovs_header)) +
833                   NlAttrSize(payload) +
834                   NlAttrSize(OvsFlowKeyAttrSize());
835
836     /* OVS_PACKET_ATTR_USERDATA */
837     if (userData) {
838         size += NlAttrTotalSize(userDataLen);
839     }
840     /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */
841     /* Is it included in the flow key attr XXX */
842     if (tunnelKey) {
843         size += NlAttrTotalSize(OvsTunKeyAttrSize());
844     }
845     return size;
846 }
847
848 /*
849  *----------------------------------------------------------------------------
850  * This function completes the IP Header csum. record the L4 payload offset and
851  * if there is a need to calculate the TCP or UDP csum. The actual csum will be
852  * caluculated simopultaneossly with the copy of the payload to the destination
853  * buffer when the packet is read.
854  *----------------------------------------------------------------------------
855  */
856 static VOID
857 OvsCompletePacketHeader(UINT8 *packet,
858                         BOOLEAN isRecv,
859                         NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo,
860                         POVS_PACKET_HDR_INFO hdrInfoIn,
861                         POVS_PACKET_HDR_INFO hdrInfoOut)
862 {
863     if ((isRecv && csumInfo.Receive.IpChecksumValueInvalid) ||
864         (!isRecv && csumInfo.Transmit.IsIPv4 &&
865         csumInfo.Transmit.IpHeaderChecksum)) {
866         PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoOut->l3Offset);
867         ASSERT(hdrInfoIn->isIPv4);
868         ASSERT(ipHdr->Version == 4);
869         ipHdr->HeaderChecksum = IPChecksum((UINT8 *)ipHdr,
870             ipHdr->HeaderLength << 2,
871             (UINT16)~ipHdr->HeaderChecksum);
872         ovsUserStats.ipCsum++;
873     }
874     ASSERT(hdrInfoIn->tcpCsumNeeded == 0 && hdrInfoOut->udpCsumNeeded == 0);
875     /*
876      * calculate TCP/UDP pseudo checksum
877      */
878     if (isRecv && csumInfo.Receive.TcpChecksumValueInvalid) {
879         /*
880          * Only this case, we need to reclaculate pseudo checksum
881          * all other cases, it is assumed the pseudo checksum is
882          * filled already.
883          *
884          */
885         PTCP_HDR tcpHdr = (PTCP_HDR)(packet + hdrInfoIn->l4Offset);
886         if (hdrInfoIn->isIPv4) {
887             PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoIn->l3Offset);
888             hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) -
889                                     (ipHdr->HeaderLength << 2));
890             tcpHdr->th_sum = IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress,
891                                          (UINT32 *)&ipHdr->DestinationAddress,
892                                          IPPROTO_TCP, hdrInfoOut->l4PayLoad);
893         } else {
894             PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(packet + hdrInfoIn->l3Offset);
895             hdrInfoOut->l4PayLoad =
896                 (UINT16)(ntohs(ipv6Hdr->PayloadLength) +
897                 hdrInfoIn->l3Offset + sizeof(IPV6_HEADER)-
898                 hdrInfoIn->l4Offset);
899             ASSERT(hdrInfoIn->isIPv6);
900             tcpHdr->th_sum =
901                 IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress,
902                 (UINT32 *)&ipv6Hdr->DestinationAddress,
903                 IPPROTO_TCP, hdrInfoOut->l4PayLoad);
904         }
905         hdrInfoOut->tcpCsumNeeded = 1;
906         ovsUserStats.recalTcpCsum++;
907     } else if (!isRecv) {
908         if (csumInfo.Transmit.TcpChecksum) {
909             hdrInfoOut->tcpCsumNeeded = 1;
910         } else if (csumInfo.Transmit.UdpChecksum) {
911             hdrInfoOut->udpCsumNeeded = 1;
912         }
913         if (hdrInfoOut->tcpCsumNeeded || hdrInfoOut->udpCsumNeeded) {
914 #ifdef DBG
915             UINT16 sum, *ptr;
916             UINT8 proto =
917                 hdrInfoOut->tcpCsumNeeded ? IPPROTO_TCP : IPPROTO_UDP;
918 #endif
919             if (hdrInfoIn->isIPv4) {
920                 PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoIn->l3Offset);
921                 hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) -
922                     (ipHdr->HeaderLength << 2));
923 #ifdef DBG
924                 sum = IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress,
925                     (UINT32 *)&ipHdr->DestinationAddress,
926                     proto, hdrInfoOut->l4PayLoad);
927 #endif
928             } else {
929                 PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(packet +
930                     hdrInfoIn->l3Offset);
931                 hdrInfoOut->l4PayLoad =
932                     (UINT16)(ntohs(ipv6Hdr->PayloadLength) +
933                     hdrInfoIn->l3Offset + sizeof(IPV6_HEADER)-
934                     hdrInfoIn->l4Offset);
935                 ASSERT(hdrInfoIn->isIPv6);
936 #ifdef DBG
937                 sum = IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress,
938                     (UINT32 *)&ipv6Hdr->DestinationAddress,
939                     proto, hdrInfoOut->l4PayLoad);
940 #endif
941             }
942 #ifdef DBG
943             ptr = (UINT16 *)(packet + hdrInfoIn->l4Offset +
944                 (hdrInfoOut->tcpCsumNeeded ?
945             TCP_CSUM_OFFSET : UDP_CSUM_OFFSET));
946             ASSERT(*ptr == sum);
947 #endif
948         }
949     }
950 }
951
952 static NTSTATUS
953 OvsGetPid(POVS_VPORT_ENTRY vport, PNET_BUFFER nb, UINT32 *pid)
954 {
955     UNREFERENCED_PARAMETER(nb);
956
957     ASSERT(vport);
958
959     /* XXX select a pid from an array of pids using a flow based hash */
960     *pid = vport->upcallPid;
961     return STATUS_SUCCESS;
962 }
963
964 /*
965  *----------------------------------------------------------------------------
966  * OvsCreateQueueNlPacket --
967  *
968  *  Create a packet which will be forwarded to user space.
969  *
970  * InputParameter:
971  *   userData: when cmd is user action, this field contain
972  *      user action data.
973  *   userDataLen: as name indicated
974  *   cmd: either miss or user action
975  *   inPort: datapath port id from which the packet is received.
976  *   key: flow Key with a tunnel key if available
977  *   nbl:  the NET_BUFFER_LIST which contain the packet
978  *   nb: the packet
979  *   isRecv: This is used to decide how to interprete the csum info
980  *   hdrInfo: include hdr info initialized during flow extraction.
981  *
982  * Results:
983  *    NULL if fail to create the packet
984  *    The packet element otherwise
985  *----------------------------------------------------------------------------
986  */
987 POVS_PACKET_QUEUE_ELEM
988 OvsCreateQueueNlPacket(PVOID userData,
989                        UINT32 userDataLen,
990                        UINT32 cmd,
991                        POVS_VPORT_ENTRY vport,
992                        OvsFlowKey *key,
993                        PNET_BUFFER_LIST nbl,
994                        PNET_BUFFER nb,
995                        BOOLEAN isRecv,
996                        POVS_PACKET_HDR_INFO hdrInfo)
997 {
998 #define VLAN_TAG_SIZE 4
999     UINT32 allocLen, dataLen, extraLen;
1000     POVS_PACKET_QUEUE_ELEM elem;
1001     UINT8 *src, *dst;
1002     NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
1003     NDIS_NET_BUFFER_LIST_8021Q_INFO vlanInfo;
1004     OvsIPv4TunnelKey *tunnelKey = (OvsIPv4TunnelKey *)&key->tunKey;
1005     UINT32 pid;
1006     UINT32 nlMsgSize;
1007     NL_BUFFER nlBuf;
1008     PNL_MSG_HDR nlMsg;
1009
1010     if (vport == NULL){
1011         /* No vport is not fatal. */
1012         return NULL;
1013     }
1014
1015     OvsGetPid(vport, nb, &pid);
1016
1017     if (!pid) {
1018         /*
1019          * There is no userspace queue created yet, so there is no point for
1020          * creating a new packet to be queued.
1021          */
1022         return NULL;
1023     }
1024
1025     csumInfo.Value = NET_BUFFER_LIST_INFO(nbl, TcpIpChecksumNetBufferListInfo);
1026
1027     if (isRecv && (csumInfo.Receive.TcpChecksumFailed ||
1028                   (csumInfo.Receive.UdpChecksumFailed && !hdrInfo->udpCsumZero) ||
1029                   csumInfo.Receive.IpChecksumFailed)) {
1030         OVS_LOG_INFO("Packet dropped due to checksum failure.");
1031         ovsUserStats.dropDuetoChecksum++;
1032         return NULL;
1033     }
1034
1035     vlanInfo.Value = NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo);
1036     extraLen = vlanInfo.TagHeader.VlanId ? VLAN_TAG_SIZE : 0;
1037
1038     dataLen = NET_BUFFER_DATA_LENGTH(nb);
1039
1040     if (NlAttrSize(dataLen) > MAXUINT16) {
1041         return NULL;
1042     }
1043
1044     nlMsgSize = OvsGetUpcallMsgSize(userData, userDataLen, tunnelKey,
1045                                     dataLen + extraLen);
1046
1047     allocLen = sizeof (OVS_PACKET_QUEUE_ELEM) + nlMsgSize;
1048     elem = (POVS_PACKET_QUEUE_ELEM)OvsAllocateMemoryWithTag(allocLen,
1049                                                             OVS_USER_POOL_TAG);
1050     if (elem == NULL) {
1051         ovsUserStats.dropDuetoResource++;
1052         return NULL;
1053     }
1054     elem->hdrInfo.value = hdrInfo->value;
1055     elem->upcallPid = pid;
1056     elem->packet.totalLen = nlMsgSize;
1057     /* XXX remove queueid */
1058     elem->packet.queue = 0;
1059     /* XXX  no need as the length is already in the NL attrib */
1060     elem->packet.userDataLen = userDataLen;
1061     elem->packet.inPort = vport->portNo;
1062     elem->packet.cmd = cmd;
1063     if (cmd == (UINT32)OVS_PACKET_CMD_MISS) {
1064         ovsUserStats.miss++;
1065     } else if (cmd == (UINT32)OVS_PACKET_CMD_ACTION) {
1066         ovsUserStats.action++;
1067     } else {
1068         ASSERT(FALSE);
1069         goto fail;
1070     }
1071     /* XXX Should we have both packetLen and TotalLen*/
1072     elem->packet.packetLen = dataLen + extraLen;
1073
1074     NlBufInit(&nlBuf, (PCHAR)elem->packet.data, nlMsgSize);
1075
1076     /*
1077      * Initialize the OVS header
1078      * Since we are pre allocating memory for the NL buffer
1079      * the attribute settings should not fail
1080      */
1081     if (!NlFillOvsMsg(&nlBuf, OVS_WIN_NL_PACKET_FAMILY_ID, 0,
1082                       0, pid, (UINT8)cmd, OVS_PACKET_VERSION,
1083                       gOvsSwitchContext->dpNo)) {
1084         goto fail;
1085     }
1086
1087     if (MapFlowKeyToNlKey(&nlBuf, key, OVS_PACKET_ATTR_KEY,
1088                           OVS_KEY_ATTR_TUNNEL) != STATUS_SUCCESS) {
1089         goto fail;
1090     }
1091
1092     /* XXX must send OVS_PACKET_ATTR_EGRESS_TUN_KEY if set by vswtchd */
1093     if (userData){
1094         if (!NlMsgPutTailUnspec(&nlBuf, OVS_PACKET_ATTR_USERDATA,
1095                                 userData, (UINT16)userDataLen)) {
1096             goto fail;
1097         }
1098     }
1099
1100     /*
1101      * Make space for the payload to be copied and set the attribute
1102      * XXX Uninit set initilizes the buffer with xero, we don't actually need
1103      * that the payload to be initailized
1104      */
1105     dst = (UINT8 *)NlMsgPutTailUnspecUninit(&nlBuf, OVS_PACKET_ATTR_PACKET,
1106                                             (UINT16)(dataLen + extraLen));
1107     if (!dst) {
1108         goto fail;
1109     }
1110
1111     /* Store the payload for csum calculation when packet is read */
1112     elem->packet.payload = dst;
1113     dst += extraLen;
1114
1115     src = NdisGetDataBuffer(nb, dataLen, dst, 1, 0);
1116     if (src == NULL) {
1117         ovsUserStats.dropDuetoResource++;
1118         goto fail;
1119     }    else if (src != dst) {
1120         /* Copy the data from the NDIS buffer to dst. */
1121         RtlCopyMemory(dst, src, dataLen);
1122     }
1123
1124     /* Set csum if was offloaded */
1125     OvsCompletePacketHeader(dst, isRecv, csumInfo, hdrInfo, &elem->hdrInfo);
1126
1127     /*
1128      * Finally insert VLAN tag
1129      */
1130     if (extraLen) {
1131         dst = elem->packet.payload;
1132         src = dst + extraLen;
1133         ((UINT32 *)dst)[0] = ((UINT32 *)src)[0];
1134         ((UINT32 *)dst)[1] = ((UINT32 *)src)[1];
1135         ((UINT32 *)dst)[2] = ((UINT32 *)src)[2];
1136         dst += 12;
1137         ((UINT16 *)dst)[0] = htons(0x8100);
1138         ((UINT16 *)dst)[1] = htons(vlanInfo.TagHeader.VlanId |
1139             (vlanInfo.TagHeader.UserPriority << 13));
1140         elem->hdrInfo.l3Offset += VLAN_TAG_SIZE;
1141         elem->hdrInfo.l4Offset += VLAN_TAG_SIZE;
1142         ovsUserStats.vlanInsert++;
1143     }
1144
1145     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuf, 0, 0);
1146     nlMsg->nlmsgLen = NlBufSize(&nlBuf);
1147     /* 'totalLen' should be size of valid data. */
1148     elem->packet.totalLen = nlMsg->nlmsgLen;
1149
1150     return elem;
1151 fail:
1152     OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG);
1153     return NULL;
1154 }
1155
1156 /*
1157  * --------------------------------------------------------------------------
1158  *  Handler for the subscription for a packet queue
1159  * --------------------------------------------------------------------------
1160  */
1161 NTSTATUS
1162 OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1163                             UINT32 *replyLen)
1164 {
1165     NDIS_STATUS status;
1166     BOOLEAN rc;
1167     UINT8 join;
1168     UINT32 pid;
1169     const NL_POLICY policy[] =  {
1170         [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 },
1171         [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 }
1172         };
1173     PNL_ATTR attrs[ARRAY_SIZE(policy)];
1174
1175     UNREFERENCED_PARAMETER(replyLen);
1176
1177     POVS_OPEN_INSTANCE instance =
1178         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1179     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1180
1181     rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1182          NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, ARRAY_SIZE(policy),
1183                        attrs, ARRAY_SIZE(attrs));
1184     if (!rc) {
1185         status = STATUS_INVALID_PARAMETER;
1186         goto done;
1187     }
1188
1189     join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_PID]);
1190     pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]);
1191
1192     /* The socket subscribed with must be the same socket we perform receive*/
1193     ASSERT(pid == instance->pid);
1194
1195     status = OvsSubscribeDpIoctl(instance, pid, join);
1196
1197     /*
1198      * XXX Need to add this instance to a global data structure
1199      * which hold all packet based instances. The data structure (hash)
1200      * should be searched through the pid field of the instance for
1201      * placing the missed packet into the correct queue
1202      */
1203 done:
1204     return status;
1205 }
1206
1207 /*
1208  * --------------------------------------------------------------------------
1209  * Handler for queueing an IRP used for missed packet notification. The IRP is
1210  * completed when a packet received and mismatched. STATUS_PENDING is returned
1211  * on success. User mode keep a pending IRP at all times.
1212  * --------------------------------------------------------------------------
1213  */
1214 NTSTATUS
1215 OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1216                        UINT32 *replyLen)
1217 {
1218     UNREFERENCED_PARAMETER(replyLen);
1219
1220     POVS_OPEN_INSTANCE instance =
1221         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1222
1223     /*
1224      * XXX access to packet queue must be through acquiring a lock as user mode
1225      * could unsubscribe and the instnace will be freed.
1226      */
1227     return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject);
1228 }
1229
1230 /*
1231  * --------------------------------------------------------------------------
1232  * Handler for reading missed pacckets from the driver event queue. This
1233  * handler is executed when user modes issues a socket receive on a socket
1234  * --------------------------------------------------------------------------
1235  */
1236 NTSTATUS
1237 OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1238                        UINT32 *replyLen)
1239 {
1240 #ifdef DBG
1241     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1242 #endif
1243     POVS_OPEN_INSTANCE instance =
1244         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1245     NTSTATUS status;
1246
1247     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1248
1249     /* Should never read events with a dump socket */
1250     ASSERT(instance->dumpState.ovsMsg == NULL);
1251
1252     /* Must have an packet queue */
1253     ASSERT(instance->packetQueue != NULL);
1254
1255     /* Output buffer has been validated while validating read dev op. */
1256     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1257
1258     /* Read a packet from the instance queue */
1259     status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
1260                             usrParamsCtx->outputLength, replyLen);
1261     return status;
1262 }