uio的UIO_MEM_PHYS/UIO_MEM_VIRTUAL/UIO_MEM_VIRTUAL 的区别

来源:互联网 发布:淘宝上卖av的店铺 编辑:程序博客网 时间:2024/04/27 19:18
当uio mmp的时候如果memtype是UIO_MEM_VIRTUAL和UIO_MEM_VIRTUAL的时候
static int uio_mmap_logical(struct vm_area_struct *vma)
{
    vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
    vma->vm_ops = &uio_logical_vm_ops;
    return 0;
}
static const struct vm_operations_struct uio_logical_vm_ops = {
    .fault = uio_vma_fault,
};

vm_ops 就是指向uio_logical_vm_ops,那这个vm_ops什么时候调用呢?
从下面的code中可以看出是在发生缺页异常的时候调用的
static int __do_fault(struct fault_env *fe, pgoff_t pgoff,
        struct page *cow_page, struct page **page, void **entry)
{
    struct vm_area_struct *vma = fe->vma;
    struct vm_fault vmf;
    int ret;

    vmf.virtual_address = (void __user *)(fe->address & PAGE_MASK);
    vmf.pgoff = pgoff;
    vmf.flags = fe->flags;
    vmf.page = NULL;
    vmf.gfp_mask = __get_fault_gfp_mask(vma);
    vmf.cow_page = cow_page;

    ret = vma->vm_ops->fault(vma, &vmf);
}
那我们来看看uio_vma_fault
static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
    struct uio_device *idev = vma->vm_private_data;
    struct page *page;
    unsigned long offset;
    void *addr;

    int mi = uio_find_mem_index(vma);
    if (mi < 0)
        return VM_FAULT_SIGBUS;

    /*
     * We need to subtract mi because userspace uses offset = N*PAGE_SIZE
     * to use mem[N].
     */
    offset = (vmf->pgoff - mi) << PAGE_SHIFT;

    addr = (void *)(unsigned long)idev->info->mem[mi].addr + offset;
    if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
        page = virt_to_page(addr);
    else
        page = vmalloc_to_page(addr);
    get_page(page);
    vmf->page = page;
    return 0;
}
在uio_vma_fault 中首先通过uio_find_mem_index 来判断vm_pgoff对应的mem是否为0,如果为0的话,则uio_vma_fault 直接退出了
static int uio_find_mem_index(struct vm_area_struct *vma)
{
    struct uio_device *idev = vma->vm_private_data;

    if (vma->vm_pgoff < MAX_UIO_MAPS) {
        if (idev->info->mem[vma->vm_pgoff].size == 0)
            return -1;
        return (int)vma->vm_pgoff;
    }
    return -1;
}
回到uio_vma_fault 中计算出addr
    offset = (vmf->pgoff - mi) << PAGE_SHIFT;

    addr = (void *)(unsigned long)idev->info->mem[mi].addr + offset;

然后再根据UIO_MEM_LOGICAL还是UIO_MEM_VIRTUAL调用不同的函数返回page
如果是UIO_MEM_LOGICAL的话,说明memory是落在low memory区,通过page = virt_to_page(addr);得到page
如果是UIO_MEM_VIRTUAL的话,说明memory是落在vmalloc去,通过page = vmalloc_to_page(addr);得到page
最后通过get_page(page);增加page计数后将page赋值给vmf->page = page;。

如果UIO_MEM_PHYS的话,则通过uio_mmap_physical->remap_pfn_range 将kernel的memory映射到user space。其中在remap_pfn_range会为vm_area_struct *vma 建立pgd到pte的映射。这里再一次证明了vm_area_struct 表示user space使用的memory

0 0
原创粉丝点击