• 正文
    • 什么是CMA
    • 數(shù)據(jù)結(jié)構(gòu)
    •  
    • CMA區(qū)域 cma_areas 的創(chuàng)建
    •  
    • 將CMA區(qū)域添加到Buddy System
    •  
    • CMA分配
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

Linux 內(nèi)存管理之CMA

2021/04/09
300
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

什么是CMA

CMA是reserved的一塊內(nèi)存,用于分配連續(xù)的大塊內(nèi)存。當(dāng)設(shè)備驅(qū)動不用時,內(nèi)存管理系統(tǒng)將該區(qū)域用于分配和管理可移動類型頁面;當(dāng)設(shè)備驅(qū)動使用時,此時已經(jīng)分配的頁面需要進行遷移,又用于連續(xù)內(nèi)存分配;其用法與DMA子系統(tǒng)結(jié)合在一起充當(dāng)DMA的后端,具體可參考《沒有IOMMU的DMA操作》。

數(shù)據(jù)結(jié)構(gòu)

struct cma {
 //CMA區(qū)域物理地址的起始頁幀號
 unsigned long   base_pfn; 
 //CMA區(qū)域總體的頁數(shù)
 unsigned long   count;
 //位圖,用于描述頁的分配情況
 unsigned long   *bitmap;
 //位圖中每個bit描述的物理頁面的order值,其中頁面數(shù)為2^order值
 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;
  • bitmap來管理其內(nèi)存的分配,0表示free,1表示已經(jīng)分配。如果order_per_bit等于0,表示按照一個一個page來分配和釋放,如果order_per_bit等于1,表示按照2個page組成的block來分配和釋放,以此類推。count說明該cma_areas內(nèi)存有多少個page。它和order_per_bit一起決定了bitmap指針指向內(nèi)存的大小。base_pfn定義了該cma_areas的起始page frame number,base_pfn和count一起定義了該cma_areas在內(nèi)存中的范圍。

from loyenwang

 

CMA區(qū)域 cma_areas 的創(chuàng)建

CMA區(qū)域的創(chuàng)建有兩種方法,一種是通過dts的reserved memory,另外一種是通過command line參數(shù)和內(nèi)核配置參數(shù)。

 

dts方式:

reserved-memory {
        /* global autoconfigured region for contiguous allocations */
        linux,cma {
                compatible = "shared-dma-pool";
                reusable;
                size = <0 0x28000000>;
                alloc-ranges = <0 0xa0000000 0 0x40000000>;
                linux,cma-default;
        };
};

device tree中可以包含reserved-memory node,系統(tǒng)啟動的時候會打開rmem_cma_setup

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

command line方式:

cma=nn[MG]@[start[MG][-end[MG]]]

static int __init early_cma(char *p)
{
 pr_debug("%s(%s)n", __func__, p);
 size_cmdline = memparse(p, &p);
 if (*p != '@') {
  /*
  if base and limit are not assigned,
  set limit to high memory bondary to use low memory.
  */
  limit_cmdline = __pa(high_memory);
  return 0;
 }
 base_cmdline = memparse(p + 1, &p);
 if (*p != '-') {
  limit_cmdline = base_cmdline + size_cmdline;
  return 0;
 }
 limit_cmdline = memparse(p + 1, &p);

 return 0;
}
early_param("cma", early_cma);

系統(tǒng)在啟動的過程中會把cmdline里的nn, start, end傳給函數(shù)dma_contiguous_reserve,流程如下:

setup_arch--->arm64_memblock_init--->dma_contiguous_reserve->dma_contiguous_reserve_area->cma_declare_contiguous

 

將CMA區(qū)域添加到Buddy System

為了避免這塊reserved的內(nèi)存在不用時候的浪費,內(nèi)存管理模塊會將CMA區(qū)域添加到Buddy System中,用于可移動頁面的分配和管理。CMA區(qū)域是通過cma_init_reserved_areas接口來添加到Buddy System中的。

static int __init cma_init_reserved_areas(void)
{
 int i;

 for (i = 0; i < cma_area_count; i++) {
  int ret = cma_activate_area(&cma_areas[i]);

  if (ret)
   return ret;
 }

 return 0;
}
core_initcall(cma_init_reserved_areas);

其實現(xiàn)比較簡單,主要分為兩步:

  1. 把該頁面設(shè)置為MIGRATE_CMA標(biāo)志通過__free_pages將頁面添加到buddy system中

 

CMA分配

《沒有IOMMU的DMA操作》里講過,CMA是通過cma_alloc分配的。cma_alloc->alloc_contig_range(..., MIGRATE_CMA,...),向剛才釋放給buddy system的MIGRATE_CMA類型頁面,重新“收集”過來。

用CMA的時候有一點需要注意:

也就是上圖中黃色部分的判斷。CMA內(nèi)存在分配過程是一個比較“重”的操作,可能涉及頁面遷移、頁面回收等操作,因此不適合用于atomic context。比如之前遇到過一個問題,當(dāng)內(nèi)存不足的情況下,向U盤寫數(shù)據(jù)的同時操作界面會出現(xiàn)卡頓的現(xiàn)象,這是因為CMA在遷移的過程中需要等待當(dāng)前頁面中的數(shù)據(jù)回寫到U盤之后,才會進一步的規(guī)整為連續(xù)內(nèi)存供gpu/display使用,從而出現(xiàn)卡頓的現(xiàn)象。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

針對嵌入式人工智能,物聯(lián)網(wǎng)等專業(yè)技術(shù)分享和交流平臺,內(nèi)容涉及arm,linux,android等各方面。