[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Memory mapping with character device
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