[ovs-dev] [PATCH 11/15] datapath-windows: Add RefCount.h

Samuel Ghinet sghinet at cloudbasesolutions.com
Wed Aug 6 16:14:42 UTC 2014


Add RefCount.h

The struct that needs to benefit of reference counting should have an "OVS_REF_COUNT" field as the
first field in that struct.

What it does:
* It helps shortening the time a spin lock / rw lock is held (the spin lock & rw lock block the cpu).
* It allows deferred object destruction / deletion.

How it works:
Say we have a list of OVS_FLOWs, where OVS_FLOW uses OVS_REF_COUNT.

* The OVS_REF_COUNT must be initialized to 0 at object creation.
* Assign a destruction function to the func ptr "Destroy" of OVS_REF_COUNT.
* When you need to retrieve a ptr to an OVS_FLOW, from the list, you must do the following:
*** lock the OVS_FLOW list for read (obviously)
*** do the lookup: a ptr to an existing OVS_FLOW, pFlow, is found.
*** call "OVS_REFCOUNT_REFERENCE(pFlow);" - by doing so, you deny destruction of pFlow, while the object
is referenced, ensuring that you keep a valid pointer to a flow.
*** unlock the list
*** return pFlow.
* When you need to modify fields in the pFlow, use the rw lock / spinlock field of pFlow.
* When you no longer need the ptr, use "OVS_REFCOUNT_DEREFERENCE(pFlow);"
* When you need to do deferred deletion, call "OVS_REFCOUNT_DESTROY(pFlow);": this will
mark the pFlow for deletion. If the object is not referenced, it will be destroyed immediately. Otherwise,
it will be destroyed when the refCount = 0.

Signed-off-by: Samuel Ghinet <sghinet at cloudbasesolutions.com>
---
 datapath-windows/ovsext/Core/RefCount.h        | 274 +++++++++++++++++++++++++
 datapath-windows/ovsext/ovsext.vcxproj         |   1 +
 datapath-windows/ovsext/ovsext.vcxproj.filters |   3 +
 3 files changed, 278 insertions(+)
 create mode 100644 datapath-windows/ovsext/Core/RefCount.h

diff --git a/datapath-windows/ovsext/Core/RefCount.h b/datapath-windows/ovsext/Core/RefCount.h
new file mode 100644
index 0000000..3694e37
--- /dev/null
+++ b/datapath-windows/ovsext/Core/RefCount.h
@@ -0,0 +1,274 @@
+/*
+Copyright 2014 Cloudbase Solutions Srl
+
+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.
+*/
+
+#pragma once
+
+#include "precomp.h"
+
+#ifdef DBG
+#define OVS_USE_REFCOUNT_CALL_STACK    1
+#else
+#define OVS_USE_REFCOUNT_CALL_STACK    0
+#endif
+
+#if OVS_USE_REFCOUNT_CALL_STACK
+#define OVS_REFCOUNT_MAX_THREAD_COUNT      16
+#define OVS_REFCOUNT_MAX_FUNC_COUNT        10
+
+C_ASSERT(OVS_REFCOUNT_MAX_THREAD_COUNT <= 64);
+#endif
+
+//NOTE: must be the first field of any struct using the OVS_REF_COUNT
+typedef struct _OVS_REF_COUNT
+{
+#if OVS_USE_REFCOUNT_CALL_STACK
+    //for debugging purposes: when a refCount is increased, it is first added to countsPerThread[currentThreadNumber]
+    //then the func name (__FUNCTION__)  will be placed at funcs[curThreadNumber][refCountPerCurrentThread]
+    //if there is not enough room to store the func name in the stack (i.e. refCount >= OVS_REFCOUNT_MAX_FUNC_COUNT) => we consider it a bug
+    const char* funcs[OVS_REFCOUNT_MAX_THREAD_COUNT][OVS_REFCOUNT_MAX_FUNC_COUNT];
+    ULONG refCountsPerThread[OVS_REFCOUNT_MAX_THREAD_COUNT];
+    PKTHREAD threads[OVS_REFCOUNT_MAX_THREAD_COUNT];
+    ULONG noOfThreads;
+#endif
+
+    //if refCount > 0, the object deletion will be postponed
+    //it is used so that we can keep a pointer to the object, while allowing it to be modified (but not deleted) by other threads.
+    volatile ULONG refCount;
+    //if the object should be destroyed, but some thread currently holds a reference to it, it will be marked for deletion instead.
+    //when the refCount reaches 0, if deletetionPending is set, the dereference function will destroy the object.
+    volatile BOOLEAN deletionPending;
+
+    VOID (*Destroy)(VOID*);
+}OVS_REF_COUNT;
+
+extern NDIS_RW_LOCK_EX* g_pRefRwLock;
+
+/**************************************/
+
+static __inline VOID RefCount_DereferenceOnly(VOID* pObj)
+{
+    LOCK_STATE_EX lockState;
+    OVS_REF_COUNT* pRefCount = pObj;
+
+    if (!pObj)
+    {
+        return;
+    }
+
+    NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0);
+
+    OVS_CHECK(pRefCount->refCount > 0);
+#if OVS_USE_REFCOUNT_CALL_STACK
+    {
+        ULONG threadNumber = MAXULONG;
+        ULONG curThreadRefCount = 0;
+        PKTHREAD pThread = NULL;
+
+        OVS_CHECK(pRefCount->refCount < OVS_REFCOUNT_MAX_FUNC_COUNT);
+        pThread = KeGetCurrentThread();
+
+        for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i)
+        {
+            if (pRefCount->threads[i] == pThread)
+            {
+                threadNumber = i;
+                break;
+            }
+        }
+
+        OVS_CHECK(threadNumber != MAXULONG);
+
+        OVS_CHECK(pRefCount->refCountsPerThread[threadNumber] > 0);
+        pRefCount->refCountsPerThread[threadNumber]--;
+        curThreadRefCount = pRefCount->refCountsPerThread[threadNumber];
+        pRefCount->funcs[threadNumber][curThreadRefCount] = NULL;
+
+        if (curThreadRefCount == 0)
+        {
+            pRefCount->threads[threadNumber] = NULL;
+            pRefCount->noOfThreads--;
+        }
+    }
+#endif
+    --pRefCount->refCount;
+
+    NdisReleaseRWLock(g_pRefRwLock, &lockState);
+}
+
+#define OVS_REFCOUNT_DEREFERENCE_ONLY(pObj)    RefCount_DereferenceOnly(pObj)
+
+static __inline VOID RefCount_Dereference(VOID* pObj)
+{
+    LOCK_STATE_EX lockState;
+    OVS_REF_COUNT* pRefCount = pObj;
+
+    if (!pObj)
+    {
+        return;
+    }
+
+    OVS_CHECK(pRefCount->Destroy);
+
+    NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0);
+
+    OVS_CHECK(pRefCount->refCount > 0);
+#if OVS_USE_REFCOUNT_CALL_STACK
+    {
+        ULONG threadNumber = MAXULONG;
+        ULONG curThreadRefCount = 0;
+        PKTHREAD pThread = NULL;
+
+        OVS_CHECK(pRefCount->refCount < OVS_REFCOUNT_MAX_FUNC_COUNT);
+        pThread = KeGetCurrentThread();
+
+        for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i)
+        {
+            if (pRefCount->threads[i] == pThread)
+            {
+                threadNumber = i;
+                break;
+            }
+        }
+
+        OVS_CHECK(threadNumber != MAXULONG);
+
+        OVS_CHECK(pRefCount->refCountsPerThread[threadNumber] > 0);
+        pRefCount->refCountsPerThread[threadNumber]--;
+        curThreadRefCount = pRefCount->refCountsPerThread[threadNumber];
+        pRefCount->funcs[threadNumber][curThreadRefCount] = NULL;
+
+        if (curThreadRefCount == 0)
+        {
+            pRefCount->threads[threadNumber] = NULL;
+            pRefCount->noOfThreads--;
+        }
+    }
+#endif
+    --pRefCount->refCount;
+
+    if (pRefCount->refCount == 0 && pRefCount->deletionPending)
+    {
+        pRefCount->Destroy(pObj);
+    }
+
+    NdisReleaseRWLock(g_pRefRwLock, &lockState);
+}
+
+#define OVS_REFCOUNT_DEREFERENCE(pObj) {RefCount_Dereference(pObj); pObj = NULL; }
+
+static __inline VOID* RefCount_Reference(VOID* pObj, const char* funcName)
+{
+    LOCK_STATE_EX lockState;
+    OVS_REF_COUNT* pRefCount = pObj;
+
+    if (!pObj)
+    {
+        return NULL;
+    }
+
+    NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0);
+
+    if (pRefCount->deletionPending)
+    {
+        pObj = NULL;
+    }
+    else
+    {
+#if OVS_USE_REFCOUNT_CALL_STACK
+        ULONG threadNumber = MAXULONG;
+        ULONG curThreadRefCount = 0;
+        PKTHREAD pThread = NULL;
+
+        OVS_CHECK(pRefCount->noOfThreads < OVS_REFCOUNT_MAX_THREAD_COUNT);
+
+        pThread = KeGetCurrentThread();
+
+        for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i)
+        {
+            if (pRefCount->threads[i] == pThread)
+            {
+                threadNumber = i;
+                break;
+            }
+        }
+
+        if (threadNumber == MAXULONG)
+        {
+            OVS_CHECK(pRefCount->noOfThreads + 1 < OVS_REFCOUNT_MAX_THREAD_COUNT);
+
+            for (ULONG i = 0; i < OVS_REFCOUNT_MAX_THREAD_COUNT; ++i)
+            {
+                if (pRefCount->threads[i] == NULL)
+                {
+                    pRefCount->threads[i] = pThread;
+                    pRefCount->noOfThreads++;
+
+                    threadNumber = i;
+                    break;
+                }
+            }
+        }
+
+        OVS_CHECK(threadNumber != MAXULONG);
+
+        curThreadRefCount = pRefCount->refCountsPerThread[threadNumber];
+        OVS_CHECK(curThreadRefCount + 1 < OVS_REFCOUNT_MAX_FUNC_COUNT);
+
+        pRefCount->funcs[threadNumber][curThreadRefCount] = funcName;
+        pRefCount->refCountsPerThread[threadNumber]++;
+#else
+        UNREFERENCED_PARAMETER(funcName);
+#endif
+
+        ++pRefCount->refCount;
+    }
+
+    NdisReleaseRWLock(g_pRefRwLock, &lockState);
+
+    return pObj;
+}
+
+#define OVS_REFCOUNT_REFERENCE(pObj) RefCount_Reference(pObj, __FUNCTION__);
+
+static __inline VOID RefCount_Destroy(VOID* pObj)
+{
+    LOCK_STATE_EX lockState;
+    OVS_REF_COUNT* pRefCount = pObj;
+
+    if (!pObj)
+    {
+        return;
+    }
+
+    OVS_CHECK(pRefCount->Destroy);
+
+    NdisAcquireRWLockWrite(g_pRefRwLock, &lockState, 0);
+
+    if (pRefCount->refCount > 0)
+    {
+        pRefCount->deletionPending = TRUE;
+    }
+    else
+    {
+        pRefCount->Destroy(pObj);
+    }
+
+    NdisReleaseRWLock(g_pRefRwLock, &lockState);
+}
+
+#define OVS_REFCOUNT_DESTROY(pObj) { RefCount_Destroy(pObj); pObj = NULL; }
+
+#define OVS_REFCOUNT_DEREF_AND_DESTROY(pObj)    { OVS_REFCOUNT_DEREFERENCE_ONLY(pObj); OVS_REFCOUNT_DESTROY(pObj); }
\ No newline at end of file
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index cce9df0..99aa6f6 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj
+++ b/datapath-windows/ovsext/ovsext.vcxproj
@@ -75,6 +75,7 @@
     <ClInclude Include="Core\IpHelper.h" />
     <ClInclude Include="Core\Jhash.h" />
     <ClInclude Include="Core\List.h" />
+    <ClInclude Include="Core\RefCount.h" />
     <ClInclude Include="Core\SpookyHash.h" />
     <ClInclude Include="Core\Types.h" />
     <ClInclude Include="Core\Util.h" />
diff --git a/datapath-windows/ovsext/ovsext.vcxproj.filters b/datapath-windows/ovsext/ovsext.vcxproj.filters
index c23ca42..fc3ba3b 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj.filters
+++ b/datapath-windows/ovsext/ovsext.vcxproj.filters
@@ -83,6 +83,9 @@
     <ClInclude Include="Core\SpookyHash.h">
       <Filter>Core</Filter>
     </ClInclude>
+    <ClInclude Include="Core\RefCount.h">
+      <Filter>Core</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="ovsext.rc" />
--
1.8.3.msysgit.0





More information about the dev mailing list