CMA continuous physical memory user space ing --- (1), cma ---
Background:
In multimedia and image processing applications, large blocks of memory are often used, especially hardware codec. The kernel needs to allocate large blocks of physical continuous memory.
We hope to map the continuous physical memory allocated from the kernel to the user space. After processing the user space, we can also join the driver.
Prerequisites:
Configure and adjust the CMA size as needed in Kernel Config.
Method:
(1)
1. The driver registers the misc device;
2. The driver allocates IOCTL memory and uses dma_alloc_writecombine to extract a memory from CMA;
3. The driver implements mmap and maps the physical memory allocated by the dma in the second step to the user space through remap_pfn_range;
(2)
1. the user opens the device node/dev/cma_mem;
2. Use the ioctl command to set the size to be allocated;
3. mmap ing;
Test environment:
Linux-3.9.7
Arm-linux-gcc 4.5.1
S5pv210
Source code:
Driver
Cma_mem.c
#include <linux/miscdevice.h>#include <linux/platform_device.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/mm.h>#include <linux/list.h>#include <linux/mutex.h>#include <linux/debugfs.h>#include <linux/mempolicy.h>#include <linux/sched.h>#include <linux/module.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/cacheflush.h>#include <linux/dma-mapping.h>#include <linux/export.h>#include "cma_mem.h"#define DEVICE_NAME "cma_mem" enum cma_status{UNKNOW_STATUS = 0,HAVE_ALLOCED = 1,HAVE_MMAPED =2,};struct cmamem_dev {unsigned int count;struct miscdevice dev;struct mutex cmamem_lock;struct list_head info_list;};struct current_status{int status;int id_count;dma_addr_t phy_base;};static struct current_status cmamem_status;static struct cmamem_dev cmamem_dev;static struct cmamem_info cma_info[32];static long cmamem_ioctl(struct file *file, unsigned int cmd, unsigned long arg){int ret = 0;int size = 0;dma_addr_t map_dma;switch(cmd){case CMEM_ALLOCATE:{printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE\n");cmamem_status.id_count = cmamem_dev.count++;cma_info[cmamem_status.id_count].id = cmamem_status.id_count;if ((ret = copy_from_user(&cma_info[cmamem_status.id_count], (void __user *)arg,sizeof(struct cmamem_info)))){printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE:copy_from_user error:%d\n", ret);ret = -EFAULT;goto err;}size = cma_info[cmamem_status.id_count].len;size = PAGE_ALIGN(size);if(size == 0){printk(KERN_ERR"size is 0\n");ret = -ENOMEM;goto err;}printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE:start alloc:%d,size:%d\n", cmamem_status.id_count, cma_info[cmamem_status.id_count].len);cma_info[cmamem_status.id_count].mem_base = (unsigned int)dma_alloc_writecombine(NULL, size, &map_dma, GFP_KERNEL);if (!cma_info[cmamem_status.id_count].mem_base){printk(KERN_ERR "dma alloc fail:%d!\n", __LINE__);ret = -ENOMEM;goto err;}printk(KERN_ERR"map_dma:0x%08x,size:%d\n", map_dma, size);cma_info[cmamem_status.id_count].phy_base = map_dma;cmamem_status.phy_base = map_dma;mutex_lock(&cmamem_dev.cmamem_lock);cmamem_status.status = HAVE_ALLOCED;mutex_unlock(&cmamem_dev.cmamem_lock);break;}default:{printk(KERN_INFO "cma mem not support command\n");break;}}err:return ret;}static int cmamem_mmap(struct file *filp, struct vm_area_struct *vma){unsigned long start = vma->vm_start;unsigned long size = vma->vm_end - vma->vm_start;unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;unsigned long page, pos;//dump_stack();if(cmamem_status.status != HAVE_ALLOCED){printk(KERN_ERR"%s, you should allocted memory firstly\n", __func__);return -EINVAL; }printk( "start=0x%08x offset=0x%08x\n", (unsigned int)start, (unsigned int)offset );pos = (unsigned long)cmamem_status.phy_base + offset;page = pos >> PAGE_SHIFT ;if( remap_pfn_range( vma, start, page, size, PAGE_SHARED )) {return -EAGAIN;}else{printk( "remap_pfn_range %u\n success\n", (unsigned int)page );}vma->vm_flags &= ~VM_IO; vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP);return 0;}static struct file_operations dev_fops = { .owner = THIS_MODULE, .unlocked_ioctl = cmamem_ioctl, .mmap = cmamem_mmap,};static int __init cmamem_init(void){printk(KERN_ERR"%s\n", __func__);mutex_init(&cmamem_dev.cmamem_lock);INIT_LIST_HEAD(&cmamem_dev.info_list);cmamem_dev.count = 0;cmamem_dev.dev.name = DEVICE_NAME;cmamem_dev.dev.minor = MISC_DYNAMIC_MINOR;cmamem_dev.dev.fops = &dev_fops;cmamem_status.status = UNKNOW_STATUS;cmamem_status.id_count = -1;cmamem_status.phy_base = 0;return misc_register(&cmamem_dev.dev);}static void __exit cmamem_exit(void) { printk(KERN_ERR"%s\n", __func__);misc_deregister(&cmamem_dev.dev); } module_init(cmamem_init);module_exit(cmamem_exit);MODULE_LICENSE("GPL");
Cma_mem.h
#ifndef _CMA_MEM_H_#define _CMA_MEM_H_#define CMEM_IOCTL_MAGIC 'm'#define CMEM_GET_PHYS_IOW(CMEM_IOCTL_MAGIC, 1, unsigned int)#define CMEM_MAP_IOW(CMEM_IOCTL_MAGIC, 2, unsigned int)#define CMEM_GET_SIZE_IOW(CMEM_IOCTL_MAGIC, 3, unsigned int)#define CMEM_UNMAP_IOW(CMEM_IOCTL_MAGIC, 4, unsigned int)#define CMEM_ALLOCATE_IOW(CMEM_IOCTL_MAGIC, 5, unsigned int)#define CMEM_CONNECT_IOW(CMEM_IOCTL_MAGIC, 6, unsigned int)#define CMEM_GET_TOTAL_SIZE_IOW(CMEM_IOCTL_MAGIC, 7, unsigned int)#define CMEM_CACHE_FLUSH_IOW(CMEM_IOCTL_MAGIC, 8, unsigned int)struct cmamem_info {char *name;char is_cache;unsigned int id;unsigned int offset;unsigned int len;unsigned int phy_base;unsigned int mem_base;//struct list_head list;};#endif
Makefile
KERN_DIR = /work/kernel/linux-3.9.7all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m+= cma_mem.o
User Testing Program
# Include <stdio. h> # include <stdarg. h> # include <string. h> # include <errno. h> # include <stdlib. h> # include <sys/types. h> # include <sys/stat. h> # include <fcntl. h> # include <time. h> # include <sys/mman. h> # include <assert. h> # include <linux/videodev2.h> # include <linux/fb. h> # include <pthread. h> # include <poll. h> # include <semaphore. h> # define CMEM_IOCTL_MAGIC 'M' # define CMEM_GET_PHYS_IOW (CMEM_IOCTL_MAGIC, 1, unsigne D int) # define CMEM_MAP_IOW (CMEM_IOCTL_MAGIC, 2, unsigned int) # define evaluate (CMEM_IOCTL_MAGIC, 3, unsigned int) # define evaluate (CMEM_IOCTL_MAGIC, 4, unsigned int) # define evaluate (Measures, 5, unsigned int) # define CMEM_CONNECT_IOW (CMEM_IOCTL_MAGIC, 6, unsigned int) # define evaluate (CMEM_IOCTL_MAGIC, 7, unsigned int) # define evaluate TL_MAGIC, 8, unsigned int) struct cmamem_info {char * name; char is_cache; unsigned long id; unsigned long offset; unsigned long len; unsigned long phy_base; unsigned long mem_base; // struct list_head list;}; int main () {int cmem_fd; void * cmem_base; unsigned int size; struct cmamem_info region; int I; cmem_fd = open ("/dev/cma_mem", O_RDWR, 0); // open the device. To operate the hardware engine, printf ("cmem_fd: % d \ n ", cmem_fd); if (cmem_fd> = 0) {memset (& region, 0x00, sizeof (struct cmamem_info); region. len = 800*480*4; if (ioctl (cmem_fd, CMEM_ALLOCATE, & region) <0) // obtain all space {perror ("PMEM_GET_TOTAL_SIZE failed \ n "); return-1;} size = region. len; cmem_base = mmap (0, size, PROT_READ | PROT_WRITE, MAP_SHARED, cmem_fd, 0); // mmap operation printf ("cmem_base: 0x % 08x, region. len: 0x % 08x offset: 0x % 08x \ n ", (unsigned int) cmem_base, region. len, region. offs Et); if (cmem_base = MAP_FAILED) {cmem_base = 0; close (cmem_fd); cmem_fd =-1; perror ("mmap pmem error! \ N ") ;}for (I = 0; I <10; I ++) (unsigned int *) cmem_base) [I] = I; printf (" pmem_base: 0x % 08x \ n ", cmem_base); for (I = 0; I <10; I ++) printf (" % d \ n ", (unsigned int *) cmem_base) [I]);} close (cmem_fd); return 0 ;}
If the memory usage is endless, it will be applied to a space larger than the physical memory. Why? (Unix environment)
Let's first check the physical memory of the computer.
We can see that the physical memory of the computer is 1.8 GB.
Next we will run this program
At the beginning, the kernel can also simply use idle physical memory to meet the memory requirements of applications. However, when the physical memory is used up, it began to use the so-called swap space (swap space ). In most UNIX versions, swap space refers to an independent hard disk space. If you are familiar with Microsoft's Windows, you can see similarities between UNIX swap space and Microsoft's Windows swap file. However, unlike Microsoft Windows, there is no local heap or global heap in the UNIX swap space) or other things that can discard the memory segment and other worrying things-the UNIX operating system kernel has replaced the user package.
The kernel is responsible for moving data and program code between the physical memory and the swap space. Therefore, whenever the user reads and writes the memory, data is always waiting in the physical memory, but in fact it is allocated or exchanged before the user is ready to access it.
In more professional terms, UNIX implements a virtual memory system for request pages. All the memory you see in the program is virtual, that is, there is no real memory on the physical address used by the program. UNIX divides all the memory into one page. A page is usually 4096 bytes. Every time a program tries to access the memory, there will be a conversion between the virtual memory and the physical memory. The specific method and time it takes will depend on the specific situation of the hardware used by the user. If the accessed memory is not in the physical memory, a page error (page fault) will be generated, and control will be handed over to the UNIX operating system kernel.
UNIX checks the accessed memory address. If this is a valid address that allows the program to use, it determines the physical memory page to be provided to the program. Then, if the data has never been written, a new memory page will be allocated for it; if the data has been saved to the swap space of the hard disk, read the memory page containing the data back to the physical memory (an existing page may need to be transferred to the hard disk ). Next, map the virtual memory address to the corresponding physical memory, and then let the user program continue to execute. These operations do not need to be performed by the UNIX application itself, because they are hidden in the kernel of the UNIX operating system.
In the end, if the application consumes physical memory and swap space, or if the stack exceeds its maximum length, the kernel of the UNIX operating system will reject the subsequent Memory application.
According to this practice, there is obviously no limit to the memory supply, so does this mean that it makes no sense to check the malloc return status? No. C language programs that use dynamic memory address allocation often encounter such a common problem: they try to write data outside of a allocated memory block. When this happens, the program will not terminate immediately, but it is likely to overwrite some data used inside the malloc function library routine.
After this problem occurs, the common result is that subsequent malloc calls cannot continue, not because there is no memory available for allocation, but because the memory structure is damaged. It is quite difficult to track such problems, and the sooner the problem is located in the program, the more chance it will be to locate the problem.
Problems with using windows API functions to control virtual memory and physical memory
4 GB is the virtual address space of each process. When the program runs, the operating system maps parts to the physical memory in paging mode.
All functions are operated on virtual memory, which is the default 4 GB, even in the kernel mode. Physical memory is invisible to users and cannot be operated.
The memory ing file is similar to the virtual memory, but the difference is that the virtual memory is the system page file, and the memory ing file is the physical disk file. the job is to map a certain segment of a file as the start and end. For example, if the file size is 100 kb, I map it from the beginning to the end, which is 1024*1024 * bytes, then we can reserve an address space in the middle of the virtual memory, for example, 0X12345 (first) ---->? <End>, when necessary, we can submit the disk file to that area,
Then we can operate the files ..dlland .exe in this memory.
Therefore, all functions do not directly operate the physical memory.