在第linux内存管理笔记(十一)—CMA中,我们学习了操作系统预留的大量的连续内存,我们对其引入的原因和使用方法进行了分析,其主要是用于内核中分配连续的大块内存。
- 当设备驱动不适用时,内存管理系统将该区域用于分配和管理可移动类型页面
- 当驱动程序使用时,用于连续内存分配,此时已经分配的页面进行迁移
这样既可以在内核中分配连续的大内存,同时又不浪费内存空间,因为当内存不足的时候,可以通过内存迁移实现内存重新分配的方式。通过之前的分析,当启动的时候通过arm_memblock_init->dma_contiguous_reserve->dma_contiguous_reserve_area->dma_contiguous_early_fixup,将CMA的起始地址和空间长度存储在dma_mmu_remap
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++;
}
在paging_init做完低端内存映射后,就调用到dma_contiguous_remap进行相关设置,看看这个函数做了些什么呢?
void __init dma_contiguous_remap(void)
{
int i;
for (i = 0; i < dma_mmu_remap_num; i++) {
phys_addr_t start = dma_mmu_remap[i].base; -------------(1)
phys_addr_t end = start + dma_mmu_remap[i].size;
struct map_desc map;
unsigned long addr;
if (end > arm_lowmem_limit)
end = arm_lowmem_limit;
if (start >= end)
continue;
map.pfn = __phys_to_pfn(start); -------------(2)
map.virtual = __phys_to_virt(start);
map.length = end - start;
map.type = MT_MEMORY_DMA_READY;
for (addr = __phys_to_virt(start); addr < __phys_to_virt(end); -------------(3)
addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
flush_tlb_kernel_range(__phys_to_virt(start), -------------(4)
__phys_to_virt(end));
iotable_init(&map, 1); -------------(5)
}
}
- 1.这个区域是之前通过dma_contiguous_reserve来设置的保留区域,其start = 0x8c00 0000,length = 0x1400 0000,这个空间大小与dts中配置的"shared-dma-pool"大小一致。
- 2.配置该区间的Map_desc,为后边的映射create_mapping做准备
- 3.清0x8c00 0000 - 0xA000 0000空间的pmd表
- 4.刷新该的区域对应的tlb
- 5.iotable_init函数来建立页映射关系,主要是CAM的映射
iotable_init函数就是最终实现建立虚拟地址映射的函数,其处理流程如下
void __init iotable_init(struct map_desc *io_desc, int nr)
{
struct map_desc *md;
struct vm_struct *vm;
struct static_vm *svm;
if (!nr)
return;
svm = early_alloc_aligned(sizeof(*svm) * nr, __alignof__(*svm));
for (md = io_desc; nr; md++, nr--) {
create_mapping(md);
vm = &svm->vm;
vm->addr = (void *)(md->virtual & PAGE_MASK);
vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
vm->phys_addr = __pfn_to_phys(md->pfn);
vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING;
vm->flags |= VM_ARM_MTYPE(md->type);
vm->caller = iotable_init;
add_static_vm_early(svm++);
}
}
也是调用create_mapping依次对传入的io端口地址进行映射,而且利用add_static_vm_early函数添加到了vmlist和static_vmlist链表,这两个全局链表应该是用来管理虚拟内存的,标记io映射使用的虚拟内存。具体的后面再分析CMA与伙伴系统的关系的时候,再深入分析之间的联系。
总结下,我们主要是分析了dma_contiguous_remap的作用,主要是针对CMA的区域进行了重新的映射,主要是0x8c00 000 ~ 0xe000 0000这个区域清了之前低端内存的映射关系,又重新作了二级映射。