Hallo. I try to use a mmap for communicate userspace application and other servers via kernel character driver. and its doesn't work. but, of course its work in native linux. my mmap call return no error, but when i try acces to memmory via returned pointer, i got seg fault
Here is driver source and example program, based on mmap example: any of vmalloc or kalloc got seg fault:
mmap ok, vaddr = 40034000 Segmentation fault
//#include <linux/config.h> #include <linux/version.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/mm.h> #ifdef MODVERSIONS # include <linux/modversions.h> #endif #include <asm/io.h>
/* character device structures */ static dev_t vhsm_dev; static struct cdev vhsm_cdev;
/* methods of the character device */ static int vhsm_open(struct inode *inode, struct file *filp); static int vhsm_release(struct inode *inode, struct file *filp); static int vhsm_mmap(struct file *filp, struct vm_area_struct *vma);
/* the file operations, i.e. all character device methods */ static struct file_operations vhsm_fops = { .open = vhsm_open, .release = vhsm_release, .mmap = vhsm_mmap, .owner = THIS_MODULE, };
// internal data // length of the two memory areas #define NPAGES 16 // pointer to the vmalloc'd area - alway page aligned static int *vmalloc_area;
// pointer to the kmalloc'd area, rounded up to a page boundary static int *kmalloc_area; // original pointer for kmalloc'd area as returned by kmalloc static void *kmalloc_ptr;
/* character device open method */ static int vhsm_open(struct inode *inode, struct file *filp) { return 0; } /* character device last close method */ static int vhsm_release(struct inode *inode, struct file *filp) { return 0; }
// helper function, mmap's the kmalloc'd area which is physically contiguous int vhsm_kmem(struct file *filp, struct vm_area_struct *vma) { int ret; long length = vma->vm_end - vma->vm_start;
/* check length - do not allow larger mappings than the number of pages allocated */ if (length > NPAGES * PAGE_SIZE) return -EIO;
/* map the whole physically contiguous area in one piece */ if ((ret = remap_pfn_range(vma, vma->vm_start, virt_to_phys((void *)kmalloc_area)
PAGE_SHIFT,
length, vma->vm_page_prot)) < 0) { return ret; }
return 0; }
/* character device mmap method */ static int vhsm_mmap(struct file *filp, struct vm_area_struct *vma) { /* at offset 0 we map the vmalloc'd area */ if (vma->vm_pgoff == 0) { return vhsm_vmem(filp, vma); } /* at offset NPAGES we map the kmalloc'd area */ if (vma->vm_pgoff == NPAGES) { return vhsm_kmem(filp, vma); } /* at any other offset we return an error */ return -EIO;
}
/* module initialization - called at module load time */ static int __init vhsm_init(void) { int ret = 0; int i;
/* allocate a memory area with kmalloc. Will be rounded up to a page boundary */ if ((kmalloc_ptr = kmalloc((NPAGES + 2) * PAGE_SIZE, GFP_KERNEL)) == NULL) { ret = -ENOMEM; goto out; } /* round it up to the page bondary */ kmalloc_area = (int *)((((unsigned long)kmalloc_ptr) + PAGE_SIZE - 1) & PAGE_MASK);
/* allocate a memory area with vmalloc. */ if ((vmalloc_area = (int *)vmalloc(NPAGES * PAGE_SIZE)) == NULL) { ret = -ENOMEM; goto out_kfree; }
/* get the major number of the character device */ if ((ret = alloc_chrdev_region(&vhsm_dev, 0, 1, "mmap")) < 0) { printk(KERN_ERR "could not allocate major number for mmap\n"); goto out_vfree; }
/* initialize the device structure and register the device with the kernel */ cdev_init(&vhsm_cdev, &vhsm_fops); if ((ret = cdev_add(&vhsm_cdev, vhsm_dev, 1)) < 0) { printk(KERN_ERR "could not allocate chrdev for mmap\n"); goto out_unalloc_region; }
/* mark the pages reserved */ for (i = 0; i < NPAGES * PAGE_SIZE; i+= PAGE_SIZE) { SetPageReserved(vmalloc_to_page((void *)(((unsigned long)vmalloc_area) + i))); SetPageReserved(virt_to_page(((unsigned long)kmalloc_area) + i)); }
/* store a pattern in the memory - the test application will check for it */ for (i = 0; i < (NPAGES * PAGE_SIZE / sizeof(int)); i += 2) { 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; }
return ret;
out_unalloc_region: unregister_chrdev_region(vhsm_dev, 1); out_vfree: vfree(vmalloc_area); out_kfree: kfree(kmalloc_ptr); out: return ret; }
/* module unload */ static void __exit vhsm_exit(void) { int i;
/* remove the character deivce */ cdev_del(&vhsm_cdev); unregister_chrdev_region(vhsm_dev, 1);
/* unreserve the pages */ for (i = 0; i < NPAGES * PAGE_SIZE; i+= PAGE_SIZE) { SetPageReserved(vmalloc_to_page((void *)(((unsigned long)vmalloc_area) + i))); SetPageReserved(virt_to_page(((unsigned long)kmalloc_area) + i)); } /* free the memory areas */ vfree(vmalloc_area); kfree(kmalloc_ptr); }
module_init(vhsm_init); module_exit(vhsm_exit);
test program: #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h>
#define NPAGES 16
/* this is a test program that opens the mmap_drv. It reads out values of the kmalloc() and vmalloc() allocated areas and checks for correctness. You need a device special file to access the driver. The device special file is called 'node' and searched in the current directory. To create it - load the driver 'insmod mmap_mod.o' - find the major number assigned to the driver 'grep mmapdrv /proc/devices' - and create the special file (assuming major number 254) 'mknod node c 254 0' */
int main(void) { int fd; unsigned int *vadr; unsigned int *kadr;
int len = NPAGES * getpagesize();
if ((fd=open("node", O_RDWR|O_SYNC))<0) { perror("open"); exit(-1); }
vadr = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0);
if (vadr == MAP_FAILED) { perror("mmap"); exit(-1); } if ((vadr[0]!=0xaffe0000) || (vadr[1]!=0xbeef0000) || (vadr[len/sizeof(int)-2]!=(0xaffe0000+len/sizeof(int)-2)) || (vadr[len/sizeof(int)-1]!=(0xbeef0000+len/sizeof(int)-2))) { printf("0x%x 0x%x\n", vadr[0], vadr[1]); printf("0x%x 0x%x\n", vadr[len/sizeof(int)-2], vadr[len/sizeof(int)-1]); }
kadr = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED| MAP_LOCKED, fd, len);
if (kadr == MAP_FAILED) { perror("mmap"); exit(-1); }
if ((kadr[0]!=0xdead0000) || (kadr[1]!=0xbeef0000) || (kadr[len / sizeof(int) - 2] != (0xdead0000 + len / sizeof(int) - 2)) || (kadr[len / sizeof(int) - 1] != (0xbeef0000 + len / sizeof(int) - 2))) { printf("0x%x 0x%x\n", kadr[0], kadr[1]); printf("0x%x 0x%x\n", kadr[len / sizeof(int) - 2], kadr[len / sizeof(int) - 1]); }
close(fd); return(0); }
On Wed Aug 03, 2011 at 16:17:04 +0400, Vasily Sartakov wrote:
I try to use a mmap for communicate userspace application and other servers via kernel character driver. and its doesn't work. but, of course its work in native linux. my mmap call return no error, but when i try acces to memmory via returned pointer, i got seg fault
Here is driver source and example program, based on mmap example: any of vmalloc or kalloc got seg fault:
mmap ok, vaddr = 40034000 Segmentation fault
Which memory access is it? I do not see corresponding printf.
/* map the whole physically contiguous area in one piece */ if ((ret = remap_pfn_range(vma, vma->vm_start, virt_to_phys((void *)kmalloc_area)
PAGE_SHIFT,
length, vma->vm_page_prot)) < 0) { return ret; }
virt_to_phys is probably not a good choice. On native, __pa and virt_to_phys indeed do the same but on L4Linux virt_to_phys does what the name says and __pa does what you probably want here. And shouldn't kmalloc memory be always contiguous?
Adam
l4-hackers@os.inf.tu-dresden.de