aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2008-02-26 07:01:56 +0100
committerJeremy Kerr <jk@ozlabs.org>2008-02-29 15:17:49 +1100
commitcc4b7c1814c9ad375e8167ea4a9ec4a0ec1ada04 (patch)
treed3bfb7c9c3a07c44519024f3d43a2ad08e6fe0ac /arch/powerpc
parentfae9ca791507876c3ccaa8ab686b2ce42dc7a560 (diff)
downloadkernel_goldelico_gta04-cc4b7c1814c9ad375e8167ea4a9ec4a0ec1ada04.zip
kernel_goldelico_gta04-cc4b7c1814c9ad375e8167ea4a9ec4a0ec1ada04.tar.gz
kernel_goldelico_gta04-cc4b7c1814c9ad375e8167ea4a9ec4a0ec1ada04.tar.bz2
[POWERPC] spufs: invalidate SLB translation before adding a new entry
When we replace an SLB entry in the MFC after using up all the available entries, there is a short window in which an incorrect entry is marked as valid. The problem is that the 'valid' bit is stored in the ESID, which is always written after the VSID. Overwriting the VSID first will make the original ESID entry point to the new VSID, which means that any concurrent DMA accessing the old ESID ends up being redirected to the new virtual address. A few cycles later, we write the new ESID and everything is fine again. That race can be closed by writing a zero entry to the ESID first, which makes sure that the VSID is not accessed until we write the new ESID. Note that we don't actually need to invalidate the SLB entry using the invalidation register, which would also flush any ERAT entries for that segment, because the segment translation does not become invalid but is only removed from the SLB cache. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/platforms/cell/spu_base.c4
1 files changed, 4 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index 87eb07f..cfc28e9 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -148,7 +148,11 @@ static inline void spu_load_slb(struct spu *spu, int slbe, struct spu_slb *slb)
__func__, slbe, slb->vsid, slb->esid);
out_be64(&priv2->slb_index_W, slbe);
+ /* set invalid before writing vsid */
+ out_be64(&priv2->slb_esid_RW, 0);
+ /* now it's safe to write the vsid */
out_be64(&priv2->slb_vsid_RW, slb->vsid);
+ /* setting the new esid makes the entry valid again */
out_be64(&priv2->slb_esid_RW, slb->esid);
}