【问题标题】:mapping memory reserved by mmap kernel boot param into user space将 mmap 内核引导参数保留的内存映射到用户空间
【发布时间】:2012-09-29 05:09:16
【问题描述】:

正如this 问题中所讨论的,我正在使用内核引导参数memmap=8G$64G 在引导时保留一个内存块

我编写了一个character driver 内核模块,在初始化期间会对该保留的内存块进行 ioremap。 正如here 解释的那样,在我的驱动程序mmap 中,我需要做的就是remap_pfn_range 用于ioremap 返回的这个内存块指针。

我在 3.0 linux 内核上运行它。我的用户空间应用程序将此内存块作为驱动程序安装的设备打开。当我从使用空间应用程序中执行 mmap 时,我看到 系统挂起。 我的 dmesg 没有提供太多信息。

任何输入?

static int __init myDev_module_init(void)
{
   int retval;

   myDev_major = register_chrdev(0, DEVICE_NAME, &myDevfops);
   if (myDev_major < 0) 
   {
       err("failed to register device: error %d\n", myDev_major);
       retval = myDev_major;
       goto FAILED_CHRDEVREG;
   }

   myDev_class = class_create(THIS_MODULE, CLASS_NAME);
   if (IS_ERR(myDev_class)) 
   {   
       err("failed to register device class '%s'\n", CLASS_NAME);
       retval = PTR_ERR(myDev_class);
       goto FAILED_CLASSREG;
   }


   myDev_device = device_create(myDev_class, NULL, MKDEV(myDev_major, 0), NULL, CLASS_NAME "_" DEVICE_NAME);
   if (IS_ERR(myDev_device)) 
   {
       err("failed to create device '%s_%s'\n", CLASS_NAME, DEVICE_NAME);
       retval = PTR_ERR(myDev_device);
       goto FAILED_DEVREG;
   }

这里myDev.startOffset被初始化为#defined 64GB,myDev.memSize被初始化为8GB。

 //myDev.startAddr = ioremap(myDev.startOffset,myDev.memSize);

 //memset_io(myDev.startAddr, 0, myDev.memSize);  
 return 0;

  FAILED_DEVREG:
   class_unregister(myDev_class);
   class_destroy(myDev_class);
  FAILED_CLASSREG:
   unregister_chrdev(myDev_major, DEVICE_NAME);
  FAILED_CHRDEVREG:
   return -1;
}

static int myDev_device_open(struct inode* inode, struct file* filp)
{
    dbg("");

    if ( ((filp->f_flags & O_ACCMODE) == O_WRONLY) || ((filp->f_flags & O_ACCMODE) == O_RDWR) ) 
    {
        warn(" Opening the device with write access\n");
        //return -EACCES;
    }

    info(" device Open is called\n");
    filp->private_data = &myDev;
    return 0;
 }

mmap 非常简单。

static int myDev_device_mmap(struct file * f, struct vm_area_struct * vma)
{
 int retval = 0;
struct myDevDev * pDev = (struct myDevDev *)(f->private_data);

dbg("");
if(vma)
{
    if(f)
    {
        if(f->private_data)
            warn("mmap: f->private_data  : %p\n", f->private_data);
        else
            warn(" mmap :f->private_data  : NULL \n");
    }
    else
    {
        warn("mmap: f  :NULL\n");
    }
    warn(": mmap: vm start : %lu\n", vma->vm_start);
    warn(" mmap: vm end  : %lu\n", vma->vm_end);
    warn(" mmap: vm pg offset  : %lu\n", vma->vm_pgoff);


    
    //retval = remap_pfn_range(vma, vma->vm_start, pDev->startOffset >> PAGE_SHIFT,    pDev->memSize, PAGE_SHARED) ;
    // retval = remap_pfn_range(vma, vma->vm_start, pDev->startAddr >> PAGE_SHIFT,    pDev->memSize, PAGE_SHARED) ;
    //retval = remap_pfn_range(vma,pDev->startAddr ,pDev->startOffset >> PAGE_SHIFT,  pDev->memSize, PAGE_SHARED);
    retval = remap_pfn_range(vma,vma->vm_start ,pDev->startOffset >> PAGE_SHIFT,  pDev->memSize, PAGE_SHARED);
    if(retval <0)
    {
        warn(" ERROR : in mapping kernel virtual space to user space return value : %d \n",retval);
        return -EINVAL;
    }
    
    //if(0)
    {
        vma->vm_flags |=VM_LOCKED;
        vma->vm_ops = &myRemapVMOps;
        vma->vm_flags |= VM_RESERVED;

        vma->vm_private_data = f->private_data;
        myDevice_VMA_Open(vma);
    }
}
else
{
    warn ("vma is NULL");
}

dbg(" Done ");
warn("mmpaing done : \n");

return 0;
}

从我的用户空间应用程序中,我正在执行以下操作:

int err, i=0;
void * mptr = NULL;
printf(" Access the reserved memory chunk \n  ");
int fd = open("/dev/myDevice", O_RDWR | O_SYNC);

if(fd <=0)
{
    printf("ERROR: my device driver is not loaded \n");
    return 1;
}

  printf("\n mmaping mem chunk size :%llu pagesize :%lu input mptr :%p\n", sz,getpagesize (), mptr);

  mptr = mmap(0, sz , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0);

if(mptr == MAP_FAILED) 
{
    close(fd);
    perror("Error mmapping the file");
    printf("\nmmapped mem address %p\n",mptr);
    exit(1);
}
printf("\nmmapped mem address %p\n",mptr);

//char * ptr = (char *)mptr;
//*ptr = 'a';

//int * pInt =  (int *) (((char *) mptr)+1); 
//for(;i<10000; ++i)
{
  //  pInt[i] = 2*i;
}

 /* free the mmapped memory
 */
if (munmap(mptr, sz) == -1) 
{
    perror("Error un-mmapping the file");
}

close(fd);

观察:

我没有看到 (vma-&gt;vm_end - vma-&gt;vm_start) 中反映的大小,出于某种原因,它始终为 4K。

【问题讨论】:

  • 通常在调用 ioremap() 之前调用 request_mem_region() 来声明/保留物理地址空间块。 /proc/iomem 显示什么?如果“系统挂起”(而不仅仅是用户应用程序),那么问题就出在内核中!
  • /proc/iomem 显示要保留的块 1000000000-1280000000 : reserved
  • numactl --hardware 已经告诉我内存块没有反映在 numa 节点中,因此它对内核空间不可见。
  • 不确定我们是否还需要做request_mem_region()
  • /proc/vmallocinfo 显示了ioremapped 内存块,尽管!我在设备驱动程序init 中打印了ioremap 返回的地址,它与vmallocinfo 条目匹配!

标签: linux-kernel kernel linux-device-driver embedded-linux kernel-module


【解决方案1】:
  1. 您可以逐步使用标准 phram 驱动程序从用户空间访问您的内存。不需要编码。最多重新编译内核。

  2. 您真的有超过 64Gb 的 RAM 吗?你的硬件真的支持吗?

【讨论】:

  • 我们支持超过 128GB 的​​ RAM
  • 嗯。它很感兴趣。您使用哪种架构? AFAIK Linux 通常不期望超过 64GB 内存。你解决了这个问题吗?
【解决方案2】:

当我从使用空间应用程序执行 mmap 时,我看到系统挂起。

在您可以使用 remap_pfn_range() API 之前,请参阅内嵌在 mm/memory.c(内核源代码)中的文档,它表明您必须先持有 mm 信号量。

/**
 * remap_pfn_range - remap kernel memory to userspace
 * @vma: user vma to map to
 * @addr: target user address to start at
 * @pfn: physical address of kernel memory
 * @size: size of map area
 * @prot: page protection flags for this mapping
 *
 *  Note: this is only safe if the mm semaphore is held when called.
 */

如何获取信号量是通过 up_read() 和 down_read() API - 只需在 mm 目录中对 up_read 进行“grep”即可,示例很多:

mempolicy.c:    up_read(&mm->mmap_sem);
migrate.c:  up_read(&mm->mmap_sem);
mincore.c:      up_read(&current->mm->mmap_sem);
mmap.c:     up_read(&mm->mmap_sem);
msync.c:            up_read(&mm->mmap_sem);
nommu.c:    up_read(&mm->mmap_sem);

【讨论】:

  • 如果您写入该区域,您可能需要调用 up_write/down_write。
猜你喜欢
  • 2016-03-10
  • 2017-06-28
  • 2012-02-05
  • 1970-01-01
  • 2014-06-10
  • 2017-01-12
  • 2012-11-15
  • 2011-05-02
  • 2014-01-14
相关资源
最近更新 更多