[ovs-dev] [PATCH v8 1/3] datapath-windows: Support for custom VXLAN tunnel port

Ben Pfaff blp at nicira.com
Wed May 27 16:37:47 UTC 2015


Unfortunately I'm still seeing a patch failure against current master:

    Applying: datapath-windows: Support for custom VXLAN tunnel port
    /home/blp/nicira/ovs/.git/rebase-apply/patch:919: trailing whitespace.

    /home/blp/nicira/ovs/.git/rebase-apply/patch:1959: trailing whitespace.
    NTSTATUS OvsRemoveAndDeleteVport(PVOID usrParamsCtx, 
    /home/blp/nicira/ovs/.git/rebase-apply/patch:2018: trailing whitespace.
     * 
    error: patch failed: datapath-windows/ovsext/Datapath.c:925
    error: datapath-windows/ovsext/Datapath.c: patch does not apply
    Patch failed at 0001 datapath-windows: Support for custom VXLAN tunnel port
    The copy of the patch that failed is found in:
       /home/blp/nicira/ovs/.git/rebase-apply/patch
    When you have resolved this problem, run "git am --continue".
    If you prefer to skip this patch, run "git am --skip" instead.
    To restore the original branch and stop patching, run "git am --abort".

On Wed, May 27, 2015 at 04:31:01PM +0000, Sorin Vinturis wrote:
> The kernel datapath supports only port 4789 for VXLAN tunnel creation.
> Added support in order to allow for the VXLAN tunnel port to be
> configurable to any port number set by the userspace.
> 
> The patch also checks to see if an existing WFP filter, for the
> necessary UDP tunnel port, is already created before adding a new one.
> This is a double check, because currently the userspace also verifies
> this, but it is necessary to avoid future issues.
> 
> Custom VXLAN tunnel port requires the addition of a new WFP filter
> with the new UDP tunnel port. The creation of a new WFP filter is
> triggered in OvsInitVxlanTunnel function and the removal of the WFP
> filter in OvsCleanupVxlanTunnel function.
> But the latter functions are running at IRQL = DISPATCH_LEVEL, due
> to the NDIS RW lock acquisition, and all WFP calls must be running at
> IRQL = PASSIVE_LEVEL. This is why I have created a system thread which
> records all filter addition/removal requests into a list for later
> processing by the system thread. The ThreadStart routine processes all
> received requests at IRQL = PASSIVE_LEVEL, which is the required IRQL
> for the necessary WFP calls for adding/removal of the WFP filters.
> 
> The WFP filter for the default VXLAN port 4789 is not added anymore at
> filter attach. All WFP filters for the tunnel ports are added when the
> tunnel ports are initialized and are removed at cleanup. WFP operation
> status is then reported to userspace.
> 
> It is necessary that OvsTunnelFilterUninitialize function is called
> after OvsClearAllSwitchVports in order to allow for the added WFP
> filters to be removed. OvsTunnelFilterUninitialize function closes the
> global engine handle used by most of the WFP calls, including filter
> removal.
> 
> Signed-off-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
> Reported-by: Alin Gabriel Serdean <aserdean at cloudbasesolutions.com>
> Reported-at: https://github.com/openvswitch/ovs-issues/issues/66
> Acked-by: Nithin Raju <nithin at vmware.com>
> ---
> v8: Added acked.
> ---
>  datapath-windows/ovsext/Datapath.c             |   8 +-
>  datapath-windows/ovsext/Netlink/Netlink.c      |   2 +
>  datapath-windows/ovsext/Netlink/NetlinkError.h |  19 +-
>  datapath-windows/ovsext/Switch.c               |   2 +-
>  datapath-windows/ovsext/Tunnel.h               |   6 -
>  datapath-windows/ovsext/TunnelFilter.c         | 881 ++++++++++++++++++++-----
>  datapath-windows/ovsext/TunnelIntf.h           |  15 +
>  datapath-windows/ovsext/Vport.c                | 445 ++++++++++---
>  datapath-windows/ovsext/Vport.h                |  12 +-
>  datapath-windows/ovsext/Vxlan.c                | 102 ++-
>  datapath-windows/ovsext/Vxlan.h                |  13 +-
>  11 files changed, 1226 insertions(+), 279 deletions(-)
> 
> diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c
> index 7646f0a..d482825 100644
> --- a/datapath-windows/ovsext/Datapath.c
> +++ b/datapath-windows/ovsext/Datapath.c
> @@ -670,7 +670,6 @@ OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
>      return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
>  }
>  
> -
>  /*
>   * --------------------------------------------------------------------------
>   * IOCTL function handler for the device.
> @@ -925,9 +924,12 @@ exit:
>      KeMemoryBarrier();
>      instance->inUse = 0;
>  
> -    /* Should not complete a pending IRP unless proceesing is completed */
> +    /* Should not complete a pending IRP unless proceesing is completed. */
>      if (status == STATUS_PENDING) {
> -        return status;
> +        /* STATUS_PENDING is returned by the NL handler when the request is
> +         * to be processed later, so we mark the IRP as pending and complete
> +         * it in another thread when the request is processed. */
> +        IoMarkIrpPending(irp);
>         return status;
>      }
>      return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
>  }
> diff --git a/datapath-windows/ovsext/Netlink/Netlink.c b/datapath-windows/ovsext/Netlink/Netlink.c
> index 589e3a1..a62d760 100644
> --- a/datapath-windows/ovsext/Netlink/Netlink.c
> +++ b/datapath-windows/ovsext/Netlink/Netlink.c
> @@ -112,6 +112,8 @@ NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgError, UINT errorCode)
>  {
>      NL_BUFFER nlBuffer;
>  
> +    ASSERT(errorCode != NL_ERROR_PENDING);
> +
>      NlBufInit(&nlBuffer, (PCHAR)msgError, sizeof *msgError);
>      NlFillNlHdr(&nlBuffer, NLMSG_ERROR, 0,
>                  msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid);
> diff --git a/datapath-windows/ovsext/Netlink/NetlinkError.h b/datapath-windows/ovsext/Netlink/NetlinkError.h
> index 53c935f..eefa89e 100644
> --- a/datapath-windows/ovsext/Netlink/NetlinkError.h
> +++ b/datapath-windows/ovsext/Netlink/NetlinkError.h
> @@ -195,14 +195,16 @@ typedef enum _NL_ERROR_
>      NL_ERROR_TIMEDOUT = ((ULONG)-138),
>      /* The given text file is busy */
>      NL_ERROR_TXTBSY = ((ULONG)-139),
> -    /*the operation would block */
> +    /* The operation would block */
>      NL_ERROR_WOULDBLOCK = ((ULONG)-140),
> +    /* The operation is not finished */
> +    NL_ERROR_PENDING = ((ULONG)-141),
>  } NL_ERROR;
>  
>  static __inline
>  NlMapStatusToNlErr(NTSTATUS status)
>  {
> -    NL_ERROR ret = NL_ERROR_INVAL;
> +    NL_ERROR ret;
>  
>      switch (status)
>      {
> @@ -215,7 +217,20 @@ NlMapStatusToNlErr(NTSTATUS status)
>      case STATUS_SUCCESS:
>        ret = NL_ERROR_SUCCESS;
>        break;
> +    case STATUS_PENDING:
> +      ret = NL_ERROR_PENDING;
> +      break;
> +    case STATUS_CANCELLED:
> +      ret = NL_ERROR_CANCELED;
> +      break;
> +    case STATUS_INVALID_PARAMETER:
> +      ret = NL_ERROR_INVAL;
> +      break;
> +    case STATUS_OBJECT_NAME_EXISTS:
> +      ret = NL_ERROR_EXIST;
> +      break;
>      default:
> +      ret = NL_ERROR_OTHER;
>        break;
>      }
>  
> diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c
> index 032153d..416bcc0 100644
> --- a/datapath-windows/ovsext/Switch.c
> +++ b/datapath-windows/ovsext/Switch.c
> @@ -263,8 +263,8 @@ OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
>      if (switchContext)
>      {
>          dpNo = switchContext->dpNo;
> -        OvsUninitTunnelFilter(gOvsExtDriverObject);
>          OvsClearAllSwitchVports(switchContext);
> +        OvsUninitTunnelFilter(gOvsExtDriverObject);
>          OvsUninitSwitchContext(switchContext);
>      }
>      OVS_LOG_TRACE("Exit: deleted switch %p  dpNo: %d", switchContext, dpNo);
> diff --git a/datapath-windows/ovsext/Tunnel.h b/datapath-windows/ovsext/Tunnel.h
> index 2978bb3..2c45e35 100644
> --- a/datapath-windows/ovsext/Tunnel.h
> +++ b/datapath-windows/ovsext/Tunnel.h
> @@ -32,12 +32,6 @@ typedef struct OVS_TUNNEL_PENDED_PACKET_
>     FWPS_CLASSIFY_OUT *classifyOut;
>  } OVS_TUNNEL_PENDED_PACKET;
>  
> -/* Shared global data. */
> -
> -extern UINT16 configNewDestPort;
> -
> -extern UINT32 gCalloutIdV4;
> -
>  //
>  // Shared function prototypes
>  //
> diff --git a/datapath-windows/ovsext/TunnelFilter.c b/datapath-windows/ovsext/TunnelFilter.c
> index c2186eb..00b3c0e 100644
> --- a/datapath-windows/ovsext/TunnelFilter.c
> +++ b/datapath-windows/ovsext/TunnelFilter.c
> @@ -48,27 +48,24 @@
>  /* Infinite timeout */
>  #define INFINITE                        0xFFFFFFFF
>  
> -/*
> - * The provider name should always match the provider string from the install
> - * file.
> - */
> +/* The provider name should always match the provider string from the install
> + * file. */
>  #define OVS_TUNNEL_PROVIDER_NAME        L"Open vSwitch"
>  
> -/*
> - * The provider description should always contain the OVS service description
> - * string from the install file.
> - */
> +/* The provider description should always contain the OVS service description
> + * string from the install file. */
>  #define OVS_TUNNEL_PROVIDER_DESC        L"Open vSwitch Extension tunnel provider"
>  
>  /* The session name isn't required but it's useful for diagnostics. */
>  #define OVS_TUNNEL_SESSION_NAME         L"OVS tunnel session"
>  
> -/* Configurable parameters (addresses and ports are in host order) */
> -UINT16   configNewDestPort = VXLAN_UDP_PORT;
> +/* Maximum number of tunnel threads to be created. */
> +#define OVS_TUNFLT_MAX_THREADS          8
>  
>  /*
>   * Callout and sublayer GUIDs
>   */
> +
>  // b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8
>  DEFINE_GUID(
>      OVS_TUNNEL_CALLOUT_V4,
> @@ -106,41 +103,108 @@ DEFINE_GUID(
>      );
>  
>  /*
> - * Callout driver global variables
> + * Callout driver type definitions
>   */
> -PDEVICE_OBJECT gDeviceObject;
> +typedef enum _OVS_TUNFLT_OPERATION {
> +    OVS_TUN_FILTER_CREATE = 0,
> +    OVS_TUN_FILTER_DELETE
> +} OVS_TUNFLT_OPERATION;
> +
> +typedef struct _OVS_TUNFLT_REQUEST {
> +    LIST_ENTRY              entry;
> +    /* Tunnel filter destination port. */
> +    UINT16                  port;
> +    /* XXX: We also need to specify the tunnel L4 protocol, because there are
> +     * different protocols that can use the same destination port.*/
> +    union {
> +        /* Tunnel filter identification used for filter deletion. */
> +        UINT64                  delID;
> +        /* Pointer used to return filter ID to the caller on filter creation. */
> +        PUINT64                 addID;
> +    }filterID;
> +    /* Requested operation to be performed. */
> +    OVS_TUNFLT_OPERATION    operation;
> +    /* Current I/O request to be completed when requested
> +     * operation is finished. */
> +    PIRP                    irp;
> +    /* Callback function called before completing the IRP. */
> +    PFNTunnelVportPendingOp callback;
> +    /* Context passed to the callback function. */
> +    PVOID                   context;
> +} OVS_TUNFLT_REQUEST, *POVS_TUNFLT_REQUEST;
> +
> +typedef struct _OVS_TUNFLT_REQUEST_LIST {
> +    /* SpinLock for syncronizing access to the requests list. */
> +    NDIS_SPIN_LOCK spinlock;
> +    /* Head of the requests list. */
> +    LIST_ENTRY     head;
> +    /* Number of requests in the list. This variable is used by
> +     * InterlockedCompareExchange function and needs to be aligned
> +     * at 32-bit boundaries. */
> +    UINT32         numEntries;
> +} OVS_TUNFLT_REQUEST_LIST, *POVS_TUNFLT_REQUEST_LIST;
> +
> +typedef struct _OVS_TUNFLT_THREAD_CONTEXT {
> +    /* Thread identification. */
> +    UINT                    threadID;
> +    /* Thread's engine session handle. */
> +    HANDLE                  engineSession;
> +    /* Reference of the thread object. */
> +    PVOID                   threadObject;
> +    /* Requests queue list. */
> +    OVS_TUNFLT_REQUEST_LIST listRequests;
> +    /* Event signaling that there are requests to process. */
> +    KEVENT                  requestEvent;
> +    /* Event for stopping thread execution. */
> +    KEVENT                  stopEvent;
> +} OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT;
> +
> +KSTART_ROUTINE  OvsTunnelFilterThreadProc;
> +
> +static NTSTATUS OvsTunnelFilterStartThreads();
> +static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
> +static VOID     OvsTunnelFilterStopThreads();
> +static VOID     OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
> +                                          BOOLEAN signalEvent);
> +static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
> +static VOID     OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
>  
> -HANDLE gEngineHandle = NULL;
> -HANDLE gTunnelProviderBfeHandle = NULL;
> -HANDLE gTunnelInitBfeHandle = NULL;
> -UINT32 gCalloutIdV4;
> +/*
> + * Callout driver global variables
> + */
>  
> +static PDEVICE_OBJECT            gDeviceObject = NULL;
> +static HANDLE                    gEngineHandle = NULL;
> +static HANDLE                    gTunnelProviderBfeHandle = NULL;
> +static HANDLE                    gTunnelInitBfeHandle = NULL;
> +static HANDLE                    gBfeSubscriptionHandle = NULL;
> +static UINT32                    gCalloutIdV4 = 0;
> +static OVS_TUNFLT_THREAD_CONTEXT gTunnelThreadCtx[OVS_TUNFLT_MAX_THREADS] = { 0 };
>  
> -/* Callout driver implementation */
> +/*
> + * Callout driver implementation.
> + */
>  
>  NTSTATUS
> -OvsTunnelEngineOpen(HANDLE *handle)
> +OvsTunnelEngineOpen(HANDLE *engineSession)
>  {
>      NTSTATUS status = STATUS_SUCCESS;
>      FWPM_SESSION session = { 0 };
>  
> -    /* The session name isn't required but may be useful for diagnostics. */
> -    session.displayData.name = OVS_TUNNEL_SESSION_NAME;
>      /*
>      * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
>      * errors while waiting to acquire the transaction lock.
>      */
>      session.txnWaitTimeoutInMSec = INFINITE;
> -    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
>  
>      /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
>      status = FwpmEngineOpen(NULL,
>                              RPC_C_AUTHN_DEFAULT,
>                              NULL,
>                              &session,
> -                            handle);
> +                            engineSession);
>      if (!NT_SUCCESS(status)) {
> -        OVS_LOG_ERROR("Fail to open filtering engine session, status: %x.",
> +        OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.",
>                        status);
>      }
>  
> @@ -148,23 +212,23 @@ OvsTunnelEngineOpen(HANDLE *handle)
>  }
>  
>  VOID
> -OvsTunnelEngineClose(HANDLE *handle)
> +OvsTunnelEngineClose(HANDLE *engineSession)
>  {
> -    if (*handle) {
> -        FwpmEngineClose(*handle);
> -        *handle = NULL;
> +    if (*engineSession) {
> +        FwpmEngineClose(*engineSession);
> +        *engineSession = NULL;
>      }
>  }
>  
>  VOID
> -OvsTunnelAddSystemProvider(HANDLE handle)
> +OvsTunnelAddSystemProvider(HANDLE engineSession)
>  {
>      NTSTATUS status = STATUS_SUCCESS;
>      BOOLEAN inTransaction = FALSE;
>      FWPM_PROVIDER provider = { 0 };
>  
>      do {
> -        status = FwpmTransactionBegin(handle, 0);
> +        status = FwpmTransactionBegin(engineSession, 0);
>          if (!NT_SUCCESS(status)) {
>              break;
>          }
> @@ -180,7 +244,7 @@ OvsTunnelAddSystemProvider(HANDLE handle)
>           */
>          provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
>  
> -        status = FwpmProviderAdd(handle,
> +        status = FwpmProviderAdd(engineSession,
>                                   &provider,
>                                   NULL);
>          if (!NT_SUCCESS(status)) {
> @@ -191,7 +255,7 @@ OvsTunnelAddSystemProvider(HANDLE handle)
>              }
>          }
>  
> -        status = FwpmTransactionCommit(handle);
> +        status = FwpmTransactionCommit(engineSession);
>          if (!NT_SUCCESS(status)) {
>              break;
>          }
> @@ -200,30 +264,30 @@ OvsTunnelAddSystemProvider(HANDLE handle)
>      } while (inTransaction);
>  
>      if (inTransaction){
> -        FwpmTransactionAbort(handle);
> +        FwpmTransactionAbort(engineSession);
>      }
>  }
>  
>  VOID
> -OvsTunnelRemoveSystemProvider(HANDLE handle)
> +OvsTunnelRemoveSystemProvider(HANDLE engineSession)
>  {
>      NTSTATUS status = STATUS_SUCCESS;
>      BOOLEAN inTransaction = FALSE;
>  
>      do {
> -        status = FwpmTransactionBegin(handle, 0);
> +        status = FwpmTransactionBegin(engineSession, 0);
>          if (!NT_SUCCESS(status)) {
>              break;
>          }
>          inTransaction = TRUE;
>  
> -        status = FwpmProviderDeleteByKey(handle,
> +        status = FwpmProviderDeleteByKey(engineSession,
>                                           &OVS_TUNNEL_PROVIDER_KEY);
>          if (!NT_SUCCESS(status)) {
>              break;
>          }
>  
> -        status = FwpmTransactionCommit(handle);
> +        status = FwpmTransactionCommit(engineSession);
>          if (!NT_SUCCESS(status)) {
>              break;
>          }
> @@ -232,29 +296,30 @@ OvsTunnelRemoveSystemProvider(HANDLE handle)
>      } while (inTransaction);
>  
>      if (inTransaction){
> -        FwpmTransactionAbort(handle);
> +        FwpmTransactionAbort(engineSession);
>      }
>  }
>  
>  NTSTATUS
> -OvsTunnelAddFilter(PWSTR filterName,
> +OvsTunnelAddFilter(HANDLE engineSession,
> +                   PWSTR filterName,
>                     const PWSTR filterDesc,
>                     USHORT remotePort,
>                     FWP_DIRECTION direction,
>                     UINT64 context,
>                     const GUID *filterKey,
>                     const GUID *layerKey,
> -                   const GUID *calloutKey)
> +                   const GUID *calloutKey,
> +                   UINT64 *filterID)
>  {
>      NTSTATUS status = STATUS_SUCCESS;
>      FWPM_FILTER filter = {0};
>      FWPM_FILTER_CONDITION filterConditions[3] = {0};
>      UINT conditionIndex;
>  
> -    UNREFERENCED_PARAMETER(remotePort);
> -    UNREFERENCED_PARAMETER(direction);
> -
> -    filter.filterKey = *filterKey;
> +    if (filterKey) {
> +        filter.filterKey = *filterKey;
> +    }
>      filter.layerKey = *layerKey;
>      filter.displayData.name = (wchar_t*)filterName;
>      filter.displayData.description = (wchar_t*)filterDesc;
> @@ -284,64 +349,18 @@ OvsTunnelAddFilter(PWSTR filterName,
>  
>      filter.numFilterConditions = conditionIndex;
>  
> -    status = FwpmFilterAdd(gEngineHandle,
> +    status = FwpmFilterAdd(engineSession,
>                             &filter,
>                             NULL,
> -                           NULL);
> -
> -    return status;
> -}
> -
> -NTSTATUS
> -OvsTunnelRemoveFilter(const GUID *filterKey,
> -                      const GUID *sublayerKey)
> -{
> -    NTSTATUS status = STATUS_SUCCESS;
> -    BOOLEAN inTransaction = FALSE;
> -
> -    do {
> -        status = FwpmTransactionBegin(gEngineHandle, 0);
> -        if (!NT_SUCCESS(status)) {
> -            break;
> -        }
> -
> -        inTransaction = TRUE;
> -
> -        /*
> -         * We have to delete the filter first since it references the
> -         * sublayer. If we tried to delete the sublayer first, it would fail
> -         * with FWP_ERR_IN_USE.
> -         */
> -        status = FwpmFilterDeleteByKey(gEngineHandle,
> -                                       filterKey);
> -        if (!NT_SUCCESS(status)) {
> -            break;
> -        }
> -
> -        status = FwpmSubLayerDeleteByKey(gEngineHandle,
> -                                         sublayerKey);
> -        if (!NT_SUCCESS(status)) {
> -            break;
> -        }
> +                           filterID);
>  
> -        status = FwpmTransactionCommit(gEngineHandle);
> -        if (!NT_SUCCESS(status)){
> -            break;
> -        }
> -
> -        inTransaction = FALSE;
> -    } while (inTransaction);
> -
> -    if (inTransaction) {
> -        FwpmTransactionAbort(gEngineHandle);
> -    }
>      return status;
>  }
>  
>  /*
>   * --------------------------------------------------------------------------
> - * This function registers callouts and filters that intercept UDP traffic at
> - * WFP FWPM_LAYER_DATAGRAM_DATA_V4
> + * This function registers callouts for intercepting UDP traffic at WFP
> + * FWPM_LAYER_DATAGRAM_DATA_V4 layer.
>   * --------------------------------------------------------------------------
>   */
>  NTSTATUS
> @@ -368,10 +387,7 @@ OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
>      sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
>  #endif
>  
> -    status = FwpsCalloutRegister(deviceObject,
> -                                 &sCallout,
> -                                 calloutId);
> -
> +    status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
>      if (!NT_SUCCESS(status)) {
>          goto Exit;
>      }
> @@ -384,24 +400,11 @@ OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
>      mCallout.displayData = displayData;
>      mCallout.applicableLayer = *layerKey;
>  
> -    status = FwpmCalloutAdd(gEngineHandle,
> -                            &mCallout,
> -                            NULL,
> -                            NULL);
> -
> +    status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
>      if (!NT_SUCCESS(status)) {
>          goto Exit;
>      }
>  
> -    status = OvsTunnelAddFilter(L"Datagram-Data OVS Filter (Inbound)",
> -                                L"address/port for UDP",
> -                                configNewDestPort,
> -                                FWP_DIRECTION_INBOUND,
> -                                0,
> -                                &OVS_TUNNEL_FILTER_KEY,
> -                                layerKey,
> -                                calloutKey);
> -
>  Exit:
>  
>      if (!NT_SUCCESS(status)){
> @@ -416,24 +419,16 @@ Exit:
>  
>  /*
>   * --------------------------------------------------------------------------
> - * This function registers dynamic callouts and filters that intercept UDP
> - * Callouts and filters will be removed during De-Initialize.
> + * This function registers non-dynamic callouts for intercepting UDP traffic.
> + * Callouts will be removed during un-initializing phase.
>   * --------------------------------------------------------------------------
>   */
>  NTSTATUS
>  OvsTunnelRegisterCallouts(VOID *deviceObject)
>  {
> -    NTSTATUS status = STATUS_SUCCESS;
> -    FWPM_SUBLAYER OvsTunnelSubLayer;
> -
> -    BOOLEAN engineOpened = FALSE;
> -    BOOLEAN inTransaction = FALSE;
> -
> -    status = OvsTunnelEngineOpen(&gEngineHandle);
> -    if (!NT_SUCCESS(status)) {
> -        goto Exit;
> -    }
> -    engineOpened = TRUE;
> +    NTSTATUS        status = STATUS_SUCCESS;
> +    BOOLEAN         inTransaction = FALSE;
> +    FWPM_SUBLAYER   OvsTunnelSubLayer;
>  
>      status = FwpmTransactionBegin(gEngineHandle, 0);
>      if (!NT_SUCCESS(status)) {
> @@ -476,22 +471,17 @@ Exit:
>          if (inTransaction) {
>              FwpmTransactionAbort(gEngineHandle);
>          }
> -        if (engineOpened) {
> -            OvsTunnelEngineClose(&gEngineHandle);
> -        }
>      }
>  
>      return status;
>  }
>  
>  VOID
> -OvsTunnelUnregisterCallouts(VOID)
> +OvsTunnelUnregisterCallouts()
>  {
> -    OvsTunnelRemoveFilter(&OVS_TUNNEL_FILTER_KEY,
> -                          &OVS_TUNNEL_SUBLAYER);
>      FwpsCalloutUnregisterById(gCalloutIdV4);
> +    FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
>      FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
> -    OvsTunnelEngineClose(&gEngineHandle);
>  }
>  
>  VOID
> @@ -499,16 +489,22 @@ OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
>  {
>      UNREFERENCED_PARAMETER(driverObject);
>  
> +    OvsTunnelFilterStopThreads();
> +
>      OvsTunnelUnregisterCallouts();
> -    IoDeleteDevice(gDeviceObject);
> +    OvsTunnelEngineClose(&gEngineHandle);
> +
> +    if (gDeviceObject) {
> +        IoDeleteDevice(gDeviceObject);
> +    }
>  }
>  
>  
>  NTSTATUS
>  OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
>  {
> -    NTSTATUS status = STATUS_SUCCESS;
> -    UNICODE_STRING deviceName;
> +    NTSTATUS        status = STATUS_SUCCESS;
> +    UNICODE_STRING  deviceName;
>  
>      RtlInitUnicodeString(&deviceName,
>                           L"\\Device\\OvsTunnelFilter");
> @@ -522,21 +518,31 @@ OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
>                              &gDeviceObject);
>  
>      if (!NT_SUCCESS(status)){
> +        OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
> +                      status);
> +        goto Exit;
> +    }
> +
> +    status = OvsTunnelFilterStartThreads();
> +    if (!NT_SUCCESS(status)){
> +        goto Exit;
> +    }
> +
> +    status = OvsTunnelEngineOpen(&gEngineHandle);
> +    if (!NT_SUCCESS(status)){
>          goto Exit;
>      }
>  
>      status = OvsTunnelRegisterCallouts(gDeviceObject);
> +    if (!NT_SUCCESS(status)) {
> +        OVS_LOG_ERROR("Failed to register callout, status: %x.",
> +                      status);
> +    }
>  
>  Exit:
>  
>      if (!NT_SUCCESS(status)){
> -        if (gEngineHandle != NULL) {
> -            OvsTunnelUnregisterCallouts();
> -        }
> -
> -        if (gDeviceObject) {
> -            IoDeleteDevice(gDeviceObject);
> -        }
> +        OvsTunnelFilterUninitialize(driverObject);
>      }
>  
>      return status;
> @@ -546,16 +552,16 @@ VOID NTAPI
>  OvsTunnelProviderBfeCallback(PVOID context,
>                               FWPM_SERVICE_STATE bfeState)
>  {
> -    HANDLE handle = NULL;
> +    HANDLE engineSession = NULL;
>  
>      DBG_UNREFERENCED_PARAMETER(context);
>  
>      if (FWPM_SERVICE_RUNNING == bfeState) {
> -        OvsTunnelEngineOpen(&handle);
> -        if (handle) {
> -            OvsTunnelAddSystemProvider(handle);
> +        OvsTunnelEngineOpen(&engineSession);
> +        if (engineSession) {
> +            OvsTunnelAddSystemProvider(engineSession);
>          }
> -        OvsTunnelEngineClose(&handle);
> +        OvsTunnelEngineClose(&engineSession);
>      }
>  }
>  
> @@ -599,16 +605,16 @@ VOID
>  OvsRegisterSystemProvider(PVOID deviceObject)
>  {
>      NTSTATUS status = STATUS_SUCCESS;
> -    HANDLE handle = NULL;
> +    HANDLE engineSession = NULL;
>  
>      status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
>      if (NT_SUCCESS(status)) {
>          if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
> -            OvsTunnelEngineOpen(&handle);
> -            if (handle) {
> -                OvsTunnelAddSystemProvider(handle);
> +            OvsTunnelEngineOpen(&engineSession);
> +            if (engineSession) {
> +                OvsTunnelAddSystemProvider(engineSession);
>              }
> -            OvsTunnelEngineClose(&handle);
> +            OvsTunnelEngineClose(&engineSession);
>  
>              OvsUnsubscribeTunnelProviderBfeStateChanges();
>          }
> @@ -617,13 +623,13 @@ OvsRegisterSystemProvider(PVOID deviceObject)
>  
>  VOID OvsUnregisterSystemProvider()
>  {
> -    HANDLE handle = NULL;
> +    HANDLE engineSession = NULL;
>  
> -    OvsTunnelEngineOpen(&handle);
> -    if (handle) {
> -        OvsTunnelRemoveSystemProvider(handle);
> +    OvsTunnelEngineOpen(&engineSession);
> +    if (engineSession) {
> +        OvsTunnelRemoveSystemProvider(engineSession);
>      }
> -    OvsTunnelEngineClose(&handle);
> +    OvsTunnelEngineClose(&engineSession);
>  
>      OvsUnsubscribeTunnelProviderBfeStateChanges();
>  }
> @@ -711,3 +717,566 @@ VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
>      OvsTunnelFilterUninitialize(driverObject);
>      OvsUnsubscribeTunnelInitBfeStateChanges();
>  }
> +
> +NTSTATUS
> +OvsTunnelAddFilterEx(HANDLE engineSession,
> +                     UINT32 filterPort,
> +                     UINT64 *filterID)
> +{
> +    NTSTATUS status = STATUS_SUCCESS;
> +
> +    status = OvsTunnelAddFilter(engineSession,
> +                                L"Datagram-Data OVS Filter (Inbound)",
> +                                L"address/port for UDP",
> +                                (USHORT)filterPort,
> +                                FWP_DIRECTION_INBOUND,
> +                                0,
> +                                NULL,
> +                                &FWPM_LAYER_DATAGRAM_DATA_V4,
> +                                &OVS_TUNNEL_CALLOUT_V4,
> +                                filterID);
> +    if (!NT_SUCCESS(status)) {
> +        OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
> +                      filterPort, status);
> +    } else {
> +        OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
> +                     filterPort, *filterID);
> +    }
> +
> +    return status;
> +}
> +
> +NTSTATUS
> +OvsTunnelRemoveFilterEx(HANDLE engineSession,
> +                        UINT64 filterID)
> +{
> +    NTSTATUS status = STATUS_SUCCESS;
> +    BOOLEAN  error = TRUE;
> +
> +    do {
> +        if (filterID == 0) {
> +            OVS_LOG_INFO("No tunnel filter to remove.");
> +            break;
> +        }
> +
> +        status = FwpmFilterDeleteById(engineSession, filterID);
> +        if (!NT_SUCCESS(status)) {
> +            OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
> +                           status: %x.", filterID, status);
> +            break;
> +        }
> +        OVS_LOG_INFO("Filter removed, filter ID: %d.",
> +                     filterID);
> +
> +        error = FALSE;
> +    } while (error);
> +
> +    return status;
> +}
> +
> +NTSTATUS
> +OvsTunnelFilterExecuteAction(HANDLE engineSession,
> +                             POVS_TUNFLT_REQUEST request)
> +{
> +    NTSTATUS status = STATUS_SUCCESS;
> +
> +    switch (request->operation)
> +    {
> +    case OVS_TUN_FILTER_CREATE:
> +        status = OvsTunnelAddFilterEx(engineSession,
> +                                      request->port,
> +                                      request->filterID.addID);
> +        break;
> +    case OVS_TUN_FILTER_DELETE:
> +        status = OvsTunnelRemoveFilterEx(engineSession,
> +                                         request->filterID.delID);
> +        break;
> +    default:
> +        status = STATUS_NOT_SUPPORTED;
> +        break;
> +    }
> +
> +    return status;
> +}
> +
> +VOID
> +OvsTunnelFilterRequestPopList(POVS_TUNFLT_REQUEST_LIST listRequests,
> +                              PLIST_ENTRY head,
> +                              UINT32 *count)
> +{
> +    NdisAcquireSpinLock(&listRequests->spinlock);
> +
> +    if (!IsListEmpty(&listRequests->head)) {
> +        PLIST_ENTRY PrevEntry;
> +        PLIST_ENTRY NextEntry;
> +
> +        NextEntry = listRequests->head.Flink;
> +        PrevEntry = listRequests->head.Blink;
> +
> +        head->Flink = NextEntry;
> +        NextEntry->Blink = head;
> +
> +        head->Blink = PrevEntry;
> +        PrevEntry->Flink = head;
> +
> +        *count = listRequests->numEntries;
> +
> +        InitializeListHead(&listRequests->head);
> +        listRequests->numEntries = 0;
> +    }
> +
> +    NdisReleaseSpinLock(&listRequests->spinlock);
> +}
> +
> +VOID
> +OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
> +                           POVS_TUNFLT_REQUEST request)
> +{
> +    NdisAcquireSpinLock(&listRequests->spinlock);
> +
> +    InsertTailList(&listRequests->head, &(request->entry));
> +    listRequests->numEntries++;
> +
> +    NdisReleaseSpinLock(&listRequests->spinlock);
> +}
> +
> +VOID
> +OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
> +{
> +    UINT32 threadIndex;
> +
> +    threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
> +
> +    OvsTunnelFilterRequestPush(
> +        &gTunnelThreadCtx[threadIndex].listRequests,
> +        request);
> +
> +    KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
> +               IO_NO_INCREMENT,
> +               FALSE);
> +}
> +
> +VOID
> +OvsTunnelFilterCompleteRequest(PIRP irp,
> +                               PFNTunnelVportPendingOp callback,
> +                               PVOID context,
> +                               NTSTATUS status)
> +{
> +    UINT32 replyLen = 0;
> +
> +    if (callback) {
> +        callback(context, status, &replyLen);
> +        /* Release the context passed to the callback function. */
> +        OvsFreeMemory(context);
> +    }
> +
> +    if (irp) {
> +        OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
> +    }
> +}
> +
> +VOID
> +OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
> +{
> +    POVS_TUNFLT_REQUEST request = NULL;
> +    PLIST_ENTRY         link = NULL;
> +    PLIST_ENTRY         next = NULL;
> +    LIST_ENTRY          head;
> +    NTSTATUS            status = STATUS_SUCCESS;
> +    UINT32              count = 0;
> +    BOOLEAN             inTransaction = FALSE;
> +    BOOLEAN             error = TRUE;
> +
> +    do
> +    {
> +        if (!InterlockedCompareExchange(
> +            (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
> +            OVS_LOG_INFO("Nothing to do... request list is empty.");
> +            break;
> +        }
> +
> +        status = FwpmTransactionBegin(threadCtx->engineSession, 0);
> +        if (!NT_SUCCESS(status)) {
> +            OVS_LOG_ERROR("Failed to start transaction, status: %x.",
> +                          status);
> +            break;
> +        }
> +        inTransaction = TRUE;
> +
> +        InitializeListHead(&head);
> +        OvsTunnelFilterRequestPopList(&threadCtx->listRequests, &head, &count);
> +
> +        LIST_FORALL_SAFE(&head, link, next) {
> +            request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
> +
> +            status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
> +                                                  request);
> +            if (!NT_SUCCESS(status)) {
> +                RemoveEntryList(&request->entry);
> +                count--;
> +
> +                /* Complete the IRP with the failure status. */
> +                OvsTunnelFilterCompleteRequest(request->irp,
> +                                               request->callback,
> +                                               request->context,
> +                                               status);
> +                OvsFreeMemory(request);
> +                request = NULL;
> +            } else {
> +                error = FALSE;
> +            }
> +        }
> +        
> +        if (error) {
> +            /* No successful requests were made, so there is no point to commit
> +             * the transaction. */
> +            break;
> +        }
> +
> +        status = FwpmTransactionCommit(threadCtx->engineSession);
> +        if (!NT_SUCCESS(status)){
> +            OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
> +                          status);
> +            break;
> +        }
> +
> +        inTransaction = FALSE;
> +    } while (inTransaction);
> +
> +    if (inTransaction) {
> +        FwpmTransactionAbort(threadCtx->engineSession);
> +        OVS_LOG_ERROR("Failed to execute request, status: %x.\
> +                       Transaction aborted.", status);
> +    }
> +
> +    /* Complete the requests successfully executed with the transaction commit
> +     * status. */
> +    while (count) {
> +        request = (POVS_TUNFLT_REQUEST)RemoveHeadList(&head);
> +        count--;
> +
> +        OvsTunnelFilterCompleteRequest(request->irp,
> +                                       request->callback,
> +                                       request->context,
> +                                       status);
> +        OvsFreeMemory(request);
> +        request = NULL;
> +    }
> +}
> +
> +/*
> + *----------------------------------------------------------------------------
> + *  System thread routine that handles tunnel filter create/delete requests.
> + *----------------------------------------------------------------------------
> + */
> +_Use_decl_annotations_
> +VOID
> +OvsTunnelFilterThreadProc(PVOID context)
> +{
> +    NTSTATUS                   status = STATUS_SUCCESS;
> +    POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
> +    PKEVENT                    eventArray[2] = { 0 };
> +    ULONG                      count = 0;
> +    BOOLEAN                    exit = FALSE;
> +    BOOLEAN                    error = TRUE;
> +
> +    OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
> +                 threadCtx->threadID);
> +
> +    eventArray[0] = &threadCtx->stopEvent;
> +    eventArray[1] = &threadCtx->requestEvent;
> +    count = ARRAY_SIZE(eventArray);
> +
> +    do {
> +        status = OvsTunnelFilterThreadInit(threadCtx);
> +        if (!NT_SUCCESS(status)) {
> +            OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
> +                threadCtx->threadID);
> +            break;
> +        }
> +
> +        do {
> +            status = KeWaitForMultipleObjects(count,
> +                                              (PVOID)eventArray,
> +                                              WaitAny,
> +                                              Executive,
> +                                              KernelMode,
> +                                              FALSE,
> +                                              NULL,
> +                                              NULL);
> +            switch (status) {
> +                case STATUS_WAIT_1:
> +                    /* Start processing requests. */
> +                    OvsTunnelFilterRequestListProcess(threadCtx);
> +                    break;
> +                default:
> +                    /* Finish processing the received requests and exit. */
> +                    OvsTunnelFilterRequestListProcess(threadCtx);
> +                    exit = TRUE;
> +                    break;
> +            }
> +        } while (!exit);
> +
> +        OvsTunnelFilterThreadUninit(threadCtx);
> +
> +        error = FALSE;
> +    } while (error);
> +
> +    OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
> +                 threadCtx->threadID);
> +
> +    PsTerminateSystemThread(STATUS_SUCCESS);
> +};
> +
> +static NTSTATUS
> +OvsTunnelFilterStartThreads()
> +{
> +    NTSTATUS status = STATUS_SUCCESS;
> +
> +    for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
> +        gTunnelThreadCtx[index].threadID = index;
> +
> +        status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
> +        if (!NT_SUCCESS(status)) {
> +            OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
> +            break;
> +        }
> +    }
> +
> +    return status;
> +}
> +
> +static NTSTATUS
> +OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
> +{
> +    NTSTATUS    status = STATUS_SUCCESS;
> +    HANDLE      threadHandle = NULL;
> +    BOOLEAN     error = TRUE;
> +
> +    do {
> +        status = PsCreateSystemThread(&threadHandle,
> +                                      SYNCHRONIZE,
> +                                      NULL,
> +                                      NULL,
> +                                      NULL,
> +                                      OvsTunnelFilterThreadProc,
> +                                      threadCtx);
> +        if (!NT_SUCCESS(status)) {
> +            OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
> +                          status);
> +            break;
> +        }
> +
> +        ObReferenceObjectByHandle(threadHandle,
> +                                  SYNCHRONIZE,
> +                                  NULL,
> +                                  KernelMode,
> +                                  &threadCtx->threadObject,
> +                                  NULL);
> +        ZwClose(threadHandle);
> +        threadHandle = NULL;
> +
> +        error = FALSE;
> +    } while (error);
> +
> +    return status;
> +}
> +
> +static VOID
> +OvsTunnelFilterStopThreads()
> +{
> +    /* Signal all threads to stop and ignore all subsequent requests. */
> +    for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
> +        OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
> +    }
> +
> +    /* Wait for all threads to finish processing the requests. */
> +    for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
> +        OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
> +    }
> +}
> +
> +static VOID
> +OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
> +                          BOOLEAN signalEvent)
> +{
> +    if (signalEvent) {
> +        /* Signal stop thread event. */
> +        OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
> +                     threadCtx->threadID);
> +        KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
> +    } else {
> +        /* Wait for the tunnel thread to finish. */
> +        KeWaitForSingleObject(threadCtx->threadObject,
> +                              Executive,
> +                              KernelMode,
> +                              FALSE,
> +                              NULL);
> +
> +        ObDereferenceObject(threadCtx->threadObject);
> +    }
> +}
> +
> +static NTSTATUS
> +OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
> +{
> +    NTSTATUS status = STATUS_SUCCESS;
> +    BOOLEAN error = TRUE;
> +
> +    do {
> +        /* Create thread's engine session object. */
> +        status = OvsTunnelEngineOpen(&threadCtx->engineSession);
> +        if (!NT_SUCCESS(status)) {
> +            break;
> +        }
> +
> +        NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
> +
> +        InitializeListHead(&threadCtx->listRequests.head);
> +
> +        KeInitializeEvent(&threadCtx->stopEvent,
> +            NotificationEvent,
> +            FALSE);
> +
> +        KeInitializeEvent(&threadCtx->requestEvent,
> +            SynchronizationEvent,
> +            FALSE);
> +
> +        error = FALSE;
> +    } while (error);
> +
> +    return status;
> +}
> +
> +static VOID
> +OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
> +{
> +    if (threadCtx->engineSession) {
> +        /* Close thread's FWPM session. */
> +        OvsTunnelEngineClose(&threadCtx->engineSession);
> +
> +        NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
> +    }
> +}
> +
> +NTSTATUS
> +OvsTunnelFilterQueueRequest(PIRP irp,
> +                            UINT16 remotePort,
> +                            UINT64 *filterID,
> +                            OVS_TUNFLT_OPERATION operation,
> +                            PFNTunnelVportPendingOp callback,
> +                            PVOID tunnelContext)
> +{
> +    POVS_TUNFLT_REQUEST request = NULL;
> +    NTSTATUS            status = STATUS_PENDING;
> +    BOOLEAN             error = TRUE;
> +    UINT64              timeout = 0;
> +
> +    do {
> +        /* Verify if the stop event was signaled. */
> +        if (STATUS_SUCCESS == KeWaitForSingleObject(
> +                                  &gTunnelThreadCtx[0].stopEvent,
> +                                  Executive,
> +                                  KernelMode,
> +                                  FALSE,
> +                                  (LARGE_INTEGER *)&timeout)) {
> +            /* The stop event is signaled. Completed the IRP with
> +             * STATUS_CANCELLED. */
> +            status = STATUS_CANCELLED;
> +            break;
> +        }
> +
> +        if (NULL == filterID) {
> +            OVS_LOG_ERROR("Invalid request.");
> +            status = STATUS_INVALID_PARAMETER;
> +            break;
> +        }
> +
> +        request = (POVS_TUNFLT_REQUEST) OvsAllocateMemory(sizeof(*request));
> +        if (NULL == request) {
> +            OVS_LOG_ERROR("Failed to allocate list item.");
> +            status = STATUS_INSUFFICIENT_RESOURCES;
> +            break;
> +        }
> +
> +        request->port = remotePort;
> +        request->operation = operation;
> +        switch (operation) {
> +            case OVS_TUN_FILTER_CREATE:
> +                request->filterID.addID = filterID;
> +                break;
> +            case OVS_TUN_FILTER_DELETE:
> +                request->filterID.delID = *filterID;
> +                break;
> +        }
> +        request->irp = irp;
> +        request->callback = callback;
> +        request->context = tunnelContext;
> +
> +        OvsTunnelFilterThreadPush(request);
> +
> +        error = FALSE;
> +    } while (error);
> +
> +    if (error) {
> +        OvsTunnelFilterCompleteRequest(irp, callback, tunnelContext, status);
> +        if (request) {
> +            OvsFreeMemory(request);
> +            request = NULL;
> +        }
> +    }
> +
> +    return status;
> +}
> +
> +/*
> + * --------------------------------------------------------------------------
> + *  This function adds a new WFP filter for the received port and returns the
> + *  ID of the created WFP filter.
> + *
> + *  Note:
> + *  All necessary calls to the WFP filtering engine must be running at IRQL =
> + *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
> + *  we register an OVS_TUN_FILTER_CREATE request that will be processed by
> + *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
> + * --------------------------------------------------------------------------
> + */
> +NTSTATUS
> +OvsTunelFilterCreate(PIRP irp,
> +                     UINT16 filterPort,
> +                     UINT64 *filterID,
> +                     PFNTunnelVportPendingOp callback,
> +                     PVOID tunnelContext)
> +{
> +    return OvsTunnelFilterQueueRequest(irp,
> +                                       filterPort,
> +                                       filterID,
> +                                       OVS_TUN_FILTER_CREATE,
> +                                       callback,
> +                                       tunnelContext);
> +}
> +
> +/*
> + * --------------------------------------------------------------------------
> + *  This function removes a WFP filter using the received filter ID.
> + *
> + *  Note:
> + *  All necessary calls to the WFP filtering engine must be running at IRQL =
> + *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
> + *  we register an OVS_TUN_FILTER_DELETE request that will be processed by
> + *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
> + * --------------------------------------------------------------------------
> + */
> +NTSTATUS
> +OvsTunelFilterDelete(PIRP irp,
> +                     UINT64 filterID,
> +                     PFNTunnelVportPendingOp callback,
> +                     PVOID tunnelContext)
> +{
> +    return OvsTunnelFilterQueueRequest(irp,
> +                                       0,
> +                                       &filterID,
> +                                       OVS_TUN_FILTER_DELETE,
> +                                       callback,
> +                                       tunnelContext);
> +}
> diff --git a/datapath-windows/ovsext/TunnelIntf.h b/datapath-windows/ovsext/TunnelIntf.h
> index 82a5145..eb8c1d5 100644
> --- a/datapath-windows/ovsext/TunnelIntf.h
> +++ b/datapath-windows/ovsext/TunnelIntf.h
> @@ -17,6 +17,10 @@
>  #ifndef __TUNNEL_INTF_H_
>  #define __TUNNEL_INTF_H_ 1
>  
> +typedef VOID(*PFNTunnelVportPendingOp)(PVOID context,
> +                                       NTSTATUS status,
> +                                       UINT32 *replyLen);
> +
>  /* Tunnel callout driver load/unload functions */
>  NTSTATUS OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject);
>  
> @@ -26,4 +30,15 @@ VOID OvsRegisterSystemProvider(PVOID deviceObject);
>  
>  VOID OvsUnregisterSystemProvider();
>  
> +NTSTATUS OvsTunelFilterCreate(PIRP irp,
> +                              UINT16 filterPort,
> +                              UINT64 *filterID,
> +                              PFNTunnelVportPendingOp callback,
> +                              PVOID context);
> +
> +NTSTATUS OvsTunelFilterDelete(PIRP irp,
> +                              UINT64 filterID,
> +                              PFNTunnelVportPendingOp callback,
> +                              PVOID context);
> +
>  #endif /* __TUNNEL_INTF_H_ */
> diff --git a/datapath-windows/ovsext/Vport.c b/datapath-windows/ovsext/Vport.c
> index 1423ace..66f9189 100644
> --- a/datapath-windows/ovsext/Vport.c
> +++ b/datapath-windows/ovsext/Vport.c
> @@ -47,6 +47,20 @@
>  
>  #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC    100
>  
> +/* Context structure used to pass back and forth information to the tunnel
> + * filter threads. */
> +typedef struct _OVS_TUNFLT_INIT_CONTEXT {
> +    POVS_SWITCH_CONTEXT switchContext;
> +    UINT32 outputLength;
> +    PVOID outputBuffer;
> +    PVOID inputBuffer;
> +    POVS_VPORT_ENTRY vport;
> +    BOOLEAN hvSwitchPort;
> +    BOOLEAN hvDelete;
> +    BOOLEAN ovsDelete;
> +} OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
> +
> +
>  extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
>  
>  static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
> @@ -69,6 +83,18 @@ static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
>  static NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
>                                       POVS_VPORT_ENTRY vport,
>                                       BOOLEAN newPort);
> +static VOID OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
> +                                  POVS_VPORT_ENTRY vport,
> +                                  BOOLEAN hvSwitchPort,
> +                                  BOOLEAN hvDelete,
> +                                  BOOLEAN ovsDelete);
> +static VOID OvsTunnelVportPendingInit(PVOID context,
> +                                      NTSTATUS status,
> +                                      UINT32 *replyLen);
> +static VOID OvsTunnelVportPendingUninit(PVOID context,
> +                                        NTSTATUS status,
> +                                        UINT32 *replyLen);
> +
>  
>  /*
>   * Functions implemented in relaton to NDIS port manipulation.
> @@ -246,7 +272,7 @@ HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
>       * delete will delete the vport.
>      */
>      if (vport) {
> -        OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
> +        OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
>      } else {
>          OVS_LOG_WARN("Vport not present.");
>      }
> @@ -534,13 +560,14 @@ HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
>          goto done;
>      }
>  
> +    vport->nicState = NdisSwitchNicStateUnknown;
> +    vport->ovsState = OVS_STATE_PORT_CREATED;
> +
>      portNo = vport->portNo;
>      if (vport->portType == NdisSwitchPortTypeExternal &&
>          vport->nicIndex != 0) {
> -        OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
> +        OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
>      }
> -    vport->nicState = NdisSwitchNicStateUnknown;
> -    vport->ovsState = OVS_STATE_PORT_CREATED;
>  
>      NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
>      /* XXX if portNo != INVALID or always? */
> @@ -850,11 +877,14 @@ OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
>   * --------------------------------------------------------------------------
>   */
>  NTSTATUS
> -OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
> +OvsInitTunnelVport(PVOID userContext,
> +                   POVS_VPORT_ENTRY vport,
>                     OVS_VPORT_TYPE ovsType,
>                     UINT16 dstPort)
>  {
>      NTSTATUS status = STATUS_SUCCESS;
> +    POVS_USER_PARAMS_CONTEXT usrParamsCtx =
> +        (POVS_USER_PARAMS_CONTEXT)userContext;
>  
>      vport->isBridgeInternal = FALSE;
>      vport->ovsType = ovsType;
> @@ -865,8 +895,26 @@ OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
>      case OVS_VPORT_TYPE_GRE64:
>          break;
>      case OVS_VPORT_TYPE_VXLAN:
> -        status = OvsInitVxlanTunnel(vport, dstPort);
> +    {
> +        POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
> +
> +        tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
> +        if (tunnelContext == NULL) {
> +            status = STATUS_INSUFFICIENT_RESOURCES;
> +            break;
> +        }
> +        tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
> +        tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
> +        tunnelContext->outputLength = usrParamsCtx->outputLength;
> +        tunnelContext->vport = vport;
> +
> +        status = OvsInitVxlanTunnel(usrParamsCtx->irp,
> +                                    vport,
> +                                    dstPort,
> +                                    OvsTunnelVportPendingInit,
> +                                    (PVOID)tunnelContext);
>          break;
> +    }
>      default:
>          ASSERT(0);
>      }
> @@ -1012,7 +1060,6 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
>  
>      switch(vport->ovsType) {
>      case OVS_VPORT_TYPE_VXLAN:
> -        ASSERT(switchContext->vxlanVport == NULL);
>          switchContext->vxlanVport = vport;
>          switchContext->numNonHvVports++;
>          break;
> @@ -1043,6 +1090,64 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
>      return STATUS_SUCCESS;
>  }
>  
> +static VOID
> +OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
> +                      POVS_VPORT_ENTRY vport,
> +                      BOOLEAN hvSwitchPort,
> +                      BOOLEAN hvDelete,
> +                      BOOLEAN ovsDelete)
> +{
> +    BOOLEAN deletedOnOvs = FALSE;
> +    BOOLEAN deletedOnHv = FALSE;
> +
> +    /*
> +     * 'hvDelete' == TRUE indicates that the port should be removed from the
> +     * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
> +     * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
> +     *
> +     * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
> +     */
> +    if (vport->isPresentOnHv == TRUE) {
> +        deletedOnHv = TRUE;
> +    }
> +    if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
> +        deletedOnOvs = TRUE;
> +    }
> +
> +    if (hvDelete && !deletedOnHv) {
> +        vport->isPresentOnHv = TRUE;
> +
> +        /* Remove the port from the relevant lists. */
> +        RemoveEntryList(&vport->portIdLink);
> +        InitializeListHead(&vport->portIdLink);
> +        deletedOnHv = TRUE;
> +    }
> +    if (ovsDelete && !deletedOnOvs) {
> +        vport->portNo = OVS_DPPORT_NUMBER_INVALID;
> +        vport->ovsName[0] = '\0';
> +
> +        /* Remove the port from the relevant lists. */
> +        RemoveEntryList(&vport->ovsNameLink);
> +        InitializeListHead(&vport->ovsNameLink);
> +        RemoveEntryList(&vport->portNoLink);
> +        InitializeListHead(&vport->portNoLink);
> +        deletedOnOvs = TRUE;
> +    }
> +
> +    /*
> +     * Deallocate the port if it has been deleted on the Hyper-V switch as well
> +     * as OVS userspace.
> +     */
> +    if (deletedOnHv && deletedOnOvs) {
> +        if (hvSwitchPort) {
> +            switchContext->numHvVports--;
> +        }
> +        else {
> +            switchContext->numNonHvVports--;
> +        }
> +        OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
> +    }
> +}
>  
>  /*
>   * --------------------------------------------------------------------------
> @@ -1055,19 +1160,17 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
>   * port being removed from OVS userspace.
>   * --------------------------------------------------------------------------
>   */
> -VOID
> -OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
> +NTSTATUS
> +OvsRemoveAndDeleteVport(PVOID usrParamsContext,
> +                        POVS_SWITCH_CONTEXT switchContext,
>                          POVS_VPORT_ENTRY vport,
>                          BOOLEAN hvDelete,
> -                        BOOLEAN ovsDelete,
> -                        BOOLEAN *vportDeallocated)
> +                        BOOLEAN ovsDelete)
>  {
> +    NTSTATUS status = STATUS_SUCCESS;
> +    POVS_USER_PARAMS_CONTEXT usrParamsCtx =
> +        (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
>      BOOLEAN hvSwitchPort = FALSE;
> -    BOOLEAN deletedOnOvs = FALSE, deletedOnHv = FALSE;
> -
> -    if (vportDeallocated) {
> -        *vportDeallocated = FALSE;
> -    }
>  
>      if (vport->isExternal) {
>          if (vport->nicIndex == 0) {
> @@ -1075,10 +1178,7 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
>              switchContext->virtualExternalPortId = 0;
>              switchContext->virtualExternalVport = NULL;
>              OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
> -            if (vportDeallocated) {
> -                *vportDeallocated = TRUE;
> -            }
> -            return;
> +            return STATUS_SUCCESS;
>          } else {
>              ASSERT(switchContext->numPhysicalNics);
>              switchContext->numPhysicalNics--;
> @@ -1096,9 +1196,38 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
>          }
>          break;
>      case OVS_VPORT_TYPE_VXLAN:
> -        OvsCleanupVxlanTunnel(vport);
> +    {
> +        POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
> +        PIRP irp = NULL;
> +
> +        tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
> +        if (tunnelContext == NULL) {
> +            status = STATUS_INSUFFICIENT_RESOURCES;
> +            break;
> +        }
> +        RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
> +
> +        tunnelContext->switchContext = switchContext;
> +        tunnelContext->hvSwitchPort = hvSwitchPort;
> +        tunnelContext->hvDelete = hvDelete;
> +        tunnelContext->ovsDelete = ovsDelete;
> +        tunnelContext->vport = vport;
> +
> +        if (usrParamsCtx) {
> +            tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
> +            tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
> +            tunnelContext->outputLength = usrParamsCtx->outputLength;
> +            irp = usrParamsCtx->irp;
> +        }
> +
> +        status = OvsCleanupVxlanTunnel(irp,
> +                                       vport,
> +                                       OvsTunnelVportPendingUninit,
> +                                       tunnelContext);
> +
>          switchContext->vxlanVport = NULL;
>          break;
> +    }
>      case OVS_VPORT_TYPE_GRE:
>      case OVS_VPORT_TYPE_GRE64:
>          break;
> @@ -1108,55 +1237,15 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
>          break;
>      }
>  
> -    /*
> -     * 'hvDelete' == TRUE indicates that the port should be removed from the
> -     * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
> -     * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
> -     *
> -     * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
> -     */
> -    if (vport->isPresentOnHv == TRUE) {
> -        deletedOnHv = TRUE;
> -    }
> -    if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
> -        deletedOnOvs = TRUE;
> -    }
> -
> -    if (hvDelete && !deletedOnHv) {
> -        vport->isPresentOnHv = TRUE;
> -
> -        /* Remove the port from the relevant lists. */
> -        RemoveEntryList(&vport->portIdLink);
> -        InitializeListHead(&vport->portIdLink);
> -        deletedOnHv = TRUE;
> +    if (STATUS_SUCCESS == status) {
> +        OvsCleanupVportCommon(switchContext,
> +                              vport,
> +                              hvSwitchPort,
> +                              hvDelete,
> +                              ovsDelete);
>      }
> -    if (ovsDelete && !deletedOnOvs) {
> -        vport->portNo = OVS_DPPORT_NUMBER_INVALID;
> -        vport->ovsName[0] = '\0';
>  
> -        /* Remove the port from the relevant lists. */
> -        RemoveEntryList(&vport->ovsNameLink);
> -        InitializeListHead(&vport->ovsNameLink);
> -        RemoveEntryList(&vport->portNoLink);
> -        InitializeListHead(&vport->portNoLink);
> -        deletedOnOvs = TRUE;
> -    }
> -
> -    /*
> -     * Deallocate the port if it has been deleted on the Hyper-V switch as well
> -     * as OVS userspace.
> -     */
> -    if (deletedOnHv && deletedOnOvs) {
> -        if (hvSwitchPort) {
> -            switchContext->numHvVports--;
> -        } else {
> -            switchContext->numNonHvVports--;
> -        }
> -        OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
> -        if (vportDeallocated) {
> -            *vportDeallocated = TRUE;
> -        }
> -    }
> +    return status;
>  }
>  
>  NDIS_STATUS
> @@ -1294,7 +1383,7 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
>          LIST_FORALL_SAFE(head, link, next) {
>              POVS_VPORT_ENTRY vport;
>              vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
> -            OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
> +            OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
>          }
>      }
>      /*
> @@ -1302,9 +1391,8 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
>       * 'portIdHashArray'.
>       */
>      if (switchContext->virtualExternalVport) {
> -        OvsRemoveAndDeleteVport(switchContext,
> -            (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE,
> -            NULL);
> +        OvsRemoveAndDeleteVport(NULL, switchContext,
> +            (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
>      }
>  
>      for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
> @@ -1317,7 +1405,7 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
>              ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
>                     (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
>                      vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
> -            OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
> +            OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
>          }
>      }
>  
> @@ -1895,7 +1983,7 @@ Cleanup:
>  
>  /*
>   * --------------------------------------------------------------------------
> - *  Command Handler for 'OVS_VPORT_CMD_NEW'.
> + *  Command Handler for 'OVS_VPORT_CMD_GET'.
>   *
>   *  The function handles the initial call to setup the dump state, as well as
>   *  subsequent calls to continue dumping data.
> @@ -2020,8 +2108,6 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
>      } else {
>          ASSERT(OvsIsTunnelVportType(portType) ||
>                 (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
> -        ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
> -               !OvsIsTunnelVportType(portType));
>  
>          vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
>          if (vport == NULL) {
> @@ -2031,11 +2117,23 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
>          vportAllocated = TRUE;
>  
>          if (OvsIsTunnelVportType(portType)) {
> -            status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
> +            UINT16 udpPortDest = VXLAN_UDP_PORT;
> +            PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
> +                                             OVS_TUNNEL_ATTR_DST_PORT);
> +            if (attr) {
> +                udpPortDest = NlAttrGetU16(attr);
> +            }
> +
> +            status = OvsInitTunnelVport(usrParamsCtx,
> +                                        vport,
> +                                        portType,
> +                                        udpPortDest);
> +
>              nlError = NlMapStatusToNlErr(status);
>          } else {
>              OvsInitBridgeInternalVport(vport);
>          }
> +
>          vportInitialized = TRUE;
>  
>          if (nlError == NL_ERROR_SUCCESS) {
> @@ -2047,6 +2145,8 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
>               * corresponding hyper-v switch part.
>               */
>              vport->isPresentOnHv = TRUE;
> +        } else {
> +            goto Cleanup;
>          }
>      }
>  
> @@ -2106,14 +2206,14 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
>  Cleanup:
>      NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
>  
> -    if (nlError != NL_ERROR_SUCCESS) {
> +    if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
>          POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
>              usrParamsCtx->outputBuffer;
>  
>          if (vport && vportAllocated == TRUE) {
>              if (vportInitialized == TRUE) {
>                  if (OvsIsTunnelVportType(portType)) {
> -                    OvsCleanupVxlanTunnel(vport);
> +                    OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
>                  }
>              }
>              OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
> @@ -2123,7 +2223,7 @@ Cleanup:
>          *replyLen = msgError->nlMsg.nlmsgLen;
>      }
>  
> -    return STATUS_SUCCESS;
> +    return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
>  }
>  
>  
> @@ -2297,18 +2397,25 @@ OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
>                                     usrParamsCtx->outputLength,
>                                     gOvsSwitchContext->dpNo);
>  
> +    *replyLen = msgOut->nlMsg.nlmsgLen;
> +
>      /*
>       * Mark the port as deleted from OVS userspace. If the port does not exist
>       * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
>       */
> -    OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
> -
> -    *replyLen = msgOut->nlMsg.nlmsgLen;
> +    status = OvsRemoveAndDeleteVport(usrParamsCtx,
> +                                     gOvsSwitchContext,
> +                                     vport,
> +                                     FALSE,
> +                                     TRUE);
> +    if (status) {
> +        nlError = NlMapStatusToNlErr(status);
> +    }
>  
>  Cleanup:
>      NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
>  
> -    if (nlError != NL_ERROR_SUCCESS) {
> +    if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
>          POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
>              usrParamsCtx->outputBuffer;
>  
> @@ -2316,5 +2423,173 @@ Cleanup:
>          *replyLen = msgError->nlMsg.nlmsgLen;
>      }
>  
> -    return STATUS_SUCCESS;
> +    return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
> +}
> +
> +static VOID
> +OvsTunnelVportPendingUninit(PVOID context,
> +                            NTSTATUS status,
> +                            UINT32 *replyLen)
> +{
> +    POVS_TUNFLT_INIT_CONTEXT tunnelContext =
> +        (POVS_TUNFLT_INIT_CONTEXT) context;
> +    POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
> +    POVS_VPORT_ENTRY vport = tunnelContext->vport;
> +    POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
> +    POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
> +    NL_ERROR nlError = NlMapStatusToNlErr(status);
> +    LOCK_STATE_EX lockState;
> +
> +    NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
> +
> +    if (msgIn && msgOut) {
> +        /* Check the received status to reply to the caller. */
> +        if (STATUS_SUCCESS == status) {
> +            OvsCreateMsgFromVport(vport,
> +                                  msgIn,
> +                                  msgOut,
> +                                  tunnelContext->outputLength,
> +                                  switchContext->dpNo);
> +
> +            *replyLen = msgOut->nlMsg.nlmsgLen;
> +        } else {
> +            POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
> +
> +            NlBuildErrorMsg(msgIn, msgError, nlError);
> +            *replyLen = msgError->nlMsg.nlmsgLen;
> +        }
> +    }
> +
> +    OvsCleanupVportCommon(switchContext,
> +                          vport,
> +                          tunnelContext->hvSwitchPort,
> +                          tunnelContext->hvDelete,
> +                          tunnelContext->ovsDelete);
> +
> +    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
> +}
> +
> +static VOID
> +OvsTunnelVportPendingInit(PVOID context,
> +                          NTSTATUS status,
> +                          UINT32 *replyLen)
> +{
> +    POVS_TUNFLT_INIT_CONTEXT tunnelContext =
> +        (POVS_TUNFLT_INIT_CONTEXT) context;
> +    POVS_VPORT_ENTRY vport = tunnelContext->vport;
> +    POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
> +    POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
> +    PCHAR portName;
> +    ULONG portNameLen = 0;
> +    UINT32 portType = 0;
> +    NL_ERROR nlError = NL_ERROR_SUCCESS;
> +    BOOLEAN error = TRUE;
> +
> +    do {
> +        if (!NT_SUCCESS(status)) {
> +            nlError = NlMapStatusToNlErr(status);
> +            break;
> +        }
> +
> +        static const NL_POLICY ovsVportPolicy[] = {
> +            [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
> +            [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
> +            [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
> +            .optional = FALSE },
> +            [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
> +            .optional = FALSE },
> +            [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
> +        };
> +
> +        PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
> +
> +        /* input buffer has been validated while validating write dev op. */
> +        ASSERT(msgIn != NULL);
> +
> +        /* Output buffer has been validated while validating transact dev op. */
> +        ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
> +
> +        if (!NlAttrParse((PNL_MSG_HDR)msgIn,
> +            NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
> +            NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
> +            ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
> +            nlError = NL_ERROR_INVAL;
> +            break;
> +        }
> +
> +        portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
> +        portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
> +        portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
> +
> +        if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
> +            nlError = NL_ERROR_EXIST;
> +            break;
> +        }
> +
> +        vport->ovsState = OVS_STATE_CONNECTED;
> +        vport->nicState = NdisSwitchNicStateConnected;
> +
> +        /*
> +         * Allow the vport to be deleted, because there is no
> +         * corresponding hyper-v switch part.
> +         */
> +        vport->isPresentOnHv = TRUE;
> +
> +        if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
> +            /*
> +             * XXX: when we implement the limit for OVS port number to be
> +             * MAXUINT16, we'll need to check the port number received from the
> +             * userspace.
> +             */
> +            vport->portNo =
> +                NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
> +        } else {
> +            vport->portNo =
> +                OvsComputeVportNo(gOvsSwitchContext);
> +            if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
> +                nlError = NL_ERROR_NOMEM;
> +                break;
> +            }
> +        }
> +
> +        /* The ovs port name must be uninitialized. */
> +        ASSERT(vport->ovsName[0] == '\0');
> +        ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
> +
> +        RtlCopyMemory(vport->ovsName, portName, portNameLen);
> +        /* if we don't have options, then vport->portOptions will be NULL */
> +        vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
> +
> +        /*
> +         * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
> +         * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
> +         * it means we have an array of pids, instead of a single pid.
> +         * ATM we assume we have one pid only.
> +         */
> +        vport->upcallPid =
> +            NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
> +
> +        status = InitOvsVportCommon(gOvsSwitchContext, vport);
> +        ASSERT(status == STATUS_SUCCESS);
> +
> +        OvsCreateMsgFromVport(vport,
> +                              msgIn,
> +                              msgOut,
> +                              tunnelContext->outputLength,
> +                              gOvsSwitchContext->dpNo);
> +
> +        *replyLen = msgOut->nlMsg.nlmsgLen;
> +
> +        error = FALSE;
> +    } while (error);
> +
> +    if (error) {
> +        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
> +
> +        OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
> +        OvsFreeMemory(vport);
> +
> +        NlBuildErrorMsg(msgIn, msgError, nlError);
> +        *replyLen = msgError->nlMsg.nlmsgLen;
> +    }
>  }
> diff --git a/datapath-windows/ovsext/Vport.h b/datapath-windows/ovsext/Vport.h
> index 348fbfd..4cfda30 100644
> --- a/datapath-windows/ovsext/Vport.h
> +++ b/datapath-windows/ovsext/Vport.h
> @@ -207,15 +207,15 @@ OvsIsBridgeInternalVport(POVS_VPORT_ENTRY vport)
>      return vport->isBridgeInternal == TRUE;
>  }
>  
> -VOID OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
> -                             POVS_VPORT_ENTRY vport,
> -                             BOOLEAN hvDelete, BOOLEAN ovsDelete,
> -                             BOOLEAN *vportDeallocated);
> +NTSTATUS OvsRemoveAndDeleteVport(PVOID usrParamsCtx, 
> +                                 POVS_SWITCH_CONTEXT switchContext,
> +                                 POVS_VPORT_ENTRY vport,
> +                                 BOOLEAN hvDelete, BOOLEAN ovsDelete);
>  
>  NDIS_STATUS InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
>                                 POVS_VPORT_ENTRY vport);
> -NTSTATUS OvsInitTunnelVport(POVS_VPORT_ENTRY vport, OVS_VPORT_TYPE ovsType,
> -                            UINT16 dstport);
> +NTSTATUS OvsInitTunnelVport(PVOID usrParamsCtx, POVS_VPORT_ENTRY vport,
> +                            OVS_VPORT_TYPE ovsType, UINT16 dstport);
>  NTSTATUS OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport);
>  
>  POVS_VPORT_ENTRY OvsAllocateVport(VOID);
> diff --git a/datapath-windows/ovsext/Vxlan.c b/datapath-windows/ovsext/Vxlan.c
> index 8c57185..bf16e35 100644
> --- a/datapath-windows/ovsext/Vxlan.c
> +++ b/datapath-windows/ovsext/Vxlan.c
> @@ -50,14 +50,57 @@
>  extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
>  
>  /*
> + *----------------------------------------------------------------------------
> + * This function verifies if the VXLAN tunnel already exists, in order to
> + * avoid sending a duplicate request to the WFP base filtering engine.
> + *----------------------------------------------------------------------------
> + */
> +static BOOLEAN
> +OvsIsTunnelFilterCreated(POVS_SWITCH_CONTEXT switchContext,
> +                         UINT16 udpPortDest)
> +{
> +    for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
> +        PLIST_ENTRY head, link, next;
> +
> +        head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
> +        LIST_FORALL_SAFE(head, link, next) {
> +            POVS_VPORT_ENTRY vport = NULL;
> +            POVS_VXLAN_VPORT vxlanPort = NULL;
> +            vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
> +            vxlanPort = (POVS_VXLAN_VPORT)vport->priv;
> +            if (vxlanPort) {
> +                if ((udpPortDest == vxlanPort->dstPort)) {
> +                    /* The VXLAN tunnel was already created. */
> +                    return TRUE;
> +                }
> +            }
> +        }
> +    }
> +
> +    return FALSE;
> +}
> +
> +/*
> + *----------------------------------------------------------------------------
> + * This function allocates and initializes the OVS_VXLAN_VPORT. The function
> + * also creates a WFP tunnel filter for the necessary destination port. The
> + * tunnel filter create request is passed to the tunnel filter threads that
> + * will complete the request at a later time when IRQL is lowered to
> + * PASSIVE_LEVEL.
> + * 
>   * udpDestPort: the vxlan is set as payload to a udp frame. If the destination
>   * port of an udp frame is udpDestPort, we understand it to be vxlan.
> + *----------------------------------------------------------------------------
>   */
>  NTSTATUS
> -OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
> -                   UINT16 udpDestPort)
> +OvsInitVxlanTunnel(PIRP irp,
> +                   POVS_VPORT_ENTRY vport,
> +                   UINT16 udpDestPort,
> +                   PFNTunnelVportPendingOp callback,
> +                   PVOID tunnelContext)
>  {
> -    POVS_VXLAN_VPORT vxlanPort;
> +    NTSTATUS status = STATUS_SUCCESS;
> +    POVS_VXLAN_VPORT vxlanPort = NULL;
>  
>      vxlanPort = OvsAllocateMemoryWithTag(sizeof (*vxlanPort),
>                                           OVS_VXLAN_POOL_TAG);
> @@ -67,28 +110,56 @@ OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
>  
>      RtlZeroMemory(vxlanPort, sizeof(*vxlanPort));
>      vxlanPort->dstPort = udpDestPort;
> -    /*
> -     * since we are installing the WFP filter before the port is created
> -     * We need to check if it is the same number
> -     * XXX should be removed later
> -     */
> -    ASSERT(vxlanPort->dstPort == VXLAN_UDP_PORT);
>      vport->priv = (PVOID)vxlanPort;
>  
> -    return STATUS_SUCCESS;
> -}
> +    if (!OvsIsTunnelFilterCreated(gOvsSwitchContext, udpDestPort)) {
> +        status = OvsTunelFilterCreate(irp,
> +                                      udpDestPort,
> +                                      &vxlanPort->filterID,
> +                                      callback,
> +                                      tunnelContext);
> +    } else {
> +        status = STATUS_OBJECT_NAME_EXISTS;
> +    }
>  
> +    return status;
> +}
>  
> -VOID
> -OvsCleanupVxlanTunnel(POVS_VPORT_ENTRY vport)
> +/*
> + *----------------------------------------------------------------------------
> + * This function releases the OVS_VXLAN_VPORT. The function also deletes the
> + * WFP tunnel filter previously created. The tunnel filter delete request is
> + * passed to the tunnel filter threads that will complete the request at a
> + * later time when IRQL is lowered to PASSIVE_LEVEL.
> + *----------------------------------------------------------------------------
> + */
> +NTSTATUS
> +OvsCleanupVxlanTunnel(PIRP irp,
> +                      POVS_VPORT_ENTRY vport,
> +                      PFNTunnelVportPendingOp callback,
> +                      PVOID tunnelContext)
>  {
> +    NTSTATUS status = STATUS_SUCCESS;
> +    POVS_VXLAN_VPORT vxlanPort = NULL;
> +
>      if (vport->ovsType != OVS_VPORT_TYPE_VXLAN ||
>          vport->priv == NULL) {
> -        return;
> +        return STATUS_SUCCESS;
> +    }
> +
> +    vxlanPort = (POVS_VXLAN_VPORT)vport->priv;
> +
> +    if (vxlanPort->filterID != 0) {
> +        status = OvsTunelFilterDelete(irp,
> +                                      vxlanPort->filterID,
> +                                      callback,
> +                                      tunnelContext);
>      }
>  
>      OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
>      vport->priv = NULL;
> +
> +    return status;
>  }
>  
>  
> @@ -475,9 +546,6 @@ OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
>              break;
>          }
>  
> -        /* XXX Should be tested against the dynamic port # in the VXLAN vport */
> -        ASSERT(udp->dest == RtlUshortByteSwap(VXLAN_UDP_PORT));
> -
>          VxlanHeader = (VXLANHdr *)OvsGetPacketBytes(packet,
>                                                      sizeof(*VxlanHeader),
>                                                      layers.l7Offset,
> diff --git a/datapath-windows/ovsext/Vxlan.h b/datapath-windows/ovsext/Vxlan.h
> index d84796d..248a5dc 100644
> --- a/datapath-windows/ovsext/Vxlan.h
> +++ b/datapath-windows/ovsext/Vxlan.h
> @@ -24,6 +24,7 @@ typedef struct _OVS_VXLAN_VPORT {
>      UINT64 outPkts;
>      UINT64 slowInPkts;
>      UINT64 slowOutPkts;
> +    UINT64 filterID;
>      /*
>       * To be filled
>       */
> @@ -47,10 +48,16 @@ typedef struct VXLANHdr {
>      UINT32   reserved2:8;
>  } VXLANHdr;
>  
> -NTSTATUS OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
> -                            UINT16 udpDestPort);
> +NTSTATUS OvsInitVxlanTunnel(PIRP irp,
> +                            POVS_VPORT_ENTRY vport,
> +                            UINT16 udpDestPort,
> +                            PFNTunnelVportPendingOp callback,
> +                            PVOID tunnelContext);
>  
> -VOID OvsCleanupVxlanTunnel(POVS_VPORT_ENTRY vport);
> +NTSTATUS OvsCleanupVxlanTunnel(PIRP irp,
> +                               POVS_VPORT_ENTRY vport,
> +                               PFNTunnelVportPendingOp callback,
> +                               PVOID tunnelContext);
>  
>  NDIS_STATUS OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
>                                    OvsIPv4TunnelKey *tunnelKey);
> -- 
> 1.9.0.msysgit.0
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list