Commit 5a3a29fe authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Greg Kroah-Hartman

ARM: 5691/1: fix cache aliasing issues between kmap() and kmap_atomic() with highmem

commit 7929eb9c upstream.

Let's suppose a highmem page is kmap'd with kmap().  A pkmap entry is
used, the page mapped to it, and the virtual cache is dirtied.  Then
kunmap() is used which does virtually nothing except for decrementing a
usage count.

Then, let's suppose the _same_ page gets mapped using kmap_atomic().
It is therefore mapped onto a fixmap entry instead, which has a
different virtual address unaware of the dirty cache data for that page
sitting in the pkmap mapping.

Fortunately it is easy to know if a pkmap mapping still exists for that
page and use it directly with kmap_atomic(), thanks to kmap_high_get().

And actual testing with a printk in the added code path shows that this
condition is actually met *extremely* frequently.  Seems that we've been
quite lucky that things have worked so well with highmem so far.
Signed-off-by: default avatarNicolas Pitre <nico@marvell.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c922ffee
...@@ -40,11 +40,16 @@ void *kmap_atomic(struct page *page, enum km_type type) ...@@ -40,11 +40,16 @@ void *kmap_atomic(struct page *page, enum km_type type)
{ {
unsigned int idx; unsigned int idx;
unsigned long vaddr; unsigned long vaddr;
void *kmap;
pagefault_disable(); pagefault_disable();
if (!PageHighMem(page)) if (!PageHighMem(page))
return page_address(page); return page_address(page);
kmap = kmap_high_get(page);
if (kmap)
return kmap;
idx = type + KM_TYPE_NR * smp_processor_id(); idx = type + KM_TYPE_NR * smp_processor_id();
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
#ifdef CONFIG_DEBUG_HIGHMEM #ifdef CONFIG_DEBUG_HIGHMEM
...@@ -80,6 +85,9 @@ void kunmap_atomic(void *kvaddr, enum km_type type) ...@@ -80,6 +85,9 @@ void kunmap_atomic(void *kvaddr, enum km_type type)
#else #else
(void) idx; /* to kill a warning */ (void) idx; /* to kill a warning */
#endif #endif
} else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
/* this address was obtained through kmap_high_get() */
kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
} }
pagefault_enable(); pagefault_enable();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment