Index: linux-2.6/include/asm-generic/memory_model.h =================================================================== --- linux-2.6.orig/include/asm-generic/memory_model.h 2007-03-25 20:43:22.000000000 -0700 +++ linux-2.6/include/asm-generic/memory_model.h 2007-03-25 20:45:01.000000000 -0700 @@ -47,6 +47,13 @@ }) #elif defined(CONFIG_SPARSEMEM) +#ifdef CONFIG_VMEMMAP +/* + * We have a virtual memmap that makes lookups very simple + */ +#define __pfn_to_page(pfn) (vmemmap + (pfn)) +#define __page_to_pfn(page) ((page) - vmemmap) +#else /* * Note: section's mem_map is encorded to reflect its start_pfn. * section[i].section_mem_map == mem_map's address - start_pfn; @@ -62,6 +69,7 @@ struct mem_section *__sec = __pfn_to_section(__pfn); \ __section_mem_map_addr(__sec) + __pfn; \ }) +#endif #endif /* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */ #ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE Index: linux-2.6/mm/sparse.c =================================================================== --- linux-2.6.orig/mm/sparse.c 2007-03-25 20:43:22.000000000 -0700 +++ linux-2.6/mm/sparse.c 2007-03-25 22:16:05.000000000 -0700 @@ -101,7 +101,7 @@ /* * Although written for the SPARSEMEM_EXTREME case, this happens - * to also work for the flat array case becase + * to also work for the flat array case because * NR_SECTION_ROOTS==NR_MEM_SECTIONS. */ int __section_nr(struct mem_section* ms) @@ -209,6 +209,89 @@ return 1; } +#ifdef CONFIG_VMEMMAP + +unsigned long vmemmap_alloc_block(unsigned long size, int node) +{ + if (slab_is_available()) { + struct page *page = + alloc_pages_node(node, GFP_KERNEL, + get_order(size)); + + if (!page) + return 0; + + return page_to_pfn(page) << PAGE_SHIFT; + } else + return (unsigned long)alloc_bootmem_node(NODE_DATA(node), + size); +} + + +#ifndef ARCH_VMEMMAP_POPULATE +void vmemmap_populate(struct page *start_page, unsigned long nr, int node) +#else +/* + * VMEMMAP populate functionality for architectures that support + * PMDs for huge pages. + */ +static void vmemmap_pop_pmd(pud_t *pud, unsigned long addr, unsigned long end, int node) +{ + pmd_t *pmd; + unsigned long next; + + pmd = pmd_alloc(&init_mm, pud, addr); + do { + next = pmd_addr_end(addr, end); + if (pmd_none(*pmd)) { + unsigned long block = vmemmap_alloc_block(PMD_SIZE, node); + pte_t pte = pfn_pte(block >> PAGE_SHIFT, PAGE_KERNEL); + + BUG_ON(!block); + pte_mkdirty(pte); + pte_mkwrite(pte); + pte_mkyoung(pte); + mk_pte_huge(pte); + set_pmd(pmd, __pmd(pte_val(pte))); + } + pmd++; + addr = next; + } while (addr < end); +} + +static void vmemmap_pop_pud(pgd_t *pgd, unsigned long addr, unsigned long end, int node) +{ + pud_t *pud; + unsigned long next; + + pud = pud_alloc(&init_mm, pgd, addr); + do { + next = pud_addr_end(addr, end); + vmemmap_pop_pmd(pud, addr, next, node); + pud++; + addr = next; + } while (addr < end); +} + + +static void vmemmap_populate(struct page *start_page, unsigned long nr, + int node) +{ + unsigned long addr = (unsigned long)start_page; + unsigned long end = (unsigned long)(start_page + nr); + unsigned long next; + pgd_t *pgd; + + pgd = pgd_offset_k(addr); + do { + next = pgd_addr_end(addr, end); + vmemmap_pop_pud(pgd, addr, end, node); + pgd++; + addr = next; + } while (addr < end); +} +#endif + static struct page *sparse_early_mem_map_alloc(unsigned long pnum) { struct page *map; @@ -219,8 +302,13 @@ if (map) return map; +#ifdef CONFIG_VMEMMAP + map = pfn_to_page(pnum * PAGES_PER_SECTION); + vmemmap_populate(map, PAGES_PER_SECTION, nid); +#else map = alloc_bootmem_node(NODE_DATA(nid), sizeof(struct page) * PAGES_PER_SECTION); +#endif if (map) return map;