aboutsummaryrefslogtreecommitdiff
path: root/arch/sh/mm
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2009-07-22 19:20:49 +0900
committerPaul Mundt <lethal@linux-sh.org>2009-07-22 19:20:49 +0900
commit2277ab4a1df50e05bc732fe9488d4e902bb8399a (patch)
treef41cb47f15e02bbd1f79bf08ef7762d3bba934f6 /arch/sh/mm
parentc0b96cf639aa1bfa8983f734d4225091aa813e00 (diff)
sh: Migrate from PG_mapped to PG_dcache_dirty.
This inverts the delayed dcache flush a bit to be more in line with other platforms. At the same time this also gives us the ability to do some more optimizations and cleanup. Now that the update_mmu_cache() callsite only tests for the bit, the implementation can gradually be split out and made generic, rather than relying on special implementations for each of the peculiar CPU types. SH7705 in 32kB mode and SH-4 still need slightly different handling, but this is something that can remain isolated in the varying page copy/clear routines. On top of that, SH-X3 is dcache coherent, so there is no need to bother with any of these tests in the PTEAEX version of update_mmu_cache(), so we kill that off too. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/mm')
-rw-r--r--arch/sh/mm/cache-sh4.c10
-rw-r--r--arch/sh/mm/cache-sh7705.c7
-rw-r--r--arch/sh/mm/pg-sh4.c74
-rw-r--r--arch/sh/mm/pg-sh7705.c52
-rw-r--r--arch/sh/mm/tlb-pteaex.c17
-rw-r--r--arch/sh/mm/tlb-sh3.c20
-rw-r--r--arch/sh/mm/tlb-sh4.c23
7 files changed, 73 insertions, 130 deletions
diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c
index 5cfe08dbb59..c3a09b27f8d 100644
--- a/arch/sh/mm/cache-sh4.c
+++ b/arch/sh/mm/cache-sh4.c
@@ -14,6 +14,7 @@
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/mutex.h>
+#include <linux/fs.h>
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
@@ -246,7 +247,14 @@ static inline void flush_cache_4096(unsigned long start,
*/
void flush_dcache_page(struct page *page)
{
- if (test_bit(PG_mapped, &page->flags)) {
+ struct address_space *mapping = page_mapping(page);
+
+#ifndef CONFIG_SMP
+ if (mapping && !mapping_mapped(mapping))
+ set_bit(PG_dcache_dirty, &page->flags);
+ else
+#endif
+ {
unsigned long phys = PHYSADDR(page_address(page));
unsigned long addr = CACHE_OC_ADDRESS_ARRAY;
int i, n;
diff --git a/arch/sh/mm/cache-sh7705.c b/arch/sh/mm/cache-sh7705.c
index 22dacc77882..fa37bff306b 100644
--- a/arch/sh/mm/cache-sh7705.c
+++ b/arch/sh/mm/cache-sh7705.c
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/mman.h>
#include <linux/mm.h>
+#include <linux/fs.h>
#include <linux/threads.h>
#include <asm/addrspace.h>
#include <asm/page.h>
@@ -128,7 +129,11 @@ static void __uses_jump_to_uncached __flush_dcache_page(unsigned long phys)
*/
void flush_dcache_page(struct page *page)
{
- if (test_bit(PG_mapped, &page->flags))
+ struct address_space *mapping = page_mapping(page);
+
+ if (mapping && !mapping_mapped(mapping))
+ set_bit(PG_dcache_dirty, &page->flags);
+ else
__flush_dcache_page(PHYSADDR(page_address(page)));
}
diff --git a/arch/sh/mm/pg-sh4.c b/arch/sh/mm/pg-sh4.c
index 2fe14da1f83..f3c4b2a54fc 100644
--- a/arch/sh/mm/pg-sh4.c
+++ b/arch/sh/mm/pg-sh4.c
@@ -15,8 +15,6 @@
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
-#define CACHE_ALIAS (current_cpu_data.dcache.alias_mask)
-
#define kmap_get_fixmap_pte(vaddr) \
pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)), (vaddr))
@@ -68,10 +66,9 @@ static inline void kunmap_coherent(struct page *page)
*/
void clear_user_page(void *to, unsigned long address, struct page *page)
{
- __set_bit(PG_mapped, &page->flags);
-
clear_page(to);
- if ((((address & PAGE_MASK) ^ (unsigned long)to) & CACHE_ALIAS))
+
+ if (pages_do_alias((unsigned long)to, address & PAGE_MASK))
__flush_wback_region(to, PAGE_SIZE);
}
@@ -79,13 +76,14 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
unsigned long vaddr, void *dst, const void *src,
unsigned long len)
{
- void *vto;
-
- __set_bit(PG_mapped, &page->flags);
-
- vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
- memcpy(vto, src, len);
- kunmap_coherent(vto);
+ if (page_mapped(page) && !test_bit(PG_dcache_dirty, &page->flags)) {
+ void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ memcpy(vto, src, len);
+ kunmap_coherent(vto);
+ } else {
+ memcpy(dst, src, len);
+ set_bit(PG_dcache_dirty, &page->flags);
+ }
if (vma->vm_flags & VM_EXEC)
flush_cache_page(vma, vaddr, page_to_pfn(page));
@@ -95,13 +93,14 @@ void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
unsigned long vaddr, void *dst, const void *src,
unsigned long len)
{
- void *vfrom;
-
- __set_bit(PG_mapped, &page->flags);
-
- vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
- memcpy(dst, vfrom, len);
- kunmap_coherent(vfrom);
+ if (page_mapped(page) && !test_bit(PG_dcache_dirty, &page->flags)) {
+ void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ memcpy(dst, vfrom, len);
+ kunmap_coherent(vfrom);
+ } else {
+ memcpy(dst, src, len);
+ set_bit(PG_dcache_dirty, &page->flags);
+ }
}
void copy_user_highpage(struct page *to, struct page *from,
@@ -109,14 +108,19 @@ void copy_user_highpage(struct page *to, struct page *from,
{
void *vfrom, *vto;
- __set_bit(PG_mapped, &to->flags);
-
vto = kmap_atomic(to, KM_USER1);
- vfrom = kmap_coherent(from, vaddr);
- copy_page(vto, vfrom);
- kunmap_coherent(vfrom);
- if (((vaddr ^ (unsigned long)vto) & CACHE_ALIAS))
+ if (page_mapped(from) && !test_bit(PG_dcache_dirty, &from->flags)) {
+ vfrom = kmap_coherent(from, vaddr);
+ copy_page(vto, vfrom);
+ kunmap_coherent(vfrom);
+ } else {
+ vfrom = kmap_atomic(from, KM_USER0);
+ copy_page(vto, vfrom);
+ kunmap_atomic(vfrom, KM_USER0);
+ }
+
+ if (pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
__flush_wback_region(vto, PAGE_SIZE);
kunmap_atomic(vto, KM_USER1);
@@ -124,23 +128,3 @@ void copy_user_highpage(struct page *to, struct page *from,
smp_wmb();
}
EXPORT_SYMBOL(copy_user_highpage);
-
-/*
- * For SH-4, we have our own implementation for ptep_get_and_clear
- */
-pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
-{
- pte_t pte = *ptep;
-
- pte_clear(mm, addr, ptep);
- if (!pte_not_present(pte)) {
- unsigned long pfn = pte_pfn(pte);
- if (pfn_valid(pfn)) {
- struct page *page = pfn_to_page(pfn);
- struct address_space *mapping = page_mapping(page);
- if (!mapping || !mapping_writably_mapped(mapping))
- __clear_bit(PG_mapped, &page->flags);
- }
- }
- return pte;
-}
diff --git a/arch/sh/mm/pg-sh7705.c b/arch/sh/mm/pg-sh7705.c
index eaf25147194..684891b5c8c 100644
--- a/arch/sh/mm/pg-sh7705.c
+++ b/arch/sh/mm/pg-sh7705.c
@@ -26,7 +26,7 @@
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
-static inline void __flush_purge_virtual_region(void *p1, void *virt, int size)
+static void __flush_purge_virtual_region(void *p1, void *virt, int size)
{
unsigned long v;
unsigned long begin, end;
@@ -75,19 +75,13 @@ static inline void __flush_purge_virtual_region(void *p1, void *virt, int size)
*/
void clear_user_page(void *to, unsigned long address, struct page *pg)
{
- struct page *page = virt_to_page(to);
-
- __set_bit(PG_mapped, &page->flags);
- if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) {
- clear_page(to);
- __flush_wback_region(to, PAGE_SIZE);
- } else {
+ if (pages_do_alias(address, (unsigned long)to))
__flush_purge_virtual_region(to,
(void *)(address & 0xfffff000),
PAGE_SIZE);
- clear_page(to);
- __flush_wback_region(to, PAGE_SIZE);
- }
+
+ clear_page(to);
+ __flush_wback_region(to, PAGE_SIZE);
}
/*
@@ -98,41 +92,11 @@ void clear_user_page(void *to, unsigned long address, struct page *pg)
*/
void copy_user_page(void *to, void *from, unsigned long address, struct page *pg)
{
- struct page *page = virt_to_page(to);
-
-
- __set_bit(PG_mapped, &page->flags);
- if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) {
- copy_page(to, from);
- __flush_wback_region(to, PAGE_SIZE);
- } else {
+ if (pages_do_alias(address, (unsigned long)to))
__flush_purge_virtual_region(to,
(void *)(address & 0xfffff000),
PAGE_SIZE);
- copy_page(to, from);
- __flush_wback_region(to, PAGE_SIZE);
- }
-}
-/*
- * For SH7705, we have our own implementation for ptep_get_and_clear
- * Copied from pg-sh4.c
- */
-pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
-{
- pte_t pte = *ptep;
-
- pte_clear(mm, addr, ptep);
- if (!pte_not_present(pte)) {
- unsigned long pfn = pte_pfn(pte);
- if (pfn_valid(pfn)) {
- struct page *page = pfn_to_page(pfn);
- struct address_space *mapping = page_mapping(page);
- if (!mapping || !mapping_writably_mapped(mapping))
- __clear_bit(PG_mapped, &page->flags);
- }
- }
-
- return pte;
+ copy_page(to, from);
+ __flush_wback_region(to, PAGE_SIZE);
}
-
diff --git a/arch/sh/mm/tlb-pteaex.c b/arch/sh/mm/tlb-pteaex.c
index 2aab3ea934d..c39b7736335 100644
--- a/arch/sh/mm/tlb-pteaex.c
+++ b/arch/sh/mm/tlb-pteaex.c
@@ -27,23 +27,6 @@ void update_mmu_cache(struct vm_area_struct * vma,
if (vma && current->active_mm != vma->vm_mm)
return;
-#ifndef CONFIG_CACHE_OFF
- {
- unsigned long pfn = pte_pfn(pte);
-
- if (pfn_valid(pfn)) {
- struct page *page = pfn_to_page(pfn);
-
- if (!test_bit(PG_mapped, &page->flags)) {
- unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
- __flush_wback_region((void *)P1SEGADDR(phys),
- PAGE_SIZE);
- __set_bit(PG_mapped, &page->flags);
- }
- }
- }
-#endif
-
local_irq_save(flags);
/* Set PTEH register */
diff --git a/arch/sh/mm/tlb-sh3.c b/arch/sh/mm/tlb-sh3.c
index 17cb7c3adf2..9b8459c74ab 100644
--- a/arch/sh/mm/tlb-sh3.c
+++ b/arch/sh/mm/tlb-sh3.c
@@ -33,25 +33,25 @@ void update_mmu_cache(struct vm_area_struct * vma,
unsigned long flags;
unsigned long pteval;
unsigned long vpn;
+ unsigned long pfn = pte_pfn(pte);
+ struct page *page;
/* Ptrace may call this routine. */
if (vma && current->active_mm != vma->vm_mm)
return;
+ page = pfn_to_page(pfn);
+ if (pfn_valid(pfn) && page_mapping(page)) {
#if defined(CONFIG_SH7705_CACHE_32KB)
- {
- struct page *page = pte_page(pte);
- unsigned long pfn = pte_pfn(pte);
+ int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags);
+ if (dirty) {
+ unsigned long addr = (unsigned long)page_address(page);
- if (pfn_valid(pfn) && !test_bit(PG_mapped, &page->flags)) {
- unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
-
- __flush_wback_region((void *)P1SEGADDR(phys),
- PAGE_SIZE);
- __set_bit(PG_mapped, &page->flags);
+ if (pages_do_alias(addr, address & PAGE_MASK))
+ __flush_wback_region((void *)addr, PAGE_SIZE);
}
- }
#endif
+ }
local_irq_save(flags);
diff --git a/arch/sh/mm/tlb-sh4.c b/arch/sh/mm/tlb-sh4.c
index f0c7b7397fa..cf50082d243 100644
--- a/arch/sh/mm/tlb-sh4.c
+++ b/arch/sh/mm/tlb-sh4.c
@@ -21,27 +21,26 @@ void update_mmu_cache(struct vm_area_struct * vma,
unsigned long flags;
unsigned long pteval;
unsigned long vpn;
+ unsigned long pfn = pte_pfn(pte);
+ struct page *page;
/* Ptrace may call this routine. */
if (vma && current->active_mm != vma->vm_mm)
return;
-#ifndef CONFIG_CACHE_OFF
- {
- unsigned long pfn = pte_pfn(pte);
+ page = pfn_to_page(pfn);
+ if (pfn_valid(pfn) && page_mapping(page)) {
+#ifndef CONFIG_SMP
+ int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags);
+ if (dirty) {
- if (pfn_valid(pfn)) {
- struct page *page = pfn_to_page(pfn);
+ unsigned long addr = (unsigned long)page_address(page);
- if (!test_bit(PG_mapped, &page->flags)) {
- unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
- __flush_wback_region((void *)P1SEGADDR(phys),
- PAGE_SIZE);
- __set_bit(PG_mapped, &page->flags);
- }
+ if (pages_do_alias(addr, address & PAGE_MASK))
+ __flush_wback_region((void *)addr, PAGE_SIZE);
}
- }
#endif
+ }
local_irq_save(flags);