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

Linux 內存管理之CMA

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

什么是CMA

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

數(shù)據(jù)結構

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來管理其內存的分配,0表示free,1表示已經(jīng)分配。如果order_per_bit等于0,表示按照一個一個page來分配和釋放,如果order_per_bit等于1,表示按照2個page組成的block來分配和釋放,以此類推。count說明該cma_areas內存有多少個page。它和order_per_bit一起決定了bitmap指針指向內存的大小。base_pfn定義了該cma_areas的起始page frame number,base_pfn和count一起定義了該cma_areas在內存中的范圍。

from loyenwang

 

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

CMA區(qū)域的創(chuàng)建有兩種方法,一種是通過dts的reserved memory,另外一種是通過command line參數(shù)和內核配置參數(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的內存在不用時候的浪費,內存管理模塊會將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. 把該頁面設置為MIGRATE_CMA標志通過__free_pages將頁面添加到buddy system中

 

CMA分配

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

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

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

相關推薦

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

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