【问题标题】:MMAP fails above 4k sizeMMAP 超过 4k 大小失败
【发布时间】:2013-01-11 20:44:14
【问题描述】:

这是我的第一篇文章,如果有任何错误,请告诉我。

我的目标是让大约 150MB 的数据从 KERNEL 传输到用户空间。 [这是因为我正在 OMAP l138 上为 DMA 设备构建驱动程序,以在 DMA DEVICE 和 FPGA 之间传输和接收数据]

  1. 现在在 LINUX 内核中,我使用 dma_alloc_coherent 分配大小可变的缓冲区
  2. 然后我将此缓冲区的物理地址传递给用户空间以成为用户 用于从用户空间调用 mmap 的 OFFSET 参数。
  3. 然后从用户空间向内核复制和读回数据

这个逻辑可以正常工作,直到缓冲区大小为 4096。超过 4k 时,mmap 失败并返回“MAP_FAILED”

static int driver_mmap(struct file *f, struct vm_area_struct *vma)
{

    u32bit ret;
    u32bit size = (vma->vm_end)-(vma->vm_start);

    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

    if (size > (NUM_PAGE*PAGE_SIZE)){
        return(-1);  
    }

    if ((ret = remap_pfn_range(vma,vma->vm_start,
                    (virt_to_phys((void *)krnl_area) >> PAGE_SHIFT),
                    size,vma->vm_page_prot)) < 0)
    {
        return ret;  
    }
    printk("\nDVR:The MMAP returned %x to USER SAPCE \n",ret);
    return 0;  

}


//MMAP STEP 1
dmasrc_ptr = dma_alloc_coherent( NULL ,GLOBAL_BUFFER_SIZE , &dmasrc ,0);
if( !dmasrc_ptr ) {
    printk(KERN_INFO "DMA_ALLOC_FAILED for the source buffer ...\n");
    return -ENOMEM;
}else{
    printk( "\n--->The address of SRC is %x..\n",dmasrc_ptr);       
}

temp_source=dmasrc_ptr;



//MMAP STEP 2
// Round the allocated KERNEL MEMORY to the page bondary   
krnl_area=(int *)((((unsigned long)dmasrc_ptr) + PAGE_SIZE - 1)&PAGE_MASK); 
printk(KERN_CRIT "DVR:The KERNEL VIRTUAL ADDRS is %x..\n",krnl_area);

//MMAP STEP 3
// Marking the PAGES as RESERVED 
for (i = 0; i < (NUM_PAGE * PAGE_SIZE); i+= PAGE_SIZE) {  
    SetPageReserved(virt_to_page(((unsigned long)krnl_area) + i)); 


//Application code part

while(1){

    fflush(stdin);
    fflush(stdout);

    printf("\n\n\n----------------------------------------------------\n");
    printf("USR:Please enter your requirement ");
    printf("\n----------------------------------------------------\n");
    printf("\t1----->GET_UPP_OFFSET\n");
    printf("\t2----->UPP_MMAP_CALL\n");
    printf("\t3----->IOCTL_UPP_WRITE\n");
    printf("\t4----->IOCTL_UPP_READ\n");

    printf("\n");
    scanf("%d",&option);
    printf("\nThe OPTION is %d..\n",option);
    printf("\n");

    switch(option){

    case 1 :
    {
        offset=0;
        ret = ioctl(dev_FD ,IOCTL_UPP_START, &info);
        if (ret < 0) {
            printf("dma buffer info ioctl failed\n");
        }       
        offset = info.var;      
        printf("THE ADDRESS WE GOT IS %X..\n",offset);

    }
    break;

    case 2 :
    {
        printf("THE OFFSET is %X..\n",offset);
        mmap_Ptr= mmap(0,BUFFER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dev_FD, 0);     
        if (mmap_Ptr == MAP_FAILED){
            printf("USR[UPP] :MMAP FAiled \n\n");
            close(dev_FD);
            exit(-1);   
        }
        printf("THE MMAP address is %X..\n",mmap_Ptr);

    }
    break;

    case 3:
    {
        struct upp_struct user_local_struct;

        printf("\n***************************************************\n");
        for (i = 0; i <(1024);i++) {  
            *(mmap_Ptr+i)=test_var;
            printf("WR:%X ",*(mmap_Ptr+i));
            //test_var++; 
        } 
        ioctl(dev_FD , IOCTL_UPP_WRITE ,&user_local_struct);    

        printf("\n***************************************************\n\n\n");
        for(i=0;i<20402;i++){
            //NOP
        }

        //test_var=0x00;
    }
    break;

    case 4:
    {
        struct upp_struct user_local_struct;
        ioctl(dev_FD , IOCTL_UPP_READ,&user_local_struct);

        for(i=0;i<20402;i++){
            //NOP
        }
        printf("\n***************************************************\n");
        for (i = 0; i <(1024);i++) {  
            printf("RD:%X",*(mmap_Ptr+i));  
        }
        printf("\n***************************************************\n\n\n");  
    }
    break;

    default:
    { 
        printf("USR:You have entered an wrong option \n");
        printf("\nUSR:CLosing the FILE ENTERIES ...\n");
        munmap(mmap_Ptr,BUFFER_SIZE);
        free(source_ptr);
        free(dest_ptr);
        close(dev_FD);
        exit(0);
    }
    break;

} //END OF SWITCH LOOP  

} //END OF WHILE LOOP

【问题讨论】:

  • 你熟悉检查errno的做法吗?
  • NUM_PAGE 1 吗?你到达 driver_mmap 了吗?
  • Hasturkun:是的。实际上,最初我已经为 KMALLOC 构建了基于 PAGESIZE 逻辑的 MMAP 逻辑。然后我修改了 dma_alloc_coherent 的逻辑,其中涉及提及 SIZE 。但是这条线意味着 KMALLOC 导致错误,因为它曾经返回 (-1) 的任何超过 4k 的缓冲区大小。
  • 如果我没记错的话,对于大块内存,你需要使用vmalloc

标签: linux mmap


【解决方案1】:

使用 get_free_pages 分配多个页面,或使用 vmalloc,但您需要在每个页面的基础上调用 remap_pfn_range,因为 vmalloc 的物理内存可能在物理上不是连续的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-20
    • 2011-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多