Abstract:
User space ing relative to the CMA continuous physical memory of the previous test program --- (1)
Added features:
1. Allocation and ing are centrally placed in ioctl, which can be allocated and mapped to user space multiple times in a row to improve operability;
2. The drive adds a linked list, so that the allocated multiple blocks are managed in the linked list to facilitate addition and deletion;
3. increase memory release and un ing;
4. When you use rmmod to delete the driver module, all memory will be released;
Ing process:
1. the user allocates the size via IOCTL to the driver IOCTL --------------------------------------->
2. The driver uses dma_alloc_writecombine or dma_alloc_coherent based on whether the user uses writebuffer, and the physical memory ------------------------>
3. Use vm_mmap to find a free space in the user space for ing -------------------------------->
Vm_mmap can be used in kernel Versions later than linux3.7, and sys_mmap can be used in the old kernel.
Refer to call stack of MMAP
[ 409.762850] [<c00184c4>] (unwind_backtrace+0x0/0xf8) from [<bf000020>] (cmamem_mmap+0x20/0xd0 [cma_mem])[ 409.774141] [<bf000020>] (cmamem_mmap+0x20/0xd0 [cma_mem]) from [<c0095ab8>] (mmap_region+0x310/0x540)[ 409.774771] [<c0095ab8>] (mmap_region+0x310/0x540) from [<c0095f80>] (do_mmap_pgoff+0x298/0x330)[ 409.784230] [<c0095f80>] (do_mmap_pgoff+0x298/0x330) from [<c00886d0>] (vm_mmap_pgoff+0x64/0x94)[ 409.792291] [<c00886d0>] (vm_mmap_pgoff+0x64/0x94) from [<c00947a8>] (sys_mmap_pgoff+0x54/0xa8)[ 409.800962] [<c00947a8>] (sys_mmap_pgoff+0x54/0xa8) from [<c0013940>] (ret_fast_syscall+0x0/0x30)
4. vm_mmap will call the MMAP interface function in the driver.
Map physical memory to user space through remap_pfn_range in MMAP .;
5. The ing user space, kernel space, virtual kernel, and physical memory are stored in the linked list;
6. During the delete operation, query the linked list, unmap, release the memory, and remove from the linked list;
7. All memory is released when the driver module is released;
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 <linux/syscalls.h>#include <linux/mman.h>#include "cma_mem.h"#define DEVICE_NAME "cma_mem" #define MEM_DEBUG 1enum 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 cmamem_block {char name[10];char is_use_buffer;char is_free;int id;unsigned long offset;unsigned long len;unsigned long phy_base;unsigned long mem_base;void *kernel_base;struct list_head memqueue_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_block *cmamem_block_head;static int mem_block_count = 0;static void dump_mem(struct cmamem_block *memory_block){printk("%s:CMA name:%s\n",__func__, memory_block->name);printk("%s:CMA id:%d\n",__func__, memory_block->id);printk("%s:Is usebuf:%d\n",__func__, memory_block->is_use_buffer);printk("%s:PHY Base:0x%08lx\n",__func__, memory_block->phy_base);printk("%s:KER Base:0x%08x\n",__func__, (unsigned int)(memory_block->kernel_base));printk("%s:USR Base:0x%08lx\n",__func__, memory_block->mem_base);}static long cmamem_alloc(struct file *file, unsigned long arg){struct cmamem_block *memory_block;struct mem_block cma_info_temp;int size;int ret;if ((ret = copy_from_user(&cma_info_temp, (void __user *)arg,sizeof(struct mem_block)))){printk(KERN_ERR"cmamem_alloc:copy_from_user error:%d\n", ret);return -1;}if(cma_info_temp.name[0] == '\0'){printk(KERN_ERR "%s, no set mem name, please set\n", __func__);return -1;}if(cma_info_temp.len){size = PAGE_ALIGN(cma_info_temp.len);cma_info_temp.len = size;#ifdefMEM_DEBUG//printk(KERN_INFO "%s len:%ld, is_use_buffer:%d\n", __func__, cma_info_temp.len, cma_info_temp.is_use_buffer);#endifif(cma_info_temp.is_use_buffer)cma_info_temp.kernel_base = dma_alloc_writecombine(NULL, size, (dma_addr_t *)(&(cma_info_temp.phy_base)), GFP_KERNEL);elsecma_info_temp.kernel_base = dma_alloc_coherent(NULL, size, (dma_addr_t *)(&(cma_info_temp.phy_base)), GFP_KERNEL);if (!cma_info_temp.phy_base){printk(KERN_ERR "dma alloc fail:%d!\n", __LINE__);return -ENOMEM;}cma_info_temp.id = ++mem_block_count;cmamem_status.phy_base = cma_info_temp.phy_base;cmamem_status.id_count = cma_info_temp.id;cmamem_status.status = HAVE_ALLOCED;cma_info_temp.mem_base = vm_mmap(file, 0, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0);if(cma_info_temp.mem_base < 0){printk(KERN_ERR "do_mmap fail:%d!\n", __LINE__);cma_info_temp.id = --mem_block_count;return -ENOMEM;}printk(KERN_INFO "cma_info_temp.mem_base:0x%lx\n", cma_info_temp.mem_base);//mem_block_count ++;}else{printk(KERN_ERR"cmamem_alloc: the len is NULL\n");return -1;}if(copy_to_user((void __user *)arg, (void *)(&cma_info_temp), sizeof(struct mem_block)))return -EFAULT;/* setup the memory block */memory_block = (struct cmamem_block *)kmalloc(sizeof(struct cmamem_block), GFP_KERNEL);if(memory_block == NULL){printk(KERN_ERR "%s error line:%d\n", __func__, __LINE__);mem_block_count --;return -1;}if(cma_info_temp.name[0] != '\0')memcpy(memory_block->name, cma_info_temp.name, 10);memory_block->id=cma_info_temp.id;memory_block->is_free=0;memory_block->is_use_buffer=cma_info_temp.is_use_buffer;memory_block->mem_base =cma_info_temp.mem_base;memory_block->kernel_base =cma_info_temp.kernel_base;memory_block->phy_base =cma_info_temp.phy_base;memory_block->len=cma_info_temp.len;#ifdefMEM_DEBUGdump_mem(memory_block);#endif#ifdef CMA_TESTint i;for(i = 0; i < 10; i++)((char *)(cma_info_temp.kernel_base))[i] = (cma_info_temp.id * i);#endif/* add to memory block queue */list_add_tail(&memory_block->memqueue_list, &cmamem_block_head->memqueue_list);return 0;}static int cmamem_free(struct file *file, unsigned long arg){struct cmamem_block *memory_block;struct mem_block cma_info_temp;int ret;if ((ret = copy_from_user(&cma_info_temp, (void __user *)arg,sizeof(struct mem_block)))){printk(KERN_ERR"cmamem_alloc:copy_from_user error:%d\n", ret);return -1;}printk(KERN_INFO "will delete the mem name:%s\n", cma_info_temp.name);list_for_each_entry(memory_block, &cmamem_block_head->memqueue_list, memqueue_list){if(memory_block){//if(memory_block->id == cma_info_temp.id || !strcmp(cma_info_temp.name, memory_block->name)){if(!strcmp(cma_info_temp.name, memory_block->name)){if(memory_block->is_free == 0){printk(KERN_INFO "delete the mem id:%d, name:%s\n", cma_info_temp.id, cma_info_temp.name);vm_munmap(memory_block->mem_base, memory_block->len);if(memory_block->is_use_buffer)dma_free_coherent(NULL,memory_block->len, memory_block->kernel_base, memory_block->phy_base);elsedma_free_writecombine(NULL, memory_block->len, memory_block->kernel_base, memory_block->phy_base);memory_block->is_free = 1;list_del(&memory_block->memqueue_list); break;}}}} return 0;}static int cmamem_freeall(void){struct cmamem_block *memory_block;printk(KERN_INFO "will delete all cma mem\n");list_for_each_entry(memory_block, &cmamem_block_head->memqueue_list, memqueue_list){if(memory_block && memory_block->id > 0){if(memory_block->is_free == 0){printk(KERN_INFO "delete the mem id:%d, name:%s\n", memory_block->id, memory_block->name);if(memory_block->is_use_buffer)dma_free_coherent(NULL, memory_block->len, memory_block->kernel_base, memory_block->phy_base);elsedma_free_writecombine(NULL, memory_block->len, memory_block->kernel_base, memory_block->phy_base);memory_block->is_free = 1;}}} return 0;}static long cmamem_ioctl(struct file *file, unsigned int cmd, unsigned long arg){int ret = 0;switch(cmd){case CMEM_ALLOCATE:{printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE\n");mutex_lock(&cmamem_dev.cmamem_lock);cmamem_alloc(file, arg);if(ret < 0)goto alloc_err;mutex_unlock(&cmamem_dev.cmamem_lock);break;}case CMEM_UNMAP:{printk(KERN_ERR"cmamem_ioctl:CMEM_UNMAP\n");mutex_lock(&cmamem_dev.cmamem_lock);ret = cmamem_free(file, arg);if(ret < 0)goto free_err;mutex_unlock(&cmamem_dev.cmamem_lock);break;}default:{printk(KERN_INFO "cma mem not support command\n");break;}}return 0;alloc_err:mutex_unlock(&cmamem_dev.cmamem_lock);printk(KERN_ERR "%s alloc error\n", __func__);return ret;free_err:mutex_unlock(&cmamem_dev.cmamem_lock);printk(KERN_ERR "%s free error\n", __func__);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;//if(size > MMAP_MEM_SIZE)//return -EINVAL; if(cmamem_status.status != HAVE_ALLOCED){printk(KERN_ERR"%s, you should allocted memory firstly\n", __func__);return -EINVAL; }//printk( "cmamem_mmap:vma: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 ;//printk( "cmamem_status.phy_base:0x%08x\n", (unsigned int)cmamem_status.phy_base);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);cmamem_status.status = HAVE_MMAPED;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_INFO "%s\n", __func__);mutex_init(&cmamem_dev.cmamem_lock);//NIT_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_block_head = (struct cmamem_block *)kmalloc(sizeof(struct cmamem_block), GFP_KERNEL);cmamem_block_head->id = -1;mem_block_count = 0;INIT_LIST_HEAD(&cmamem_block_head->memqueue_list);/* 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__);cmamem_freeall();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 mem_block {char name[10];char is_use_buffer;int id;unsigned long offset;unsigned long len;unsigned long phy_base;unsigned long mem_base;void *kernel_base;};#endif
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 [10]; char is_use_buffer; int ID; unsigned long offset; unsigned long Len; unsigned long phy_base; unsigned long mem_base; void * kernel_base;}; struct mem_block {char name [10]; char is_use_buffer; int ID; unsigned long offset; unsigned long Len; unsigned long phy_base; unsigned long mem_base; void * kernel_base;}; int main () {int cmem_fd; void * cmem_base; unsigne D int size; struct mem_block region; int I, j; char STR [10]; memset (& region, 0x00, sizeof (struct mem_block )); cmem_fd = open ("/dev/cma_mem", o_rdwr, 0); // open the device. To operate the hardware engine, printf ("cmem_fd: % d \ n ", cmem_fd); j = 0; If (cmem_fd> = 0) while (j <= 2) {J ++; sprintf (STR, "mem % d", J ); memset (& region, 0x00, sizeof (struct mem_block); region. len = 800*480*4; region. is_use_buffer = 1; memcpy (region. name, STR, STR Len (STR); printf ("sizeof (struct mem_block): % d \ n", sizeof (struct mem_block); printf ("region. mem_base: 0x % 08x \ n ", region. mem_base); If (IOCTL (cmem_fd, cmem_allocate, & Region) <0) // obtain all space {perror ("pmem_get_total_size failed \ n"); Return-1 ;} // size = region. len; printf ("region. len: 0x % 08x offset: 0x % 08x \ n ", region. len, region. offset); printf ("region. mem_base: 0x % 08x \ n ", region. mem_base); for (I = 0; I <10; I + +) Printf ("% d \ n", (char *) (region. mem_base) [I]);/* cmem_base = MMAP (0, size, prot_read | prot_write, map_shared, cmem_fd, 0); // MMAP operation 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]); */printf ("\ n ********************* \ n ");} printf ("free the mem \ n"); getchar (); j = 0;/* While (j <= 2) // release test {J ++; sprintf (STR, "mem % d", J); memset (& region, 0x00, sizeof (struct mem_block); region. id = J; region. is_use_buffer = 1; memcpy (region. name, STR, strlen (STR); printf ("user will DEL: % s, id = % d \ n", STR, region. ID); If (IOCTL (cmem_fd, cmem_unmap, & Region) <0) // obtain all space {perror ("pmem_get_total_size failed \ n"); Return-1 ;}} getchar (); */close (cmem_fd); Return 0 ;}