디바이스 드라이버가 사용하는 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()