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 --- 25-akpm/include/linux/vmalloc.h | 8 +-- 25-akpm/mm/vmalloc.c | 85 ++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 36 deletions(-) diff -puN include/linux/vmalloc.h~revert-vmalloc-use-list-of-pages-instead-of-array-in-vm_struct include/linux/vmalloc.h --- 25/include/linux/vmalloc.h~revert-vmalloc-use-list-of-pages-instead-of-array-in-vm_struct 2005-03-16 03:28:42.000000000 -0800 +++ 25-akpm/include/linux/vmalloc.h 2005-03-16 03:28:46.000000000 -0800 @@ -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 siz 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(uns 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 -puN mm/vmalloc.c~revert-vmalloc-use-list-of-pages-instead-of-array-in-vm_struct mm/vmalloc.c --- 25/mm/vmalloc.c~revert-vmalloc-use-list-of-pages-instead-of-array-in-vm_struct 2005-03-16 03:28:42.000000000 -0800 +++ 25-akpm/mm/vmalloc.c 2005-03-16 03:28:46.000000000 -0800 @@ -84,7 +84,7 @@ void unmap_vm_area(struct vm_struct *are } 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, un 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 * } 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 * 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, 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); @@ -303,11 +301,20 @@ void __vunmap(void *addr, int deallocate WARN_ON(1); return; } - + 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 { 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 *)); - while (nr_pages--) { - struct page *page = alloc_page(gfp_mask); - if (!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); + + 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; _