我有一个可以写入视频缓冲区的设备。该缓冲区是使用 CMA 在系统内存中分配的,我想实现从该缓冲区到块设备的流式写入。我的应用程序使用 mmap 打开视频缓冲区,我想使用
O_DIRECT
写入来避免与页面缓存相关的开销。基本上,应用程序的伪代码如下所示:
f_in = open("/dev/videobuf", O_RDONLY);
f_mmap = mmap(0, BUFFER_SIZE, PROT_READ, MAP_SHARED, f_in, 0);
f_out = open("/dev/sda", O_WRONLY | O_DIRECT);
write(f_out, f_mmap, BLOCK_SIZE);
其中 BLOCK_SIZE 是扇区对齐值。 f_out 打开没有任何错误,但将结果写入
EFAULT
。我试图找出这个问题,结果发现视频缓冲区驱动程序中的 mmap 实现使用 remap_pfn_range()
,它为 VMA 设置 VM_IO
和 VM_PFNMAP
标志。块设备驱动程序中的 O_DIRECT
路径检查这些标志并返回 EFAULT
。据我了解,O_DIRECT
写入需要固定内存页面,但VMA标志表明底层内存缺少struct page
,这会导致错误。我在这儿吗?
主要问题是如何正确实现
O_DIRECT
从映射缓冲区写入?我有视频缓冲区驱动程序,可以适当修改它。
我发现了类似的问题,但这些没有明确的答案。
函数
remap_pfn_range
通过使用special
将您的虚拟内存区域指定为pte_mkspecial
,并将VM_IO/VM_PFNMAP
包含在vma中。因此,它无法通过 Direct I/O
的特定验证。
由于您的记忆来自
CMA
,它已经支持struct page
,您可以通过以下步骤使用vm_insert_pages
:
CMA
区域 (DTS
)。dma_page = dma_alloc_contiguous(&pdev->dev, size, GFP_KERNEL);
if (!dma_page) {
pr_err("%s %d, dma_alloc_contiguous fail\n", __func__, __LINE__);
return -ENOMEM;
}
nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
pages = kvmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);
for (i = 0; i < nr_pages; i++)
pages[i] = &dma_page[i];
mmap
:int your_mmap(struct file *file, struct vm_area_struct *vma) {
int ret = 0;
unsigned long temp_nr_pages;
if (vma->vm_end - vma->vm_start > size)
return -EINVAL;
/* Duplicate nr_pages as vm_insert_pages can change nr_pages */
temp_nr_pages = nr_pages;
ret = vm_insert_pages(vma, vma->vm_start, pages, &temp_nr_pages);
if (ret < 0)
pr_err("%s vm_insert_pages fail, error is %d\n", __func__, ret);
return ret;
}
dma_alloc_contiguous
(唯一的内存管理代码更改,但可以接受)。modified kernel/dma/contiguous.c
@@ -332,6 +332,7 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
return cma_alloc_aligned(dma_contiguous_default_area, size, gfp); }
+EXPORT_SYMBOL(dma_alloc_contiguous);
/**
* dma_free_contiguous() - release allocated pages