Messages in this thread | | | Date | Sun, 23 May 2004 15:55:31 -0700 (PDT) | From | fc scsi <> | Subject | dmaable memory in user space |
| |
#include <linux/kernel.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/ioport.h> #include <asm/io.h> #include <asm/delay.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/mm.h> #include <linux/pci.h> #include <linux/wrapper.h>
int major = -1;
/* * structure to keep address of kmalloc'ed area so that we can free it * in close() for the vma. size is not required. */ struct my_module_mem_mgr { unsigned long address; short size; };
struct my_module_mem_mgr *tmp_mem_mgr_ptr;
/* * structure in which first two arguments are set by user space and used in * driver and the third is set in driver and used by user space application. */ struct mem_ioctl_data { unsigned long requested_size; unsigned long start_of_vma; unsigned long return_offset; };
static int my_module_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) { printk("\nmy_module_ioctl() invoked\n"); if(cmd == 1) { struct vm_area_struct *vmalist = NULL; unsigned long requested_size; struct mem_ioctl_data * mid = (struct mem_ioctl_data *)arg; unsigned long start_of_vma;
printk("\nioctl cmd 2: arg = 0x%lx\n", arg); printk("\nmid.requested_size = 0x%lx mid.start_of_vma = 0x%lx \ mid.return_offset = 0x%lx\n", mid->requested_size, mid->start_of_vma, mid->return_offset);
requested_size = mid->requested_size; start_of_vma = mid->start_of_vma;
/* * Go through the vmas of the current process and see which one * corresponds to the starting address which we got in the ioctl * arguments */ vmalist = current->mm->mmap; if (vmalist == NULL) { printk("\nvmalist is null\n"); } else { for(;vmalist != NULL; vmalist=vmalist->vm_next) { if(vmalist->vm_start == start_of_vma) { printk("\nfound the required vma: vmalist is 0x%p start \ = 0x%lx\n", vmalist, vmalist->vm_start); /* * We should have already allocated the vm_private_data * structure in the mmap() call, so it should be non-NULL */ if (vmalist->vm_private_data != NULL) { int *unaligned_kptr; int *kptr; struct vm_area_struct * vma = vmalist; unsigned long size = vma->vm_end - vma->vm_start; int rv; unsigned long virt_addr; printk("\nvma = 0x%p\n", vma); printk("\nvm_start = 0x%lx\n", vma->vm_start); printk("\nvm_end = 0x%lx\n", vma->vm_end); printk("\nvm_pgoff = 0x%lx\n", vma->vm_pgoff); /* * Now allocate the DMAable memory for use in * user space */ unaligned_kptr = kmalloc(requested_size, GFP_DMA); printk("\nkptr with out alignment = 0x%p\n", unaligned_kptr); if(unaligned_kptr == 0) { printk("\nkmalloc failed for size = %ld\n", requested_size); return -1; } /* * Reserve the page of the kmalloc'ed area * Without this it doesn't work, why? */ kptr=(int *)(((unsigned long)unaligned_kptr) & PAGE_MASK); printk("\nkptr with alignment = 0x%p\n", kptr); for (virt_addr=(unsigned long)kptr; virt_addr<(unsigned long)kptr+size; virt_addr+=PAGE_SIZE) { //FIXME: will this effect the free of other data // in the same page, memory leakage ??? printk("\ndoing mem_map_reserve for address \ 0x%p\n", virt_to_page(virt_addr)); mem_map_reserve(virt_to_page(virt_addr)); //get_page(virt_to_page(virt_addr)); } /* * Write a signature on the allocated area * which we can verify from user space after mapping * this page and calculating the correct offset in the * page */ unaligned_kptr[0]=(0xabcdefab); rv = remap_page_range(vma, vma->vm_start, virt_to_phys(kptr), size, PAGE_SHARED); if(rv) { printk("\nremap_page_range failed rv = %d\n", rv); return -5; } else { printk("\nremap_page_range succeeded\n"); } /* * Store the address of the kmalloc'ed area so that * we can free it whenever the close() for the vma is * called (storing size is redundant, for debugging) */ if (vma->vm_private_data == NULL) { //FIXME: free allocated mem printk("\nvm_private_data is null in ioctl cmd 2!!!\n"); return -1; } else { tmp_mem_mgr_ptr = (struct my_module_mem_mgr *)(vmalist->vm_private_data); tmp_mem_mgr_ptr->address = (unsigned long)unaligned_kptr; tmp_mem_mgr_ptr->size = requested_size; } /* * Calculate the offset of the kmalloc'ed area in the * page and return it in the ioctl argument to the * user space, so that the user space can get the * correct ptr in the page */ mid->return_offset = (((unsigned long)unaligned_kptr) & (PAGE_SIZE -1)); printk("\noffset to be returned is = 0x%lx\n", mid->return_offset); return 0;
} else { printk("\nvmalist->vm_private_data is null in ioctl cmd 2!!!\n"); return -1; } } } } printk("\ncouldn't find the vma!!!\n"); return -1; } else { printk("\nInvalid ioctl, cmd = %d\n", cmd); return -EINVAL; } return 0; }
void my_module_vm_open(struct vm_area_struct * area) { printk("\nmy_module_vm_open ....vma = 0x%p\n", area); }
void my_module_vm_close(struct vm_area_struct * area) { void * unaligned_address; void * address; unsigned long virt_addr; unsigned long size = area->vm_end - area->vm_start;
printk("\nmy_module_vm_close ....vma = 0x%p\n", area); if (area->vm_private_data != NULL) { unaligned_address = (void *)((struct my_module_mem_mgr *)(area->vm_private_data))->address; printk("\nmy_module_vm_close(): vma->vm_private_data-> address = 0x%p \ vma->vm_private_data->size = 0x%d\n", unaligned_address, \ ((struct my_module_mem_mgr *)(area->vm_private_data))->size);
/* * Unreserve the pages which we reserved */ address=(int *)(((unsigned long)unaligned_address ) & PAGE_MASK); printk("\nmy_module_vm_close(): address with alignment = 0x%p\n", address);
for (virt_addr=(unsigned long)address; virt_addr<(unsigned long)address+size; virt_addr+=PAGE_SIZE) { printk("\ndoing mem_map_unreserve for address 0x%p\n", virt_to_page(virt_addr)); mem_map_unreserve(virt_to_page(virt_addr)); //put_page(virt_to_page(virt_addr)); }
printk("\nfreeing unaligned_address 0x%p\n", unaligned_address); kfree(unaligned_address);
} else { printk("\nmy_module_vm_close(): vm_private_data is NULLLLLL\n"); } }
struct page * my_module_nopage(struct vm_area_struct * vma, unsigned long address, int unused) { printk("\nERROR!!! my_module_nopage called \n"); return NOPAGE_SIGBUS; }
struct vm_operations_struct my_module_vm_ops = { nopage: &my_module_nopage, open: &my_module_vm_open, close: &my_module_vm_close, };
//FIXME: return in error cases dont free the mem allocated above them int my_module_mmap(struct file * filp, struct vm_area_struct *vma) {
printk("\nmy_module_mmap: \n"); printk("\nvma = 0x%p\n", vma); printk("\nvm_start = 0x%lx\n", vma->vm_start); printk("\nvm_end = 0x%lx\n", vma->vm_end);
if (vma->vm_private_data != NULL) { printk("\nmy_module_mmap(): vm_private_data = 0x%p ERROR!!!\n", vma->vm_private_data); return -1; } else { /* * Allocate the memory for storing the kmalloc ptr so that we can * free it in the close() for this vma */ tmp_mem_mgr_ptr = kmalloc(sizeof(struct my_module_mem_mgr), GFP_KERNEL); if (tmp_mem_mgr_ptr == NULL) { printk("\nmy_module_mmap(): kmalloc failed for tmp_mem_mgr_ptr\n"); return -1; } vma->vm_private_data = tmp_mem_mgr_ptr; }
vma->vm_ops = &my_module_vm_ops;
return 0; }
void *retvalp = NULL;
struct file_operations my_module_ops = { ioctl: my_module_ioctl, mmap: my_module_mmap, }; int init_module() { printk("\ninit module\n");
major = register_chrdev(0, "my_module", &my_module_ops);
if(major < 0) { printk("\nFailure register_chrdev\n"); return -1; } else { //printk("\nregister_chrdev Successful\n"); }
return 0; }
void cleanup_module() { unregister_chrdev(major, "my_module"); printk("\nCleanup module\n"); } INCLUDES+= /usr/src/linux-2.4/include
all: remap_page_range.o dma_alloc
remap_page_range.o: remap_page_range.c $(CC) -c -O -I$(INCLUDES) -Wall -DMODULE -D__KERNEL__ -o $@ $^
dma_alloc: dma_alloc.c $(CC) -I$(INCLUDES) -Wall -o $@ $^
clean: rm -f remap_page_range.o dma_alloc.c #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <stdlib.h> #include <sys/mman.h>
struct mem_ioctl_data { unsigned long requested_size; unsigned long start_of_vma; unsigned long return_offset; };
void * dma_malloc(unsigned int size) { int fd = -1; unsigned int retval; int mmap_size; struct mem_ioctl_data mid; void * mmaprv;
printf("\ndma_malloc called with size %u\n", size);
if ((fd = open("/dev/my_module", O_RDWR)) < 0) { perror("open failed for /dev/my_module"); return NULL; } else { //printf("\nopen succeeded\n"); }
mmap_size = (size/4096) + 1;
printf("\nmmap_size = 0x%x\n", mmap_size);
mmaprv = mmap(0, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(mmaprv == MAP_FAILED) { printf("\nmmap failed\n"); perror("mmap failure"); return NULL; } else { printf("\nmmap succeeded mmaprv = 0x%p\n", mmaprv); }
mid.requested_size = size; mid.start_of_vma = (unsigned long)mmaprv; mid.return_offset = 0xffffffff;
retval = ioctl(fd, 1, &mid); if(retval) { perror("ioctl failed"); return NULL; } else { printf("\nioctl succeeded\n"); }
if(mid.return_offset == 0xffffffff) { printf("\nmid.return_offset = 0x%lx\n", mid.return_offset); return NULL; }
printf("\nmid.return_offset = 0x%lx\n", mid.return_offset); return (void *)(mmaprv + mid.return_offset); }
int main(int argc, char * argv[]) { unsigned int size; char *cptr; void * ptr;
printf("\nEnter the size of allocation ...\n"); while (scanf("%u", &size)) { printf("\nSize asked is %u\n", size); ptr = dma_malloc(size); if (ptr == NULL) { printf("\ndma_malloc failed for size %d ptr = 0x%p\n\n", size, ptr); return 0; } else { printf("\ndma_malloc succeeded for size %d ptr = 0x%p\n\n", size, ptr); }
cptr = (char *)ptr;
/* * This should print the signature that we wrote to the kmalloc'ed * area in the driver */ printf("\nptr = 0x%lx\n", *(unsigned long *)ptr); }
return 0; }
| |