当前位置:网站首页>DRM driven MMAP detailed explanation: (I) preliminary knowledge

DRM driven MMAP detailed explanation: (I) preliminary knowledge

2022-06-12 17:56:00 _ kerneler

!!! Statement !!!
This article is transferred from : Hexiaolong
link :https://blog.csdn.net/hexiaolong2009/article/details/87392266
Reprint just to learn to back up .

video : Three mmap Driver implementation method

Preface

In the last article 《DRM GEM Driver development (dumb)》 We learned how to write the simplest DRM GEM The driver . The driver only provides dumb buffer The ability to operate , Allow applications to dumb buffer Conduct create and mmap operation .

dumb buffer Simple operation , But in drm Drive directory , The implementation of various manufacturers is diverse . because dumb buffer Only through mmap To visit , So this series of articles will take you to learn more DRM Medium mmap Drive development .

In the official explanation DRM mmap Before , I think it is necessary to let you know a common mmap How drivers should be written . This article is not intended to explain mmap The principle of system call and its related details , Because it involves linux Many concepts of memory management , You just need to know how to write a simple mmap The driver is OK , For subsequent drm mmap Prepare for writing the driver . If you read this article , Want to learn more mmap Knowledge of system calls , I recommend you to read pengdonglin's blog 《 Memory mapping functions remap_pfn_range Study 》 And Hu Xiao's blog 《 Careful analysis mmap: What is it? Why? How to use it? 》.

Text

stay kernel Driving medium , Realization mmap System calls are inseparable from two key steps :(1) Memory allocation (2) Establish a mapping relationship . This just corresponds to DRM Medium dumb_create and mmap operation .

Let's start with mapping , stay linux There are mainly two methods to establish mapping relationship in driver :

  • One time mapping —— stay mmap In the callback function , Establish the mapping relationship of the whole block of memory at one time , Usually, the remap_pfn_range() As a representative .
  • Page Fault —— mmap Don't create a mapping relationship first , When the upper layer triggers a page missing exception , stay fault Establish a mapping relationship in the interrupt handling function , Which one is missing, which one is missing , Usually, the vm_insert_page() As a representative .

The timing of memory allocation will also affect the design of the driver , It can be roughly divided into the following three types :

  • stay mmap Assign before system call
  • stay mmap Allocated during system call
  • stay fault Allocated in the interrupt handler

So different allocation timing + Different mapping mechanisms , You'll get a different answer mmap The implementation strategy of . That's why DRM Driving medium , Each family's dumb_create and mmap The reason why the implementation code is very different .

The following is a sample code to show you several typical mmap Driver implementation mode .

Example 1

mmap Before the assignment + One time mapping :

 Insert picture description here

describe :

  1. The driver shall be allocated before initialization 3 individual PAGE.
  2. The upper layer executes mmap When the system is called , In the underlying mmap The callback function passes remap_pfn_range() Set up all the mapping relationships at once , And return the mapped starting virtual address to the application .
  3. The application uses the returned virtual address for memory read and write operations .

Driver code :

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>

static void *kaddr;

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
    
	return remap_pfn_range(vma, vma->vm_start,
				(virt_to_phys(kaddr) >> PAGE_SHIFT) + vma->vm_pgoff,
				vma->vm_end - vma->vm_start, vma->vm_page_prot);
}

static struct file_operations my_fops = {
    
	.owner	= THIS_MODULE,
	.mmap	= my_mmap,
};
 
static struct miscdevice mdev = {
    
	.minor = MISC_DYNAMIC_MINOR,
	.name = "my_dev",
	.fops = &my_fops,
};
 
static int __init my_init(void)
{
    
	kaddr = kzalloc(PAGE_SIZE * 3, GFP_KERNEL);
	return misc_register(&mdev);
}
module_init(my_init);

Example 2

mmap Before the assignment + Page Fault:

 Insert picture description here

describe :

  1. The driver is pre allocated during initialization 3 individual PAGE.
  2. The upper layer executes mmap system call , The underlying drivers are mmap No mapping relationship is established in the callback function , It will be implemented locally vm_ops Attached to the process vma->vm_ops On the pointer , Then the function returns .
  3. The upper layer obtains an unmapped process address space , And read and write the memory , This causes a page missing exception to be triggered . The page missing exception will eventually call the previously attached vm_ops->fault() Callback interface , In this connection and adjustment, pass vm_insert_page() Establish the mapping relationship between physical memory and user address space .
  4. After the exception returns , The application can then resume the previously interrupted read / write operation .

Be careful : This happens every time Page Fault Only one interrupt can be mapped Page.

Driver code :

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>

static void *kaddr;

static int my_fault(struct vm_fault *vmf)
{
    
	struct vm_area_struct *vma = vmf->vma;
	int offset, ret;

	offset = vmf->pgoff * PAGE_SIZE;
	ret = vm_insert_page(vma, vmf->address, virt_to_page(kaddr + offset));
	if (ret)
		return VM_FAULT_SIGBUS;

	return VM_FAULT_NOPAGE;
}

static const struct vm_operations_struct vm_ops = {
    
	.fault = my_fault,
};

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
    
	vma->vm_flags |= VM_MIXEDMAP;
	vma->vm_ops = &vm_ops;
	return 0;
}

static struct file_operations my_fops = {
    
	.owner	= THIS_MODULE,
	.mmap	= my_mmap,
};
 
static struct miscdevice mdev = {
    
	.minor = MISC_DYNAMIC_MINOR,
	.name = "my_dev",
	.fops = &my_fops,
};
 
static int __init my_init(void)
{
    
	kaddr = kzalloc(PAGE_SIZE * 3, GFP_KERNEL);
	return misc_register(&mdev);
}
module_init(my_init);

DRM Typical driving representatives :tegraudl

Example 3

Page Fault The distribution of + mapping :

 Insert picture description here

describe :
The mapping process is exactly the same as in example 2 , The timing of memory allocation is just page fault In the interrupt handler .

Driver code :

Here to simplify the code , Only one... Is allocated in total page, Multiple page It can be done by vmf->pgoff To distinguish .

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>

static struct page *page;

static int my_fault(struct vm_fault *vmf)
{
    
	struct vm_area_struct *vma = vmf->vma;
	int ret;

	if (!page)
		page = alloc_page(GFP_KERNEL);

	ret = vm_insert_page(vma, vmf->address, page);
	if (ret)
		return VM_FAULT_SIGBUS;

	return VM_FAULT_NOPAGE;
}

static const struct vm_operations_struct vm_ops = {
    
	.fault = my_fault,
};

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
    
	vma->vm_flags |= VM_MIXEDMAP;
	vma->vm_ops = &vm_ops;
	return 0;
}

static struct file_operations my_fops = {
    
	.owner	= THIS_MODULE,
	.mmap	= my_mmap,
};
 
static struct miscdevice mdev = {
    
	.minor = MISC_DYNAMIC_MINOR,
	.name = "my_dev",
	.fops = &my_fops,
};
 
static int __init my_init(void)
{
    
	return misc_register(&mdev);
}
module_init(my_init);

DRM Typical driving representatives :vkmsvgem

Example 4

Same as example 2 (mmap Before the assignment + Page Fault mapping ), The difference lies in fault Interrupt handling function , No longer use vm_insert_page() To map , Instead, the physics page Return to vmf->page The pointer .

Driver code :

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>

static void *kaddr;

static int my_fault(struct vm_fault *vmf)
{
    
	vmf->page = virt_to_page(kaddr + vmf->pgoff * PAGE_SIZE);
	get_page(vmf->page);
	return 0;
}

static const struct vm_operations_struct vm_ops = {
    
	.fault = my_fault,
};

static int my_mmap(struct file *file, struct vm_area_struct *vma)
{
    
	vma->vm_flags |= VM_MIXEDMAP;
	vma->vm_ops = &vm_ops;
	return 0;
}

static struct file_operations my_fops = {
    
	.owner	= THIS_MODULE,
	.mmap	= my_mmap,
};
 
static struct miscdevice mdev = {
    
	.minor = MISC_DYNAMIC_MINOR,
	.name = "my_dev",
	.fops = &my_fops,
};
 
static int __init my_init(void)
{
    
	kaddr = kzalloc(PAGE_SIZE * 3, GFP_KERNEL);
	return misc_register(&mdev);
}
module_init(my_init);

Be careful : Use vmf->page Mode mapping ,fault The return value of the interrupt function should be 0. While using vm_insert_page() Mode mapping , The return value should be VM_FAULT_NOPAGE.

DRM Typical driving representatives :vkmsvgem

Example 5

mmap The distribution of + Page Fault mapping : The code is actually the same as example 3 (Page Fault The distribution of + mapping ) Little difference , It's just mmap Memory allocation in callback , I'm not going to do that here .

DRM Typical driving representatives :shmem(linux 4.19)

The test program

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    
	int fd;

	fd = open("/dev/my_dev", O_RDWR);

	char *str = mmap(NULL, 4096 * 3, PROT_WRITE, MAP_SHARED, fd, 0);
	strcpy(str, "hello world\n");
	munmap(str, 4096 * 3);

	str = mmap(NULL, 4096 * 3, PROT_READ, MAP_SHARED, fd, 0);
	printf("%s\n", str);
	munmap(str, 4096 * 3);

	close(fd);

	return 0;
}

describe :
Execute successively 2 Time mmap/munmap operation , The first is a write operation , The second is a read operation .

Running results :

# ./a.out
hello world

Conclusion

No matter what DRM Drive development , Or other device driver development , The above examples are worth referring to . I hope that through this article , It can make beginners understand mmap The implementation of driver has a general understanding , In this way, we will follow up DRM Only in the driving explanation can we know well .

Source download

Github: sample-code/mmap
Test platform :QEMU vexpress-a9

Reference material

  1. 《 Memory mapping functions remap_pfn_range Study 》
  2. 《 Careful analysis mmap: What is it? Why? How to use it? 》
  3. LWN: fault()

Extended reading :《dma-buf from the shallower to the deeper ( Four ) —— mmap》
A summary of the article :DRM (Direct Rendering Manager) Learn about

原网站

版权声明
本文为[_ kerneler]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/163/202206121707444358.html