From 68151379419236151cf8eb10c342d366ebd9d1bb Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 3 Oct 2007 20:42:46 -0700 Subject: [PATCH] vcompound: Virtual compound page freeing in interrupt context If we are in an interrupt context then simply defer the free via a workqueue. Removing a virtual mappping *must* be done with interrupts enabled since tlb_xx functions are called that rely on interrupts for processor to processor communications. Signed-off-by: Christoph Lameter --- mm/vmalloc.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) Index: linux-2.6.25-rc5-mm1/mm/vmalloc.c =================================================================== --- linux-2.6.25-rc5-mm1.orig/mm/vmalloc.c 2008-03-20 19:47:05.093418023 -0700 +++ linux-2.6.25-rc5-mm1/mm/vmalloc.c 2008-03-20 19:55:35.455823588 -0700 @@ -1074,18 +1074,13 @@ EXPORT_SYMBOL(vmalloc_address); * page conventions. I.e. following page->first_page if PageTail(page) is set * can be used to determine the head page. */ -static void ____free_vcompound(void *addr, struct page *page) +static void __vcompound_free(void *addr) { struct page **pages; int i; int order; - BUG_ON((!PageVcompound(page) || !PageHead(page))); - - if (!put_page_testzero(page)) - return; - pages = vunmap(addr); order = (unsigned long)pages[1]->lru.prev; @@ -1093,12 +1088,12 @@ static void ____free_vcompound(void *add * First page will have zero refcount since it maintains state * for the compound and was decremented before we got here. */ - set_page_address(page, NULL); - __ClearPageVcompound(page); - free_hot_page(page); + set_page_address(pages[0], NULL); + __ClearPageVcompound(pages[0]); + free_hot_page(pages[0]); for (i = 1; i < (1 << order); i++) { - page = pages[i]; + struct page *page = pages[i]; BUG_ON(!PageTail(page) || !PageVcompound(page)); set_page_address(page, NULL); __ClearPageVcompound(page); @@ -1107,12 +1102,38 @@ static void ____free_vcompound(void *add kfree(pages); } +static void vcompound_free_work(struct work_struct *w) +{ + __vcompound_free((void *)w); +} + +static void vcompound_free(void *addr, struct page *page) +{ + struct work_struct *w = addr; + + BUG_ON((!PageVcompound(page) || !PageHead(page))); + + if (!put_page_testzero(page)) + return; + + if (!preemptible()) { + /* + * Need to defer the free until we are in + * a preemptible context. + */ + INIT_WORK(w, vcompound_free_work); + schedule_work(w); + } else + __vcompound_free(addr); +} + + void __free_vcompound(void *addr) { struct page *page; if (unlikely(is_vmalloc_addr(addr))) - ____free_vcompound(addr, vmalloc_to_page(addr)); + vcompound_free(addr, vmalloc_to_page(addr)); page = virt_to_page(addr); free_pages((unsigned long)addr, compound_order(page)); @@ -1121,7 +1142,7 @@ void __free_vcompound(void *addr) void free_vcompound(struct page *page) { if (unlikely(PageVcompound(page))) - ____free_vcompound(page_address(page), page); + vcompound_free(page_address(page), page); else __free_pages(page, compound_order(page)); }