/*
 * Copyright (c) 2014 VMware, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * This file contains the definition of the switch object for the OVS.
 */

#ifndef __SWITCH_H_
#define __SWITCH_H_ 1

#include "NetProto.h"
#include "BufferMgmt.h"
#include "TunnelIntf.h"
#define OVS_MAX_VPORT_ARRAY_SIZE 1024
#define OVS_MAX_PID_ARRAY_SIZE   1024

#define OVS_VPORT_MASK (OVS_MAX_VPORT_ARRAY_SIZE - 1)
#define OVS_PID_MASK (OVS_MAX_PID_ARRAY_SIZE - 1)

#define OVS_INTERNAL_VPORT_DEFAULT_INDEX 0

//Tunnel port indicies
#define RESERVED_START_INDEX1    1
#define OVS_TUNNEL_INDEX_START RESERVED_START_INDEX1
#define OVS_VXLAN_VPORT_INDEX    2
#define OVS_GRE_VPORT_INDEX      3
#define OVS_TUNNEL_INDEX_END OVS_GRE_VPORT_INDEX

#define OVS_MAX_PHYS_ADAPTERS    32
#define OVS_MAX_IP_VPOR          32

#define OVS_HASH_BASIS   0x13578642

typedef struct _OVS_VPORT_ENTRY *POVS_VPORT_ENTRY;

typedef struct _OVS_DATAPATH
{
   PLIST_ENTRY             flowTable;       // Contains OvsFlows.
   UINT32                  nFlows;          // Number of entries in flowTable.

   // List_Links              queues[64];      // Hash table of queue IDs.

   /* Statistics. */
   UINT64                  hits;            // Number of flow table hits.
   UINT64                  misses;          // Number of flow table misses.
   UINT64                  lost;            // Number of dropped misses.

   /* Used to protect the flows in the flowtable. */
   PNDIS_RW_LOCK_EX        lock;
} OVS_DATAPATH, *POVS_DATAPATH;

/*
 * OVS_SWITCH_CONTEXT
 *
 * The context allocated per switch., For OVS, we only
 * support one switch which corresponding to one datapath.
 * Each datapath can have multiple logical bridges configured
 * which is maintained by vswitchd.
 */

typedef enum OVS_SWITCH_DATAFLOW_STATE
{
    OvsSwitchPaused,
    OvsSwitchRunning
} OVS_SWITCH_DATAFLOW_STATE, *POVS_SWITCH_DATAFLOW_STATE;

typedef enum OVS_SWITCH_CONTROFLOW_STATE
{
    OvsSwitchUnknown,
    OvsSwitchAttached,
    OvsSwitchDetached
} OVS_SWITCH_CONTROLFLOW_STATE, *POVS_SWITCH_CONTROLFLOW_STATE;

// XXX: Take care of alignment and grouping members by cacheline
typedef struct _OVS_SWITCH_CONTEXT
{
    /* Coarse and fine-grained switch states. */
    OVS_SWITCH_DATAFLOW_STATE dataFlowState;
    OVS_SWITCH_CONTROLFLOW_STATE controlFlowState;
    BOOLEAN                 isActivated;
    BOOLEAN                 isActivateFailed;

    UINT32                  dpNo;

    /*
     * 'virtualExternalVport' represents default external interface. This is
     * a virtual interface. The friendly name of such an interface has
     * been observed to be: "Microsoft Default External Interface". This NIC
     * has 'NicIndex' == 0.
     *
     * The "real" physical external NIC has 'NicIndex' > 0. For each
     * external interface, virtual or physical, NDIS gives an NIC level
     * OID callback. Note that, even though there are multiple "NICs",
     * there's only one underlying Hyper-V port. Thus, we get a single
     * NDIS port-level callback, but multiple NDIS NIC-level callbacks.
     *
     * The virtual external NIC can be accessed at 'virtualExternalVport', and
     * is assigned the name "external.defaultAdapter". The virtual external
     * NIC is not inserted into the 'portIdHashArray' since the port must not
     * be exposed to OVS userspace.
     *
     * The physical external NICs are assigned names "external.%INDEX%",
     * where '%INDEX%' represents the 'NicIndex' of the NIC.
     *
     * While adding a physical external NIC in OvsInitConfiguredSwitchNics(),
     * some required properties of the vport are available only at the
     * NDIS port-level. So, these are copied from 'virtualExternalVport'.
     * The vport created for the physical external NIC is inserted into the
     * 'portIdHashArray'.
     *
     * When the virtual external NIC is torn down or deleted, the
     * corresponding physical external ports are also torn down or
     * deleted. The number of physical external NICs is tracked by
     * 'numPhysicalNics'.
     */
    NDIS_SWITCH_PORT_ID     virtualExternalPortId;
    POVS_VPORT_ENTRY        virtualExternalVport;   /* the virtual adapter
                                                     * vport */
    INT32                   countInternalVports;    /* the number of internal
                                                     * vports */
    UINT32                  ipHelperBoundVportNo;   /* vportNo bound to
                                                     * IpHelper */

    /*
     * 'portIdHashArray' ONLY contains ports that exist on the Hyper-V switch,
     * namely: VIF (vNIC) ports, external port and Hyper-V internal port.
     * 'numHvVports' counts the ports in 'portIdHashArray'. If a port got
     * deleted on the Hyper-V switch, it gets deleted from 'portIdHashArray'.
     * The port itself will not get deallocated if it has been added from OVS
     * userspace. 'numHvVports' is decremented when the port is deallocated.
     *
     * 'portNoHashArray' ONLY contains ports that are added from OVS userspace,
     * regardless of whether that port exists on the Hyper-V switch or not.
     * Tunnel ports and bridge-internal ports are examples of ports that do not
     * exist on the Hyper-V switch, and 'numNonHvVports' counts such ports in
     * 'portNoHashArray'.
     *
     * 'tunnelVportsArray' contains tunnel ports that are added from OVS
     * userspace. Currently only VXLAN tunnels are added in this list.
     *
     * 'ovsPortNameHashArray' contains the same entries as 'portNoHashArray' but
     * hashed on a different key.
     */
    PLIST_ENTRY             portIdHashArray;        // based on Hyper-V portId
    PLIST_ENTRY             portNoHashArray;        // based on ovs port number
    PLIST_ENTRY             tunnelVportsArray;      // based on ovs dst port number
    PLIST_ENTRY             ovsPortNameHashArray;   // based on ovsName
    PLIST_ENTRY             pidHashArray;           // based on packet pids
    NDIS_SPIN_LOCK          pidHashLock;            // Lock for pidHash table

    UINT32                  numPhysicalNics;        // the number of physical
                                                    // external NICs.
    UINT32                  numHvVports;
    UINT32                  numNonHvVports;

    /* Lock taken over the switch. This protects the ports on the switch. */
    PNDIS_RW_LOCK_EX        dispatchLock;

    /* The flowtable. */
    OVS_DATAPATH            datapath;

    /* Handle to the OVSExt filter driver. Same as 'gOvsExtDriverHandle'. */
    NDIS_HANDLE NdisFilterHandle;

    /* Handle and callbacks exposed by the underlying hyper-v switch. */
    NDIS_SWITCH_CONTEXT NdisSwitchContext;
    NDIS_SWITCH_OPTIONAL_HANDLERS NdisSwitchHandlers;

    volatile LONG pendingInjectedNblCount;
    volatile LONG pendingOidCount;

    OVS_NBL_POOL            ovsPool;
} OVS_SWITCH_CONTEXT, *POVS_SWITCH_CONTEXT;


_IRQL_raises_(DISPATCH_LEVEL)
_IRQL_saves_global_(OldIrql, lockState)
_Acquires_lock_(datapath->lock)
static __inline VOID
OvsAcquireDatapathRead(OVS_DATAPATH *datapath,
                       LOCK_STATE_EX *lockState,
                       BOOLEAN dispatch)
{
    ASSERT(datapath);
    NdisAcquireRWLockRead(datapath->lock, lockState,
                          dispatch ? NDIS_RWL_AT_DISPATCH_LEVEL : 0);
}

_IRQL_raises_(DISPATCH_LEVEL)
_IRQL_saves_global_(OldIrql, lockState)
_Acquires_lock_(datapath->lock)
static __inline VOID
OvsAcquireDatapathWrite(OVS_DATAPATH *datapath,
                        LOCK_STATE_EX *lockState,
                        BOOLEAN dispatch)
{
    ASSERT(datapath);
    NdisAcquireRWLockWrite(datapath->lock, lockState,
                           dispatch ? NDIS_RWL_AT_DISPATCH_LEVEL : 0);
}

_IRQL_requires_(DISPATCH_LEVEL)
_IRQL_restores_global_(OldIrql, lockState)
_Requires_lock_held_(datapath->lock)
_Releases_lock_(datapath->lock)
static __inline VOID
OvsReleaseDatapath(OVS_DATAPATH *datapath,
                   LOCK_STATE_EX *lockState)
{
    ASSERT(datapath);
    NdisReleaseRWLock(datapath->lock, lockState);
}

BOOLEAN
OvsAcquireSwitchContext(VOID);

VOID
OvsReleaseSwitchContext(POVS_SWITCH_CONTEXT switchContext);

#endif /* __SWITCH_H_ */
