디바이스 드라이버가 사용하는 DMA 영역을 CMA(Contiguous Memory Allocator)에 등록하고 reserve memblock 추가한 후 해당 영역의 메모리 할당관리를 수행한다.
dma_contiguous_reserve()
drivers/base/dma-contiguous.c
/**
* dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
* @limit: End address of the reserved memory (optional, 0 for any).
*
* This function reserves memory from early allocator. It should be
* called by arch specific code once the early allocator (memblock or bootmem)
* has been activated and all other subsystems have already allocated/reserved
* memory.
*/
void __init dma_contiguous_reserve(phys_addr_t limit)
{
phys_addr_t selected_size = 0;
phys_addr_t selected_base = 0;
phys_addr_t selected_limit = limit;
bool fixed = false;
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
if (size_cmdline != -1) {
selected_size = size_cmdline;
selected_base = base_cmdline;
selected_limit = min_not_zero(limit_cmdline, limit);
if (base_cmdline + size_cmdline == limit_cmdline)
fixed = true;
} else {
#ifdef CONFIG_CMA_SIZE_SEL_MBYTES
selected_size = size_bytes;
#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE)
selected_size = cma_early_percent_memory();
#elif defined(CONFIG_CMA_SIZE_SEL_MIN)
selected_size = min(size_bytes, cma_early_percent_memory());
#elif defined(CONFIG_CMA_SIZE_SEL_MAX)
selected_size = max(size_bytes, cma_early_percent_memory());
#endif
}
if (selected_size && !dma_contiguous_default_area) {
pr_debug("%s: reserving %ld MiB for global area\n", __func__,
(unsigned long)selected_size / SZ_1M);
dma_contiguous_reserve_area(selected_size, selected_base,
selected_limit,
&dma_contiguous_default_area,
fixed);
}
}
커널 파라메터 또는 커널 옵션으로 요청한 메모리 사이즈만큼의 영역을 CMA(Contigugou Memory Allocator)에 관리 항목으로 추가하되 한 번만 요청을 받는다.
- 커널 파라메터에 “cma=”가 설정되어 early_cma() 루틴이 호출되는 경우 size_cmdline, base_cmdline, limit_cmdline 등을 알아온다.
- 요청 영역이 limit와 동일한 경우 fixed는 true로 설정하여 정확히 base(시작주소)에 할당 할 수 있도록 요청한다.
- fixed가 false인 경우 base 부터 limit 사이즈 크기로 요청한다.
- if (size_cmdline != -1) {
- size_cmdline 정보를 알아온 경우 파싱된 전역변수 값들을 사용한다.
- CONFIG_CMA_SIZE_SEL_MBYTES
- size_cmdline 정보를 알아오지 못한 경우에 커널 옵션으로 설정한 값을 사용한다.
- size_bytes = CMA_SIZE_MBYTES * SZ_1M
- CMA_SIZE_MBYTES = CONFIG_CMA_SIZE_MBYTES
- if (selected_size && !dma_contiguous_default_area) {
- 사이즈가 0보다 크면서 cma 관리 영역을 처음 설정되는 경우(null)
- dma_contiguous_reserve_area(selected_size, selected_base, selected_limit, &dma_contiguous_default_area, fixed);
- cma를 구성할 메모리 시작 주소, 사이즈, limit 및 fixed 값으로 cma에 관리 항목을 추가하고 추후 리매핑을 하기 위해 리매핑 정보도 구성한다.
- cma 관리 항목에 추가하고 reserve memblock에도 추가된다.
dma_contiguous_reserve_area()
drivers/base/dma-contiguous.c
/**
* dma_contiguous_reserve_area() - reserve custom contiguous area
* @size: Size of the reserved area (in bytes),
* @base: Base address of the reserved area optional, use 0 for any
* @limit: End address of the reserved memory (optional, 0 for any).
* @res_cma: Pointer to store the created cma region.
* @fixed: hint about where to place the reserved area
*
* This function reserves memory from early allocator. It should be
* called by arch specific code once the early allocator (memblock or bootmem)
* has been activated and all other subsystems have already allocated/reserved
* memory. This function allows to create custom reserved areas for specific
* devices.
*
* If @fixed is true, reserve contiguous area at exactly @base. If false,
* reserve in range from @base to @limit.
*/
int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
phys_addr_t limit, struct cma **res_cma,
bool fixed)
{
int ret;
ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, res_cma);
if (ret)
return ret;
/* Architecture specific contiguous memory fixup. */
dma_contiguous_early_fixup(cma_get_base(*res_cma),
cma_get_size(*res_cma));
return 0;
}
- ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, res_cma);
- cma_areas[]에 관리 항목을 추가하고 관리할 메모리 크기 정보인 base, size, limit, alignment 및 order_per_bit를 사용하여 base_pfn, count, bitmap 할당, alignment 및 order_per_bit 를 설정한다.
- cma 에서 사용하는 비트맵은 각 비트가 1개 페이지를 표현하도록 하게 한다.
- 영역의 alignment 단위는 1 페이지로 한다.
- 해당 영역은 reserve memblock에도 등록된다.
- 실패하는 경우 에러를 리턴한다.
- 추가한 엔트리들은 CMA 드라이버가 로드될 때 호출되어 초기화한다.
- cma_areas[]에 관리 항목을 추가하고 관리할 메모리 크기 정보인 base, size, limit, alignment 및 order_per_bit를 사용하여 base_pfn, count, bitmap 할당, alignment 및 order_per_bit 를 설정한다.
- dma_contiguous_early_fixup(cma_get_base(*res_cma), cma_get_size(*res_cma));
- cma에서 관리할 메모리 영역을 dma_mmu_remap[] 배열에 추가한다.
- 이렇게 배열에 정보만 단순히 추가한 후 나중에 setup_arch() -> paging_init() -> dma_contiguous_remap() 순서대로 함수를 호출하여 리매핑을 하게 한다.
dma_contiguous_early_fixup()
arch/arm/mm/dma-mapping.c
void __init dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
{
dma_mmu_remap[dma_mmu_remap_num].base = base;
dma_mmu_remap[dma_mmu_remap_num].size = size;
dma_mmu_remap_num++;
}
- dma_mmu_remap[] 배열에 추가한다.
CMA 메모리 early 설정 루틴
early_cma()
“cma= ” 커널 파라메터에 의해 호출되어 사용된다.
drivers/base/dma-contiguous.c
/*
* Default global CMA area size can be defined in kernel's .config.
* This is useful mainly for distro maintainers to create a kernel
* that works correctly for most supported systems.
* The size can be set in bytes or as a percentage of the total memory
* in the system.
*
* Users, who want to set the size of global CMA area for their system
* should use cma= kernel parameter.
*/
static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M;
static phys_addr_t size_cmdline = -1;
static phys_addr_t base_cmdline;
static phys_addr_t limit_cmdline;
static int __init early_cma(char *p)
{
pr_debug("%s(%s)\n", __func__, p);
size_cmdline = memparse(p, &p);
if (*p != '@')
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);
다음과 같이 사용예를 알아본다.
- cma=4M
- 0x0000_0000 ~ arm_dma_limit(DMA 사이즈) 영역내에서 4M의 CMA용 공간을 할당한다.
- cma=4M@0x00c00000
- 0x00c0_0000 ~ arm_dma_limit(DMA 사이즈) 영역내에서 4M의 CMA용 공간을 할당한다.
- cma=4M@0x10000000-0x40000000
- 0x1000_0000 ~ 0x4000_0000 영역내에서 4M의 CMA용 공간을 할당한다.
- cma=4M@0x10000000-0x10400000
- 정확히 0x1000_0000 위치에서 4M의 CMA용 공간을 할당한다. (fix된 위치)
구조체 및 전역 변수
dma_contig_early_reserve 구조체
arch/arm/mm/dma-mapping.c
struct dma_contig_early_reserve {
phys_addr_t base;
unsigned long size;
};
전역변수
arch/arm/mm/dma-mapping.c
static struct dma_contig_early_reserve dma_mmu_remap[MAX_CMA_AREAS] __initdata; static int dma_mmu_remap_num __initdata;
- dma_mmu_remap[]
- MAX_CMA_AREAS는 CONFIG_CMA_AREAS 커널 옵션 + 1 값이다.
- dma_mmu_remap_num
- 리매핑할 항목의 수
참고
- CMA(Contiguous Memory Allocator) | 문c
- dma_contiguous_remap()