This paper deeply analyzes how the dynamic memory of Hongmeng light kernel M core supports multi segment discontinuous memory

Posted by landavia on Sun, 19 Dec 2021 13:28:02 +0100

Abstract: the M core of Hongmeng light kernel newly supports multi segment discontinuous memory areas, which logically integrates multiple discontinuous memory, and users do not perceive different memory blocks at the bottom.

This article is shared from Huawei cloud community< Hongmeng light kernel M core source code analysis series 9 Dynamic Memory dynamic memory supplement >, author: zhushy.

Some on-chip RAM size can not meet the requirements, so it needs to be expanded with off-chip physical memory. For multi segment discontinuous memory, it needs to be managed uniformly by the memory management module. When using the memory interface, applications do not need to pay attention to which physical memory the memory allocation belongs and do not perceive multiple memory blocks.

Multi segment discontinuous memory is shown in the following figure:

Hongmeng light kernel M core newly supports multi segment discontinuous memory areas, which logically integrates multiple discontinuous memory, and users do not perceive different memory blocks at the bottom. This paper analyzes the source code of dynamic memory module supporting multi segment discontinuous memory to help readers master its use. The source code involved in this article, taking the OpenHarmony LiteOS-M kernel as an example, can be found on the open source site https://gitee.com/openharmony/kernel_liteos_m Get. Next, let's look at the source code of the new structure, macro and external interface.

1. Structure definition and common macro definition

In the file kernel / include / Los_ memory. The structure LosMemRegion is added in H to maintain multiple discontinuous 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;      /* Length of memory area */
} LosMemRegion;

Note that the macro loscfg needs to be enabled for the definition of this structure_ MEM_ MUL_ This macro is also a configuration macro that supports discontinuous memory regions. It is defined in the file kernel/include/los_config.h medium.

Let's continue to look at the new macro functions, which are defined in the file kernel/src/mm/los_memory.c. The code is as follows:

The notes are quite clear. When loscfg is enabled_ MEM_ MUL_ When regions supports the discontinuous memory feature, the Gap area between two discontinuous memory areas will be marked as a virtual used memory node. Of course, this node cannot be released, nor can it be counted in the memory debugging feature. Because we just regard it as a used memory node, but it is not. 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 magic word to mark that it is the interval of a memory area.

A magic word OS is defined at (1)_ MEM_ Gap_ NODE_ Magic, used to represent the Gap area before two discontinuous memory areas. (2) and (3) define two macros, which are respectively used to set the magic word and 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 to identify that this is a gap node. 
*/
⑴  #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)
⑶  #define 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)    FALSE
#endif

2. Dynamic memory common operations

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

According to the collection diagram, we understand the following steps for merging discontinuous memory into a memory pool:

  • 1. Call Los for the first memory area of the multi segment memory area_ Meminit to initialize
  • 2. Get the start address and length of the next memory area, and calculate the interval size gapSize between the memory area and the previous memory area.
  • 3. Treat the memory block interval as a virtual used node, use the tail node of the previous memory block, and set its size to gapSize+ OS_MEM_NODE_HEAD_SIZE.
  • 4. The current memory area is divided into a free memory block and a tail node, and the free memory block is inserted into the free linked list. And set the front and back link relationship of each node.
  • 5. If there are more discontinuous memory blocks, repeat steps 2-4 above.

2.1 new interface LOS_MemRegionsAdd

The interface description document of the new interface is shown below. The notes are detailed and summarized as follows:

  • LOSCFG_MEM_MUL_REGIONS=0:

Multi segment discontinuous memory is not supported, and the related code is not enabled.

  • LOSCFG_MEM_MUL_REGIONS=1:

Multi segment discontinuous memory is supported, and the related code is enabled. The user configures the multi segment memory area and calls the interface
LOS_MemRegionsAdd(VOID *pool, const LosMemRegion * const multipleMemRegions) integrates memory pools:

    • If the pool is empty, it is merged into the main memory heap m_aucSysMem0.
    • If it is not empty, initialize a new memory pool and merge multiple memory regions 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);

2.2 new interface LOS_MemRegionsAdd implementation

Combined with the above schematic diagram and notes, the implementation is relatively clear. Just read the code directly.

#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;
}
#endif

Summary

This paper leads you to analyze how the dynamic memory of Hongmeng light kernel M core supports multi segment discontinuous memory, including structure, operation diagram, new interface and so on. Thank you for reading. If you have any questions or suggestions, you can leave comments. Thank you.

For more learning content, please click to follow IoT Internet of things community , add Huawei cloud IoT assistant wechat (HWC IoT) to get more information

Click focus to learn about Huawei cloud's new technologies for the first time~

Topics: C stm32