From 2549d03f1f3d4234d05888dc54171223398cffae Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Mon, 6 Mar 2017 13:39:56 -0800 Subject: [PATCH] Add Dynamic Memory Support to otLwf.sys (#1414) * Add Dynamic Memory Support * Use single list for free buffers. Null mNext on return of new buffer. * Use Paged memory for Buffers. --- .../include/openthread-core-windows-config.h | 10 + .../drivers/windows/otLwf/eventprocessing.c | 23 +-- examples/drivers/windows/otLwf/filter.h | 32 ++++ examples/drivers/windows/otLwf/precomp.h | 1 + examples/drivers/windows/otLwf/radio.c | 3 +- examples/drivers/windows/otLwf/thread.c | 174 +++++++++++++++++- examples/drivers/windows/otLwf/thread.h | 10 + include/openthread/platform/messagepool.h | 8 +- src/core/common/message.cpp | 19 +- src/core/common/message.hpp | 6 +- src/core/net/ip6.cpp | 1 + tests/unit/test_message.cpp | 4 +- tests/unit/test_message_queue.cpp | 4 +- tests/unit/test_ncp_buffer.cpp | 4 +- tests/unit/test_priority_queue.cpp | 4 +- 15 files changed, 259 insertions(+), 44 deletions(-) diff --git a/examples/drivers/windows/include/openthread-core-windows-config.h b/examples/drivers/windows/include/openthread-core-windows-config.h index 98102f1b9..65cf2080c 100644 --- a/examples/drivers/windows/include/openthread-core-windows-config.h +++ b/examples/drivers/windows/include/openthread-core-windows-config.h @@ -67,6 +67,16 @@ */ #define OPENTHREAD_CONFIG_MAX_JOINER_ENTRIES 16 +/** + * @def OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT + * + * The message pool is managed by platform defined logic when this flag is set. + * This feature would typically be used when operating in a multi-threaded system + * and multiple threads need to access the message pool. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT 1 + /** * @def OPENTHREAD_CONFIG_LOG_LEVEL * diff --git a/examples/drivers/windows/otLwf/eventprocessing.c b/examples/drivers/windows/otLwf/eventprocessing.c index 87df010c4..9873b1f32 100644 --- a/examples/drivers/windows/otLwf/eventprocessing.c +++ b/examples/drivers/windows/otLwf/eventprocessing.c @@ -1090,28 +1090,7 @@ otLwfEventWorkerThread( exit: - if (pFilter->otCtx != NULL) - { - otInstanceFinalize(pFilter->otCtx); - pFilter->otCtx = NULL; - -#if DEBUG_ALLOC - { - NT_ASSERT(pFilter->otOutstandingAllocationCount == 0); - NT_ASSERT(pFilter->otOutstandingMemoryAllocated == 0); - PLIST_ENTRY Link = pFilter->otOutStandingAllocations.Flink; - while (Link != &pFilter->otOutStandingAllocations) - { - OT_ALLOC* AllocHeader = CONTAINING_RECORD(Link, OT_ALLOC, Link); - Link = Link->Flink; - - LogVerbose(DRIVER_DEFAULT, "Leaked Alloc ID:%u", AllocHeader->ID); - - ExFreePoolWithTag(AllocHeader, 'OTDM'); - } - } -#endif - } + otLwfReleaseInstance(pFilter); if (pFilter->otInstanceBuffer != NULL) { diff --git a/examples/drivers/windows/otLwf/filter.h b/examples/drivers/windows/otLwf/filter.h index 37ca88fb8..c62d8415a 100644 --- a/examples/drivers/windows/otLwf/filter.h +++ b/examples/drivers/windows/otLwf/filter.h @@ -111,6 +111,29 @@ typedef enum OTLWF_DEVICE_STATUS #define OT_EVENT_TIMER_RUNNING 1 #define OT_EVENT_TIMER_FIRED 2 +#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT + +typedef struct BufferPool +{ + struct BufferPool* Next; + uint8_t Buffers[0]; + +} BufferPool; + +enum +{ + kPageSize = PAGE_SIZE, + kPagesPerBufferPool = 1, + kMaxPagesForBufferPools = 64, + kMaxBytesForBufferPools = kPageSize * kMaxPagesForBufferPools, + + kEstimatedBufferSize = 128, // sizeof(Thread::Buffer) + kEstimatedBufferPoolSize = ((kPageSize * kPagesPerBufferPool) - sizeof(BufferPool)) / kEstimatedBufferSize, + kEstimatedMaxBuffers = kMaxPagesForBufferPools * kEstimatedBufferPoolSize +}; + +#endif + // // Define the filter struct // @@ -244,6 +267,15 @@ typedef struct _MS_FILTER BOOLEAN otPendingMacOffloadEnabled; +#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT + uint16_t otBufferSize; // Bytes in a single buffer + uint16_t otBufferPoolByteSize; // Bytes in a buffer pool + uint16_t otBufferPoolBufferCount; // Number of buffers in a pool + uint16_t otBuffersLeft; // Number of buffers left to return + BufferPool* otBufferPoolHead; // List of buffer pools + otMessage* otFreeBuffers; // List of buffers to return +#endif + #if DEBUG_ALLOC // Used for tracking memory allocations HANDLE otThreadId; diff --git a/examples/drivers/windows/otLwf/precomp.h b/examples/drivers/windows/otLwf/precomp.h index 40a16c5a6..5a90d66ab 100644 --- a/examples/drivers/windows/otLwf/precomp.h +++ b/examples/drivers/windows/otLwf/precomp.h @@ -76,6 +76,7 @@ RtlCopyBufferToMdl( #include #include #include +#include #include #include diff --git a/examples/drivers/windows/otLwf/radio.c b/examples/drivers/windows/otLwf/radio.c index f89b98783..a638a1bde 100644 --- a/examples/drivers/windows/otLwf/radio.c +++ b/examples/drivers/windows/otLwf/radio.c @@ -65,8 +65,7 @@ otPlatReset( (void)otLwfCmdResetDevice(pFilter, TRUE); // Finalize previous OpenThread instance - otInstanceFinalize(pFilter->otCtx); - pFilter->otCtx = NULL; + otLwfReleaseInstance(pFilter); // Reset radio layer pFilter->otPhyState = kStateDisabled; diff --git a/examples/drivers/windows/otLwf/thread.c b/examples/drivers/windows/otLwf/thread.c index d96f3466f..a9e48b984 100644 --- a/examples/drivers/windows/otLwf/thread.c +++ b/examples/drivers/windows/otLwf/thread.c @@ -208,17 +208,69 @@ otLwfFindFromCurrentThread() } #endif +#define OTPLAT_CALLOC_TAG 'OTDM' +#define BUFFER_POOL_TAG 'OTBP' + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +otLwfReleaseInstance( + _In_ PMS_FILTER pFilter + ) +{ + LogFuncEntry(DRIVER_DEFAULT); + + if (pFilter->otCtx != NULL) + { + otInstanceFinalize(pFilter->otCtx); + pFilter->otCtx = NULL; + +#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT + + // Free all the pools as there should be no outstanding + // references to the buffers any more. + BufferPool *curPool = pFilter->otBufferPoolHead; + while (curPool != NULL) + { + BufferPool *nextPool = curPool->Next; + ExFreePoolWithTag(curPool, BUFFER_POOL_TAG); + curPool = nextPool; + } + +#endif + +#if DEBUG_ALLOC + + NT_ASSERT(pFilter->otOutstandingAllocationCount == 0); + NT_ASSERT(pFilter->otOutstandingMemoryAllocated == 0); + PLIST_ENTRY Link = pFilter->otOutStandingAllocations.Flink; + while (Link != &pFilter->otOutStandingAllocations) + { + OT_ALLOC* AllocHeader = CONTAINING_RECORD(Link, OT_ALLOC, Link); + Link = Link->Flink; + + LogVerbose(DRIVER_DEFAULT, "Leaked Alloc ID:%u", AllocHeader->ID); + + ExFreePoolWithTag(AllocHeader, OTPLAT_CALLOC_TAG); + } + +#endif + } + + LogFuncExit(DRIVER_DEFAULT); +} + // // OpenThread Platform functions // +_IRQL_requires_max_(PASSIVE_LEVEL) void *otPlatCAlloc(size_t aNum, size_t aSize) { size_t totalSize = aNum * aSize; #if DEBUG_ALLOC totalSize += sizeof(OT_ALLOC); #endif - PVOID mem = ExAllocatePoolWithTag(NonPagedPoolNx, totalSize, 'OTDM'); + PVOID mem = ExAllocatePoolWithTag(PagedPool, totalSize, OTPLAT_CALLOC_TAG); if (mem) { RtlZeroMemory(mem, totalSize); @@ -240,7 +292,8 @@ void *otPlatCAlloc(size_t aNum, size_t aSize) return mem; } -void otPlatFree(void *aPtr) +_IRQL_requires_max_(PASSIVE_LEVEL) +void otPlatFree(_In_opt_ void *aPtr) { if (aPtr == NULL) return; #if DEBUG_ALLOC @@ -253,9 +306,124 @@ void otPlatFree(void *aPtr) InterlockedAdd(&pFilter->otOutstandingMemoryAllocated, -AllocHeader->Length); RemoveEntryList(&AllocHeader->Link); #endif - ExFreePoolWithTag(aPtr, 'OTDM'); + ExFreePoolWithTag(aPtr, OTPLAT_CALLOC_TAG); } +#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT + +_IRQL_requires_max_(PASSIVE_LEVEL) +BufferPool* AllocBufferPool(_In_ PMS_FILTER pFilter) +{ + // Allocate the memory + BufferPool* bufPool = (BufferPool*)ExAllocatePoolWithTag(PagedPool, pFilter->otBufferPoolByteSize, BUFFER_POOL_TAG); + if (bufPool == NULL) + { + LogWarning(DRIVER_DEFAULT, "Failed to allocate new buffer pool!"); + return NULL; + } + + // Zero out the memory + RtlZeroMemory(bufPool, pFilter->otBufferPoolByteSize); + + // Set all mNext for the buffers + otMessage* prevBuf = (otMessage*)bufPool->Buffers; + for (uint16_t i = 1; i < pFilter->otBufferPoolBufferCount; i++) + { + otMessage* curBuf = + (otMessage*)&bufPool->Buffers[i * pFilter->otBufferSize]; + + prevBuf->mNext = curBuf; + prevBuf = curBuf; + } + + LogVerbose(DRIVER_DEFAULT, "Allocated new buffer pool (%d bytes)!", pFilter->otBufferPoolByteSize); + + return bufPool; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +otMessage* GetNextFreeBufferFromPool(_In_ PMS_FILTER pFilter) +{ + // Immediately return if we have hit our limit + if (pFilter->otBuffersLeft == 0) return NULL; + + // If we don't have any free buffers left, allocate another pool + if (pFilter->otFreeBuffers == NULL) + { + BufferPool *newPool = AllocBufferPool(pFilter); + if (newPool == NULL) return NULL; // Out of physical memory + + // Push on top of the pool list + newPool->Next = pFilter->otBufferPoolHead; + pFilter->otBufferPoolHead = newPool; + + // Set the free buffer list + pFilter->otFreeBuffers = (otMessage*)newPool->Buffers; + } + + // Pop the top free buffer + otMessage* buffer = pFilter->otFreeBuffers; + pFilter->otFreeBuffers = pFilter->otFreeBuffers->mNext; + pFilter->otBuffersLeft--; + buffer->mNext = NULL; + return buffer; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +void otPlatMessagePoolInit(_In_ otInstance *otCtx, uint16_t aMinNumFreeBuffers, size_t aBufferSize) +{ + NT_ASSERT(otCtx); + PMS_FILTER pFilter = otCtxToFilter(otCtx); + + LogFuncEntry(DRIVER_DEFAULT); + UNREFERENCED_PARAMETER(aMinNumFreeBuffers); + + // Initialize parameters + pFilter->otBufferSize = (uint16_t)aBufferSize; + pFilter->otBufferPoolByteSize = (uint16_t)(kPageSize * kPagesPerBufferPool); + pFilter->otBufferPoolBufferCount = (uint16_t)((pFilter->otBufferPoolByteSize - sizeof(BufferPool)) / aBufferSize); + pFilter->otBuffersLeft = kMaxPagesForBufferPools * pFilter->otBufferPoolBufferCount; + + // Allocate first pool + pFilter->otBufferPoolHead = AllocBufferPool(pFilter); + ASSERT(pFilter->otBufferPoolHead); // Should this API allow for failure ??? + + // Set initial free buffer list + pFilter->otFreeBuffers = (otMessage*)pFilter->otBufferPoolHead->Buffers; + + LogFuncExit(DRIVER_DEFAULT); +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +otMessage *otPlatMessagePoolNew(_In_ otInstance *otCtx) +{ + NT_ASSERT(otCtx); + PMS_FILTER pFilter = otCtxToFilter(otCtx); + return GetNextFreeBufferFromPool(pFilter); +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +void otPlatMessagePoolFree(_In_ otInstance *otCtx, _In_ otMessage *aBuffer) +{ + NT_ASSERT(otCtx); + PMS_FILTER pFilter = otCtxToFilter(otCtx); + + // Put buffer back on the list + aBuffer->mNext = pFilter->otFreeBuffers; + pFilter->otFreeBuffers = aBuffer; + pFilter->otBuffersLeft++; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +uint16_t otPlatMessagePoolNumFreeBuffers(_In_ otInstance *otCtx) +{ + NT_ASSERT(otCtx); + PMS_FILTER pFilter = otCtxToFilter(otCtx); + return pFilter->otBuffersLeft; +} + +#endif + uint32_t otPlatRandomGet() { LARGE_INTEGER Counter = KeQueryPerformanceCounter(NULL); diff --git a/examples/drivers/windows/otLwf/thread.h b/examples/drivers/windows/otLwf/thread.h index d2594318a..5f875f5e7 100644 --- a/examples/drivers/windows/otLwf/thread.h +++ b/examples/drivers/windows/otLwf/thread.h @@ -63,6 +63,16 @@ otLwfUninitializeThreadMode( _In_ PMS_FILTER pFilter ); +// +// Clean up otInstance +// + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +otLwfReleaseInstance( + _In_ PMS_FILTER pFilter + ); + // // Event Processing Functions // diff --git a/include/openthread/platform/messagepool.h b/include/openthread/platform/messagepool.h index f0fae800b..3900962f6 100644 --- a/include/openthread/platform/messagepool.h +++ b/include/openthread/platform/messagepool.h @@ -63,7 +63,7 @@ extern "C" { * @param[in] aBufferSize The size in bytes of a Buffer object. * */ -void otPlatMessagePoolInit(uint16_t aMinNumFreeBuffers, size_t aBufferSize); +void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, size_t aBufferSize); /** * Allocate a buffer from the platform managed buffer pool. @@ -71,7 +71,7 @@ void otPlatMessagePoolInit(uint16_t aMinNumFreeBuffers, size_t aBufferSize); * @returns A pointer to the Buffer or NULL if no Buffers are available. * */ -otMessage *otPlatMessagePoolNew(void); +otMessage *otPlatMessagePoolNew(otInstance *aInstance); /** * This function is used to free a Buffer back to the platform managed buffer pool. @@ -79,7 +79,7 @@ otMessage *otPlatMessagePoolNew(void); * @param[in] aBuffer The Buffer to free. * */ -void otPlatMessagePoolFree(otMessage *aBuffer); +void otPlatMessagePoolFree(otInstance *aInstance, otMessage *aBuffer); /** * Get the number of free buffers. @@ -87,7 +87,7 @@ void otPlatMessagePoolFree(otMessage *aBuffer); * @returns The number of buffers currently free and available to OpenThread. * */ -uint16_t otPlatMessagePoolNumFreeBuffers(void); +uint16_t otPlatMessagePoolNumFreeBuffers(otInstance *aInstance); #ifdef __cplusplus } // extern "C" diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index f073f3382..5e562952c 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -41,13 +41,17 @@ namespace Thread { -MessagePool::MessagePool(void) : +MessagePool::MessagePool(otInstance *aInstance) : +#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT + mInstance(aInstance), +#endif mAllQueue() { #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT // Initialize Platform buffer pool management. - otPlatMessagePoolInit(kNumBuffers, sizeof(Buffer)); + otPlatMessagePoolInit(mInstance, kNumBuffers, sizeof(Buffer)); #else + (void)aInstance; memset(mBuffers, 0, sizeof(mBuffers)); mFreeBuffers = mBuffers; @@ -101,11 +105,11 @@ Buffer *MessagePool::NewBuffer(void) Buffer *buffer = NULL; #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT - buffer = static_cast(otPlatMessagePoolNew()); + buffer = static_cast(otPlatMessagePoolNew(mInstance)); if (buffer == NULL) { - otLogInfoMac("No available message buffer\n"); + otLogInfoMac("No available message buffer"); } #else @@ -120,9 +124,10 @@ Buffer *MessagePool::NewBuffer(void) mFreeBuffers = mFreeBuffers->GetNextBuffer(); buffer->SetNextBuffer(NULL); mNumFreeBuffers--; -#endif exit: +#endif + return buffer; } @@ -134,7 +139,7 @@ ThreadError MessagePool::FreeBuffers(Buffer *aBuffer) { tmpBuffer = aBuffer->GetNextBuffer(); #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT - otPlatMessagePoolFree(aBuffer); + otPlatMessagePoolFree(mInstance, aBuffer); #else // OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT aBuffer->SetNextBuffer(mFreeBuffers); mFreeBuffers = aBuffer; @@ -151,7 +156,7 @@ ThreadError MessagePool::ReclaimBuffers(int aNumBuffers) uint16_t numFreeBuffers; #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT - numFreeBuffers = otPlatMessagePoolNumFreeBuffers(); + numFreeBuffers = otPlatMessagePoolNumFreeBuffers(mInstance); #else numFreeBuffers = mNumFreeBuffers; #endif diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index d9081539b..db6839b94 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -1053,7 +1053,7 @@ public: * This constructor initializes the object. * */ - MessagePool(void); + MessagePool(otInstance *aInstance); /** * This method is used to obtain a new message. The default priority `kDefaultMessagePriority` @@ -1103,7 +1103,7 @@ public: * */ #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT - uint16_t GetFreeBufferCount(void) const { return otPlatMessagePoolNumFreeBuffers(); } + uint16_t GetFreeBufferCount(void) const { return otPlatMessagePoolNumFreeBuffers(mInstance); } #else uint16_t GetFreeBufferCount(void) const { return mNumFreeBuffers; } #endif @@ -1123,6 +1123,8 @@ private: uint16_t mNumFreeBuffers; Buffer mBuffers[kNumBuffers]; Buffer *mFreeBuffers; +#else + otInstance *mInstance; #endif PriorityQueue mAllQueue; }; diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index f1e38499c..b8aa16bf1 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -54,6 +54,7 @@ Ip6::Ip6(void): mIcmp(*this), mUdp(*this), mMpl(*this), + mMessagePool(GetInstance()), mForwardingEnabled(false), mSendQueueTask(mTaskletScheduler, HandleSendQueue, this), mReceiveIp6DatagramCallback(NULL), diff --git a/tests/unit/test_message.cpp b/tests/unit/test_message.cpp index ac4777890..33710e13d 100644 --- a/tests/unit/test_message.cpp +++ b/tests/unit/test_message.cpp @@ -28,13 +28,15 @@ #include "test_util.h" #include "openthread/openthread.h" +#include #include #include #include void TestMessage(void) { - Thread::MessagePool messagePool; + otInstance instance; + Thread::MessagePool messagePool(&instance); Thread::Message *message; uint8_t writeBuffer[1024]; uint8_t readBuffer[1024]; diff --git a/tests/unit/test_message_queue.cpp b/tests/unit/test_message_queue.cpp index 3196c6063..e0b8987b5 100644 --- a/tests/unit/test_message_queue.cpp +++ b/tests/unit/test_message_queue.cpp @@ -30,6 +30,7 @@ #include "openthread/openthread.h" +#include #include #include @@ -72,7 +73,8 @@ void VerifyMessageQueueContent(Thread::MessageQueue &aMessageQueue, int aExpecte void TestMessageQueue(void) { - Thread::MessagePool messagePool; + otInstance instance; + Thread::MessagePool messagePool(&instance); Thread::MessageQueue messageQueue; Thread::Message *msg[kNumTestMessages]; ThreadError error; diff --git a/tests/unit/test_ncp_buffer.cpp b/tests/unit/test_ncp_buffer.cpp index 82307fe5b..5475b5211 100644 --- a/tests/unit/test_ncp_buffer.cpp +++ b/tests/unit/test_ncp_buffer.cpp @@ -29,6 +29,7 @@ #include #include "test_util.h" #include "openthread/openthread.h" +#include #include #include #include @@ -49,7 +50,8 @@ static const uint8_t sHelloText[] = "Hello there!"; static const uint8_t sMottoText[] = "Think good thoughts, say good words, do good deeds!"; static const uint8_t sMysteryText[] = "4871(\\):|(3$}{4|/4/2%14(\\)"; -static MessagePool sMessagePool; +static otInstance sInstance; +static MessagePool sMessagePool(&sInstance); struct CallbackContext { diff --git a/tests/unit/test_priority_queue.cpp b/tests/unit/test_priority_queue.cpp index 2ed4c9ec1..0836d248a 100644 --- a/tests/unit/test_priority_queue.cpp +++ b/tests/unit/test_priority_queue.cpp @@ -28,6 +28,7 @@ #include "test_util.h" #include "openthread/openthread.h" +#include #include #include #include @@ -203,7 +204,8 @@ void VerifyMsgQueueContent(Thread::MessageQueue &aMessageQueue, int aExpectedLen void TestPriorityQueue(void) { - Thread::MessagePool messagePool; + otInstance instance; + Thread::MessagePool messagePool(&instance); Thread::PriorityQueue queue; Thread::MessageQueue messageQueue; Thread::Message *msgHigh [kNumTestMessages];