]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/mm/init_64.c
Merge remote-tracking branch 'wireless-next/master'
[karo-tx-linux.git] / arch / powerpc / mm / init_64.c
index 8ed035d2edb5a51540d580e4b6a6a5a106b3a5f9..e3734edffa697ac42cf9d4563fb754b8602d3bcd 100644 (file)
@@ -304,5 +304,54 @@ void register_page_bootmem_memmap(unsigned long section_nr,
                                  struct page *start_page, unsigned long size)
 {
 }
-#endif /* CONFIG_SPARSEMEM_VMEMMAP */
 
+/*
+ * We do not have access to the sparsemem vmemmap, so we fallback to
+ * walking the list of sparsemem blocks which we already maintain for
+ * the sake of crashdump. In the long run, we might want to maintain
+ * a tree if performance of that linear walk becomes a problem.
+ *
+ * realmode_pfn_to_page functions can fail due to:
+ * 1) As real sparsemem blocks do not lay in RAM continously (they
+ * are in virtual address space which is not available in the real mode),
+ * the requested page struct can be split between blocks so get_page/put_page
+ * may fail.
+ * 2) When huge pages are used, the get_page/put_page API will fail
+ * in real mode as the linked addresses in the page struct are virtual
+ * too.
+ */
+struct page *realmode_pfn_to_page(unsigned long pfn)
+{
+       struct vmemmap_backing *vmem_back;
+       struct page *page;
+       unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
+       unsigned long pg_va = (unsigned long) pfn_to_page(pfn);
+
+       for (vmem_back = vmemmap_list; vmem_back; vmem_back = vmem_back->list) {
+               if (pg_va < vmem_back->virt_addr)
+                       continue;
+
+               /* Check that page struct is not split between real pages */
+               if ((pg_va + sizeof(struct page)) >
+                               (vmem_back->virt_addr + page_size))
+                       return NULL;
+
+               page = (struct page *) (vmem_back->phys + pg_va -
+                               vmem_back->virt_addr);
+               return page;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(realmode_pfn_to_page);
+
+#elif defined(CONFIG_FLATMEM)
+
+struct page *realmode_pfn_to_page(unsigned long pfn)
+{
+       struct page *page = pfn_to_page(pfn);
+       return page;
+}
+EXPORT_SYMBOL_GPL(realmode_pfn_to_page);
+
+#endif /* CONFIG_SPARSEMEM_VMEMMAP/CONFIG_FLATMEM */