Re: Memory mapping with character device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Maybe I can help you tomorrow, I implement it one month ago...

On Sun, 2007-04-01 at 14:40 +0200, black hole wrote:
> Hello,
> 
> I am trying to implement a simple character device that
> implements the mmap() method (struct file_operations).
> I can't get it work, the source code is appended.
> 
> This is the scenario:
> The kernel module, that implements the device, allocates
> a page from the high memory zone upon insmod. The page
> is freed upon rmmod. The mmap() method implementation
> uses the nopage() approach (as described in LDD3 Ch15).
> 
> I understand it in a way, that the mmap() method just
> sets up the nopage() method to be called when the
> process first accesses the mmapped memory. The nopage()
> method is supposed to provide the actual page to satisfy
> the request. In my scenario, I want to use the page allocated
> at insmod time. That's what the code (hopefully) does.
> 
> The problem is that the user space process segfaults,
> when it tries to access the mmapped memory. It also
> seems to me that the nopage() method is not called.
> I am obviously doing something wrong but I have
> a hard time figuring it out.
> 
> This is the source code of the character device implementation:
> 
> // Linux Kernel 2.6.12.2
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/fs.h>
> #include <asm/page.h>
> #include <linux/gfp.h>
> #include <linux/string.h>
> #include <linux/cdev.h>
> #include <linux/mm.h>
> 
> #define FIRST_MINOR 0
> #define MINOR_COUNT 1
> #define DEVICE_NAME "mmap"
> 
> static dev_t device_number;
> static struct cdev device;
> 
> static unsigned long page;
> 
> static int open_fop(struct inode *, struct file *);
> static int release_fop(struct inode *, struct file *);
> static int mmap_fop(struct file *, struct vm_area_struct *);
> 
> static struct file_operations fops=
> {
>     owner:THIS_MODULE,
>     open:open_fop,
>     release:release_fop,
>     mmap:mmap_fop,
> };
> 
> static void open_vm_op(struct vm_area_struct *area);
> static void close_vm_op(struct vm_area_struct *area);
> struct page* nopage_vm_op(struct vm_area_struct *area,
>         unsigned long address, int *type);
> 
> static struct vm_operations_struct vm_ops=
> {
>     open:open_vm_op,
>     close:close_vm_op,
>     nopage:nopage_vm_op,
> };
> 
> static int __init init(void)
> {
>     int ret;
> 
> 
>     if(!(page=__get_free_page(GFP_HIGHUSER))){
>         ret=-ENOMEM;
>         goto out;
>     }
>     memset((char*)page, 0, PAGE_SIZE);
> 
>     if((ret=alloc_chrdev_region(&device_number, FIRST_MINOR,
>             MINOR_COUNT, DEVICE_NAME))<0)
>         goto err1;
> 
>     cdev_init(&device, &fops);
>     if((ret=cdev_add(&device, device_number, MINOR_COUNT))<0)
>         goto err2;
> 
> out:
>     return ret;
> 
> 
> err2:
>     unregister_chrdev_region(device_number, MINOR_COUNT);
> err1:
>     free_page(page);
>     goto out;
> }
> 
> 
> static void __exit exit(void)
> {
>     cdev_del(&device);
>     unregister_chrdev_region(device_number, MINOR_COUNT);
>     free_page(page);
> }
> 
> 
> module_init(init);
> module_exit(exit);
> MODULE_LICENSE("GPL");
> 
> 
> static int open_fop(struct inode *inode, struct file *file)
> {
>     printk(KERN_NOTICE "open()\n");
>     try_module_get(THIS_MODULE);
>     return 0;
> }
> 
> static int release_fop(struct inode *inode, struct file *file)
> {
>     printk(KERN_NOTICE "release()\n");
>     module_put(THIS_MODULE);
>     return 0;
> }
> 
> static int mmap_fop(struct file *file, struct vm_area_struct *vma)
> {
>     int size=vma->vm_end-vma->vm_start;
> 
>     printk(KERN_NOTICE "mmap()\n");
> 
>     if(size>PAGE_SIZE){
>         printk(KERN_ERR "bad size (%d)\n", size);
>         return -EINVAL;
>     }
> 
>     if(vma->vm_pgoff!=0){
>         printk(KERN_ERR "bad offset (%lu)\n", vma->vm_pgoff);
>         return -EINVAL;
>     }
> 
>     vma->vm_flags=VM_RESERVED;
>     vma->vm_ops=&vm_ops;
> 
>     return 0;
> }
> 
> static void open_vm_op(struct vm_area_struct *area)
> {
>     printk(KERN_NOTICE "open_vm()\n");
> }
> 
> static void close_vm_op(struct vm_area_struct *area)
> {
>     printk(KERN_NOTICE "close_vm()\n");
> }
> 
> // Does not seem to be called when the user space process
> // tries to access the mmap-ed memory.
> struct page* nopage_vm_op(struct vm_area_struct *area,
>         unsigned long address, int *type)
> {
>     struct page *ret;
> 
>     printk("nopage_vm(%lu)\n", address);
> 
>     ret=pfn_to_page(__pa(page)>>PAGE_SHIFT);
>     get_page(ret);
> 
>     if(type)
>         *type=VM_FAULT_MINOR;
>     return ret;
> }
> 
> 
> 
> This is the source code of the program that maps the memory:
> #include <assert.h>
> #include <errno.h>
> #include <stdio.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <fcntl.h>
> #include <unistd.h>
> #include <sys/mman.h>
> 
> 
> #define START       0
> #define LENGTH      4096 // man 2 getpagesize
> #define PROTECTION  ( PROT_READ | PROT_WRITE )
> #define FLAGS       MAP_SHARED
> #define OFFSET      0
> #define COUNT       128
> 
> 
> int main(int argc, char *argv[])
> {
>     // mknod mmap c 253 0
>     char *device="mmap";
>     volatile int *shp;
>     int sh;
>     void *mem;
>     int fd;
>     int it;
> 
> 
>     if(argc==2)
>         device=argv[1];
> 
>     fd=open(device, O_RDWR);
>     assert(fd!=-1);
> 
>     mem=mmap(START, LENGTH, PROTECTION, FLAGS, fd, OFFSET);
>     if(mem==(void*)-1){
>         perror("mmap");
>         assert(0);
>     }
> 
>     printf("pid(%d)\n", getpid());
> 
> //    sleep(10);
> 
>     it=COUNT;
>     shp=(int*)mem;
>     while(it--){
>         // Segmentation fault here.
>         sh=*shp;
>         printf("read(%d), write(%d)\n", sh, ++sh);
>         *shp=sh;
>         sleep(1);
>     }
> 
>     munmap(mem, LENGTH);
>     return 0;
> }
> 
> 
> The output of the program:
> pid(12270)
> Segmentation fault
> 
> The output of the dmesg:
> open()
> mmap()
> close_vm()
> release()
> 
> 
> 
> BlackHole
> 
> --
> To unsubscribe from this list: send an email with
> "unsubscribe kernelnewbies" to ecartis@xxxxxxxxxxxx
> Please read the FAQ at http://kernelnewbies.org/FAQ
> 


--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to ecartis@xxxxxxxxxxxx
Please read the FAQ at http://kernelnewbies.org/FAQ


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux