smmu 学习笔记之mmap函数.

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.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值