powerpc/mm: Always invalidate tlb on hpte invalidate and update
If a hash bucket gets full, we "evict" a more/less random entry from it.
When we do that we don't invalidate the TLB (hpte_remove) because we assume
the old translation is still technically "valid". This implies that when
we are invalidating or updating pte, even if HPTE entry is not valid
we should do a tlb invalidate.
This was a regression introduced by b1022fbd29
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
Родитель
280a5ba22c
Коммит
0608d69246
|
@ -336,11 +336,18 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
|
||||||
|
|
||||||
hpte_v = hptep->v;
|
hpte_v = hptep->v;
|
||||||
actual_psize = hpte_actual_psize(hptep, psize);
|
actual_psize = hpte_actual_psize(hptep, psize);
|
||||||
|
/*
|
||||||
|
* We need to invalidate the TLB always because hpte_remove doesn't do
|
||||||
|
* a tlb invalidate. If a hash bucket gets full, we "evict" a more/less
|
||||||
|
* random entry from it. When we do that we don't invalidate the TLB
|
||||||
|
* (hpte_remove) because we assume the old translation is still
|
||||||
|
* technically "valid".
|
||||||
|
*/
|
||||||
if (actual_psize < 0) {
|
if (actual_psize < 0) {
|
||||||
native_unlock_hpte(hptep);
|
actual_psize = psize;
|
||||||
return -1;
|
ret = -1;
|
||||||
|
goto err_out;
|
||||||
}
|
}
|
||||||
/* Even if we miss, we need to invalidate the TLB */
|
|
||||||
if (!HPTE_V_COMPARE(hpte_v, want_v)) {
|
if (!HPTE_V_COMPARE(hpte_v, want_v)) {
|
||||||
DBG_LOW(" -> miss\n");
|
DBG_LOW(" -> miss\n");
|
||||||
ret = -1;
|
ret = -1;
|
||||||
|
@ -350,6 +357,7 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
|
||||||
hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) |
|
hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) |
|
||||||
(newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C));
|
(newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C));
|
||||||
}
|
}
|
||||||
|
err_out:
|
||||||
native_unlock_hpte(hptep);
|
native_unlock_hpte(hptep);
|
||||||
|
|
||||||
/* Ensure it is out of the tlb too. */
|
/* Ensure it is out of the tlb too. */
|
||||||
|
@ -409,7 +417,7 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
|
||||||
hptep = htab_address + slot;
|
hptep = htab_address + slot;
|
||||||
actual_psize = hpte_actual_psize(hptep, psize);
|
actual_psize = hpte_actual_psize(hptep, psize);
|
||||||
if (actual_psize < 0)
|
if (actual_psize < 0)
|
||||||
return;
|
actual_psize = psize;
|
||||||
|
|
||||||
/* Update the HPTE */
|
/* Update the HPTE */
|
||||||
hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) |
|
hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) |
|
||||||
|
@ -437,21 +445,27 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long vpn,
|
||||||
hpte_v = hptep->v;
|
hpte_v = hptep->v;
|
||||||
|
|
||||||
actual_psize = hpte_actual_psize(hptep, psize);
|
actual_psize = hpte_actual_psize(hptep, psize);
|
||||||
|
/*
|
||||||
|
* We need to invalidate the TLB always because hpte_remove doesn't do
|
||||||
|
* a tlb invalidate. If a hash bucket gets full, we "evict" a more/less
|
||||||
|
* random entry from it. When we do that we don't invalidate the TLB
|
||||||
|
* (hpte_remove) because we assume the old translation is still
|
||||||
|
* technically "valid".
|
||||||
|
*/
|
||||||
if (actual_psize < 0) {
|
if (actual_psize < 0) {
|
||||||
|
actual_psize = psize;
|
||||||
native_unlock_hpte(hptep);
|
native_unlock_hpte(hptep);
|
||||||
local_irq_restore(flags);
|
goto err_out;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
/* Even if we miss, we need to invalidate the TLB */
|
|
||||||
if (!HPTE_V_COMPARE(hpte_v, want_v))
|
if (!HPTE_V_COMPARE(hpte_v, want_v))
|
||||||
native_unlock_hpte(hptep);
|
native_unlock_hpte(hptep);
|
||||||
else
|
else
|
||||||
/* Invalidate the hpte. NOTE: this also unlocks it */
|
/* Invalidate the hpte. NOTE: this also unlocks it */
|
||||||
hptep->v = 0;
|
hptep->v = 0;
|
||||||
|
|
||||||
|
err_out:
|
||||||
/* Invalidate the TLB */
|
/* Invalidate the TLB */
|
||||||
tlbie(vpn, psize, actual_psize, ssize, local);
|
tlbie(vpn, psize, actual_psize, ssize, local);
|
||||||
|
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче