Abstract: Hongmon light kernel M core added support for multi-segment discontinuous memory region, the multiple discontinuous memory logical unity, users do not perceive the different memory blocks at the bottom.

This article is shared from huawei cloud community “Hongmenglight kernel M core source code analysis series nine Dynamic Memory supplement”, author: Zhushy.

Some chips cannot meet the requirements of RAM size and need to use off-chip physical memory for expansion. The memory management module is required to centrally manage the memory with multiple non-continuous segments. When using memory interfaces, applications do not need to pay attention to the physical memory allocated to the memory.

Multi-segment discontinuous memory is shown in the following figure:

Hongmon light kernel M new support multi-segment non-continuous memory region, the multiple non-continuous memory logical unity, users do not perceive the different memory blocks at the bottom. This article analyzes the dynamic memory module to support multi-segment discontinuous memory source code, to help readers master its use. The source code in this article, using the OpenHarmony LitEOS-M kernel as an example, is available on the open source site gitee.com/openharmony… To obtain. Next, take a look at the source code for the new constructs, macros, and interfaces.

Struct definition and common macro definition

The LosMemRegion structure is added to the kernel/include/los_memory.h file to maintain multiple non-contiguous memory regions, including the start address and size of each memory region. As follows:

typedef struct { VOID *startAddress; /* Start address of memory area */ UINT32 length; /* The length of the memory region */} LosMemRegion;Copy the code

Note that LOSCFG_MEM_MUL_REGIONS takes effect only if the macro LOSCFG_MEM_MUL_REGIONS is enabled. This macro is also a configuration macro that supports non-contiguous memory regions, as defined in the kernel/include/los_config.h file.

Kernel/SRC /mm/los_memory.c

When LOSCFG_MEM_MUL_REGIONS supports discontinuous memory, the Gap region between two discontinuous memory regions is marked as a virtual used memory node. This node cannot, of course, be freed or counted in the memory tuning feature. Because we just think of it as a used memory node, which it isn’t. In the dynamic memory algorithm, each memory node maintains a pointer to the preceding node. For the virtual used node, we set the pointer to the magic word to mark that it is an interval part of the memory region.

OS_MEM_GAP_NODE_MAGIC is used to represent the Gap region between two discontinuous memory regions. ⑵ and ⑶ define two macros, respectively for setting the magic word, verify the magic word.

#if (LOSCFG_MEM_MUL_REGIONS == 1) /** * When LOSCFG_MEM_MUL_REGIONS is enabled to support multiple non-continuous memory  regions, the gap between two memory regions * is marked as a used OsMemNodeHead node. The gap node could not be freed, and would also be skipped in some DFX functions. The * 'ptr.prev' pointer of this node is set to OS_MEM_GAP_NODE_MAGIC */ ⑴ #define OS_MEM_GAP_NODE_MAGIC 0xDCBAABCD ⑵ #define OS_MEM_MARK_GAP_NODE(node) (((struct OsMemNodeHead *)(node))->ptr.prev = (struct OsMemNodeHead *)OS_MEM_GAP_NODE_MAGIC) OS_MEM_IS_GAP_NODE(node) (((struct OsMemNodeHead *)(node))->ptr.prev == (struct OsMemNodeHead *)OS_MEM_GAP_NODE_MAGIC) #else ⑵ #define OS_MEM_MARK_GAP_NODE(node) ⑶ #define OS_MEM_IS_GAP_NODE(node) FALSECopy the code

2. Common operations of dynamic memory

In this section, we analyze the implementation algorithm of discontinuous memory and interface implementation code. First, the algorithm is understood through the schematic diagram:

Set diagram, we understand the steps of discontinuous memory merge into a memory pool:

1. Initialize the first memory region by calling LOS_MemInit

2. Obtain the start address and length of the next memory region, and calculate the interval size gapSize between this memory region and the previous memory region.

3. Set the size of the last memory block to gapSize+ OS_MEM_NODE_HEAD_SIZE.

4. Divide the current memory area into a free memory block and a tail node, and insert the free memory block into the free linked list. And set the links between each node before and after.

Repeat steps 2-4 for more non-contiguous memory blocks.

2.1 Added interface LOS_MemRegionsAdd

The interface description of the new interface is described in the following section. The comments are detailed and summarized as follows:

  • LOSCFG_MEM_MUL_REGIONS = 0:

Multiple segments of discontinuous memory are not supported, and related codes are not enabled.

  • LOSCFG_MEM_MUL_REGIONS = 1:

Support multiple segments of discontinuous memory, related code enabled. LOS_MemRegionsAdd(VOID *pool, const LosMemRegion * const multipleMemRegions);

If the pool is empty, it is merged to the main memory heap m_aucSysMem0.

If it is not empty, a new memory pool is initialized and the multiple memory regions are merged into a slave heap.

/** * @ingroup los_memory * @brief Initialize multiple non-continuous memory regions. * * @par Description: * <ul> * <li>This API is used to initialize multiple non-continuous memory regions. If the starting address of a pool is  specified, * the memory regions will be linked to the pool as free nodes. Otherwise, the first memory region will be initialized as a * new pool, and the rest regions will be linked as free nodes to the new pool.</li> * </ul> * * @attention * <ul> * <li>If the starting address of a memory pool is specified, the start address of the non-continuous memory regions should be * greater than the end address of the memory pool.</li>  * <li>The multiple non-continuous memory regions shouldn't conflict with each other.</li> * </ul> * * @param pool [IN] The memory pool address. If NULL is specified, the start address of first memory region will be * initialized as the memory pool address. If not NULL, it should be a valid address of a memory pool. * @param memRegions [IN] The LosMemRegion array that contains multiple non-continuous memory regions. The start address * of the memory regions are placed in ascending order. * @param memRegionCount [IN] The count of non-continuous memory regions, and it should be the length of the LosMemRegion array. * * @retval #LOS_NOK The multiple non-continuous memory regions fails to be initialized. * @retval #LOS_OK The multiple non-continuous memory regions is initialized successfully. * @par Dependency: * <ul> * <li>los_memory.h: the header file that contains the API declaration.</li> * </ul> * @see None. */ extern UINT32 LOS_MemRegionsAdd(VOID *pool, const LosMemRegion * const memRegions, UINT32 memRegionCount);Copy the code

2.2 Adding interface LOS_MemRegionsAdd

Combined with the above schematic diagram, with annotations, to achieve a more clear, directly read the code.

#if (LOSCFG_MEM_MUL_REGIONS == 1) STATIC INLINE UINT32 OsMemMulRegionsParamCheck(VOID *pool, const LosMemRegion * const memRegions, UINT32 memRegionCount) { const LosMemRegion *memRegion = NULL; VOID *lastStartAddress = NULL; VOID *curStartAddress = NULL; UINT32 lastLength; UINT32 curLength; UINT32 regionCount; if ((pool ! = NULL) && (((struct OsMemPoolHead *)pool)->info.pool ! = pool)) { PRINT_ERR("wrong mem pool addr: %p, func: %s, line: %d\n", pool, __FUNCTION__, __LINE__); return LOS_NOK; } if (pool ! = NULL) { lastStartAddress = pool; lastLength = ((struct OsMemPoolHead *)pool)->info.totalSize; } memRegion = memRegions; regionCount = 0; while (regionCount < memRegionCount) { curStartAddress = memRegion->startAddress; curLength = memRegion->length; if ((curStartAddress == NULL) || (curLength == 0)) { PRINT_ERR("Memory address or length configured wrongly:address:0x%x, the length:0x%x\n", (UINTPTR)curStartAddress, curLength); return LOS_NOK; } if (((UINTPTR)curStartAddress & (OS_MEM_ALIGN_SIZE - 1)) || (curLength & (OS_MEM_ALIGN_SIZE - 1))) { PRINT_ERR("Memory  address or length configured not aligned:address:0x%x, the length:0x%x, alignsize:%d\n", \ (UINTPTR)curStartAddress, curLength, OS_MEM_ALIGN_SIZE); return LOS_NOK; } if ((lastStartAddress ! = NULL) && (((UINT8 *)lastStartAddress + lastLength) >= (UINT8 *)curStartAddress)) { PRINT_ERR("Memory regions overlapped, the last start address:0x%x, the length:0x%x, the current start address:0x%x\n", \ (UINTPTR)lastStartAddress, lastLength, (UINTPTR)curStartAddress); return LOS_NOK; } memRegion++; regionCount++; lastStartAddress = curStartAddress; lastLength = curLength; } return LOS_OK; } STATIC INLINE VOID OsMemMulRegionsLink(struct OsMemPoolHead *poolHead, VOID *lastStartAddress, UINT32 lastLength, struct OsMemNodeHead *lastEndNode, const LosMemRegion *memRegion) { UINT32 curLength; UINT32 gapSize; struct OsMemNodeHead *curEndNode = NULL; struct OsMemNodeHead *curFreeNode = NULL; VOID *curStartAddress = NULL; curStartAddress = memRegion->startAddress; curLength = memRegion->length; // mark the gap between two regions as one used node gapSize = (UINT8 *)(curStartAddress) - ((UINT8 *)(lastStartAddress)  + lastLength); lastEndNode->sizeAndFlag = gapSize + OS_MEM_NODE_HEAD_SIZE; OS_MEM_SET_MAGIC(lastEndNode); OS_MEM_NODE_SET_USED_FLAG(lastEndNode->sizeAndFlag); // mark the gap node with magic number OS_MEM_MARK_GAP_NODE(lastEndNode); poolHead->info.totalSize += (curLength + gapSize); poolHead->info.totalGapSize += gapSize; curFreeNode = (struct OsMemNodeHead *)curStartAddress; curFreeNode->sizeAndFlag = curLength - OS_MEM_NODE_HEAD_SIZE; curFreeNode->ptr.prev = lastEndNode; OS_MEM_SET_MAGIC(curFreeNode); OsMemFreeNodeAdd(poolHead, (struct OsMemFreeNodeHead *)curFreeNode); curEndNode = OS_MEM_END_NODE(curStartAddress, curLength); curEndNode->sizeAndFlag = 0; curEndNode->ptr.prev = curFreeNode; OS_MEM_SET_MAGIC(curEndNode); OS_MEM_NODE_SET_USED_FLAG(curEndNode->sizeAndFlag); #if (LOSCFG_MEM_WATERLINE == 1) poolHead->info.curUsedSize += OS_MEM_NODE_HEAD_SIZE; poolHead->info.waterLine = poolHead->info.curUsedSize; #endif } UINT32 LOS_MemRegionsAdd(VOID *pool, const LosMemRegion *const memRegions, UINT32 memRegionCount) { UINT32 ret; UINT32 lastLength; UINT32 curLength; UINT32 regionCount; struct OsMemPoolHead *poolHead = NULL; struct OsMemNodeHead *lastEndNode = NULL; struct OsMemNodeHead *firstFreeNode = NULL; const LosMemRegion *memRegion = NULL; VOID *lastStartAddress = NULL; VOID *curStartAddress = NULL; ret = OsMemMulRegionsParamCheck(pool, memRegions, memRegionCount); if (ret ! = LOS_OK) { return ret; } memRegion = memRegions; regionCount = 0; if (pool ! = NULL) { // add the memory regions to the specified memory pool poolHead = (struct OsMemPoolHead *)pool; lastStartAddress = pool; lastLength = poolHead->info.totalSize; } else { // initialize the memory pool with the first memory region lastStartAddress = memRegion->startAddress; lastLength = memRegion->length; poolHead = (struct OsMemPoolHead *)lastStartAddress; ret = LOS_MemInit(lastStartAddress, lastLength); if (ret ! = LOS_OK) { return ret; } memRegion++; regionCount++; } firstFreeNode = OS_MEM_FIRST_NODE(lastStartAddress); lastEndNode = OS_MEM_END_NODE(lastStartAddress, lastLength); while (regionCount < memRegionCount) { // traverse the rest memory regions, and initialize them as free nodes and link together curStartAddress = memRegion->startAddress; curLength = memRegion->length; OsMemMulRegionsLink(poolHead, lastStartAddress, lastLength, lastEndNode, memRegion); lastStartAddress = curStartAddress; lastLength = curLength; lastEndNode = OS_MEM_END_NODE(curStartAddress, curLength); memRegion++; regionCount++; } firstFreeNode->ptr.prev = lastEndNode; return ret; } #endifCopy the code

summary

This paper leads us to analyze how the dynamic memory of M core supports multi-segment discontinuous memory, including structure, operation diagram, new interface and so on. Thanks for reading. If you have any questions or suggestions, please leave a comment. Thank you.

For more information, please pay attention to the IoT community and add huawei Cloud IoT Small Assistant wechat (HWC-iot) for more information

Click to follow, the first time to learn about Huawei cloud fresh technology ~