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