aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc/kernel/cache.c
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2011-04-15 13:37:22 -0400
committerJames Bottomley <James.Bottomley@suse.de>2011-04-15 13:55:56 -0400
commitb7d45818444a31948cfc7849136013a0ea54b2fb (patch)
treec24455fe3f41bffabb377fa9c855eaa7cac26a1e /arch/parisc/kernel/cache.c
parentd7dd2ff11b7fcd425aca5a875983c862d19a67ae (diff)
[PARISC] prevent speculative re-read on cache flush
According to Appendix F, the TLB is the primary arbiter of speculation. Thus, if a page has a TLB entry, it may be speculatively read into the cache. On linux, this can cause us incoherencies because if we're about to do a disk read, we call get_user_pages() to do the flush/invalidate in user space, but we still potentially have the user TLB entries, and the cache could speculate the lines back into userspace (thus causing stale data to be used). This is fixed by purging the TLB entries before we flush through the tmpalias space. Now, the only way the line could be re-speculated is if the user actually tries to touch it (which is not allowed). Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'arch/parisc/kernel/cache.c')
-rw-r--r--arch/parisc/kernel/cache.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 3f11331c2775..83335f3da5fc 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -304,10 +304,20 @@ void flush_dcache_page(struct page *page)
304 offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; 304 offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
305 addr = mpnt->vm_start + offset; 305 addr = mpnt->vm_start + offset;
306 306
307 /* The TLB is the engine of coherence on parisc: The
308 * CPU is entitled to speculate any page with a TLB
309 * mapping, so here we kill the mapping then flush the
310 * page along a special flush only alias mapping.
311 * This guarantees that the page is no-longer in the
312 * cache for any process and nor may it be
313 * speculatively read in (until the user or kernel
314 * specifically accesses it, of course) */
315
316 flush_tlb_page(mpnt, addr);
307 if (old_addr == 0 || (old_addr & (SHMLBA - 1)) != (addr & (SHMLBA - 1))) { 317 if (old_addr == 0 || (old_addr & (SHMLBA - 1)) != (addr & (SHMLBA - 1))) {
308 __flush_cache_page(mpnt, addr, page_to_phys(page)); 318 __flush_cache_page(mpnt, addr, page_to_phys(page));
309 if (old_addr) 319 if (old_addr)
310 printk(KERN_ERR "INEQUIVALENT ALIASES 0x%lx and 0x%lx in file %s\n", old_addr, addr, mpnt->vm_file ? mpnt->vm_file->f_path.dentry->d_name.name : "(null)"); 320 printk(KERN_ERR "INEQUIVALENT ALIASES 0x%lx and 0x%lx in file %s\n", old_addr, addr, mpnt->vm_file ? (char *)mpnt->vm_file->f_path.dentry->d_name.name : "(null)");
311 old_addr = addr; 321 old_addr = addr;
312 } 322 }
313 } 323 }
@@ -499,6 +509,7 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long
499{ 509{
500 BUG_ON(!vma->vm_mm->context); 510 BUG_ON(!vma->vm_mm->context);
501 511
512 flush_tlb_page(vma, vmaddr);
502 __flush_cache_page(vma, vmaddr, page_to_phys(pfn_to_page(pfn))); 513 __flush_cache_page(vma, vmaddr, page_to_phys(pfn_to_page(pfn)));
503 514
504} 515}