[PATCH] x86: fix laptop bootup hang in init_acpi()
[safe/jmp/linux-2.6] / arch / x86_64 / kernel / e820.c
index 5d1275a..4651fd2 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/string.h>
 #include <linux/kexec.h>
 #include <linux/module.h>
+#include <linux/mm.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>
@@ -53,13 +54,13 @@ static inline int bad_addr(unsigned long *addrp, unsigned long size)
 
        /* various gunk below that needed for SMP startup */
        if (addr < 0x8000) { 
-               *addrp = 0x8000;
+               *addrp = PAGE_ALIGN(0x8000);
                return 1; 
        }
 
        /* direct mapping tables of the kernel */
        if (last >= table_start<<PAGE_SHIFT && addr < table_end<<PAGE_SHIFT) { 
-               *addrp = table_end << PAGE_SHIFT; 
+               *addrp = PAGE_ALIGN(table_end << PAGE_SHIFT);
                return 1;
        } 
 
@@ -67,21 +68,28 @@ static inline int bad_addr(unsigned long *addrp, unsigned long size)
 #ifdef CONFIG_BLK_DEV_INITRD
        if (LOADER_TYPE && INITRD_START && last >= INITRD_START && 
            addr < INITRD_START+INITRD_SIZE) { 
-               *addrp = INITRD_START + INITRD_SIZE; 
+               *addrp = PAGE_ALIGN(INITRD_START + INITRD_SIZE);
                return 1;
        } 
 #endif
        /* kernel code */
-       if (last >= __pa_symbol(&_text) && last < __pa_symbol(&_end)) {
-               *addrp = __pa_symbol(&_end);
+       if (last >= __pa_symbol(&_text) && addr < __pa_symbol(&_end)) {
+               *addrp = PAGE_ALIGN(__pa_symbol(&_end));
                return 1;
        }
 
        if (last >= ebda_addr && addr < ebda_addr + ebda_size) {
-               *addrp = ebda_addr + ebda_size;
+               *addrp = PAGE_ALIGN(ebda_addr + ebda_size);
                return 1;
        }
 
+#ifdef CONFIG_NUMA
+       /* NUMA memory to node map */
+       if (last >= nodemap_addr && addr < nodemap_addr + nodemap_size) {
+               *addrp = nodemap_addr + nodemap_size;
+               return 1;
+       }
+#endif
        /* XXX ramdisk image here? */ 
        return 0;
 } 
@@ -151,7 +159,7 @@ unsigned long __init find_e820_area(unsigned long start, unsigned long end, unsi
                        continue; 
                while (bad_addr(&addr, size) && addr+size <= ei->addr+ei->size)
                        ;
-               last = addr + size;
+               last = PAGE_ALIGN(addr) + size;
                if (last > ei->addr + ei->size)
                        continue;
                if (last > end) 
@@ -161,59 +169,14 @@ unsigned long __init find_e820_area(unsigned long start, unsigned long end, unsi
        return -1UL;            
 } 
 
-/* 
- * Free bootmem based on the e820 table for a node.
- */
-void __init e820_bootmem_free(pg_data_t *pgdat, unsigned long start,unsigned long end)
-{
-       int i;
-       for (i = 0; i < e820.nr_map; i++) {
-               struct e820entry *ei = &e820.map[i]; 
-               unsigned long last, addr;
-
-               if (ei->type != E820_RAM || 
-                   ei->addr+ei->size <= start || 
-                   ei->addr >= end)
-                       continue;
-
-               addr = round_up(ei->addr, PAGE_SIZE);
-               if (addr < start) 
-                       addr = start;
-
-               last = round_down(ei->addr + ei->size, PAGE_SIZE); 
-               if (last >= end)
-                       last = end; 
-
-               if (last > addr && last-addr >= PAGE_SIZE)
-                       free_bootmem_node(pgdat, addr, last-addr);
-       }
-}
-
 /*
  * Find the highest page frame number we have available
  */
 unsigned long __init e820_end_of_ram(void)
 {
-       int i;
        unsigned long end_pfn = 0;
+       end_pfn = find_max_pfn_with_active_regions();
        
-       for (i = 0; i < e820.nr_map; i++) {
-               struct e820entry *ei = &e820.map[i]; 
-               unsigned long start, end;
-
-               start = round_up(ei->addr, PAGE_SIZE); 
-               end = round_down(ei->addr + ei->size, PAGE_SIZE); 
-               if (start >= end)
-                       continue;
-               if (ei->type == E820_RAM) { 
-               if (end > end_pfn<<PAGE_SHIFT)
-                       end_pfn = end>>PAGE_SHIFT;
-               } else { 
-                       if (end > end_pfn_map<<PAGE_SHIFT) 
-                               end_pfn_map = end>>PAGE_SHIFT;
-               } 
-       }
-
        if (end_pfn > end_pfn_map) 
                end_pfn_map = end_pfn;
        if (end_pfn_map > MAXMEM>>PAGE_SHIFT)
@@ -223,20 +186,18 @@ unsigned long __init e820_end_of_ram(void)
        if (end_pfn > end_pfn_map) 
                end_pfn = end_pfn_map; 
 
+       printk("end_pfn_map = %lu\n", end_pfn_map);
        return end_pfn; 
 }
 
-/* 
- * Compute how much memory is missing in a range.
- * Unlike the other functions in this file the arguments are in page numbers.
+/*
+ * Find the hole size in the range.
  */
-unsigned long __init
-e820_hole_size(unsigned long start_pfn, unsigned long end_pfn)
+unsigned long __init e820_hole_size(unsigned long start, unsigned long end)
 {
        unsigned long ram = 0;
-       unsigned long start = start_pfn << PAGE_SHIFT;
-       unsigned long end = end_pfn << PAGE_SHIFT;
        int i;
+
        for (i = 0; i < e820.nr_map; i++) {
                struct e820entry *ei = &e820.map[i];
                unsigned long last, addr;
@@ -257,7 +218,7 @@ e820_hole_size(unsigned long start_pfn, unsigned long end_pfn)
                if (last > addr)
                        ram += last - addr;
        }
-       return ((end - start) - ram) >> PAGE_SHIFT;
+       return ((end - start) - ram);
 }
 
 /*
@@ -294,6 +255,96 @@ void __init e820_reserve_resources(void)
        }
 }
 
+/* Mark pages corresponding to given address range as nosave */
+static void __init
+e820_mark_nosave_range(unsigned long start, unsigned long end)
+{
+       unsigned long pfn, max_pfn;
+
+       if (start >= end)
+               return;
+
+       printk("Nosave address range: %016lx - %016lx\n", start, end);
+       max_pfn = end >> PAGE_SHIFT;
+       for (pfn = start >> PAGE_SHIFT; pfn < max_pfn; pfn++)
+               if (pfn_valid(pfn))
+                       SetPageNosave(pfn_to_page(pfn));
+}
+
+/*
+ * Find the ranges of physical addresses that do not correspond to
+ * e820 RAM areas and mark the corresponding pages as nosave for software
+ * suspend and suspend to RAM.
+ *
+ * This function requires the e820 map to be sorted and without any
+ * overlapping entries and assumes the first e820 area to be RAM.
+ */
+void __init e820_mark_nosave_regions(void)
+{
+       int i;
+       unsigned long paddr;
+
+       paddr = round_down(e820.map[0].addr + e820.map[0].size, PAGE_SIZE);
+       for (i = 1; i < e820.nr_map; i++) {
+               struct e820entry *ei = &e820.map[i];
+
+               if (paddr < ei->addr)
+                       e820_mark_nosave_range(paddr,
+                                       round_up(ei->addr, PAGE_SIZE));
+
+               paddr = round_down(ei->addr + ei->size, PAGE_SIZE);
+               if (ei->type != E820_RAM)
+                       e820_mark_nosave_range(round_up(ei->addr, PAGE_SIZE),
+                                       paddr);
+
+               if (paddr >= (end_pfn << PAGE_SHIFT))
+                       break;
+       }
+}
+
+/* Walk the e820 map and register active regions within a node */
+void __init
+e820_register_active_regions(int nid, unsigned long start_pfn,
+                                                       unsigned long end_pfn)
+{
+       int i;
+       unsigned long ei_startpfn, ei_endpfn;
+       for (i = 0; i < e820.nr_map; i++) {
+               struct e820entry *ei = &e820.map[i];
+               ei_startpfn = round_up(ei->addr, PAGE_SIZE) >> PAGE_SHIFT;
+               ei_endpfn = round_down(ei->addr + ei->size, PAGE_SIZE)
+                                                               >> PAGE_SHIFT;
+
+               /* Skip map entries smaller than a page */
+               if (ei_startpfn >= ei_endpfn)
+                       continue;
+
+               /* Check if end_pfn_map should be updated */
+               if (ei->type != E820_RAM && ei_endpfn > end_pfn_map)
+                       end_pfn_map = ei_endpfn;
+
+               /* Skip if map is outside the node */
+               if (ei->type != E820_RAM ||
+                               ei_endpfn <= start_pfn ||
+                               ei_startpfn >= end_pfn)
+                       continue;
+
+               /* Check for overlaps */
+               if (ei_startpfn < start_pfn)
+                       ei_startpfn = start_pfn;
+               if (ei_endpfn > end_pfn)
+                       ei_endpfn = end_pfn;
+
+               /* Obey end_user_pfn to save on memmap */
+               if (ei_startpfn >= end_user_pfn)
+                       continue;
+               if (ei_endpfn > end_user_pfn)
+                       ei_endpfn = end_user_pfn;
+
+               add_active_range(nid, ei_startpfn, ei_endpfn);
+       }
+}
+
 /* 
  * Add a memory region to the kernel e820 map.
  */ 
@@ -581,7 +632,9 @@ static int __init parse_memmap_opt(char *p)
                 * size before original memory map is
                 * reset.
                 */
+               e820_register_active_regions(0, 0, -1UL);
                saved_max_pfn = e820_end_of_ram();
+               remove_all_active_ranges();
 #endif
                end_pfn_map = 0;
                e820.nr_map = 0;