【原創】(十六)Linux內存管理之CMA

背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:數組

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

Contiguous Memory Allocator, CMA,連續內存分配器,用於分配連續的大塊內存。
CMA分配器,會Reserve一片物理內存區域:數據結構

  1. 設備驅動不用時,內存管理系統將該區域用於分配和管理可移動類型頁面;
  2. 設備驅動使用時,用於連續內存分配,此時已經分配的頁面須要進行遷移;

此外,CMA分配器還能夠與DMA子系統集成在一塊兒,使用DMA的設備驅動程序無需使用單獨的CMA API併發

2. 數據結構

內核定義了struct cma結構,用於管理一個CMA區域,此外還定義了全局的cma數組,以下:框架

struct cma {
    unsigned long   base_pfn;
    unsigned long   count;
    unsigned long   *bitmap;
    unsigned int order_per_bit; /* Order of pages represented by one bit */
    struct mutex    lock;
#ifdef CONFIG_CMA_DEBUGFS
    struct hlist_head mem_head;
    spinlock_t mem_head_lock;
#endif
    const char *name;
};

extern struct cma cma_areas[MAX_CMA_AREAS];
extern unsigned cma_area_count;
  • base_pfn:CMA區域物理地址的起始頁幀號;
  • count:CMA區域整體的頁數;
  • *bitmap:位圖,用於描述頁的分配狀況;
  • order_per_bit:位圖中每一個bit描述的物理頁面的order值,其中頁面數爲2^order值;

來一張圖就會清晰明瞭:ide

3. 流程分析

3.1 CMA區域建立

3.1.1 方式一 根據dts來配置

以前的文章也都分析過,物理內存的描述放置在dts中,最終會在系統啓動過程當中,對dtb文件進行解析,從而完成內存信息註冊。函數

CMA的內存在dts中的描述示例以下圖:工具

dtb解析過程當中,會調用到rmem_cma_setup函數:學習

RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);

3.1.2 方式二 根據參數或宏配置

能夠經過內核參數或配置宏,來進行CMA區域的建立,最終會調用到cma_declare_contiguous函數,以下圖:ui

3.2 CMA添加到Buddy System

在建立完CMA區域後,該內存區域成了保留區域,若是單純給驅動使用,顯然會形成內存的浪費,所以內存管理模塊會將CMA區域添加到Buddy System中,用於可移動頁面的分配和管理。CMA區域是經過cma_init_reserved_areas接口來添加到Buddy System中的。this

core_initcall(cma_init_reserved_areas);

core_initcall宏將cma_init_reserved_areas函數放置到特定的段中,在系統啓動的時候會調用到該函數。

3.3 CMA分配/釋放

  • CMA分配,入口函數爲cma_alloc

  • CMA釋放,入口函數爲cma_release
    函數比較簡單,直接貼上代碼
/**
 * cma_release() - release allocated pages
 * @cma:   Contiguous memory region for which the allocation is performed.
 * @pages: Allocated pages.
 * @count: Number of allocated pages.
 *
 * This function releases memory allocated by alloc_cma().
 * It returns false when provided pages do not belong to contiguous area and
 * true otherwise.
 */
bool cma_release(struct cma *cma, const struct page *pages, unsigned int count)
{
    unsigned long pfn;

    if (!cma || !pages)
        return false;

    pr_debug("%s(page %p)\n", __func__, (void *)pages);

    pfn = page_to_pfn(pages);

    if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count)
        return false;

    VM_BUG_ON(pfn + count > cma->base_pfn + cma->count);

    free_contig_range(pfn, count);
    cma_clear_bitmap(cma, pfn, count);
    trace_cma_release(pfn, pages, count);

    return true;
}

3.4 DMA使用

代碼參考driver/base/dma-contiguous.c,主要包括的接口有:

/**
 * dma_alloc_from_contiguous() - allocate pages from contiguous area
 * @dev:   Pointer to device for which the allocation is performed.
 * @count: Requested number of pages.
 * @align: Requested alignment of pages (in PAGE_SIZE order).
 * @gfp_mask: GFP flags to use for this allocation.
 *
 * This function allocates memory buffer for specified device. It uses
 * device specific contiguous memory area if available or the default
 * global one. Requires architecture specific dev_get_cma_area() helper
 * function.
 */
struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
                       unsigned int align, gfp_t gfp_mask);
 
 /**
 * dma_release_from_contiguous() - release allocated pages
 * @dev:   Pointer to device for which the pages were allocated.
 * @pages: Allocated pages.
 * @count: Number of allocated pages.
 *
 * This function releases memory allocated by dma_alloc_from_contiguous().
 * It returns false when provided pages do not belong to contiguous area and
 * true otherwise.
 */
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
                 int count);

在上述的接口中,實際調用的就是cma_alloc/cma_release接口來實現的。

總體來看,CMA分配器仍是比較簡單易懂,也再也不深刻分析。

4.後記

內存管理的分析先告一段落,後續可能還會針對某些模塊進一步的研究與完善。
內存管理子系統,極其複雜,盤根錯節,很容易就懵圈了,儘管費了很多心力,也只能說略知皮毛。
學習就像是登山,面對一座高山,可能會有心理障礙,可是當你跨越以後,再看到一樣高的山,心理上你將再也不畏懼。

接下來將研究進程管理子系統,將任督二脈打通。

將來會持續分析內核中的各種框架,併發機制等,敬請關注,一塊兒探討。

相關文章
相關標籤/搜索