linux - 如何映射由 get_user_pages_fast 固定的页面以将它们用作驱动程序中的虚拟连续缓冲区?

标签 linux mapping driver

我正在编写一个为用户空间应用程序提供异步通信的 Linux 内核驱动程序。实现工作如下:

  • 应用程序通过ioctl传递接收数据的缓冲区地址
  • 驱动使用get_user_pages_fast确保内核访问输出缓冲区,vmap用于创建虚拟内存区域以访问内核中的缓冲区<
  • 驱动程序在中断中填充接收到的数据
  • 接收到所有数据后,内核删除映射(使用vunmap)并放回用户页面,最后通知应用程序数据可用。

下面是代码的基本部分(从我的驱动程序中提取并简化):

struct page ** pages = NULL;
long pinned = 0;
int i;
void * vbuf = NULL;
void * vresp = NULL;

if (!access_ok(VERIFY_WRITE, buffer, max_data_length)) {
   pr_alert("wrong access");
   res = -EFAULT;
   goto error1;
}
const unsigned long offset = ((unsigned long) buffer) & (PAGE_SIZE-1);
    int nr_pages = DIV_ROUND_UP(offset + max_data_length, PAGE_SIZE);
pages = (struct page **) kzalloc(sizeof(struct page)*nr_pages, GFP_KERNEL);
if(!pages) {
    pr_alert("can't alloc pages");
    res = -EFAULT;
    goto1;
}
pinned = get_user_pages_fast(((unsigned long) buffer ) & PAGE_MASK,nr_pages,1,pages);
if(pinned != nr_pages) {
    for(i=0; i<pinned; i++) {
        put_page(pages[i]);
    }
    kfree(pages);
    pr_alert("can't pin pages");
    res = -EFAULT;
    goto error1;
}
vbuf = vmap(pages,nr_pages,VM_MAP, pgprot_writecombine(PAGE_KERNEL));
vresp = vbuf + offset;
if(!vbuf) {
    pr_alert("can't vmap pages");
    res = -EFAULT;
    goto error1;
}

上述代码执行后,vresp指针用于存放接收到的数据。传输后,页面被释放:

int i;
vunmap(vbuf);
for(i=0; i < nr_pages; i++) {
   set_page_dirty(pages[i]);
   put_page(pages[i]);
}
kfree(pages);

原始代码在一些架构中工作,但在多核 ARM 机器上失败。看起来写入到 vresp 指向的缓冲区的数据在用户空间应用程序的 buffer 中是不可见的。我在代码中添加了控制打印并验证了地址是否正确。使用 vmap 为 get_user_pages_fast 交付的页面创建连续映射是否正确? 也许我应该使用除 VM_MAP 之外的其他标志或除 pgprot_writecombine(PAGE_KERNEL) 之外的其他保护?

最佳答案

今天我找到了答案。事实上,问题与 vmap 函数中的 prot 参数有关。它应该设置为 PAGE_KERNEL 而不是 pgprot_writecombine(PAGE_KERNEL))。 在用户空间应用程序中,此内存是通过缓存访问的,因此如果我创建了通过 pgprot_writecombine 部分禁用缓存的映射,则会导致对内存的访问不一致。 我已将映射行修改为:

vbuf = vmap(pages,nr_pages,VM_MAP,PAGE_KERNEL);

即使在多处理器 ARM 上,代码也能正常工作。

关于linux - 如何映射由 get_user_pages_fast 固定的页面以将它们用作驱动程序中的虚拟连续缓冲区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56908028/

相关文章:

python - Django [Errno 13] 权限被拒绝 : '/var/www/media/animals/user_uploads'

linux - 在 Linux 中替换 bash 中的 XML 值

java - Spring mvc。不区分大小写的获取参数映射

java - 使用带注释的复合主键进行元素映射

windows - 为 32 位 PCI 设备编写 Windows 64 位设备驱动程序

c# - 获取C#中声音设备的名称

linux - 管理 php 守护进程

linux - 在linux上通过vpn路由域000.000.x.x下的所有ip

java - 如何使用 lambda 实现 Java 的反向映射?

node.js - MongoError : not master