I have a device which writes to a video buffer. This buffer is allocated in system memory using CMA and I want to implement streaming write from this buffer to a block device. My application opens video buffer with mmap and I would like to use O_DIRECT
write to avoid page cache related overhead. Basically, the pseudo-code of the application looks like this:
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);
where BLOCK_SIZE is sector aligned value. f_out is opened without any errors, but write results in EFAULT
. I tried to track down this issue and it turned out that mmap implementation in video buffer's driver uses remap_pfn_range()
, which sets VM_IO
and VM_PFNMAP
flags for VMA. The O_DIRECT
path in block device drivers checks these flags and returns EFAULT
. As far as I understand, O_DIRECT
writes need to pin the memory pages, but VMA flags indicate the absence of struct page
for underlying memory which causes an error. Am I right here?
And the main question is how to correctly implement O_DIRECT
write from mmapped buffer? I have video buffer driver and can modify it appropriately.
I found similar question, but these is no clear answer there.
sendfile()
works:off_t offset = 0L; ssize_t byteSent = sendfile( f_out, f_in, &offset, BLOCK_SIZE );
, assuming the video buffer driver doesn't supportlseek()
or otherwise track file offset. (Orssize_t byteSent = sendfile( f_out, f_in, NULL, BLOCK_SIZE );
) If it works, that would avoid themmap()
and the need to pin user-space pages entirely. – Acediasendfile()
and it does not work in my situation. Iff_in
is a regular file than it works as expected, but iff_in
is a video buffer than this function returnsEINVAL
. – Ultimatum