]> err.no Git - linux-2.6/commitdiff
[PATCH] powerpc: Fix buglet with MMU hash management
authorBenjamin Herrenschmidt <benh@au1.ibm.com>
Tue, 30 May 2006 04:14:19 +0000 (14:14 +1000)
committerPaul Mackerras <paulus@samba.org>
Fri, 9 Jun 2006 11:20:59 +0000 (21:20 +1000)
Our MMU hash management code would not set the "C" bit (changed bit) in
the hardware PTE when updating a RO PTE into a RW PTE. That would cause
the hardware to possibly to a write back to the hash table to set it on
the first store access, which in addition to being a performance issue,
might also hit a bug when running with native hash management (non-HV)
as our code is specifically optimized for the case where no write back
happens.

Thus there is a very small therocial window were a hash PTE can become
corrupted if that HPTE has just been upgraded to read write, a store
access happens on it, and that races with another processor evicting
that same slot. Since eviction (caused by an almost full hash) is
extremely rare, the bug is very unlikely to happen fortunately.

This fixes by allowing the updating of the protection bits in the native
hash handling to also set (but not clear) the "C" bit, and, in order to
also improve performances in the general case, by always setting that
bit on newly inserted hash PTE so that writeback really never happens.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/mm/hash_low_64.S
arch/powerpc/mm/hash_native_64.c
include/asm-powerpc/mmu.h

index e0d02c4a2615f8787abc1b13737f7b78e67bc5a5..106fba391987376ce8c3599e610aa86cfe484674 100644 (file)
@@ -136,6 +136,7 @@ _GLOBAL(__hash_page_4K)
        and     r0,r0,r4                /* _PAGE_RW & _PAGE_DIRTY ->r0 bit 30*/
        andc    r0,r30,r0               /* r0 = pte & ~r0 */
        rlwimi  r3,r0,32-1,31,31        /* Insert result into PP lsb */
+       ori     r3,r3,HPTE_R_C          /* Always add "C" bit for perf. */
 
        /* We eventually do the icache sync here (maybe inline that
         * code rather than call a C function...) 
@@ -400,6 +401,7 @@ _GLOBAL(__hash_page_4K)
        and     r0,r0,r4                /* _PAGE_RW & _PAGE_DIRTY ->r0 bit 30*/
        andc    r0,r30,r0               /* r0 = pte & ~r0 */
        rlwimi  r3,r0,32-1,31,31        /* Insert result into PP lsb */
+       ori     r3,r3,HPTE_R_C          /* Always add "C" bit for perf. */
 
        /* We eventually do the icache sync here (maybe inline that
         * code rather than call a C function...)
@@ -671,6 +673,7 @@ _GLOBAL(__hash_page_64K)
        and     r0,r0,r4                /* _PAGE_RW & _PAGE_DIRTY ->r0 bit 30*/
        andc    r0,r30,r0               /* r0 = pte & ~r0 */
        rlwimi  r3,r0,32-1,31,31        /* Insert result into PP lsb */
+       ori     r3,r3,HPTE_R_C          /* Always add "C" bit for perf. */
 
        /* We eventually do the icache sync here (maybe inline that
         * code rather than call a C function...)
index 33654d1b1b43b596ac70e98cf834a1185157f355..3b8205033f15379c9e2a0cfb8f3c98056004d67e 100644 (file)
@@ -238,7 +238,7 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
                DBG_LOW(" -> hit\n");
                /* Update the HPTE */
                hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) |
-                       (newpp & (HPTE_R_PP | HPTE_R_N));
+                       (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C));
                native_unlock_hpte(hptep);
        }
 
index 96e47d1ce97920c53c218c10c554d761147c7ff5..8853974201046cc2a1535b3127fcb3344887bea8 100644 (file)
@@ -96,6 +96,8 @@ extern char initial_stab[];
 #define HPTE_R_FLAGS           ASM_CONST(0x00000000000003ff)
 #define HPTE_R_PP              ASM_CONST(0x0000000000000003)
 #define HPTE_R_N               ASM_CONST(0x0000000000000004)
+#define HPTE_R_C               ASM_CONST(0x0000000000000080)
+#define HPTE_R_R               ASM_CONST(0x0000000000000100)
 
 /* Values for PP (assumes Ks=0, Kp=1) */
 /* pp0 will always be 0 for linux     */