[ovs-dev] [PATCH v2] datapath-windows: Hot add CPU support.

Sorin Vinturis svinturis at cloudbasesolutions.com
Mon Apr 4 10:03:50 UTC 2016


Hot add CPU is the ability to dynamically add CPUs to a running
system. Adding CPUs can occur physically by adding new hardware,
logically by online hardware partitioning, or virtually through
a virtualization layer.

This patch add support to reallocate any per-cpu resources, in
case a new processor is added.

Signed-off-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
Reported-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
Reported-at: https://github.com/openvswitch/ovs-issues/issues/112
---
v2: Correctly reallocate per-cpu data structures when a new processor
    is about to be added to the system.
---
 datapath-windows/ovsext/Actions.c  |  11 +-
 datapath-windows/ovsext/Datapath.c |   4 -
 datapath-windows/ovsext/Driver.c   |   9 ++
 datapath-windows/ovsext/Recirc.c   | 249 +++++++++++++++++++++++++++++++------
 datapath-windows/ovsext/Recirc.h   |  87 ++++++-------
 datapath-windows/ovsext/Util.c     | 142 ++++++++++++++++++++-
 datapath-windows/ovsext/Util.h     |  35 +++++-
 7 files changed, 442 insertions(+), 95 deletions(-)

diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
index 3e5dac9..5c39951 100644
--- a/datapath-windows/ovsext/Actions.c
+++ b/datapath-windows/ovsext/Actions.c
@@ -40,6 +40,8 @@
 
 #define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
 
+extern PNDIS_RW_LOCK_EX ovsDeferredActionLevelLock;
+
 typedef struct _OVS_ACTION_STATS {
     UINT64 rxGre;
     UINT64 txGre;
@@ -1973,14 +1975,19 @@ OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext,
 
     flow = OvsLookupFlow(&ovsFwdCtx.switchContext->datapath, key, &hash, FALSE);
     if (flow) {
-        UINT32 level = OvsDeferredActionsLevelGet();
+        UINT32 level = 0;
+        LOCK_STATE_EX lockState;
+
+        NdisAcquireRWLockRead(ovsDeferredActionLevelLock, &lockState, 0);
 
+        level = OvsDeferredActionsLevelGet();
         if (level > DEFERRED_ACTION_EXEC_LEVEL) {
             OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
                 L"OVS-Dropped due to deferred actions execution level limit \
                   reached");
             ovsActionStats.deferredActionsExecLimit++;
             ovsFwdCtx.curNbl = NULL;
+            NdisReleaseRWLock(ovsDeferredActionLevelLock, &lockState);
             return NDIS_STATUS_FAILURE;
         }
 
@@ -1999,6 +2006,8 @@ OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext,
         ovsFwdCtx.curNbl = NULL;
 
         OvsDeferredActionsLevelDec();
+
+        NdisReleaseRWLock(ovsDeferredActionLevelLock, &lockState);
     } else {
         POVS_VPORT_ENTRY vport = NULL;
         LIST_ENTRY missedPackets;
diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c
index 464fa97..5725114 100644
--- a/datapath-windows/ovsext/Datapath.c
+++ b/datapath-windows/ovsext/Datapath.c
@@ -385,8 +385,6 @@ OvsInit()
     gOvsCtrlLock = &ovsCtrlLockObj;
     NdisAllocateSpinLock(gOvsCtrlLock);
     OvsInitEventQueue();
-    OvsDeferredActionsQueueAlloc();
-    OvsDeferredActionsLevelAlloc();
 }
 
 VOID
@@ -397,8 +395,6 @@ OvsCleanup()
         NdisFreeSpinLock(gOvsCtrlLock);
         gOvsCtrlLock = NULL;
     }
-    OvsDeferredActionsQueueFree();
-    OvsDeferredActionsLevelFree();
 }
 
 VOID
diff --git a/datapath-windows/ovsext/Driver.c b/datapath-windows/ovsext/Driver.c
index 853886e..f5d3f9c 100644
--- a/datapath-windows/ovsext/Driver.c
+++ b/datapath-windows/ovsext/Driver.c
@@ -152,6 +152,12 @@ DriverEntry(PDRIVER_OBJECT driverObject,
         goto cleanup;
     }
 
+    /* Allocate per-cpu structures and register processor change callback. */
+    status = OvsPerCpuDataInit(gOvsExtDriverHandle);
+    if (!NT_SUCCESS(status)) {
+        goto cleanup;
+    }
+
 cleanup:
     if (status != NDIS_STATUS_SUCCESS){
         OvsCleanup();
@@ -180,6 +186,9 @@ OvsExtUnload(struct _DRIVER_OBJECT *driverObject)
 
     OvsDeleteDeviceObject();
 
+    /* Release per-cpu structures and deregister processor change callback. */
+    OvsPerCpuDataCleanup();
+
     NdisFDeregisterFilterDriver(gOvsExtDriverHandle);
 }
 
diff --git a/datapath-windows/ovsext/Recirc.c b/datapath-windows/ovsext/Recirc.c
index 86e6f51..5ef1992 100644
--- a/datapath-windows/ovsext/Recirc.c
+++ b/datapath-windows/ovsext/Recirc.c
@@ -18,71 +18,218 @@
 #include "Flow.h"
 #include "Jhash.h"
 
-static POVS_DEFERRED_ACTION_QUEUE ovsDeferredActionQueue = NULL;
-static UINT32* ovsDeferredActionLevel = NULL;
+/*
+ * --------------------------------------------------------------------------
+ * '_OVS_DEFERRED_ACTION_QUEUE' structure is responsible for keeping track of
+ * all deferred actions. The maximum number of deferred actions should not
+ * exceed 'DEFERRED_ACTION_QUEUE_SIZE'.
+ * --------------------------------------------------------------------------
+ */
+typedef struct _OVS_DEFERRED_ACTION_QUEUE {
+    UINT32  head;
+    UINT32  tail;
+    OVS_DEFERRED_ACTION queue[DEFERRED_ACTION_QUEUE_SIZE];
+} OVS_DEFERRED_ACTION_QUEUE, *POVS_DEFERRED_ACTION_QUEUE;
+
+static POVS_DEFERRED_ACTION_QUEUE   ovsDeferredActionQueue = NULL;
+static UINT32*                      ovsDeferredActionLevel = NULL;
+static PNDIS_RW_LOCK_EX             ovsDeferredActionQueueLock = NULL;
+PNDIS_RW_LOCK_EX                    ovsDeferredActionLevelLock = NULL;
+static ULONG                        ovsActiveProcessorCount = 0;
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsQueueAlloc --
- *     The function allocates per-cpu deferred actions queue.
+ * OvsDeferredActionsLevelRealloc --
+ *     The function allocates per-processor deferred actions level.
  * --------------------------------------------------------------------------
  */
-BOOLEAN
-OvsDeferredActionsQueueAlloc()
+NTSTATUS
+OvsDeferredActionsLevelRealloc(BOOLEAN addCpu, PULONG count)
 {
-    ovsDeferredActionQueue =
-        OvsAllocateMemoryPerCpu(sizeof(*ovsDeferredActionQueue),
-                                OVS_RECIRC_POOL_TAG);
-    if (!ovsDeferredActionQueue) {
-        return FALSE;
+    NTSTATUS status = STATUS_SUCCESS;
+    PUINT32 level = NULL;
+    LOCK_STATE_EX lockState;
+
+    NdisAcquireRWLockWrite(ovsDeferredActionLevelLock, &lockState, 0);
+
+    level = OvsReallocateMemoryPerCpu(ovsDeferredActionLevel,
+                                      sizeof(*ovsDeferredActionLevel),
+                                      OVS_RECIRC_POOL_TAG,
+                                      addCpu,
+                                      ovsActiveProcessorCount,
+                                      count);
+    if (!level) {
+        NdisReleaseRWLock(ovsDeferredActionLevelLock, &lockState);
+        status = NDIS_STATUS_RESOURCES;
+        goto exit;
     }
-    return TRUE;
+
+    ovsDeferredActionLevel = level;
+
+    NdisReleaseRWLock(ovsDeferredActionLevelLock, &lockState);
+
+exit:
+    return status;
 }
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsQueueFree --
- *     The function frees per-cpu deferred actions queue.
+ * OvsDeferredActionsQueueRealloc --
+ *     The function allocates per-processor deferred actions queue.
  * --------------------------------------------------------------------------
  */
-VOID
-OvsDeferredActionsQueueFree()
+NTSTATUS
+OvsDeferredActionsQueueRealloc(BOOLEAN addCpu, PULONG count)
 {
-    OvsFreeMemoryWithTag(ovsDeferredActionQueue,
-                         OVS_RECIRC_POOL_TAG);
-    ovsDeferredActionQueue = NULL;
+    NTSTATUS status = STATUS_SUCCESS;
+    POVS_DEFERRED_ACTION_QUEUE queue = NULL;
+    LOCK_STATE_EX lockState;
+
+    NdisAcquireRWLockWrite(ovsDeferredActionQueueLock, &lockState, 0);
+
+    queue = OvsReallocateMemoryPerCpu(ovsDeferredActionQueue,
+                                      sizeof(*ovsDeferredActionQueue),
+                                      OVS_RECIRC_POOL_TAG,
+                                      addCpu,
+                                      ovsActiveProcessorCount,
+                                      count);
+    if (!queue) {
+        NdisReleaseRWLock(ovsDeferredActionQueueLock, &lockState);
+        status = NDIS_STATUS_RESOURCES;
+        goto exit;
+    }
+
+    ovsDeferredActionQueue = queue;
+
+    NdisReleaseRWLock(ovsDeferredActionQueueLock, &lockState);
+
+exit:
+    return status;
 }
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsLevelAlloc --
- *     The function allocates per-cpu deferred actions execution level.
+ * OvsDeferredActionsRealloc --
+ *     The function reallocates per-cpu necessary resources and is triggered
+ *     by the 'OvsCpuChange' callback.
+ *
+ *     If the function was triggered by a 'KeProcessorAddStartNotify'
+ *       notification, it means that the operating system is about to add a
+ *       new processor and the function reallocates new space to accomodate
+ *       new per-processor data requirements.
+ *
+ *     If the function was triggered by a 'KeProcessorAddFailureNotify'
+ *       notification, it means that the operating system failed to add the
+ *       new processor and the function frees all per-processor resources
+ *       that were allocated for the new processor.
  * --------------------------------------------------------------------------
  */
-BOOLEAN
-OvsDeferredActionsLevelAlloc()
+NTSTATUS
+OvsDeferredActionsRealloc(BOOLEAN addCpu)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG count = 0;
+
+    status = OvsDeferredActionsQueueRealloc(addCpu, &count);
+    if (!NT_SUCCESS(status)) {
+        goto exit;
+    }
+
+    status = OvsDeferredActionsLevelRealloc(addCpu, NULL);
+    if (!NT_SUCCESS(status)) {
+        goto exit;
+    }
+
+    ovsActiveProcessorCount = count;
+
+exit:
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsInit --
+ *     The function allocates all necessary per-processor deferred actions
+ *     resources.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsDeferredActionsInit(NDIS_HANDLE NdisFilterHandle)
 {
+    NTSTATUS status = STATUS_SUCCESS;
+
+    ovsDeferredActionQueueLock = NdisAllocateRWLock(NdisFilterHandle);
+    if (!ovsDeferredActionQueueLock) {
+        status = NDIS_STATUS_RESOURCES;
+        goto cleanup;
+    }
+
+    ovsDeferredActionLevelLock = NdisAllocateRWLock(NdisFilterHandle);
+    if (!ovsDeferredActionLevelLock) {
+        status = NDIS_STATUS_RESOURCES;
+        goto cleanup;
+    }
+
+    ovsDeferredActionQueue =
+        OvsAllocateMemoryPerCpu(sizeof(*ovsDeferredActionQueue),
+                                OVS_RECIRC_POOL_TAG,
+                                FALSE,
+                                &ovsActiveProcessorCount);
+    if (!ovsDeferredActionQueue) {
+        status = NDIS_STATUS_RESOURCES;
+        goto cleanup;
+    }
+
     ovsDeferredActionLevel =
         OvsAllocateMemoryPerCpu(sizeof(*ovsDeferredActionLevel),
-                                OVS_RECIRC_POOL_TAG);
+                                OVS_RECIRC_POOL_TAG,
+                                FALSE,
+                                NULL);
     if (!ovsDeferredActionLevel) {
-        return FALSE;
+        status = NDIS_STATUS_RESOURCES;
+        goto cleanup;
     }
-    return TRUE;
+
+cleanup:
+    if (!NT_SUCCESS(status)) {
+        OvsDeferredActionsCleanup();
+    }
+
+    return status;
 }
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsLevelFree --
- *     The function frees per-cpu deferred actions execution level.
+ * OvsDeferredActionsCleanup --
+ *     The function frees all per-processor deferred actions resources.
  * --------------------------------------------------------------------------
  */
 VOID
-OvsDeferredActionsLevelFree()
+OvsDeferredActionsCleanup()
 {
-    OvsFreeMemoryWithTag(ovsDeferredActionLevel,
-                         OVS_RECIRC_POOL_TAG);
-    ovsDeferredActionLevel = NULL;
+    if (ovsDeferredActionLevel) {
+        OvsFreeMemoryWithTag(ovsDeferredActionLevel,
+                             OVS_RECIRC_POOL_TAG);
+        ovsDeferredActionLevel = NULL;
+    }
+
+    if (ovsDeferredActionQueue) {
+        OvsFreeMemoryWithTag(ovsDeferredActionQueue,
+                             OVS_RECIRC_POOL_TAG);
+        ovsDeferredActionQueue = NULL;
+    }
+
+    if (ovsDeferredActionLevelLock) {
+        NdisFreeRWLock(ovsDeferredActionLevelLock);
+        ovsDeferredActionLevelLock = NULL;
+    }
+
+    if (ovsDeferredActionQueueLock) {
+        NdisFreeRWLock(ovsDeferredActionQueueLock);
+        ovsDeferredActionQueueLock = NULL;
+    }
+
+    ovsActiveProcessorCount = 0;
 }
 
 /*
@@ -104,7 +251,9 @@ OvsDeferredActionsQueueGet()
     }
 
     index = KeGetCurrentProcessorNumberEx(NULL);
-    queue = &ovsDeferredActionQueue[index];
+    if (index < ovsActiveProcessorCount) {
+        queue = &ovsDeferredActionQueue[index];
+    }
 
     if (oldIrql < DISPATCH_LEVEL) {
         KeLowerIrql(oldIrql);
@@ -132,7 +281,9 @@ OvsDeferredActionsLevelGet()
     }
 
     index = KeGetCurrentProcessorNumberEx(NULL);
-    level = &ovsDeferredActionLevel[index];
+    if (index < ovsActiveProcessorCount) {
+        level = &ovsDeferredActionLevel[index];
+    }
 
     if (oldIrql < DISPATCH_LEVEL) {
         KeLowerIrql(oldIrql);
@@ -160,8 +311,10 @@ OvsDeferredActionsLevelInc()
     }
 
     index = KeGetCurrentProcessorNumberEx(NULL);
-    level = &ovsDeferredActionLevel[index];
-    (*level)++;
+    if (index < ovsActiveProcessorCount) {
+        level = &ovsDeferredActionLevel[index];
+        (*level)++;
+    }
 
     if (oldIrql < DISPATCH_LEVEL) {
         KeLowerIrql(oldIrql);
@@ -187,8 +340,10 @@ OvsDeferredActionsLevelDec()
     }
 
     index = KeGetCurrentProcessorNumberEx(NULL);
-    level = &ovsDeferredActionLevel[index];
-    (*level)--;
+    if (index < ovsActiveProcessorCount) {
+        level = &ovsDeferredActionLevel[index];
+        (*level)--;
+    }
 
     if (oldIrql < DISPATCH_LEVEL) {
         KeLowerIrql(oldIrql);
@@ -293,8 +448,13 @@ OvsAddDeferredActions(PNET_BUFFER_LIST nbl,
                       OvsFlowKey *key,
                       const PNL_ATTR actions)
 {
-    POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet();
+    POVS_DEFERRED_ACTION_QUEUE queue = NULL;
     POVS_DEFERRED_ACTION deferredAction = NULL;
+    LOCK_STATE_EX lockState;
+
+    NdisAcquireRWLockRead(ovsDeferredActionQueueLock, &lockState, 0);
+
+    queue =  OvsDeferredActionsQueueGet();
 
     deferredAction = OvsDeferredActionsQueuePush(queue);
     if (deferredAction) {
@@ -303,6 +463,8 @@ OvsAddDeferredActions(PNET_BUFFER_LIST nbl,
         deferredAction->key = *key;
     }
 
+    NdisReleaseRWLock(ovsDeferredActionQueueLock, &lockState);
+
     return deferredAction;
 }
 
@@ -321,8 +483,13 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
                           OVS_PACKET_HDR_INFO *layers)
 {
     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
-    POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet();
+    POVS_DEFERRED_ACTION_QUEUE queue = NULL;
     POVS_DEFERRED_ACTION deferredAction = NULL;
+    LOCK_STATE_EX lockState;
+
+    NdisAcquireRWLockRead(ovsDeferredActionQueueLock, &lockState, 0);
+
+    queue = OvsDeferredActionsQueueGet();
 
     /* Process all deferred actions. */
     while ((deferredAction = OvsDeferredActionsQueuePop(queue)) != NULL) {
@@ -345,5 +512,7 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
         }
     }
 
+    NdisReleaseRWLock(ovsDeferredActionQueueLock, &lockState);
+
     return status;
 }
diff --git a/datapath-windows/ovsext/Recirc.h b/datapath-windows/ovsext/Recirc.h
index ee05763..240b492 100644
--- a/datapath-windows/ovsext/Recirc.h
+++ b/datapath-windows/ovsext/Recirc.h
@@ -30,19 +30,6 @@ typedef struct _OVS_DEFERRED_ACTION {
 
 /*
  * --------------------------------------------------------------------------
- * '_OVS_DEFERRED_ACTION_QUEUE' structure is responsible for keeping track of
- * all deferred actions. The maximum number of deferred actions should not
- * exceed 'DEFERRED_ACTION_QUEUE_SIZE'.
- * --------------------------------------------------------------------------
- */
-typedef struct _OVS_DEFERRED_ACTION_QUEUE {
-    UINT32  head;
-    UINT32  tail;
-    OVS_DEFERRED_ACTION queue[DEFERRED_ACTION_QUEUE_SIZE];
-} OVS_DEFERRED_ACTION_QUEUE, *POVS_DEFERRED_ACTION_QUEUE;
-
-/*
- * --------------------------------------------------------------------------
  * OvsProcessDeferredActions --
  *     This function processes all deferred actions contained in the queue
  *     corresponding to the current CPU.
@@ -69,68 +56,72 @@ OvsAddDeferredActions(PNET_BUFFER_LIST packet,
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsQueueAlloc --
- *     The function allocates per-cpu deferred actions queue.
+ * OvsDeferredActionsLevelGet --
+ *     The function returns the deferred action execution level corresponding
+ *     to the current processor.
  * --------------------------------------------------------------------------
  */
-BOOLEAN
-OvsDeferredActionsQueueAlloc();
+UINT32
+OvsDeferredActionsLevelGet();
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsQueueFree --
- *     The function frees per-cpu deferred actions queue.
+ * OvsDeferredActionsLevelInc --
+ *     The function increments the deferred action execution level
+ *     corresponding to the current processor.
  * --------------------------------------------------------------------------
  */
 VOID
-OvsDeferredActionsQueueFree();
-
-/*
- * --------------------------------------------------------------------------
- * OvsDeferredActionsLevelAlloc --
- *     The function allocates per-cpu deferred actions execution level.
- * --------------------------------------------------------------------------
- */
-BOOLEAN
-OvsDeferredActionsLevelAlloc();
+OvsDeferredActionsLevelInc();
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsLevelFree --
- *     The function frees per-cpu deferred actions execution level.
+ * OvsDeferredActionsLevelDec --
+ *     The function decrements the deferred action execution level
+ *     corresponding to the current processor.
  * --------------------------------------------------------------------------
- */
+*/
 VOID
-OvsDeferredActionsLevelFree();
+OvsDeferredActionsLevelDec();
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsLevelGet --
- *     The function returns the deferred action execution level corresponding
- *     to the current processor.
+ * OvsDeferredActionsRealloc --
+ *     The function reallocates per-cpu necessary resources and is triggered
+ *     by the 'OvsCpuChange' callback.
+ *
+ *     If the function was triggered due to a 'KeProcessorAddStartNotify'
+ *       notification, it means that the operating system is about to add a
+ *       new processor and the function reallocates new space to accomodate
+ *       new per-processor data requirements.
+ *
+ *     If the function was triggered due to a 'KeProcessorAddFailureNotify'
+ *       notification, it means that the operating system failed to add the
+ *       new processor and the function frees all per-processor resources
+ *       that were allocated for the new processor.
  * --------------------------------------------------------------------------
  */
-UINT32
-OvsDeferredActionsLevelGet();
+NTSTATUS
+OvsDeferredActionsRealloc(BOOLEAN addCpu);
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsLevelInc --
- *     The function increments the deferred action execution level
- *     corresponding to the current processor.
+ * OvsDeferredActionsInit --
+ *     The function allocates necessary per-processor resources and registers
+ *     processor change callback.
  * --------------------------------------------------------------------------
  */
-VOID
-OvsDeferredActionsLevelInc();
+NTSTATUS
+OvsDeferredActionsInit(NDIS_HANDLE NdisFilterHandle);
 
 /*
  * --------------------------------------------------------------------------
- * OvsDeferredActionsLevelDec --
- *     The function decrements the deferred action execution level
- *     corresponding to the current processor.
+ * OvsDeferredActionsCleanup --
+ *     The function frees all per-processor resources and deregisters
+ *     processor change callback.
  * --------------------------------------------------------------------------
-*/
+ */
 VOID
-OvsDeferredActionsLevelDec();
+OvsDeferredActionsCleanup();
 
 #endif /* __RECIRC_H_ */
diff --git a/datapath-windows/ovsext/Util.c b/datapath-windows/ovsext/Util.c
index 14c4493..12f6eac 100644
--- a/datapath-windows/ovsext/Util.c
+++ b/datapath-windows/ovsext/Util.c
@@ -15,6 +15,7 @@
  */
 
 #include "precomp.h"
+#include "Recirc.h"
 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
 #endif
@@ -23,6 +24,7 @@
 #include "Debug.h"
 
 extern NDIS_HANDLE gOvsExtDriverHandle;
+static VOID* ovsRegistrationHandle = NULL;
 
 VOID*
 OvsAllocateMemoryWithTag(size_t size, ULONG tag)
@@ -118,17 +120,155 @@ OvsCompareString(PVOID string1, PVOID string2)
 }
 
 VOID *
-OvsAllocateMemoryPerCpu(size_t size, ULONG tag)
+OvsAllocateMemoryPerCpu(size_t size,
+                        ULONG tag,
+                        BOOLEAN addCpu,
+                        PULONG cpuCount)
 {
     VOID *ptr = NULL;
     ULONG count = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
 
     ASSERT(KeQueryActiveGroupCount() == 1);
 
+    if (addCpu) {
+        /*
+         * The incrementation of 'count' is required in case the OS is about to
+         * add a new processor to the system, i.e. 'KeProcessorAddStartNotify'
+         * notification is received, and the processor is not active yet.
+         * In this situation the new processor won't be counted by the
+         * 'KeQueryActiveProcessorCountEx' function. So we increment the
+         * 'count' here in order to allocate the memory for the new processor
+         * also.
+         */
+        count++;
+    }
+
     ptr = OvsAllocateMemoryWithTag(count * size, tag);
     if (ptr) {
         RtlZeroMemory(ptr, count * size);
+        if (cpuCount) {
+            *cpuCount = count;
+        }
     }
 
     return ptr;
 }
+
+VOID *
+OvsReallocateMemoryPerCpu(VOID *buffer,
+                          size_t bufferSize,
+                          ULONG tag,
+                          BOOLEAN addCpu,
+                          ULONG oldCpuCount,
+                          ULONG *newCpuCount)
+{
+    VOID *ptr = NULL;
+    ULONG cpuCount = 0;
+
+    ptr = OvsAllocateMemoryPerCpu(bufferSize, tag, addCpu, &cpuCount);
+    if (ptr) {
+        ULONG count = 0;
+        count = (cpuCount > oldCpuCount) ? oldCpuCount : cpuCount;
+        RtlCopyMemory(ptr, buffer, count * bufferSize);
+        if (newCpuCount) {
+            *newCpuCount = cpuCount;
+        }
+
+        if (buffer) {
+            OvsFreeMemoryWithTag(buffer, tag);
+        }
+    }
+
+    return ptr;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsCpuChange --
+ *     This is the processor change callback function that is to be called by
+ *     the operating system whenever a new processor is added to the hardware
+ *     partition.
+ *
+ *     'KeProcessorAddStartNotify' notification is received when the OS is
+ *     about to add the processor; at this state, any per-processor data
+ *     structures must be allocated for the new processor to prepare the
+ *     driver for execution on the new processor.
+ *
+ *     'KeProcessorAddFailureNotify' notification is received when the OS
+ *     failed to add the processor and any per-processor data structures that
+ *     were allocated for the new processor should be freed.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsCpuChange(__in PVOID CallbackContext,
+             __in PKE_PROCESSOR_CHANGE_NOTIFY_CONTEXT ChangeContext,
+             __inout PNTSTATUS OperationStatus)
+{
+    UNREFERENCED_PARAMETER(CallbackContext);
+
+    switch (ChangeContext->State) {
+    case KeProcessorAddFailureNotify:
+    {
+        OvsDeferredActionsRealloc(FALSE);
+        break;
+    }
+    case KeProcessorAddStartNotify:
+    {
+        NTSTATUS status = STATUS_SUCCESS;
+
+        status = OvsDeferredActionsRealloc(TRUE);
+        if (!NT_SUCCESS(status)) {
+            *OperationStatus = status;
+        }
+        break;
+    }
+    default:
+        break;
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsPerCpuDataInit --
+ *     The function allocates necessary per-processor resources and registers
+ *     processor change callback.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsPerCpuDataInit(NDIS_HANDLE ndisFilterHandle)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    status = OvsDeferredActionsInit(ndisFilterHandle);
+    if (!NT_SUCCESS(status)) {
+        goto exit;
+    }
+
+    ovsRegistrationHandle =
+        KeRegisterProcessorChangeCallback(OvsCpuChange, NULL, 0);
+    if (!ovsRegistrationHandle) {
+        status = NDIS_STATUS_FAILURE;
+        goto exit;
+    }
+
+exit:
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsPerCpuDataCleanup --
+ *     The function frees all per-processor resources and deregisters
+ *     processor change callback.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsPerCpuDataCleanup()
+{
+    if (ovsRegistrationHandle) {
+        KeDeregisterProcessorChangeCallback(ovsRegistrationHandle);
+        ovsRegistrationHandle = NULL;
+    }
+
+    OvsDeferredActionsCleanup();
+}
diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h
index 038754d..09a60d1 100644
--- a/datapath-windows/ovsext/Util.h
+++ b/datapath-windows/ovsext/Util.h
@@ -41,7 +41,16 @@
 VOID *OvsAllocateMemory(size_t size);
 VOID *OvsAllocateMemoryWithTag(size_t size, ULONG tag);
 VOID *OvsAllocateAlignedMemory(size_t size, UINT16 align);
-VOID *OvsAllocateMemoryPerCpu(size_t size, ULONG tag);
+VOID *OvsAllocateMemoryPerCpu(size_t size,
+                              ULONG tag,
+                              BOOLEAN addCpu,
+                              PULONG cpuCount);
+VOID *OvsReallocateMemoryPerCpu(VOID *ptr,
+                                size_t size,
+                                ULONG tag,
+                                BOOLEAN addCpu,
+                                ULONG oldCpuCount,
+                                ULONG *newCpuCount);
 VOID OvsFreeMemory(VOID *ptr);
 VOID OvsFreeMemoryWithTag(VOID *ptr, ULONG tag);
 VOID OvsFreeAlignedMemory(VOID *ptr);
@@ -94,4 +103,28 @@ VOID OvsAppendList(PLIST_ENTRY dst, PLIST_ENTRY src);
 
 BOOLEAN OvsCompareString(PVOID string1, PVOID string2);
 
+/*
+ * --------------------------------------------------------------------------
+ * OvsPerCpuDataInit --
+ *     The function allocates necessary per-processor resources and registers
+ *     processor change callback.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsPerCpuDataInit(NDIS_HANDLE NdisFilterHandle);
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsPerCpuDataCleanup --
+ *     The function frees all per-processor resources and deregisters
+ *     processor change callback.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsPerCpuDataCleanup();
+VOID
+OvsCpuChange(__in PVOID CallbackContext,
+__in PKE_PROCESSOR_CHANGE_NOTIFY_CONTEXT ChangeContext,
+__inout PNTSTATUS OperationStatus);
+
 #endif /* __UTIL_H_ */
-- 
1.9.0.msysgit.0



More information about the dev mailing list