This patch assumes that there is no valid usage of vmalloced_or_vmaped_page->lru. In such a case vm_struct->array could be eliminated. It saves some memory and simplifies code a bit. In vmap/vunmap case vm_struct->page_list is used only in map_vm_area(), so it is ok to do: addr1 = vmap(pages, count); addr2 = vmap(pages, count); ... vunmap(addr1); vunmap(addr2); Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton --- 25-akpm/include/linux/vmalloc.h | 8 +-- 25-akpm/mm/vmalloc.c | 82 +++++++++++++++------------------------- 2 files changed, 35 insertions(+), 55 deletions(-) diff -puN include/linux/vmalloc.h~vmalloc-use-list-of-pages-instead-of-array-in-vm_struct include/linux/vmalloc.h --- 25/include/linux/vmalloc.h~vmalloc-use-list-of-pages-instead-of-array-in-vm_struct 2005-03-06 22:20:08.000000000 -0800 +++ 25-akpm/include/linux/vmalloc.h 2005-03-06 22:20:08.000000000 -0800 @@ -2,6 +2,7 @@ #define _LINUX_VMALLOC_H #include +#include #include /* pgprot_t */ /* bits in vm_struct->flags */ @@ -14,8 +15,7 @@ struct vm_struct { void *addr; unsigned long size; unsigned long flags; - struct page **pages; - unsigned int nr_pages; + struct list_head page_list; unsigned long phys_addr; struct vm_struct *next; }; @@ -30,6 +30,7 @@ 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); @@ -41,8 +42,7 @@ 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, - struct page ***pages); +extern int map_vm_area(struct vm_struct *area, pgprot_t prot); extern void unmap_vm_area(struct vm_struct *area); /* diff -puN mm/vmalloc.c~vmalloc-use-list-of-pages-instead-of-array-in-vm_struct mm/vmalloc.c --- 25/mm/vmalloc.c~vmalloc-use-list-of-pages-instead-of-array-in-vm_struct 2005-03-06 22:20:08.000000000 -0800 +++ 25-akpm/mm/vmalloc.c 2005-03-06 22:32:22.000000000 -0800 @@ -115,7 +115,7 @@ static void unmap_area_pud(pgd_t *pgd, u static int map_area_pte(pte_t *pte, unsigned long address, unsigned long size, pgprot_t prot, - struct page ***pages) + struct list_head **pages) { unsigned long base, end; @@ -126,22 +126,23 @@ static int map_area_pte(pte_t *pte, unsi end = PMD_SIZE; do { - struct page *page = **pages; + struct page *page; + WARN_ON(!pte_none(*pte)); - if (!page) - return -ENOMEM; + + *pages = (*pages)->next; + page = list_entry(*pages, struct page, lru); set_pte_at(&init_mm, base + address, pte, mk_pte(page, prot)); address += PAGE_SIZE; pte++; - (*pages)++; } while (address < end); return 0; } static int map_area_pmd(pmd_t *pmd, unsigned long address, unsigned long size, pgprot_t prot, - struct page ***pages) + struct list_head **pages) { unsigned long base, end; @@ -166,7 +167,7 @@ static int map_area_pmd(pmd_t *pmd, unsi static int map_area_pud(pud_t *pud, unsigned long address, unsigned long end, pgprot_t prot, - struct page ***pages) + struct list_head **pages) { do { pmd_t *pmd = pmd_alloc(&init_mm, pud, address); @@ -202,8 +203,9 @@ void unmap_vm_area(struct vm_struct *are flush_tlb_kernel_range((unsigned long) area->addr, end); } -int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages) +int map_vm_area(struct vm_struct *area, pgprot_t prot) { + struct list_head *pages = &area->page_list; unsigned long address = (unsigned long) area->addr; unsigned long end = address + (area->size-PAGE_SIZE); unsigned long next; @@ -222,7 +224,7 @@ int map_vm_area(struct vm_struct *area, next = (address + PGDIR_SIZE) & PGDIR_MASK; if (next < address || next > end) next = end; - if (map_area_pud(pud, address, next, prot, pages)) { + if (map_area_pud(pud, address, next, prot, &pages)) { err = -ENOMEM; break; } @@ -294,8 +296,7 @@ found: area->flags = flags; area->addr = (void *)addr; area->size = size; - area->pages = NULL; - area->nr_pages = 0; + INIT_LIST_HEAD(&area->page_list); area->phys_addr = 0; write_unlock(&vmlist_lock); @@ -372,20 +373,12 @@ void __vunmap(void *addr, int deallocate WARN_ON(1); return; } - - if (deallocate_pages) { - int i; - for (i = 0; i < area->nr_pages; i++) { - if (unlikely(!area->pages[i])) - BUG(); - __free_page(area->pages[i]); - } + if (deallocate_pages) { + struct page *page, *tmp; - if (area->nr_pages > PAGE_SIZE/sizeof(struct page *)) - vfree(area->pages); - else - kfree(area->pages); + list_for_each_entry_safe(page, tmp, &area->page_list, lru) + __free_page(page); } kfree(area); @@ -444,13 +437,17 @@ 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; - if (map_vm_area(area, prot, &pages)) { + + while (count--) { + struct page *page = *pages++; + BUG_ON(!page); + list_add_tail(&page->lru, &area->page_list); + } + + if (map_vm_area(area, prot)) { vunmap(area->addr); return NULL; } @@ -462,37 +459,20 @@ EXPORT_SYMBOL(vmap); void *__vmalloc_area(struct vm_struct *area, int gfp_mask, pgprot_t prot) { - struct page **pages; - unsigned int nr_pages, array_size, i; + unsigned int nr_pages; 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); - - 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; + while (nr_pages--) { + struct page *page = alloc_page(gfp_mask); + if (!page) goto fail; - } + list_add_tail(&page->lru, &area->page_list); } - if (map_vm_area(area, prot, &pages)) + if (map_vm_area(area, prot)) goto fail; + return area->addr; fail: _