Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[sfrench/cifs-2.6.git] / arch / powerpc / mm / slb.c
index d3733912adb43e0c75641f66b32dc1dd2ef62dcb..ff1811ac6c81e837be9c7c5072a364bb32d5d877 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/cputable.h>
 #include <asm/cacheflush.h>
 #include <asm/smp.h>
+#include <asm/firmware.h>
 #include <linux/compiler.h>
 
 #ifdef DEBUG
@@ -52,7 +53,8 @@ static inline unsigned long mk_vsid_data(unsigned long ea, unsigned long flags)
        return (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | flags;
 }
 
-static inline void slb_shadow_update(unsigned long esid, unsigned long vsid,
+static inline void slb_shadow_update(unsigned long ea,
+                                    unsigned long flags,
                                     unsigned long entry)
 {
        /*
@@ -60,11 +62,16 @@ static inline void slb_shadow_update(unsigned long esid, unsigned long vsid,
         * updating it.
         */
        get_slb_shadow()->save_area[entry].esid = 0;
-       barrier();
-       get_slb_shadow()->save_area[entry].vsid = vsid;
-       barrier();
-       get_slb_shadow()->save_area[entry].esid = esid;
+       smp_wmb();
+       get_slb_shadow()->save_area[entry].vsid = mk_vsid_data(ea, flags);
+       smp_wmb();
+       get_slb_shadow()->save_area[entry].esid = mk_esid_data(ea, entry);
+       smp_wmb();
+}
 
+static inline void slb_shadow_clear(unsigned long entry)
+{
+       get_slb_shadow()->save_area[entry].esid = 0;
 }
 
 static inline void create_shadowed_slbe(unsigned long ea, unsigned long flags,
@@ -75,8 +82,7 @@ static inline void create_shadowed_slbe(unsigned long ea, unsigned long flags,
         * we don't get a stale entry here if we get preempted by PHYP
         * between these two statements.
         */
-       slb_shadow_update(mk_esid_data(ea, entry), mk_vsid_data(ea, flags),
-                         entry);
+       slb_shadow_update(ea, flags, entry);
 
        asm volatile("slbmte  %0,%1" :
                     : "r" (mk_vsid_data(ea, flags)),
@@ -99,12 +105,13 @@ void slb_flush_and_rebolt(void)
        vflags = SLB_VSID_KERNEL | vmalloc_llp;
 
        ksp_esid_data = mk_esid_data(get_paca()->kstack, 2);
-       if ((ksp_esid_data & ESID_MASK) == PAGE_OFFSET)
+       if ((ksp_esid_data & ESID_MASK) == PAGE_OFFSET) {
                ksp_esid_data &= ~SLB_ESID_V;
-
-       /* Only third entry (stack) may change here so only resave that */
-       slb_shadow_update(ksp_esid_data,
-                         mk_vsid_data(ksp_esid_data, lflags), 2);
+               slb_shadow_clear(2);
+       } else {
+               /* Update stack entry; others don't change */
+               slb_shadow_update(get_paca()->kstack, lflags, 2);
+       }
 
        /* We need to do this all in asm, so we're sure we don't touch
         * the stack between the slbia and rebolting it. */
@@ -122,6 +129,15 @@ void slb_flush_and_rebolt(void)
                     : "memory");
 }
 
+void slb_vmalloc_update(void)
+{
+       unsigned long vflags;
+
+       vflags = SLB_VSID_KERNEL | mmu_psize_defs[mmu_vmalloc_psize].sllp;
+       slb_shadow_update(VMALLOC_START, vflags, 1);
+       slb_flush_and_rebolt();
+}
+
 /* Flush all user entries from the segment table of the current processor. */
 void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 {
@@ -193,15 +209,10 @@ static inline void patch_slb_encoding(unsigned int *insn_addr,
 void slb_initialize(void)
 {
        unsigned long linear_llp, vmalloc_llp, io_llp;
+       unsigned long lflags, vflags;
        static int slb_encoding_inited;
        extern unsigned int *slb_miss_kernel_load_linear;
        extern unsigned int *slb_miss_kernel_load_io;
-#ifdef CONFIG_HUGETLB_PAGE
-       extern unsigned int *slb_miss_user_load_huge;
-       unsigned long huge_llp;
-
-       huge_llp = mmu_psize_defs[mmu_huge_psize].sllp;
-#endif
 
        /* Prepare our SLB miss handler based on our page size */
        linear_llp = mmu_psize_defs[mmu_linear_psize].sllp;
@@ -218,18 +229,14 @@ void slb_initialize(void)
 
                DBG("SLB: linear  LLP = %04x\n", linear_llp);
                DBG("SLB: io      LLP = %04x\n", io_llp);
-#ifdef CONFIG_HUGETLB_PAGE
-               patch_slb_encoding(slb_miss_user_load_huge,
-                                  SLB_VSID_USER | huge_llp);
-               DBG("SLB: huge    LLP = %04x\n", huge_llp);
-#endif
        }
 
+       get_paca()->stab_rr = SLB_NUM_BOLTED;
+
        /* On iSeries the bolted entries have already been set up by
         * the hypervisor from the lparMap data in head.S */
-#ifndef CONFIG_PPC_ISERIES
- {
-       unsigned long lflags, vflags;
+       if (firmware_has_feature(FW_FEATURE_ISERIES))
+               return;
 
        lflags = SLB_VSID_KERNEL | linear_llp;
        vflags = SLB_VSID_KERNEL | vmalloc_llp;
@@ -247,8 +254,4 @@ void slb_initialize(void)
         * elsewhere, we'll call _switch() which will bolt in the new
         * one. */
        asm volatile("isync":::"memory");
- }
-#endif /* CONFIG_PPC_ISERIES */
-
-       get_paca()->stab_rr = SLB_NUM_BOLTED;
 }