当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
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