我通过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 的缓冲区很好?任何帮助是极大的赞赏。