]> err.no Git - linux-2.6/blobdiff - arch/powerpc/mm/mem.c
[PATCH] powerpc: Merge vdso's and add vdso support to 32 bits kernel
[linux-2.6] / arch / powerpc / mm / mem.c
index 3ca331728d212d3541e8c21940a7ebf9cb8b4f14..e2c95fcb8055186cf2e4d452d0d125c86e024820 100644 (file)
@@ -46,9 +46,7 @@
 #include <asm/prom.h>
 #include <asm/lmb.h>
 #include <asm/sections.h>
-#ifdef CONFIG_PPC64
 #include <asm/vdso.h>
-#endif
 
 #include "mmu_decl.h"
 
 
 int init_bootmem_done;
 int mem_init_done;
+unsigned long memory_limit;
+
+extern void hash_preload(struct mm_struct *mm, unsigned long ea,
+                        unsigned long access, unsigned long trap);
 
 /*
  * This is called by /dev/mem to know if a given address has to
@@ -101,6 +103,87 @@ pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
 }
 EXPORT_SYMBOL(phys_mem_access_prot);
 
+#ifdef CONFIG_MEMORY_HOTPLUG
+
+void online_page(struct page *page)
+{
+       ClearPageReserved(page);
+       set_page_count(page, 0);
+       free_cold_page(page);
+       totalram_pages++;
+       num_physpages++;
+}
+
+/*
+ * This works only for the non-NUMA case.  Later, we'll need a lookup
+ * to convert from real physical addresses to nid, that doesn't use
+ * pfn_to_nid().
+ */
+int __devinit add_memory(u64 start, u64 size)
+{
+       struct pglist_data *pgdata = NODE_DATA(0);
+       struct zone *zone;
+       unsigned long start_pfn = start >> PAGE_SHIFT;
+       unsigned long nr_pages = size >> PAGE_SHIFT;
+
+       start += KERNELBASE;
+       create_section_mapping(start, start + size);
+
+       /* this should work for most non-highmem platforms */
+       zone = pgdata->node_zones;
+
+       return __add_pages(zone, start_pfn, nr_pages);
+
+       return 0;
+}
+
+/*
+ * First pass at this code will check to determine if the remove
+ * request is within the RMO.  Do not allow removal within the RMO.
+ */
+int __devinit remove_memory(u64 start, u64 size)
+{
+       struct zone *zone;
+       unsigned long start_pfn, end_pfn, nr_pages;
+
+       start_pfn = start >> PAGE_SHIFT;
+       nr_pages = size >> PAGE_SHIFT;
+       end_pfn = start_pfn + nr_pages;
+
+       printk("%s(): Attempting to remove memoy in range "
+                       "%lx to %lx\n", __func__, start, start+size);
+       /*
+        * check for range within RMO
+        */
+       zone = page_zone(pfn_to_page(start_pfn));
+
+       printk("%s(): memory will be removed from "
+                       "the %s zone\n", __func__, zone->name);
+
+       /*
+        * not handling removing memory ranges that
+        * overlap multiple zones yet
+        */
+       if (end_pfn > (zone->zone_start_pfn + zone->spanned_pages))
+               goto overlap;
+
+       /* make sure it is NOT in RMO */
+       if ((start < lmb.rmo_size) || ((start+size) < lmb.rmo_size)) {
+               printk("%s(): range to be removed must NOT be in RMO!\n",
+                       __func__);
+               goto in_rmo;
+       }
+
+       return __remove_pages(zone, start_pfn, nr_pages);
+
+overlap:
+       printk("%s(): memory range to be removed overlaps "
+               "multiple zones!!!\n", __func__);
+in_rmo:
+       return -1;
+}
+#endif /* CONFIG_MEMORY_HOTPLUG */
+
 void show_mem(void)
 {
        unsigned long total = 0, reserved = 0;
@@ -114,6 +197,8 @@ void show_mem(void)
        show_free_areas();
        printk("Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
        for_each_pgdat(pgdat) {
+               unsigned long flags;
+               pgdat_resize_lock(pgdat, &flags);
                for (i = 0; i < pgdat->node_spanned_pages; i++) {
                        page = pgdat_page_nr(pgdat, i);
                        total++;
@@ -126,6 +211,7 @@ void show_mem(void)
                        else if (page_count(page))
                                shared += page_count(page) - 1;
                }
+               pgdat_resize_unlock(pgdat, &flags);
        }
        printk("%ld pages of RAM\n", total);
 #ifdef CONFIG_HIGHMEM
@@ -274,7 +360,7 @@ void __init mem_init(void)
        }
 
        codesize = (unsigned long)&_sdata - (unsigned long)&_stext;
-       datasize = (unsigned long)&__init_begin - (unsigned long)&_sdata;
+       datasize = (unsigned long)&_edata - (unsigned long)&_sdata;
        initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin;
        bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start;
 
@@ -309,10 +395,8 @@ void __init mem_init(void)
 
        mem_init_done = 1;
 
-#ifdef CONFIG_PPC64
        /* Initialize the vDSO */
        vdso_init();
-#endif
 }
 
 /*
@@ -412,18 +496,10 @@ EXPORT_SYMBOL(flush_icache_user_range);
 void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
                      pte_t pte)
 {
-       /* handle i-cache coherency */
-       unsigned long pfn = pte_pfn(pte);
-#ifdef CONFIG_PPC32
-       pmd_t *pmd;
-#else
-       unsigned long vsid;
-       void *pgdir;
-       pte_t *ptep;
-       int local = 0;
-       cpumask_t tmp;
-       unsigned long flags;
+#ifdef CONFIG_PPC_STD_MMU
+       unsigned long access = 0, trap;
 #endif
+       unsigned long pfn = pte_pfn(pte);
 
        /* handle i-cache coherency */
        if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE) &&
@@ -454,30 +530,21 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
        /* We only want HPTEs for linux PTEs that have _PAGE_ACCESSED set */
        if (!pte_young(pte) || address >= TASK_SIZE)
                return;
-#ifdef CONFIG_PPC32
-       if (Hash == 0)
-               return;
-       pmd = pmd_offset(pgd_offset(vma->vm_mm, address), address);
-       if (!pmd_none(*pmd))
-               add_hash_page(vma->vm_mm->context, address, pmd_val(*pmd));
-#else
-       pgdir = vma->vm_mm->pgd;
-       if (pgdir == NULL)
-               return;
 
-       ptep = find_linux_pte(pgdir, address);
-       if (!ptep)
+       /* We try to figure out if we are coming from an instruction
+        * access fault and pass that down to __hash_page so we avoid
+        * double-faulting on execution of fresh text. We have to test
+        * for regs NULL since init will get here first thing at boot
+        *
+        * We also avoid filling the hash if not coming from a fault
+        */
+       if (current->thread.regs == NULL)
                return;
-
-       vsid = get_vsid(vma->vm_mm->context.id, address);
-
-       local_irq_save(flags);
-       tmp = cpumask_of_cpu(smp_processor_id());
-       if (cpus_equal(vma->vm_mm->cpu_vm_mask, tmp))
-               local = 1;
-
-       __hash_page(address, 0, vsid, ptep, 0x300, local);
-       local_irq_restore(flags);
-#endif
-#endif
+       trap = TRAP(current->thread.regs);
+       if (trap == 0x400)
+               access |= _PAGE_EXEC;
+       else if (trap != 0x300)
+               return;
+       hash_preload(vma->vm_mm, address, access, trap);
+#endif /* CONFIG_PPC_STD_MMU */
 }