Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Provide several kernel buffers through mmap

I have a kernel driver which allocates several buffers in kernel space (physically contiguous, aligned to page boundaries, and consisting of integral number of pages). Next, I need to make my driver able to mmap some of these buffers to userspace (one buffer per mmap() call, of course). The driver registers single character device for that purpose. Userspace program must be able to tell kernel which buffer it wants to mmap (for example, by specifying its index or unique ID, or physical address previously resolved through ioctl()).

I want to do so by using mmap()'s offset parameter, for example (from userspace):

mapped_ptr = mmap(NULL, buf_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (MAGIC + buffer_id) * PAGE_SIZE);

Where "MAGIC" is some magic number, and buffer_id is the buffer ID which I want to mmap. Next, in the kernel part there will be something like this:

static int my_dev_mmap(struct file *filp, struct vm_area_struct *vma)
{
  int bufferID = vma->vm_pgoff - MAGIC;
  /* 
   * Convert bufferID to PFN by looking through driver's buffer descriptors
   * Check length = vma->vm_end - vma->vm_start
   * Call remap_pfn_range()
   */
}

But I think it is some sort of dirty way, because "offset" in the mmap() is not supposed to specify index or identifier, its role is to provide number of skipped bytes (or pages) from the beginning of mmap-ed device(or file) memory (which is supposed to be contiguous, right?).

However, i've already seen some drivers in mainline which use "offset" to distinguish between mmap-ed buffers.

Are there any alternative solutions to this?

P.S. I need all this just because I'm dealing with some unusual SoC' graphics controller, which can operate only on physically contiguous, aligned to 8-byte boundary memory buffers. So, I can only allocate such buffers in kernel space and pass them to user space via mmap().

The most part of controller' programming (composing instruction batches and pushing them to kernel driver) is performed in user space. Also, I can't just allocate single big chunk of physically contiguous memory, because in that case it needs to be really big (for ex., 16+ MiB) and alloc_pages_exact() will fail.

like image 429
romavis Avatar asked Sep 08 '12 08:09

romavis


People also ask

What are kernel buffers?

The kernel maintains a buffer cache registry. That is, the kernel maintains a mapping between disk block numbers and pages that contain those disk blocks. Applications control the content of this cache by allocat ing physical pages, reading and writing data into these pages, and registering them in the cache.

Which kernel object is created during the mmap () call?

mmap() creates a new mapping in the virtual address space of the calling process. The starting address for the new mapping is specified in addr.

What is mmap () used for?

The mmap() function establishes a mapping between a process' address space and a stream file. The address space of the process from the address returned to the caller, for a length of len, is mapped onto a stream file starting at offset off.

What is fd in mmap?

fd is the open file descriptor of the file to map. You can assume offset is zero (it's the starting point in the file at which to map). It's OK if processes that map the same MAP_SHARED file do not share physical pages. munmap(addr, length) should remove mmap mappings in the indicated address range.


2 Answers

I don't see anything wrong with using the offset to pass the index in from userspace to your driver. If it bugs you, then just look at your driver as assembling a large buffer out of individual pages that it wants to present to userspace as virtually contiguous, so that the offset really is an offset into this buffer. But really in my opinion there's nothing wrong with doing things this way.

Another alternative, if you can use kernel 3.5 or newer, might be to use the "Contiguous Memory Allocator" (CMA) -- look at <linux/dma-contiguous.h> and drivers/base/dma-contiguous.c for more information. There's also https://lwn.net/Articles/486301/ as a reference but I don't know how much (if anything) changed between that article and getting the code merged into mainline.

like image 170
Roland Avatar answered Oct 08 '22 10:10

Roland


Finally, I've chosen to mmap exactly one buffer per one opened device file descriptor (struct file in kernel) and implement control through ioctl(): one IOCTL for allocating new buffer, one for attaching to already allocated buffer with known ID, and another one to get information about buffer. Usually, userspace will mmap() about 10..20 buffers at the same time, so it is nice and clean solution for this case.

like image 36
romavis Avatar answered Oct 08 '22 10:10

romavis