From a78a838897d8c443767b06cf5a7aaef0d4cecfc3 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 16 Mar 2005 21:46:13 -0800 Subject: [PATCH] [PATCH] revert vmalloc-use-list-of-pages-instead-of-array-in-vm_struct XFS will under some circumstances use vmap() to map pagecache pages. These are on the LRU. So the recent patch to use page->lru in the vmalloc() code corrupts these pages's ->lru pointers. Revert. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vmalloc.h | 8 ++-- mm/vmalloc.c | 83 ++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 4f6f6de8702d..968227f79508 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -2,7 +2,6 @@ #define _LINUX_VMALLOC_H #include -#include #include /* pgprot_t */ /* bits in vm_struct->flags */ @@ -15,7 +14,8 @@ struct vm_struct { void *addr; unsigned long size; unsigned long flags; - struct list_head page_list; + struct page **pages; + unsigned int nr_pages; unsigned long phys_addr; struct vm_struct *next; }; @@ -30,7 +30,6 @@ extern void *__vmalloc(unsigned long size, int gfp_mask, pgprot_t prot); extern void *__vmalloc_area(struct vm_struct *area, int gfp_mask, pgprot_t prot); extern void vfree(void *addr); -struct page; extern void *vmap(struct page **pages, unsigned int count, unsigned long flags, pgprot_t prot); extern void vunmap(void *addr); @@ -42,7 +41,8 @@ extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags); extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, unsigned long start, unsigned long end); extern struct vm_struct *remove_vm_area(void *addr); -extern int map_vm_area(struct vm_struct *area, pgprot_t prot); +extern int map_vm_area(struct vm_struct *area, pgprot_t prot, + struct page ***pages); extern void unmap_vm_area(struct vm_struct *area); /* diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 29dca085880e..23e65ce9eb0f 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -84,7 +84,7 @@ void unmap_vm_area(struct vm_struct *area) } static int vmap_pte_range(pmd_t *pmd, unsigned long addr, - unsigned long end, pgprot_t prot, struct list_head **pages) + unsigned long end, pgprot_t prot, struct page ***pages) { pte_t *pte; @@ -92,20 +92,18 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, if (!pte) return -ENOMEM; do { - struct page *page; - + struct page *page = **pages; WARN_ON(!pte_none(*pte)); - - *pages = (*pages)->next; - page = list_entry(*pages, struct page, lru); + if (!page) + return -ENOMEM; set_pte_at(&init_mm, addr, pte, mk_pte(page, prot)); - + (*pages)++; } while (pte++, addr += PAGE_SIZE, addr != end); return 0; } static inline int vmap_pmd_range(pud_t *pud, unsigned long addr, - unsigned long end, pgprot_t prot, struct list_head **pages) + unsigned long end, pgprot_t prot, struct page ***pages) { pmd_t *pmd; unsigned long next; @@ -122,7 +120,7 @@ static inline int vmap_pmd_range(pud_t *pud, unsigned long addr, } static inline int vmap_pud_range(pgd_t *pgd, unsigned long addr, - unsigned long end, pgprot_t prot, struct list_head **pages) + unsigned long end, pgprot_t prot, struct page ***pages) { pud_t *pud; unsigned long next; @@ -138,13 +136,12 @@ static inline int vmap_pud_range(pgd_t *pgd, unsigned long addr, return 0; } -int map_vm_area(struct vm_struct *area, pgprot_t prot) +int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages) { pgd_t *pgd; unsigned long next; unsigned long addr = (unsigned long) area->addr; unsigned long end = addr + area->size - PAGE_SIZE; - struct list_head *pages = &area->page_list; int err; BUG_ON(addr >= end); @@ -152,7 +149,7 @@ int map_vm_area(struct vm_struct *area, pgprot_t prot) spin_lock(&init_mm.page_table_lock); do { next = pgd_addr_end(addr, end); - err = vmap_pud_range(pgd, addr, next, prot, &pages); + err = vmap_pud_range(pgd, addr, next, prot, pages); if (err) break; } while (pgd++, addr = next, addr != end); @@ -221,7 +218,8 @@ found: area->flags = flags; area->addr = (void *)addr; area->size = size; - INIT_LIST_HEAD(&area->page_list); + area->pages = NULL; + area->nr_pages = 0; area->phys_addr = 0; write_unlock(&vmlist_lock); @@ -305,9 +303,18 @@ void __vunmap(void *addr, int deallocate_pages) } if (deallocate_pages) { - struct page *page, *tmp; - list_for_each_entry_safe(page, tmp, &area->page_list, lru) - __free_page(page); + int i; + + for (i = 0; i < area->nr_pages; i++) { + if (unlikely(!area->pages[i])) + BUG(); + __free_page(area->pages[i]); + } + + if (area->nr_pages > PAGE_SIZE/sizeof(struct page *)) + vfree(area->pages); + else + kfree(area->pages); } kfree(area); @@ -366,17 +373,13 @@ void *vmap(struct page **pages, unsigned int count, { struct vm_struct *area; + if (count > num_physpages) + return NULL; + area = get_vm_area((count << PAGE_SHIFT), flags); if (!area) return NULL; - - while (count--) { - struct page *page = *pages++; - BUG_ON(!page); - list_add_tail(&page->lru, &area->page_list); - } - - if (map_vm_area(area, prot)) { + if (map_vm_area(area, prot, &pages)) { vunmap(area->addr); return NULL; } @@ -388,21 +391,39 @@ EXPORT_SYMBOL(vmap); void *__vmalloc_area(struct vm_struct *area, int gfp_mask, pgprot_t prot) { - unsigned int nr_pages; + struct page **pages; + unsigned int nr_pages, array_size, i; nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT; + array_size = (nr_pages * sizeof(struct page *)); + + area->nr_pages = nr_pages; + /* Please note that the recursion is strictly bounded. */ + if (array_size > PAGE_SIZE) + pages = __vmalloc(array_size, gfp_mask, PAGE_KERNEL); + else + pages = kmalloc(array_size, (gfp_mask & ~__GFP_HIGHMEM)); + area->pages = pages; + if (!area->pages) { + remove_vm_area(area->addr); + kfree(area); + return NULL; + } + memset(area->pages, 0, array_size); - while (nr_pages--) { - struct page *page = alloc_page(gfp_mask); - if (!page) + for (i = 0; i < area->nr_pages; i++) { + area->pages[i] = alloc_page(gfp_mask); + if (unlikely(!area->pages[i])) { + /* Successfully allocated i pages, free them in __vunmap() */ + area->nr_pages = i; goto fail; - list_add_tail(&page->lru, &area->page_list); + } } - if (map_vm_area(area, prot)) + if (map_vm_area(area, prot, &pages)) goto fail; - return area->addr; + fail: vfree(area->addr); return NULL; -- 2.39.5