powerpc/book3s64/hash: Use secondary hash for bolted mapping if the primary is full
authorAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Thu, 24 Oct 2019 09:35:42 +0000 (15:05 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 28 Oct 2019 10:54:16 +0000 (21:54 +1100)
With bolted hash page table entry, kernel currently only use primary hash group
when inserting the hash page table entry. In the rare case where kernel find all the
8 primary hash slot occupied by bolted entries, this can result in hash page
table insert failure for bolted entries. Avoid this by using the secondary hash
group.

This is different from what kernel does for the non-bolted mapping. With
non-bolted entries kernel will try secondary before removing an existing entry
from hash page table group. With bolted prefer primary hash group and hence
try to insert the page table entry by removing a slot from primary before trying
the secondary hash group.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20191024093542.29777-3-aneesh.kumar@linux.ibm.com
arch/powerpc/mm/book3s64/hash_native.c
arch/powerpc/mm/book3s64/hash_utils.c
arch/powerpc/platforms/pseries/lpar.c

index 523e42eb11daa4674d7eab30c86779040fdcefca..d2d8237ea9d59dac62dc6d7233fe88b95109dc3a 100644 (file)
@@ -482,19 +482,12 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
        return ret;
 }
 
-static long native_hpte_find(unsigned long vpn, int psize, int ssize)
+static long __native_hpte_find(unsigned long want_v, unsigned long slot)
 {
        struct hash_pte *hptep;
-       unsigned long hash;
+       unsigned long hpte_v;
        unsigned long i;
-       long slot;
-       unsigned long want_v, hpte_v;
 
-       hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, ssize);
-       want_v = hpte_encode_avpn(vpn, psize, ssize);
-
-       /* 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;
@@ -508,6 +501,33 @@ static long native_hpte_find(unsigned long vpn, int psize, int ssize)
        return -1;
 }
 
+static long native_hpte_find(unsigned long vpn, int psize, int ssize)
+{
+       unsigned long hpte_group;
+       unsigned long want_v;
+       unsigned long hash;
+       long slot;
+
+       hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, ssize);
+       want_v = hpte_encode_avpn(vpn, psize, ssize);
+
+       /*
+        * We try to keep bolted entries always in primary hash
+        * But in some case we can find them in secondary too.
+        */
+       hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
+       slot = __native_hpte_find(want_v, hpte_group);
+       if (slot < 0) {
+               /* Try in secondary */
+               hpte_group = (~hash & htab_hash_mask) * HPTES_PER_GROUP;
+               slot = __native_hpte_find(want_v, hpte_group);
+               if (slot < 0)
+                       return -1;
+       }
+
+       return slot;
+}
+
 /*
  * Update the page protection bits. Intended to be used to create
  * guard pages for kernel data structures on pages which are bolted
index 6e5a769ebcb802ad271a416cee43a23d30e51581..a9d1f72de8484f3c347cfbf35569fcb24edb0602 100644 (file)
@@ -263,6 +263,7 @@ int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
                unsigned long vsid = get_kernel_vsid(vaddr, ssize);
                unsigned long vpn  = hpt_vpn(vaddr, vsid, ssize);
                unsigned long tprot = prot;
+               bool secondary_hash = false;
 
                /*
                 * If we hit a bad address return error.
@@ -291,17 +292,27 @@ int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
                hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP);
 
                BUG_ON(!mmu_hash_ops.hpte_insert);
+repeat:
                ret = mmu_hash_ops.hpte_insert(hpteg, vpn, paddr, tprot,
                                               HPTE_V_BOLTED, psize, psize,
                                               ssize);
                if (ret == -1) {
-                       /* Try to remove a non bolted entry */
+                       /*
+                        * Try to to keep bolted entries in primary.
+                        * Remove non bolted entries and try insert again
+                        */
                        ret = mmu_hash_ops.hpte_remove(hpteg);
                        if (ret != -1)
                                ret = mmu_hash_ops.hpte_insert(hpteg, vpn, paddr, tprot,
                                                               HPTE_V_BOLTED, psize, psize,
                                                               ssize);
+                       if (ret == -1 && !secondary_hash) {
+                               secondary_hash = true;
+                               hpteg = ((~hash & htab_hash_mask) * HPTES_PER_GROUP);
+                               goto repeat;
+                       }
                }
+
                if (ret < 0)
                        break;
 
index 3126fc02e50bf2bce9dacf6c0d403cb94dcf843b..74c59a1e96278ff45c911ee9170a17ff1d9a3f6d 100644 (file)
@@ -938,11 +938,19 @@ static long pSeries_lpar_hpte_find(unsigned long vpn, int psize, int ssize)
        hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, ssize);
        want_v = hpte_encode_avpn(vpn, psize, ssize);
 
-       /* Bolted entries are always in the primary group */
+       /*
+        * We try to keep bolted entries always in primary hash
+        * But in some case we can find them in secondary too.
+        */
        hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
        slot = __pSeries_lpar_hpte_find(want_v, hpte_group);
-       if (slot < 0)
-               return -1;
+       if (slot < 0) {
+               /* Try in secondary */
+               hpte_group = (~hash & htab_hash_mask) * HPTES_PER_GROUP;
+               slot = __pSeries_lpar_hpte_find(want_v, hpte_group);
+               if (slot < 0)
+                       return -1;
+       }
        return hpte_group + slot;
 }