smmu 学习笔记之mmap函数.

    xiaoxiao2021-03-25  150

    iommu 实现了mmap函数,也就是说iommu可以将memory映射到user space。 static int __iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,                   void *cpu_addr, dma_addr_t dma_addr, size_t size,                   unsigned long attrs) {     struct vm_struct *area;     int ret;     vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot,                          is_device_dma_coherent(dev));     if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))         return ret;     area = find_vm_area(cpu_addr);     if (WARN_ON(!area || !area->pages))         return -ENXIO;     return iommu_dma_mmap(area->pages, size, vma); } 其中dma_mmap_from_coherent 一般会返回false 因此调用find_vm_area 来查找cpu_addr 来查找对应的vm_struct struct vm_struct *find_vm_area(const void *addr) {     struct vmap_area *va;     va = find_vmap_area((unsigned long)addr);     if (va && va->flags & VM_VM_AREA)         return va->vm;     return NULL; } 函数实现比较简单,继续调用find_vmap_area static struct vmap_area *__find_vmap_area(unsigned long addr) {     struct rb_node *n = vmap_area_root.rb_node;     while (n) {         struct vmap_area *va;         va = rb_entry(n, struct vmap_area, rb_node);         if (addr < va->va_start)             n = n->rb_left;         else if (addr >= va->va_end)             n = n->rb_right;         else             return va;     }     return NULL; } 从__find_vmap_area 中可以看到,kernel中所有的vmap_area 是按照rb tree组织的,这个rb tree的root就是vmap_area_root.rb_node,因此根据虚拟地址就能找到对应的vmap_area,而vmap_area->vm_struct 就返回vm_struct了 找到vm_struct 后就调用iommu_dma_mmap 将vm_struct 中的page插入到vm_area_struct 中 如果没有找到area 或者area->pages为null的话,就返回,负责调用iommu_dma_mmap来做mmap int iommu_dma_mmap(struct page **pages, size_t size, struct vm_area_struct *vma) {     unsigned long uaddr = vma->vm_start;     unsigned int i, count = PAGE_ALIGN(size) >> PAGE_SHIFT;     int ret = -ENXIO;     for (i = vma->vm_pgoff; i < count && uaddr < vma->vm_end; i++) {         ret = vm_insert_page(vma, uaddr, pages[i]);         if (ret)             break;         uaddr += PAGE_SIZE;     }     return ret; } 在iommu_dma_mmap 中从vm_area_struct 开始vm_pgoff,一直到vm_area_struct的结束vm_end。来遍历,对用vm_insert_page将pages 插入 int vm_insert_page(struct vm_area_struct *vma, unsigned long addr,             struct page *page) {         //addr必须在vma的范围内     if (addr < vma->vm_start || addr >= vma->vm_end)         return -EFAULT; // page_count 如果大于0,说明有人正在使用     if (!page_count(page))         return -EINVAL; // 如果已经map了就置flagVM_MIXEDMAP     if (!(vma->vm_flags & VM_MIXEDMAP)) {         BUG_ON(down_read_trylock(&vma->vm_mm->mmap_sem));         BUG_ON(vma->vm_flags & VM_PFNMAP);         vma->vm_flags |= VM_MIXEDMAP;     }     return insert_page(vma, addr, page, vma->vm_page_prot); } static int insert_page(struct vm_area_struct *vma, unsigned long addr,             struct page *page, pgprot_t prot) {     struct mm_struct *mm = vma->vm_mm;     int retval;     pte_t *pte;     spinlock_t *ptl;     retval = -EINVAL;     if (PageAnon(page))         goto out;     retval = -ENOMEM;     flush_dcache_page(page);     pte = get_locked_pte(mm, addr, &ptl);     if (!pte)         goto out;     retval = -EBUSY;     if (!pte_none(*pte))         goto out_unlock;     /* Ok, finally just insert the thing.. */     get_page(page);     inc_mm_counter_fast(mm, mm_counter_file(page));     page_add_file_rmap(page, false);     set_pte_at(mm, addr, pte, mk_pte(page, prot));     retval = 0;     pte_unmap_unlock(pte, ptl);     return retval; out_unlock:     pte_unmap_unlock(pte, ptl); out:     return retval; } insert_page 这个函数 最重要的就是将page的pte添加到mm_struct。从而完成page的insert.
    转载请注明原文地址: https://ju.6miu.com/read-918.html

    最新回复(0)