Provide generic valid_pfn for FLATMEM and VIRTUAL_MEM_MAP Remove arch specific definitions and provide arch independent functionality for valid_pfn. Make valid_pfn check for a present virtual mapping in the case of VIRTUAL_MEM _MAP. This will not work for plain DISCONTIG without VIRTUAL_MEM_MAP and SPARSEMEM so we leave that code untouched (both of these have multiple lookups for many elementary VM operations. From a performance standpoint it is not advisable to use these). (This will break all arches except for IA64. The following patches are needed to fix up other arches) Signed-off-by: Christoph Lameter Index: linux-2.6.18-mm3/include/asm-ia64/page.h =================================================================== --- linux-2.6.18-mm3.orig/include/asm-ia64/page.h 2006-10-04 22:44:33.789985329 -0500 +++ linux-2.6.18-mm3/include/asm-ia64/page.h 2006-10-04 22:44:53.994726943 -0500 @@ -99,22 +99,14 @@ do { \ #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) -#ifdef CONFIG_VIRTUAL_MEM_MAP -extern int ia64_pfn_valid (unsigned long pfn); -#elif defined(CONFIG_FLATMEM) -# define ia64_pfn_valid(pfn) 1 -#endif - -#include - -#ifdef CONFIG_FLATMEM -# define pfn_valid(pfn) (((pfn) < max_mapnr) && ia64_pfn_valid(pfn)) -#elif defined(CONFIG_DISCONTIGMEM) +#if !defined(CONFIG_VIRTUAL_MEM_MAP) && defined(CONFIG_DISCONTIGMEM) extern unsigned long min_low_pfn; extern unsigned long max_low_pfn; # define pfn_valid(pfn) (((pfn) >= min_low_pfn) && ((pfn) < max_low_pfn) && ia64_pfn_valid(pfn)) #endif +#include + #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) Index: linux-2.6.18-mm3/arch/ia64/mm/init.c =================================================================== --- linux-2.6.18-mm3.orig/arch/ia64/mm/init.c 2006-10-04 22:44:01.994473683 -0500 +++ linux-2.6.18-mm3/arch/ia64/mm/init.c 2006-10-04 22:44:54.014259756 -0500 @@ -565,18 +565,6 @@ memmap_init (unsigned long size, int nid } } -int -ia64_pfn_valid (unsigned long pfn) -{ - char byte; - struct page *pg = pfn_to_page(pfn); - - return (__get_user(byte, (char __user *) pg) == 0) - && ((((u64)pg & PAGE_MASK) == (((u64)(pg + 1) - 1) & PAGE_MASK)) - || (__get_user(byte, (char __user *) (pg + 1) - 1) == 0)); -} -EXPORT_SYMBOL(ia64_pfn_valid); - int __init find_largest_hole (u64 start, u64 end, void *arg) { Index: linux-2.6.18-mm3/include/linux/mmzone.h =================================================================== --- linux-2.6.18-mm3.orig/include/linux/mmzone.h 2006-10-04 22:44:33.804634938 -0500 +++ linux-2.6.18-mm3/include/linux/mmzone.h 2006-10-04 22:44:54.027932726 -0500 @@ -338,9 +338,30 @@ struct node_active_region { }; #endif /* CONFIG_ARCH_POPULATES_NODE_MAP */ -#if defined(CONFIG_VIRTUAL_MEM_MAP) || !defined(CONFIG_DISCONTIGMEM) -/* The array of struct pages - for discontigmem use pgdat->lmem_map */ +#if defined(CONFIG_VIRTUAL_MEM_MAP) || defined(CONFIG_FLATMEM) extern struct page *mem_map; +extern unsigned long max_mapnr; + +#ifndef ARCH_PFN_OFFSET +#define ARCH_PFN_OFFSET (0UL) +#endif + +#define pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) +#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + \ + ARCH_PFN_OFFSET) + +#ifdef CONFIG_VIRTUAL_MEM_MAP +#ifndef VMEMMAP_PAGE_SIZE +#define VMEMMAP_PAGE_SIZE PAGE_SIZE +#endif +extern int __pfn_valid(unsigned long pfn); +#else +#define __pfn_valid(pfn) 1 +#endif + +static inline int pfn_valid(unsigned long pfn) { + return pfn - ARCH_PFN_OFFSET < max_mapnr && __pfn_valid(pfn); +} #endif /* Index: linux-2.6.18-mm3/mm/memory.c =================================================================== --- linux-2.6.18-mm3.orig/mm/memory.c 2006-10-04 22:44:02.033539306 -0500 +++ linux-2.6.18-mm3/mm/memory.c 2006-10-04 22:44:54.046488898 -0500 @@ -60,13 +60,56 @@ #include #include +#include + #if defined(CONFIG_VIRTUAL_MEM_MAP) || !defined(CONFIG_NEED_MULTIPLE_NODES) -/* use the per-pgdat data instead for discontigmem - mbligh */ +/* + * The memory map is either directly mapped without holes or a virtual + * memory map. Then mem_map may have holes that __pfn_valid can check for. + * + * Sparse and discontig without a virtual memory map have their own way + * of doing this (We should strive to get rid of these exceptions). + */ unsigned long max_mapnr; struct page *mem_map; EXPORT_SYMBOL(max_mapnr); EXPORT_SYMBOL(mem_map); + +#ifdef CONFIG_VIRTUAL_MEM_MAP +int __pfn_valid (unsigned long pfn) +{ + char byte; + struct page *page = pfn_to_page(pfn); + + /* + * A pfn is valid if we can read the first byte of the + * page struct it points to. + */ + int result = __get_user(byte, (char __user *)page) == 0; + + /* + & If the struct page may cross a page boundary then we also + * need to check the last byte. For the common case that + * a virtual memory map page contains exactly N struct pages + * this test will always be false and we avoid the + * additional checks. + */ + if (result && VMEMMAP_PAGE_SIZE % sizeof(struct page)) { + char *end = ((char *)page) + sizeof(struct page) - 1; + + /* + * A check is only necessary if the end of the page struct + * is on a different page of the virtual memory map. + */ + if ((unsigned long)end / VMEMMAP_PAGE_SIZE != + (unsigned long)page / VMEMMAP_PAGE_SIZE) + result = __get_user(byte, (char __user *)end) == 0; + } + return result; +} +EXPORT_SYMBOL(__pfn_valid); +#endif #endif unsigned long num_physpages; Index: linux-2.6.18-mm3/include/linux/mm.h =================================================================== --- linux-2.6.18-mm3.orig/include/linux/mm.h 2006-10-04 22:43:38.676203389 -0500 +++ linux-2.6.18-mm3/include/linux/mm.h 2006-10-04 22:44:54.065045071 -0500 @@ -21,10 +21,6 @@ struct mempolicy; struct anon_vma; -#ifndef CONFIG_DISCONTIGMEM /* Don't use mapnrs, do it properly */ -extern unsigned long max_mapnr; -#endif - extern unsigned long num_physpages; extern void * high_memory; extern unsigned long vmalloc_earlyreserve; Index: linux-2.6.18-mm3/arch/ia64/mm/discontig.c =================================================================== --- linux-2.6.18-mm3.orig/arch/ia64/mm/discontig.c 2006-10-04 22:44:01.981777355 -0500 +++ linux-2.6.18-mm3/arch/ia64/mm/discontig.c 2006-10-04 22:45:53.514140796 -0500 @@ -506,6 +506,9 @@ void __init find_memory(void) initialize_pernode_data(); max_pfn = max_low_pfn; +#ifdef CONFIG_VIRTUAL_MEM_MAP + max_mapnr = max_low_pfn; +#endif find_initrd(); } Index: linux-2.6.18-mm3/include/asm-generic/memory_model.h =================================================================== --- linux-2.6.18-mm3.orig/include/asm-generic/memory_model.h 2006-10-04 22:44:33.777289001 -0500 +++ linux-2.6.18-mm3/include/asm-generic/memory_model.h 2006-10-04 22:44:54.088484446 -0500 @@ -4,13 +4,7 @@ #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#if defined(CONFIG_FLATMEM) || defined(CONFIG_VIRTUAL_MEM_MAP) - -#ifndef ARCH_PFN_OFFSET -#define ARCH_PFN_OFFSET (0UL) -#endif - -#elif defined(CONFIG_DISCONTIGMEM) +#if defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_VIRTUAL_MEM_MAP) #ifndef arch_pfn_to_nid #define arch_pfn_to_nid(pfn) pfn_to_nid(pfn) @@ -24,14 +18,9 @@ #endif /* CONFIG_DISCONTIGMEM */ /* - * supports 3 memory models. + * supports 2 memory models. */ -#if defined(CONFIG_FLATMEM) || defined(CONFIG_VIRTUAL_MEM_MAP) - -#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) -#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \ - ARCH_PFN_OFFSET) -#elif defined(CONFIG_DISCONTIGMEM) +#if defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_VIRTUAL_MEM_MAP) #define __pfn_to_page(pfn) \ ({ unsigned long __pfn = (pfn); \ @@ -62,7 +51,6 @@ struct mem_section *__sec = __pfn_to_section(__pfn); \ __section_mem_map_addr(__sec) + __pfn; \ }) -#endif /* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */ #ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE struct page; @@ -73,6 +61,7 @@ extern unsigned long page_to_pfn(struct #define page_to_pfn __page_to_pfn #define pfn_to_page __pfn_to_page #endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */ +#endif /* DISCONTIGMEM/SPARSEMEM */ #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */