netfilter: xt_hashlimit: simplify seqfile code
[safe/jmp/linux-2.6] / arch / powerpc / mm / hash_native_64.c
index 3b82050..056d23a 100644 (file)
@@ -26,6 +26,8 @@
 #include <asm/tlb.h>
 #include <asm/cputable.h>
 #include <asm/udbg.h>
+#include <asm/kexec.h>
+#include <asm/ppc-opcode.h>
 
 #ifdef DEBUG_LOW
 #define DBG_LOW(fmt...) udbg_printf(fmt)
@@ -37,7 +39,7 @@
 
 static DEFINE_SPINLOCK(native_tlbie_lock);
 
-static inline void __tlbie(unsigned long va, unsigned int psize)
+static inline void __tlbie(unsigned long va, int psize, int ssize)
 {
        unsigned int penc;
 
@@ -47,18 +49,27 @@ static inline void __tlbie(unsigned long va, unsigned int psize)
        switch (psize) {
        case MMU_PAGE_4K:
                va &= ~0xffful;
-               asm volatile("tlbie %0,0" : : "r" (va) : "memory");
+               va |= ssize << 8;
+               asm volatile(ASM_MMU_FTR_IFCLR("tlbie %0,0", PPC_TLBIE(%1,%0),
+                                              %2)
+                            : : "r" (va), "r"(0), "i" (MMU_FTR_TLBIE_206)
+                            : "memory");
                break;
        default:
                penc = mmu_psize_defs[psize].penc;
                va &= ~((1ul << mmu_psize_defs[psize].shift) - 1);
-               va |= (0x7f >> (8 - penc)) << 12;
-               asm volatile("tlbie %0,1" : : "r" (va) : "memory");
+               va |= penc << 12;
+               va |= ssize << 8;
+               va |= 1; /* L */
+               asm volatile(ASM_MMU_FTR_IFCLR("tlbie %0,1", PPC_TLBIE(%1,%0),
+                                              %2)
+                            : : "r" (va), "r"(0), "i" (MMU_FTR_TLBIE_206)
+                            : "memory");
                break;
        }
 }
 
-static inline void __tlbiel(unsigned long va, unsigned int psize)
+static inline void __tlbiel(unsigned long va, int psize, int ssize)
 {
        unsigned int penc;
 
@@ -68,13 +79,16 @@ static inline void __tlbiel(unsigned long va, unsigned int psize)
        switch (psize) {
        case MMU_PAGE_4K:
                va &= ~0xffful;
+               va |= ssize << 8;
                asm volatile(".long 0x7c000224 | (%0 << 11) | (0 << 21)"
                             : : "r"(va) : "memory");
                break;
        default:
                penc = mmu_psize_defs[psize].penc;
                va &= ~((1ul << mmu_psize_defs[psize].shift) - 1);
-               va |= (0x7f >> (8 - penc)) << 12;
+               va |= penc << 12;
+               va |= ssize << 8;
+               va |= 1; /* L */
                asm volatile(".long 0x7c000224 | (%0 << 11) | (1 << 21)"
                             : : "r"(va) : "memory");
                break;
@@ -82,7 +96,7 @@ static inline void __tlbiel(unsigned long va, unsigned int psize)
 
 }
 
-static inline void tlbie(unsigned long va, int psize, int local)
+static inline void tlbie(unsigned long va, int psize, int ssize, int local)
 {
        unsigned int use_local = local && cpu_has_feature(CPU_FTR_TLBIEL);
        int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE);
@@ -93,17 +107,17 @@ static inline void tlbie(unsigned long va, int psize, int local)
                spin_lock(&native_tlbie_lock);
        asm volatile("ptesync": : :"memory");
        if (use_local) {
-               __tlbiel(va, psize);
+               __tlbiel(va, psize, ssize);
                asm volatile("ptesync": : :"memory");
        } else {
-               __tlbie(va, psize);
+               __tlbie(va, psize, ssize);
                asm volatile("eieio; tlbsync; ptesync": : :"memory");
        }
        if (lock_tlbie && !use_local)
                spin_unlock(&native_tlbie_lock);
 }
 
-static inline void native_lock_hpte(hpte_t *hptep)
+static inline void native_lock_hpte(struct hash_pte *hptep)
 {
        unsigned long *word = &hptep->v;
 
@@ -115,7 +129,7 @@ static inline void native_lock_hpte(hpte_t *hptep)
        }
 }
 
-static inline void native_unlock_hpte(hpte_t *hptep)
+static inline void native_unlock_hpte(struct hash_pte *hptep)
 {
        unsigned long *word = &hptep->v;
 
@@ -123,11 +137,11 @@ static inline void native_unlock_hpte(hpte_t *hptep)
        clear_bit(HPTE_LOCK_BIT, word);
 }
 
-long native_hpte_insert(unsigned long hpte_group, unsigned long va,
+static long native_hpte_insert(unsigned long hpte_group, unsigned long va,
                        unsigned long pa, unsigned long rflags,
-                       unsigned long vflags, int psize)
+                       unsigned long vflags, int psize, int ssize)
 {
-       hpte_t *hptep = htab_address + hpte_group;
+       struct hash_pte *hptep = htab_address + hpte_group;
        unsigned long hpte_v, hpte_r;
        int i;
 
@@ -152,7 +166,7 @@ long native_hpte_insert(unsigned long hpte_group, unsigned long va,
        if (i == HPTES_PER_GROUP)
                return -1;
 
-       hpte_v = hpte_encode_v(va, psize) | vflags | HPTE_V_VALID;
+       hpte_v = hpte_encode_v(va, psize, ssize) | vflags | HPTE_V_VALID;
        hpte_r = hpte_encode_r(pa, psize) | rflags;
 
        if (!(vflags & HPTE_V_BOLTED)) {
@@ -162,7 +176,7 @@ long native_hpte_insert(unsigned long hpte_group, unsigned long va,
 
        hptep->r = hpte_r;
        /* Guarantee the second dword is visible before the valid bit */
-       __asm__ __volatile__ ("eieio" : : : "memory");
+       eieio();
        /*
         * Now set the first dword including the valid bit
         * NOTE: this also unlocks the hpte
@@ -176,7 +190,7 @@ long native_hpte_insert(unsigned long hpte_group, unsigned long va,
 
 static long native_hpte_remove(unsigned long hpte_group)
 {
-       hpte_t *hptep;
+       struct hash_pte *hptep;
        int i;
        int slot_offset;
        unsigned long hpte_v;
@@ -214,13 +228,14 @@ static long native_hpte_remove(unsigned long hpte_group)
 }
 
 static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
-                                unsigned long va, int psize, int local)
+                                unsigned long va, int psize, int ssize,
+                                int local)
 {
-       hpte_t *hptep = htab_address + slot;
+       struct hash_pte *hptep = htab_address + slot;
        unsigned long hpte_v, want_v;
        int ret = 0;
 
-       want_v = hpte_encode_v(va, psize);
+       want_v = hpte_encode_v(va, psize, ssize);
 
        DBG_LOW("    update(va=%016lx, avpnv=%016lx, hash=%016lx, newpp=%x)",
                va, want_v & HPTE_V_AVPN, slot, newpp);
@@ -232,50 +247,42 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
        /* Even if we miss, we need to invalidate the TLB */
        if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
                DBG_LOW(" -> miss\n");
-               native_unlock_hpte(hptep);
                ret = -1;
        } else {
                DBG_LOW(" -> hit\n");
                /* Update the HPTE */
                hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) |
                        (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C));
-               native_unlock_hpte(hptep);
        }
+       native_unlock_hpte(hptep);
 
        /* Ensure it is out of the tlb too. */
-       tlbie(va, psize, local);
+       tlbie(va, psize, ssize, local);
 
        return ret;
 }
 
-static long native_hpte_find(unsigned long va, int psize)
+static long native_hpte_find(unsigned long va, int psize, int ssize)
 {
-       hpte_t *hptep;
+       struct hash_pte *hptep;
        unsigned long hash;
-       unsigned long i, j;
+       unsigned long i;
        long slot;
        unsigned long want_v, hpte_v;
 
-       hash = hpt_hash(va, mmu_psize_defs[psize].shift);
-       want_v = hpte_encode_v(va, psize);
+       hash = hpt_hash(va, mmu_psize_defs[psize].shift, ssize);
+       want_v = hpte_encode_v(va, psize, ssize);
 
-       for (j = 0; j < 2; j++) {
-               slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
-               for (i = 0; i < HPTES_PER_GROUP; i++) {
-                       hptep = htab_address + slot;
-                       hpte_v = hptep->v;
+       /* Bolted mappings are only ever in the primary group */
+       slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
+       for (i = 0; i < HPTES_PER_GROUP; i++) {
+               hptep = htab_address + slot;
+               hpte_v = hptep->v;
 
-                       if (HPTE_V_COMPARE(hpte_v, want_v)
-                           && (hpte_v & HPTE_V_VALID)
-                           && ( !!(hpte_v & HPTE_V_SECONDARY) == j)) {
-                               /* HPTE matches */
-                               if (j)
-                                       slot = -slot;
-                               return slot;
-                       }
-                       ++slot;
-               }
-               hash = ~hash;
+               if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID))
+                       /* HPTE matches */
+                       return slot;
+               ++slot;
        }
 
        return -1;
@@ -289,16 +296,16 @@ static long native_hpte_find(unsigned long va, int psize)
  * No need to lock here because we should be the only user.
  */
 static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
-                                      int psize)
+                                      int psize, int ssize)
 {
        unsigned long vsid, va;
        long slot;
-       hpte_t *hptep;
+       struct hash_pte *hptep;
 
-       vsid = get_kernel_vsid(ea);
-       va = (vsid << 28) | (ea & 0x0fffffff);
+       vsid = get_kernel_vsid(ea, ssize);
+       va = hpt_va(ea, vsid, ssize);
 
-       slot = native_hpte_find(va, psize);
+       slot = native_hpte_find(va, psize, ssize);
        if (slot == -1)
                panic("could not find page to bolt\n");
        hptep = htab_address + slot;
@@ -308,13 +315,13 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
                (newpp & (HPTE_R_PP | HPTE_R_N));
 
        /* Ensure it is out of the tlb too. */
-       tlbie(va, psize, 0);
+       tlbie(va, psize, ssize, 0);
 }
 
 static void native_hpte_invalidate(unsigned long slot, unsigned long va,
-                                  int psize, int local)
+                                  int psize, int ssize, int local)
 {
-       hpte_t *hptep = htab_address + slot;
+       struct hash_pte *hptep = htab_address + slot;
        unsigned long hpte_v;
        unsigned long want_v;
        unsigned long flags;
@@ -323,7 +330,7 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long va,
 
        DBG_LOW("    invalidate(va=%016lx, hash: %x)\n", va, slot);
 
-       want_v = hpte_encode_v(va, psize);
+       want_v = hpte_encode_v(va, psize, ssize);
        native_lock_hpte(hptep);
        hpte_v = hptep->v;
 
@@ -335,36 +342,73 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long va,
                hptep->v = 0;
 
        /* Invalidate the TLB */
-       tlbie(va, psize, local);
+       tlbie(va, psize, ssize, local);
 
        local_irq_restore(flags);
 }
 
-/*
- * XXX This need fixing based on page size. It's only used by
- * native_hpte_clear() for now which needs fixing too so they
- * make a good pair...
- */
-static unsigned long slot2va(unsigned long hpte_v, unsigned long slot)
+#define LP_SHIFT       12
+#define LP_BITS                8
+#define LP_MASK(i)     ((0xFF >> (i)) << LP_SHIFT)
+
+static void hpte_decode(struct hash_pte *hpte, unsigned long slot,
+                       int *psize, int *ssize, unsigned long *va)
 {
-       unsigned long avpn = HPTE_V_AVPN_VAL(hpte_v);
-       unsigned long va;
+       unsigned long hpte_r = hpte->r;
+       unsigned long hpte_v = hpte->v;
+       unsigned long avpn;
+       int i, size, shift, penc;
+
+       if (!(hpte_v & HPTE_V_LARGE))
+               size = MMU_PAGE_4K;
+       else {
+               for (i = 0; i < LP_BITS; i++) {
+                       if ((hpte_r & LP_MASK(i+1)) == LP_MASK(i+1))
+                               break;
+               }
+               penc = LP_MASK(i+1) >> LP_SHIFT;
+               for (size = 0; size < MMU_PAGE_COUNT; size++) {
 
-       va = avpn << 23;
+                       /* 4K pages are not represented by LP */
+                       if (size == MMU_PAGE_4K)
+                               continue;
 
-       if (! (hpte_v & HPTE_V_LARGE)) {
-               unsigned long vpi, pteg;
+                       /* valid entries have a shift value */
+                       if (!mmu_psize_defs[size].shift)
+                               continue;
+
+                       if (penc == mmu_psize_defs[size].penc)
+                               break;
+               }
+       }
+
+       /* This works for all page sizes, and for 256M and 1T segments */
+       shift = mmu_psize_defs[size].shift;
+       avpn = (HPTE_V_AVPN_VAL(hpte_v) & ~mmu_psize_defs[size].avpnm) << 23;
+
+       if (shift < 23) {
+               unsigned long vpi, vsid, pteg;
 
                pteg = slot / HPTES_PER_GROUP;
                if (hpte_v & HPTE_V_SECONDARY)
                        pteg = ~pteg;
-
-               vpi = ((va >> 28) ^ pteg) & htab_hash_mask;
-
-               va |= vpi << PAGE_SHIFT;
+               switch (hpte_v >> HPTE_V_SSIZE_SHIFT) {
+               case MMU_SEGSIZE_256M:
+                       vpi = ((avpn >> 28) ^ pteg) & htab_hash_mask;
+                       break;
+               case MMU_SEGSIZE_1T:
+                       vsid = avpn >> 40;
+                       vpi = (vsid ^ (vsid << 25) ^ pteg) & htab_hash_mask;
+                       break;
+               default:
+                       avpn = vpi = size = 0;
+               }
+               avpn |= (vpi << mmu_psize_defs[size].shift);
        }
 
-       return va;
+       *va = avpn;
+       *psize = size;
+       *ssize = hpte_v >> HPTE_V_SSIZE_SHIFT;
 }
 
 /*
@@ -374,15 +418,14 @@ static unsigned long slot2va(unsigned long hpte_v, unsigned long slot)
  *
  * TODO: add batching support when enabled.  remember, no dynamic memory here,
  * athough there is the control page available...
- *
- * XXX FIXME: 4k only for now !
  */
 static void native_hpte_clear(void)
 {
        unsigned long slot, slots, flags;
-       hpte_t *hptep = htab_address;
-       unsigned long hpte_v;
+       struct hash_pte *hptep = htab_address;
+       unsigned long hpte_v, va;
        unsigned long pteg_count;
+       int psize, ssize;
 
        pteg_count = htab_hash_mask + 1;
 
@@ -408,8 +451,9 @@ static void native_hpte_clear(void)
                 * already hold the native_tlbie_lock.
                 */
                if (hpte_v & HPTE_V_VALID) {
+                       hpte_decode(hptep, slot, &psize, &ssize, &va);
                        hptep->v = 0;
-                       __tlbie(slot2va(hpte_v, slot), MMU_PAGE_4K);
+                       __tlbie(va, psize, ssize);
                }
        }
 
@@ -425,13 +469,14 @@ static void native_hpte_clear(void)
 static void native_flush_hash_range(unsigned long number, int local)
 {
        unsigned long va, hash, index, hidx, shift, slot;
-       hpte_t *hptep;
+       struct hash_pte *hptep;
        unsigned long hpte_v;
        unsigned long want_v;
        unsigned long flags;
        real_pte_t pte;
        struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
        unsigned long psize = batch->psize;
+       int ssize = batch->ssize;
        int i;
 
        local_irq_save(flags);
@@ -441,14 +486,14 @@ static void native_flush_hash_range(unsigned long number, int local)
                pte = batch->pte[i];
 
                pte_iterate_hashed_subpages(pte, psize, va, index, shift) {
-                       hash = hpt_hash(va, shift);
+                       hash = hpt_hash(va, shift, ssize);
                        hidx = __rpte_to_hidx(pte, index);
                        if (hidx & _PTEIDX_SECONDARY)
                                hash = ~hash;
                        slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
                        slot += hidx & _PTEIDX_GROUP_IX;
                        hptep = htab_address + slot;
-                       want_v = hpte_encode_v(va, psize);
+                       want_v = hpte_encode_v(va, psize, ssize);
                        native_lock_hpte(hptep);
                        hpte_v = hptep->v;
                        if (!HPTE_V_COMPARE(hpte_v, want_v) ||
@@ -468,7 +513,7 @@ static void native_flush_hash_range(unsigned long number, int local)
 
                        pte_iterate_hashed_subpages(pte, psize, va, index,
                                                    shift) {
-                               __tlbiel(va, psize);
+                               __tlbiel(va, psize, ssize);
                        } pte_iterate_hashed_end();
                }
                asm volatile("ptesync":::"memory");
@@ -485,7 +530,7 @@ static void native_flush_hash_range(unsigned long number, int local)
 
                        pte_iterate_hashed_subpages(pte, psize, va, index,
                                                    shift) {
-                               __tlbie(va, psize);
+                               __tlbie(va, psize, ssize);
                        } pte_iterate_hashed_end();
                }
                asm volatile("eieio; tlbsync; ptesync":::"memory");
@@ -505,7 +550,7 @@ static inline int tlb_batching_enabled(void)
        int enabled = 1;
 
        if (root) {
-               const char *model = get_property(root, "model", NULL);
+               const char *model = of_get_property(root, "model", NULL);
                if (model && !strcmp(model, "IBM,9076-N81"))
                        enabled = 0;
                of_node_put(root);
@@ -520,7 +565,7 @@ static inline int tlb_batching_enabled(void)
 }
 #endif
 
-void hpte_init_native(void)
+void __init hpte_init_native(void)
 {
        ppc_md.hpte_invalidate  = native_hpte_invalidate;
        ppc_md.hpte_updatepp    = native_hpte_updatepp;
@@ -530,5 +575,4 @@ void hpte_init_native(void)
        ppc_md.hpte_clear_all   = native_hpte_clear;
        if (tlb_batching_enabled())
                ppc_md.flush_hash_range = native_flush_hash_range;
-       htab_finish_init();
 }