📄 demo.c
字号:
/************************************function: mmap samples for linux 2.6original: Martin Frey <frey@scs.ch>changed: fgjdate: 2007-5-5/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * */************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/cdev.h>#include <linux/version.h>#include <linux/vmalloc.h>#include <linux/ctype.h>#include <linux/pagemap.h>#include "demo.h"#define LEN (64*1024)/* pointer to page aligned area */static int *vmalloc_area = NULL;/* pointer to unaligend area */static int *vmalloc_ptr = NULL;/* pointer to page aligned area */static int *kmalloc_area = NULL;/* pointer to unaligned area */static int *kmalloc_ptr = NULL;/* open handler for vm area */void mmap_drv_vopen(struct vm_area_struct *vma);/* close handler form vm area */void mmap_drv_vclose(struct vm_area_struct *vma);/* page fault handler for callback of vmalloc area */struct page *mmap_drv_vmmap(struct vm_area_struct *vma, unsigned long address, int write_access);/* memory handler functions */static struct vm_operations_struct mmap_drv_vm_ops = { open: mmap_drv_vopen, /* mmap-open */ close: mmap_drv_vclose,/* mmap-close */ nopage: mmap_drv_vmmap, /* no-page fault handler */};/* we parse the page tables in order to find the direct mapping of the page. This works only without holding any locks for pages we are sure that they do not move in memory. Annother example achieving the same can be found in the bttv-driver (drivers/media/video).*/volatile void *virt_to_kseg(volatile void *address){ pgd_t *pgd; pmd_t *pmd; pte_t *ptep, pte; unsigned long va, ret = 0UL; va=(unsigned long)address; /* get the page directory. Use the kernel memory map. */ pgd = pgd_offset_k(va); /* check whether we found an entry */ if (!pgd_none(*pgd)) { /* get the page middle directory */ pmd = pmd_offset(pgd, va); /* check whether we found an entry */ if (!pmd_none(*pmd)) { /* get a pointer to the page table entry */ preempt_disable(); ptep = pte_offset_map(pmd, va); pte = *ptep; /* check for a valid page */ if (pte_present(pte)) { /* get the address the page is refering to */ ret = (unsigned long)page_address(pte_page(pte)); /* add the offset within the page to the page address */ ret |= (va & (PAGE_SIZE -1)); } pte_unmap(ptep); preempt_enable(); } } return((volatile void *)ret);}/* open handler for vm area */void mmap_drv_vopen(struct vm_area_struct *vma){}/* close handler form vm area */void mmap_drv_vclose(struct vm_area_struct *vma){}/* page fault handler */struct page *mmap_drv_vmmap(struct vm_area_struct *vma, unsigned long address, int write_access){ unsigned long offset; unsigned long virt_addr; /* determine the offset within the vmalloc'd area */ offset = address - vma->vm_start + (vma->vm_pgoff<<PAGE_SHIFT); /* calculate the kseg virtual address */ virt_addr = (unsigned long)virt_to_kseg(&vmalloc_area[offset/sizeof(int)]); /* check whether we found a translation */ if (virt_addr == 0UL) { printk("page fault out of range\n"); return((struct page *)0UL); } printk("mmap_drv: page fault for offset 0x%lx (kseg x%lx)\n", offset, virt_addr); return(virt_to_page(virt_addr));}struct DEMO_dev *DEMO_devices;static unsigned char demo_inc=0;int DEMO_open(struct inode *inode, struct file *filp){ struct DEMO_dev *dev; //if(demo_inc>0)return -ERESTARTSYS; demo_inc++; dev = container_of(inode->i_cdev, struct DEMO_dev, cdev); filp->private_data = dev; return 0;}int DEMO_release(struct inode *inode, struct file *filp){ demo_inc--; return 0;}static int DEMO_mmap(struct file *filp, struct vm_area_struct *vma){ unsigned long offset = vma->vm_pgoff<<PAGE_SHIFT; unsigned long size = vma->vm_end - vma->vm_start; if (offset & ~PAGE_MASK) { printk("offset not aligned: %ld\n", offset); return -ENXIO; } if (size>LEN) { printk("size too big\n"); return(-ENXIO); } /* we only support shared mappings. Copy on write mappings are rejected here. A shared mapping that is writeable must have the shared flag set. */ if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) { printk("writeable mappings must be shared, rejecting\n"); return(-EINVAL); } /* we do not want to have this area swapped out, lock it */ vma->vm_flags |= VM_LOCKED; /* there are two different mapping options implemented here: for the virtual contiguous memory area, we install a page fault handler. The page fault handler calculates the right physical page on first access of the application to the page. (method 1 is used for vmalloc'd memory, offset 0..LEN) The second way works only for a physical contigous range of pages: we create a mapping between the physical pages and the virtual addresses of the application with remap_page_range. (method 2 is used for kmalloc'd memory, offset LEN..2*LEN) */ if (offset == 0) { /* method 1: install a page handler */ vma->vm_ops = &mmap_drv_vm_ops; /* call the open routine to increment the usage count */ mmap_drv_vopen(vma); } else if (offset == LEN) { /* method 2: enter pages into mapping of application */ if (remap_page_range(vma,vma->vm_start, virt_to_phys((void*)((unsigned long)kmalloc_area)), size, PAGE_SHARED)) { printk("remap page range failed\n"); return -ENXIO; } } else { printk("offset out of range\n"); return -ENXIO; } return(0);}struct file_operations DEMO_fops = { .owner = THIS_MODULE, .open = DEMO_open, .mmap = DEMO_mmap, .release = DEMO_release,};/******************************************************* MODULE ROUTINE*******************************************************/void DEMO_cleanup_module(void){ dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR); if (DEMO_devices) { cdev_del(&DEMO_devices->cdev); kfree(DEMO_devices); } unsigned long virt_addr; /* unreserve all pages */ for(virt_addr=(unsigned long)kmalloc_area; virt_addr<(unsigned long)kmalloc_area+LEN; virt_addr+=PAGE_SIZE) { SetPageReserved(virt_to_page(virt_addr)); } for (virt_addr=(unsigned long)vmalloc_area; virt_addr<(unsigned long)(&(vmalloc_area[LEN/sizeof(int)])); virt_addr+=PAGE_SIZE) { SetPageReserved(virt_to_page(virt_to_kseg((void *)virt_addr))); } /* and free the two areas */ if (vmalloc_ptr) vfree(vmalloc_ptr); if (kmalloc_ptr) kfree(kmalloc_ptr); unregister_chrdev_region(devno,1);}int DEMO_init_module(void){ int result; dev_t dev = 0; int i; unsigned long virt_addr; dev = MKDEV(DEMO_MAJOR, DEMO_MINOR); result = register_chrdev_region(dev, 1, "DEMO"); if (result < 0) { printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR); goto out_free; } DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL); if (!DEMO_devices) { result = -ENOMEM; goto out_free; } memset(DEMO_devices, 0, sizeof(struct DEMO_dev)); init_MUTEX(&DEMO_devices->sem); cdev_init(&DEMO_devices->cdev, &DEMO_fops); DEMO_devices->cdev.owner = THIS_MODULE; DEMO_devices->cdev.ops = &DEMO_fops; result = cdev_add (&DEMO_devices->cdev, dev, 1); if(result) { printk(KERN_NOTICE "Error %d adding DEMO\n", result); goto out_free; } /* get a memory area with kmalloc and aligned it to a page. This area will be physically contigous */ kmalloc_ptr=kmalloc(LEN+2*PAGE_SIZE, GFP_KERNEL); kmalloc_area=(int *)(((unsigned long)kmalloc_ptr + PAGE_SIZE -1) & PAGE_MASK); for (virt_addr=(unsigned long)kmalloc_area; virt_addr<(unsigned long)kmalloc_area+LEN; virt_addr+=PAGE_SIZE) { /* reserve all pages to make them remapable */ SetPageReserved(virt_to_page(virt_addr)); } /* get a memory area that is only virtual contigous. */ vmalloc_ptr=vmalloc(LEN+2*PAGE_SIZE); vmalloc_area=(int *)(((unsigned long)vmalloc_ptr + PAGE_SIZE -1) & PAGE_MASK); for (virt_addr=(unsigned long)vmalloc_area; virt_addr<(unsigned long)(&(vmalloc_area[LEN/sizeof(int)])); virt_addr+=PAGE_SIZE) { SetPageReserved(virt_to_page(virt_to_kseg((void *)virt_addr))); } for (i=0; i<(LEN/sizeof(int)); i+=2) { /* initialise with some dummy values to compare later */ vmalloc_area[i]=(0xaffe<<16) + i; vmalloc_area[i+1]=(0xbeef<<16) + i; kmalloc_area[i]=(0xdead<<16) +i; kmalloc_area[i+1]=(0xbeef<<16) + i; } /* and tell the world what we did */ printk("vmalloc_area at 0x%p (phys 0x%lx)\n", vmalloc_area, virt_to_phys((void *)virt_to_kseg(vmalloc_area))); printk("kmalloc_area at 0x%p (phys 0x%lx)\n", kmalloc_area, virt_to_phys((void *)virt_to_kseg(kmalloc_area))); return 0;out_free: DEMO_cleanup_module(); return result;}module_init(DEMO_init_module);module_exit(DEMO_cleanup_module);MODULE_DESCRIPTION("mmap demo driver");MODULE_LICENSE("Dual BSD/GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -