--- mm/vmalloc.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) Index: linux-2.6/mm/vmalloc.c =================================================================== --- linux-2.6.orig/mm/vmalloc.c 2007-09-17 17:08:25.000000000 -0700 +++ linux-2.6/mm/vmalloc.c 2007-09-17 17:10:19.000000000 -0700 @@ -385,6 +385,21 @@ struct vm_struct *remove_vm_area(const v return v; } +struct vfree_work_struct { + struct work_struct work; + int mode; +}; + +static void __vunmap(const void *, int); + +static void vfree_work(struct work_struct *w) +{ + struct vfree_work_struct *vws = + container_of(w, struct vfree_work_struct, work); + + __vunmap(vws, vws->mode); +} + static void __vunmap(const void *addr, int deallocate_pages) { struct vm_struct *area; @@ -398,6 +413,20 @@ static void __vunmap(const void *addr, i return; } + if (in_interrupt()) { + struct vfree_work_struct *vws = (void *)addr; + + /* + * The object is unused and we have at least a page at + * addr. So we can just use the area to a work_struct + * in there to defer the free until a time when interrupts + * are enabled and we can safely acquire locks. + */ + INIT_WORK(&vws->work, vfree_work); + vws->mode = deallocate_pages; + schedule_work(&vws->work); + return; + } area = remove_vm_area(addr); if (unlikely(!area)) { printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n", @@ -436,12 +465,9 @@ static void __vunmap(const void *addr, i * Free the virtually contiguous memory area starting at @addr, as * obtained from vmalloc(), vmalloc_32() or __vmalloc(). If @addr is * NULL, no operation is performed. - * - * Must not be called in interrupt context. */ void vfree(const void *addr) { - BUG_ON(in_interrupt()); __vunmap(addr, 1); } EXPORT_SYMBOL(vfree); @@ -452,12 +478,9 @@ EXPORT_SYMBOL(vfree); * * Free the virtually contiguous memory area starting at @addr, * which was created from the page array passed to vmap(). - * - * Must not be called in interrupt context. */ void vunmap(const void *addr) { - BUG_ON(in_interrupt()); __vunmap(addr, 0); } EXPORT_SYMBOL(vunmap);