1

我通过memmap=nn[KMG]$ss[KMG]内核 cmd 行参数保留了几 GB 的内存。我还有一个自定义 char 设备驱动程序,它使用mmap()write()来自file_operations结构并对自定义 PCIe 设备执行 DMA 操作。我使用典型的DMA API创建分散-聚集列表。

在我的驱动程序中,是成功的,我可以整天从用户空间mmap()读/写这个内存。io_remap_pfn_range()我的驱动程序write()函数应该使用 mmap'd 缓冲区来创建分散收集列表。

但是,我的驱动程序的write()功能EFAULT在调用时失败,get_user_pages_fast()我无法解释原因。如果我使用 malloc 缓冲区而不是来自用户空间的 mmap 缓冲区,一切都会按预期工作。 处理 mmap 缓冲区的方式似乎有问题。get_user_pages_fast()解决方法是什么?

例如,

/* From user-space
 * What currently works but has poor performance
 */

int fd;
FILE *fdat;
char *buf;
ssize_t rc;
size_t len = 4*1024*1024*1024; /* 4GB */

fd = open("/dev/mycooldev", O_RDWR|O_SYNC);
rc = posix_memalign(&buf, 4096, len);
fdat = fopen("mydat.bin", "r");
fread(buf, 1, len, fdat);
fclose(fdat);
rc = write(fd, buf, len);
close(fd);
/* From user-space
 * What I want to do, i.e. use the many GB of contiguous reserved memory
 */

int fd;
FILE *fdat;
char *buf;
ssize_t rc;
size_t len = 4*1024*1024*1024; /* 4GB */

fd = open("/dev/mycooldev", O_RDWR|O_SYNC);
buf = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
fdat = fopen("mydat.bin", "r");
fread(buf, 1, len, fdat);
fclose(fdat);
rc = write(fd, buf, len);
close(fd);

write()我的设备驱动程序中的相关功能:

/* From the char device driver
 * buf is a pointer to the buffer returned from either 
 * posix_memalign() or mmap(), but mmap() doesn't work
 * because of get_user_pages_fast()
 */

ssize_t mycooldev_write(struct file *file, char __user *buf, size_t len, loff_t *pos)
{
    int rc;
    struct page **pages;
    size_t npages;
    struct sg_table sgt;
    unsigned long uaddr, start, end;

    ...

    /* EDIT: I added this call per a request in comments */
    if (access_ok(VERIFY_WRITE, buf, len) == 0) {
        pr_err("Not allowed to access user address\n");
        return -EFAULT;
    }

    uaddr = (unsigned long)buf;
    start = uaddr & PAGE_MASK_64; /* (0xFFFFFFFF00000000 | PAGE_MASK) */
    end = uaddr + len + PAGE_SIZE - 1;
    npages = (end - start) >> PAGE_SHIFT;

    pages = kcalloc(npages, sizeof(struct page *), GFP_KERNEL);

    /* This call fails with EFAULT when user passes in a buffer
     * obtained with a call to mmap(). If user calls posix_memalign()
     * to obtain their buffer, get_user_pages_fast() is successful.
     */
    rc = get_user_pages_fast(uaddr, npages, 1, pages);

    rc = sg_alloc_table_from_pages(&sgt, pages, npages, pos, len, GFP_KERNEL);

    ...
}

为什么不get_user_pages_fast()喜欢我的 mmap 缓冲区,但对 malloc 的缓冲区很好?任何帮助是极大的赞赏。

4

0 回答 0