Merge branch 'for-linus' of git://git.open-osd.org/linux-open-osd
[pandora-kernel.git] / drivers / base / memory.c
index ca8bfe5..8272d92 100644 (file)
@@ -224,45 +224,66 @@ int memory_isolate_notify(unsigned long val, void *v)
 }
 
 /*
- * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is
- * OK to have direct references to sparsemem variables in here.
+ * The probe routines leave the pages reserved, just as the bootmem code does.
+ * Make sure they're still that way.
  */
-static int check_page_reservations(unsigned long phys_index)
+static bool pages_correctly_reserved(unsigned long start_pfn,
+                                       unsigned long nr_pages)
 {
-       int i;
+       int i, j;
        struct page *page;
+       unsigned long pfn = start_pfn;
 
-       page = pfn_to_page(phys_index << PFN_SECTION_SHIFT);
+       /*
+        * memmap between sections is not contiguous except with
+        * SPARSEMEM_VMEMMAP. We lookup the page once per section
+        * and assume memmap is contiguous within each section
+        */
+       for (i = 0; i < sections_per_block; i++, pfn += PAGES_PER_SECTION) {
+               if (WARN_ON_ONCE(!pfn_valid(pfn)))
+                       return false;
+               page = pfn_to_page(pfn);
 
-       for (i = 0; i < PAGES_PER_SECTION; i++) {
-               if (PageReserved(page + i))
-                       continue;
+               for (j = 0; j < PAGES_PER_SECTION; j++) {
+                       if (PageReserved(page + j))
+                               continue;
 
-               printk(KERN_WARNING "section number %ld page number %d "
-                       "not reserved, was it already online?\n", phys_index, i);
-                       return -EBUSY;
+                       printk(KERN_WARNING "section number %ld page number %d "
+                               "not reserved, was it already online?\n",
+                               pfn_to_section_nr(pfn), j);
+
+                       return false;
+               }
        }
 
-       return 0;
+       return true;
 }
 
+/*
+ * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is
+ * OK to have direct references to sparsemem variables in here.
+ */
 static int
 memory_block_action(unsigned long phys_index, unsigned long action)
 {
        unsigned long start_pfn, start_paddr;
        unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
-       struct page *page;
+       struct page *first_page;
        int ret;
 
-       page = pfn_to_page(phys_index << PFN_SECTION_SHIFT);
+       first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT);
 
        switch (action) {
                case MEM_ONLINE:
-                       start_pfn = page_to_pfn(page);
+                       start_pfn = page_to_pfn(first_page);
+
+                       if (!pages_correctly_reserved(start_pfn, nr_pages))
+                               return -EBUSY;
+
                        ret = online_pages(start_pfn, nr_pages);
                        break;
                case MEM_OFFLINE:
-                       start_paddr = page_to_pfn(page) << PAGE_SHIFT;
+                       start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
                        ret = remove_memory(start_paddr,
                                            nr_pages << PAGE_SHIFT);
                        break;
@@ -278,7 +299,7 @@ memory_block_action(unsigned long phys_index, unsigned long action)
 static int memory_block_change_state(struct memory_block *mem,
                unsigned long to_state, unsigned long from_state_req)
 {
-       int i, ret = 0;
+       int ret = 0;
 
        mutex_lock(&mem->state_mutex);
 
@@ -290,19 +311,6 @@ static int memory_block_change_state(struct memory_block *mem,
        if (to_state == MEM_OFFLINE)
                mem->state = MEM_GOING_OFFLINE;
 
-       if (to_state == MEM_ONLINE) {
-               /*
-                * The probe routines leave the pages reserved, just
-                * as the bootmem code does.  Make sure they're still
-                * that way.
-                */
-               for (i = 0; i < sections_per_block; i++) {
-                       ret = check_page_reservations(mem->start_section_nr + i);
-                       if (ret)
-                               return ret;
-               }
-       }
-
        ret = memory_block_action(mem->start_section_nr, to_state);
 
        if (ret)