All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] HIGHMEM and cache flush fixes.
@ 2015-01-29 16:46 Steven J. Hill
  2015-01-29 16:46 ` [PATCH 1/3] MIPS: Fix cache flushing for swap pages with non-DMA I/O Steven J. Hill
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Steven J. Hill @ 2015-01-29 16:46 UTC (permalink / raw)
  To: linux-mips; +Cc: ralf

From: "Steven J. Hill" <Steven.Hill@imgtec.com>

This patchset fixes HIGHMEM and utilizes kmap coloring support. The
other two patches fix some cache flushing corner cases.

Leonid Yegoshin (3):
  MIPS: Fix cache flushing for swap pages with non-DMA I/O.
  MIPS: Highmem: Fixes for cache aliasing and color.
  MIPS: Fix I-cache flushing for kmap'd pages.

 arch/mips/Kconfig                    |    1 +
 arch/mips/include/asm/cacheflush.h   |    3 +-
 arch/mips/include/asm/cpu-features.h |    6 ++
 arch/mips/include/asm/fixmap.h       |   18 +++++-
 arch/mips/include/asm/highmem.h      |   33 +++++++++++
 arch/mips/include/asm/page.h         |    5 +-
 arch/mips/include/asm/pgtable.h      |    5 ++
 arch/mips/mm/c-r4k.c                 |   44 ++++++++++++--
 arch/mips/mm/cache.c                 |  108 ++++++++++++++++++++++------------
 arch/mips/mm/highmem.c               |   43 +++++---------
 arch/mips/mm/init.c                  |   35 ++++++-----
 arch/mips/mm/sc-mips.c               |    1 +
 12 files changed, 210 insertions(+), 92 deletions(-)

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] MIPS: Fix cache flushing for swap pages with non-DMA I/O.
  2015-01-29 16:46 [PATCH 0/3] HIGHMEM and cache flush fixes Steven J. Hill
@ 2015-01-29 16:46 ` Steven J. Hill
  2015-02-04 14:54     ` Lars Persson
  2015-01-29 16:46 ` [PATCH 2/3] MIPS: Highmem: Fixes for cache aliasing and color Steven J. Hill
  2015-01-29 16:46 ` [PATCH 3/3] MIPS: Fix I-cache flushing for kmap'd pages Steven J. Hill
  2 siblings, 1 reply; 6+ messages in thread
From: Steven J. Hill @ 2015-01-29 16:46 UTC (permalink / raw)
  To: linux-mips; +Cc: ralf

From: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>

Flush the D-cache before the page is given to a process
as an executable (I-cache) page when the backing store
is non-DMA I/O.

Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
---
 arch/mips/include/asm/cacheflush.h   |    3 ++-
 arch/mips/include/asm/cpu-features.h |    3 +++
 arch/mips/include/asm/page.h         |    5 +++-
 arch/mips/include/asm/pgtable.h      |    5 ++++
 arch/mips/mm/c-r4k.c                 |   19 +++++++++++++-
 arch/mips/mm/cache.c                 |   46 +++++++++++++++++-----------------
 arch/mips/mm/init.c                  |   23 +++++++++++------
 arch/mips/mm/sc-mips.c               |    1 +
 8 files changed, 72 insertions(+), 33 deletions(-)

diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h
index e08381a..3a4582a 100644
--- a/arch/mips/include/asm/cacheflush.h
+++ b/arch/mips/include/asm/cacheflush.h
@@ -123,7 +123,8 @@ static inline void kunmap_noncoherent(void)
 #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE
 static inline void flush_kernel_dcache_page(struct page *page)
 {
-	BUG_ON(cpu_has_dc_aliases && PageHighMem(page));
+	if (cpu_has_dc_aliases || !cpu_has_ic_fills_f_dc)
+		__flush_dcache_page(page);
 }
 
 /*
diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
index 2897cfa..23db770 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -139,6 +139,9 @@
 #ifndef cpu_has_vtag_icache
 #define cpu_has_vtag_icache	(cpu_data[0].icache.flags & MIPS_CACHE_VTAG)
 #endif
+#ifndef cpu_has_vtag_dcache
+#define cpu_has_vtag_dcache     (cpu_data[0].dcache.flags & MIPS_CACHE_VTAG)
+#endif
 #ifndef cpu_has_dc_aliases
 #define cpu_has_dc_aliases	(cpu_data[0].dcache.flags & MIPS_CACHE_ALIASES)
 #endif
diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
index 154b70a..fc7ab98 100644
--- a/arch/mips/include/asm/page.h
+++ b/arch/mips/include/asm/page.h
@@ -95,13 +95,16 @@ static inline unsigned long pages_do_alias(unsigned long addr1,
 
 struct page;
 
+#include <asm/cpu-features.h>
+
 static inline void clear_user_page(void *addr, unsigned long vaddr,
 	struct page *page)
 {
 	extern void (*flush_data_cache_page)(unsigned long addr);
 
 	clear_page(addr);
-	if (pages_do_alias((unsigned long) addr, vaddr & PAGE_MASK))
+	if (cpu_has_vtag_dcache || (cpu_has_dc_aliases &&
+	     pages_do_alias((unsigned long) addr, vaddr & PAGE_MASK)))
 		flush_data_cache_page((unsigned long)addr);
 }
 
diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h
index 62a6ba3..0a6a944 100644
--- a/arch/mips/include/asm/pgtable.h
+++ b/arch/mips/include/asm/pgtable.h
@@ -148,6 +148,7 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
 		}
 	}
 }
+#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
 
 static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
 {
@@ -185,6 +186,7 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
 	}
 #endif
 }
+#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
 
 static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
 {
@@ -401,12 +403,15 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 
 extern void __update_tlb(struct vm_area_struct *vma, unsigned long address,
 	pte_t pte);
+extern void __update_cache(struct vm_area_struct *vma, unsigned long address,
+	pte_t pte);
 
 static inline void update_mmu_cache(struct vm_area_struct *vma,
 	unsigned long address, pte_t *ptep)
 {
 	pte_t pte = *ptep;
 	__update_tlb(vma, address, pte);
+	__update_cache(vma, address, pte);
 }
 
 static inline void update_mmu_cache_pmd(struct vm_area_struct *vma,
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index dd261df..e045116 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -484,8 +484,11 @@ static inline void local_r4k_flush_cache_range(void * args)
 		return;
 
 	r4k_blast_dcache();
-	if (exec)
+	if (exec) {
+		if (!cpu_has_ic_fills_f_dc)
+			wmb();
 		r4k_blast_icache();
+	}
 }
 
 static void r4k_flush_cache_range(struct vm_area_struct *vma,
@@ -570,6 +573,14 @@ static inline void local_r4k_flush_cache_page(void *args)
 	if (!(pte_present(*ptep)))
 		return;
 
+	/*
+	 * If this page is not destined to be executable and the
+	 * data cache does not have aliases, all of the mapping
+	 * below can be skipped.
+	 */
+	if (!exec && !cpu_has_dc_aliases)
+		return;
+
 	if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID))
 		vaddr = NULL;
 	else {
@@ -589,6 +600,8 @@ static inline void local_r4k_flush_cache_page(void *args)
 	if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) {
 		vaddr ? r4k_blast_dcache_page(addr) :
 			r4k_blast_dcache_user_page(addr);
+		if (exec && !cpu_has_ic_fills_f_dc)
+			wmb();
 		if (exec && !cpu_icache_snoops_remote_store)
 			r4k_blast_scache_page(addr);
 	}
@@ -621,6 +634,8 @@ static void r4k_flush_cache_page(struct vm_area_struct *vma,
 	args.pfn = pfn;
 
 	r4k_on_each_cpu(local_r4k_flush_cache_page, &args);
+	if (cpu_has_dc_aliases)
+		ClearPageDcacheDirty(pfn_to_page(pfn));
 }
 
 static inline void local_r4k_flush_data_cache_page(void * addr)
@@ -652,6 +667,8 @@ static inline void local_r4k_flush_icache_range(unsigned long start, unsigned lo
 		}
 	}
 
+	wmb();
+
 	if (end - start > icache_size)
 		r4k_blast_icache();
 	else {
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index 7e3ea77..99db9e8 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -19,6 +19,7 @@
 #include <asm/processor.h>
 #include <asm/cpu.h>
 #include <asm/cpu-features.h>
+#include <linux/highmem.h>
 
 /* Cache operations. */
 void (*flush_cache_all)(void);
@@ -105,48 +106,47 @@ void __flush_anon_page(struct page *page, unsigned long vmaddr)
 {
 	unsigned long addr = (unsigned long) page_address(page);
 
-	if (pages_do_alias(addr, vmaddr)) {
+	if (pages_do_alias(addr, vmaddr & PAGE_MASK)) {
 		if (page_mapped(page) && !Page_dcache_dirty(page)) {
 			void *kaddr;
 
 			kaddr = kmap_coherent(page, vmaddr);
 			flush_data_cache_page((unsigned long)kaddr);
 			kunmap_coherent();
-		} else
-			flush_data_cache_page(addr);
+		} else {
+			void *kaddr;
+
+			kaddr = kmap_atomic(page);
+			flush_data_cache_page((unsigned long)kaddr);
+			kunmap_atomic(kaddr);
+			ClearPageDcacheDirty(page);
+		}
 	}
 }
 
 EXPORT_SYMBOL(__flush_anon_page);
 
-static void mips_flush_dcache_from_pte(pte_t pteval, unsigned long address)
+void __update_cache(struct vm_area_struct *vma, unsigned long address,
+	pte_t pte)
 {
 	struct page *page;
-	unsigned long pfn = pte_pfn(pteval);
+	unsigned long pfn = pte_pfn(pte);
+	int exec = (vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc;
 
-	if (unlikely(!pfn_valid(pfn)))
+	if (unlikely(!pfn_valid(pfn))) {
+		wmb();
 		return;
-
+	}
 	page = pfn_to_page(pfn);
-	if (page_mapping(page) && Page_dcache_dirty(page)) {
+	if (page_mapped(page) && Page_dcache_dirty(page)) {
 		unsigned long page_addr = (unsigned long) page_address(page);
-
-		if (!cpu_has_ic_fills_f_dc ||
-		    pages_do_alias(page_addr, address & PAGE_MASK))
+		if (exec || (cpu_has_dc_aliases &&
+		    pages_do_alias(page_addr, address & PAGE_MASK))) {
 			flush_data_cache_page(page_addr);
-		ClearPageDcacheDirty(page);
+			ClearPageDcacheDirty(page);
+		}
 	}
-}
-
-void set_pte_at(struct mm_struct *mm, unsigned long addr,
-        pte_t *ptep, pte_t pteval)
-{
-        if (cpu_has_dc_aliases || !cpu_has_ic_fills_f_dc) {
-                if (pte_present(pteval))
-                        mips_flush_dcache_from_pte(pteval, addr);
-        }
-
-        set_pte(ptep, pteval);
+	wmb();  /* finish any outstanding arch cache flushes before ret to user */
 }
 
 unsigned long _page_cachable_default;
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 448cde3..597bf7f 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -165,9 +165,15 @@ void copy_user_highpage(struct page *to, struct page *from,
 		copy_page(vto, vfrom);
 		kunmap_atomic(vfrom);
 	}
-	if ((!cpu_has_ic_fills_f_dc) ||
-	    pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
+	if (cpu_has_dc_aliases)
+		SetPageDcacheDirty(to);
+	if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) ||
+	    cpu_has_vtag_dcache || (cpu_has_dc_aliases &&
+	     pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))) {
 		flush_data_cache_page((unsigned long)vto);
+		if (cpu_has_dc_aliases)
+			ClearPageDcacheDirty(to);
+	}
 	kunmap_atomic(vto);
 	/* Make sure this page is cleared on other CPU's too before using it */
 	smp_wmb();
@@ -187,8 +193,14 @@ void copy_to_user_page(struct vm_area_struct *vma,
 		if (cpu_has_dc_aliases)
 			SetPageDcacheDirty(page);
 	}
-	if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc)
+	if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) ||
+	    (Page_dcache_dirty(page) &&
+	     pages_do_alias((unsigned long)dst & PAGE_MASK,
+			    vaddr & PAGE_MASK))) {
 		flush_cache_page(vma, vaddr, page_to_pfn(page));
+		if (cpu_has_dc_aliases)
+			ClearPageDcacheDirty(page);
+	}
 }
 
 void copy_from_user_page(struct vm_area_struct *vma,
@@ -200,11 +212,8 @@ void copy_from_user_page(struct vm_area_struct *vma,
 		void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
 		memcpy(dst, vfrom, len);
 		kunmap_coherent();
-	} else {
+	} else
 		memcpy(dst, src, len);
-		if (cpu_has_dc_aliases)
-			SetPageDcacheDirty(page);
-	}
 }
 EXPORT_SYMBOL_GPL(copy_from_user_page);
 
diff --git a/arch/mips/mm/sc-mips.c b/arch/mips/mm/sc-mips.c
index 99eb8fa..e1db410 100644
--- a/arch/mips/mm/sc-mips.c
+++ b/arch/mips/mm/sc-mips.c
@@ -24,6 +24,7 @@
  */
 static void mips_sc_wback_inv(unsigned long addr, unsigned long size)
 {
+	__sync();
 	blast_scache_range(addr, addr + size);
 }
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] MIPS: Highmem: Fixes for cache aliasing and color.
  2015-01-29 16:46 [PATCH 0/3] HIGHMEM and cache flush fixes Steven J. Hill
  2015-01-29 16:46 ` [PATCH 1/3] MIPS: Fix cache flushing for swap pages with non-DMA I/O Steven J. Hill
@ 2015-01-29 16:46 ` Steven J. Hill
  2015-01-29 16:46 ` [PATCH 3/3] MIPS: Fix I-cache flushing for kmap'd pages Steven J. Hill
  2 siblings, 0 replies; 6+ messages in thread
From: Steven J. Hill @ 2015-01-29 16:46 UTC (permalink / raw)
  To: linux-mips; +Cc: ralf

From: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>

Make HIGHMEM kmap cache coloring aware and fix some corner
cases for cache aliasing.

Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
---
 arch/mips/Kconfig               |    1 +
 arch/mips/include/asm/fixmap.h  |   18 ++++++++-
 arch/mips/include/asm/highmem.h |   33 +++++++++++++++
 arch/mips/mm/c-r4k.c            |    8 ++++
 arch/mips/mm/cache.c            |   84 ++++++++++++++++++++++++++-------------
 arch/mips/mm/highmem.c          |   43 ++++++++------------
 arch/mips/mm/init.c             |   10 +----
 7 files changed, 132 insertions(+), 65 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 3289969..1fae6f1 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -391,6 +391,7 @@ config MIPS_MALTA
 	select SYS_SUPPORTS_MULTITHREADING
 	select SYS_SUPPORTS_SMARTMIPS
 	select SYS_SUPPORTS_ZBOOT
+	select SYS_SUPPORTS_HIGHMEM
 	help
 	  This enables support for the MIPS Technologies Malta evaluation
 	  board.
diff --git a/arch/mips/include/asm/fixmap.h b/arch/mips/include/asm/fixmap.h
index 6842ffa..f7733a0 100644
--- a/arch/mips/include/asm/fixmap.h
+++ b/arch/mips/include/asm/fixmap.h
@@ -46,13 +46,27 @@
  * fix-mapped?
  */
 enum fixed_addresses {
+
+/*
+ * Must be <=8 but, last_pkmap_nr_arr[] is still initialized to
+ * 8 elements. Keep the total L1 size <= 512KB with 4-ways.
+ */
+#ifdef  CONFIG_PAGE_SIZE_64KB
+#define FIX_N_COLOURS 2
+#endif
+#ifdef  CONFIG_PAGE_SIZE_32KB
+#define FIX_N_COLOURS 4
+#endif
+#ifndef FIX_N_COLOURS
 #define FIX_N_COLOURS 8
+#endif
+
 	FIX_CMAP_BEGIN,
-	FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * 2),
+	FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * NR_CPUS * 2),
 #ifdef CONFIG_HIGHMEM
 	/* reserved pte's for temporary kernel mappings */
 	FIX_KMAP_BEGIN = FIX_CMAP_END + 1,
-	FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN+(8*NR_CPUS*FIX_N_COLOURS)-1,
 #endif
 	__end_of_fixed_addresses
 };
diff --git a/arch/mips/include/asm/highmem.h b/arch/mips/include/asm/highmem.h
index 572e63e..af6180f 100644
--- a/arch/mips/include/asm/highmem.h
+++ b/arch/mips/include/asm/highmem.h
@@ -36,11 +36,44 @@ extern pte_t *pkmap_page_table;
  * easily, subsequent pte tables have to be allocated in one physical
  * chunk of RAM.
  */
+
+/*  8 colors pages are here */
+#ifdef  CONFIG_PAGE_SIZE_4KB
+#define LAST_PKMAP 4096
+#endif
+#ifdef  CONFIG_PAGE_SIZE_8KB
+#define LAST_PKMAP 2048
+#endif
+#ifdef  CONFIG_PAGE_SIZE_16KB
+#define LAST_PKMAP 1024
+#endif
+
+/* 32KB and 64KB pages should have 4 and 2 colors to keep space under control */
+#ifndef LAST_PKMAP
 #define LAST_PKMAP 1024
+#endif
+
 #define LAST_PKMAP_MASK (LAST_PKMAP-1)
 #define PKMAP_NR(virt)	((virt-PKMAP_BASE) >> PAGE_SHIFT)
 #define PKMAP_ADDR(nr)	(PKMAP_BASE + ((nr) << PAGE_SHIFT))
 
+#define     get_pkmap_color(pg) (((unsigned long)lowmem_page_address(pg) >> PAGE_SHIFT) & (FIX_N_COLOURS-1))
+#define     get_last_pkmap_nr(cl)     (last_pkmap_nr_arr[cl])
+#define     get_next_pkmap_nr(cl)     (last_pkmap_nr_arr[cl] = \
+					    ((get_last_pkmap_nr(cl) + FIX_N_COLOURS) & LAST_PKMAP_MASK))
+#define     no_more_pkmaps(p,cl)     (p < FIX_N_COLOURS)
+#define     get_pkmap_entries_count(c)    (c - FIX_N_COLOURS)
+
+static inline wait_queue_head_t *get_pkmap_wait_queue_head(unsigned int color)
+{
+	static DECLARE_WAIT_QUEUE_HEAD(pkmap_map_wait);
+
+	return &pkmap_map_wait;
+}
+
+extern unsigned int     last_pkmap_nr_arr[];
+
+
 extern void * kmap_high(struct page *page);
 extern void kunmap_high(struct page *page);
 
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index e045116..9096c5f 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -1288,6 +1288,14 @@ static void probe_pcache(void)
 			c->dcache.flags |= MIPS_CACHE_ALIASES;
 	}
 
+#ifdef  CONFIG_HIGHMEM
+	if (((c->dcache.flags & MIPS_CACHE_ALIASES) &&
+	     ((c->dcache.waysize / PAGE_SIZE) > FIX_N_COLOURS)) ||
+	     ((c->icache.flags & MIPS_CACHE_ALIASES) &&
+	     ((c->icache.waysize / PAGE_SIZE) > FIX_N_COLOURS)))
+		panic("PAGE_SIZE*WAYS too small for L1 size, too many colors");
+#endif
+
 	switch (current_cpu_type()) {
 	case CPU_20KC:
 		/*
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index 99db9e8..49496a4 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -81,12 +81,9 @@ SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,
 
 void __flush_dcache_page(struct page *page)
 {
-	struct address_space *mapping = page_mapping(page);
-	unsigned long addr;
+	void *addr;
 
-	if (PageHighMem(page))
-		return;
-	if (mapping && !mapping_mapped(mapping)) {
+	if (page_mapping(page) && !page_mapped(page)) {
 		SetPageDcacheDirty(page);
 		return;
 	}
@@ -96,30 +93,54 @@ void __flush_dcache_page(struct page *page)
 	 * case is for exec env/arg pages and those are %99 certainly going to
 	 * get faulted into the tlb (and thus flushed) anyways.
 	 */
-	addr = (unsigned long) page_address(page);
-	flush_data_cache_page(addr);
+	if (PageHighMem(page)) {
+		addr = kmap_atomic(page);
+		flush_data_cache_page((unsigned long)addr);
+		kunmap_atomic(addr);
+	} else {
+		addr = (void *) page_address(page);
+		flush_data_cache_page((unsigned long)addr);
+	}
+	ClearPageDcacheDirty(page);
 }
 
 EXPORT_SYMBOL(__flush_dcache_page);
 
 void __flush_anon_page(struct page *page, unsigned long vmaddr)
 {
-	unsigned long addr = (unsigned long) page_address(page);
-
-	if (pages_do_alias(addr, vmaddr & PAGE_MASK)) {
-		if (page_mapped(page) && !Page_dcache_dirty(page)) {
-			void *kaddr;
-
-			kaddr = kmap_coherent(page, vmaddr);
-			flush_data_cache_page((unsigned long)kaddr);
-			kunmap_coherent();
-		} else {
-			void *kaddr;
-
-			kaddr = kmap_atomic(page);
-			flush_data_cache_page((unsigned long)kaddr);
-			kunmap_atomic(kaddr);
-			ClearPageDcacheDirty(page);
+	if (!PageHighMem(page)) {
+		unsigned long addr = (unsigned long) page_address(page);
+
+		if (pages_do_alias(addr, vmaddr & PAGE_MASK)) {
+			if (page_mapped(page) && !Page_dcache_dirty(page)) {
+				void *kaddr;
+
+				kaddr = kmap_coherent(page, vmaddr);
+				flush_data_cache_page((unsigned long)kaddr);
+				kunmap_coherent();
+			} else {
+				flush_data_cache_page(addr);
+				ClearPageDcacheDirty(page);
+			}
+		}
+	} else {
+		void *laddr = lowmem_page_address(page);
+
+		if (pages_do_alias((unsigned long)laddr, vmaddr & PAGE_MASK)) {
+			if (page_mapped(page) && !Page_dcache_dirty(page)) {
+				void *kaddr;
+
+				kaddr = kmap_coherent(page, vmaddr);
+				flush_data_cache_page((unsigned long)kaddr);
+				kunmap_coherent();
+			} else {
+				void *kaddr;
+
+				kaddr = kmap_atomic(page);
+				flush_data_cache_page((unsigned long)kaddr);
+				kunmap_atomic(kaddr);
+				ClearPageDcacheDirty(page);
+			}
 		}
 	}
 }
@@ -130,21 +151,30 @@ void __update_cache(struct vm_area_struct *vma, unsigned long address,
 	pte_t pte)
 {
 	struct page *page;
-	unsigned long pfn = pte_pfn(pte);
+	unsigned long pfn, addr;
 	int exec = (vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc;
 
+	pfn = pte_pfn(pte);
 	if (unlikely(!pfn_valid(pfn))) {
 		wmb();
 		return;
 	}
 	page = pfn_to_page(pfn);
 	if (page_mapped(page) && Page_dcache_dirty(page)) {
-		unsigned long page_addr = (unsigned long) page_address(page);
+		void *kaddr = NULL;
+		if (PageHighMem(page)) {
+			addr = (unsigned long)kmap_atomic(page);
+			kaddr = (void *)addr;
+		} else
+			addr = (unsigned long) page_address(page);
 		if (exec || (cpu_has_dc_aliases &&
-		    pages_do_alias(page_addr, address & PAGE_MASK))) {
-			flush_data_cache_page(page_addr);
+		    pages_do_alias(addr, address & PAGE_MASK))) {
+			flush_data_cache_page(addr);
 			ClearPageDcacheDirty(page);
 		}
+
+		if (kaddr)
+			kunmap_atomic((void *)kaddr);
 	}
 	wmb();  /* finish any outstanding arch cache flushes before ret to user */
 }
diff --git a/arch/mips/mm/highmem.c b/arch/mips/mm/highmem.c
index da815d2..10fc041 100644
--- a/arch/mips/mm/highmem.c
+++ b/arch/mips/mm/highmem.c
@@ -9,6 +9,7 @@
 static pte_t *kmap_pte;
 
 unsigned long highstart_pfn, highend_pfn;
+unsigned int  last_pkmap_nr_arr[FIX_N_COLOURS] = { 0, 1, 2, 3, 4, 5, 6, 7 };
 
 void *kmap(struct page *page)
 {
@@ -53,8 +54,12 @@ void *kmap_atomic(struct page *page)
 		return page_address(page);
 
 	type = kmap_atomic_idx_push();
-	idx = type + KM_TYPE_NR*smp_processor_id();
-	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+
+	idx = (((unsigned long)lowmem_page_address(page)) >> PAGE_SHIFT) & (FIX_N_COLOURS - 1);
+	idx = (FIX_N_COLOURS - idx);
+	idx = idx + FIX_N_COLOURS * (smp_processor_id() + NR_CPUS * type);
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN - 1 + idx);    /* actually - FIX_CMAP_END */
+
 #ifdef CONFIG_DEBUG_HIGHMEM
 	BUG_ON(!pte_none(*(kmap_pte - idx)));
 #endif
@@ -75,12 +80,16 @@ void __kunmap_atomic(void *kvaddr)
 		return;
 	}
 
-	type = kmap_atomic_idx();
 #ifdef CONFIG_DEBUG_HIGHMEM
 	{
-		int idx = type + KM_TYPE_NR * smp_processor_id();
+		int idx;
+		type = kmap_atomic_idx();
 
-		BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
+		idx = ((unsigned long)kvaddr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1);
+		idx = (FIX_N_COLOURS - idx);
+		idx = idx + FIX_N_COLOURS * (smp_processor_id() + NR_CPUS * type);
+
+		BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN -1 + idx));
 
 		/*
 		 * force other mappings to Oops if they'll try to access
@@ -95,26 +104,6 @@ void __kunmap_atomic(void *kvaddr)
 }
 EXPORT_SYMBOL(__kunmap_atomic);
 
-/*
- * This is the same as kmap_atomic() but can map memory that doesn't
- * have a struct page associated with it.
- */
-void *kmap_atomic_pfn(unsigned long pfn)
-{
-	unsigned long vaddr;
-	int idx, type;
-
-	pagefault_disable();
-
-	type = kmap_atomic_idx_push();
-	idx = type + KM_TYPE_NR*smp_processor_id();
-	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
-	set_pte(kmap_pte-idx, pfn_pte(pfn, PAGE_KERNEL));
-	flush_tlb_one(vaddr);
-
-	return (void*) vaddr;
-}
-
 struct page *kmap_atomic_to_page(void *ptr)
 {
 	unsigned long idx, vaddr = (unsigned long)ptr;
@@ -124,7 +113,7 @@ struct page *kmap_atomic_to_page(void *ptr)
 		return virt_to_page(ptr);
 
 	idx = virt_to_fix(vaddr);
-	pte = kmap_pte - (idx - FIX_KMAP_BEGIN);
+	pte = kmap_pte - (idx - FIX_KMAP_BEGIN + 1);
 	return pte_page(*pte);
 }
 
@@ -133,6 +122,6 @@ void __init kmap_init(void)
 	unsigned long kmap_vstart;
 
 	/* cache the first kmap pte */
-	kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
+	kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN - 1); /* actually - FIX_CMAP_END */
 	kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
 }
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 597bf7f..0dc604a 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -284,7 +284,7 @@ int page_is_ram(unsigned long pagenr)
 void __init paging_init(void)
 {
 	unsigned long max_zone_pfns[MAX_NR_ZONES];
-	unsigned long lastpfn __maybe_unused;
+	unsigned long lastpfn;
 
 	pagetable_init();
 
@@ -302,14 +302,6 @@ void __init paging_init(void)
 #ifdef CONFIG_HIGHMEM
 	max_zone_pfns[ZONE_HIGHMEM] = highend_pfn;
 	lastpfn = highend_pfn;
-
-	if (cpu_has_dc_aliases && max_low_pfn != highend_pfn) {
-		printk(KERN_WARNING "This processor doesn't support highmem."
-		       " %ldk highmem ignored\n",
-		       (highend_pfn - max_low_pfn) << (PAGE_SHIFT - 10));
-		max_zone_pfns[ZONE_HIGHMEM] = max_low_pfn;
-		lastpfn = max_low_pfn;
-	}
 #endif
 
 	free_area_init_nodes(max_zone_pfns);
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] MIPS: Fix I-cache flushing for kmap'd pages.
  2015-01-29 16:46 [PATCH 0/3] HIGHMEM and cache flush fixes Steven J. Hill
  2015-01-29 16:46 ` [PATCH 1/3] MIPS: Fix cache flushing for swap pages with non-DMA I/O Steven J. Hill
  2015-01-29 16:46 ` [PATCH 2/3] MIPS: Highmem: Fixes for cache aliasing and color Steven J. Hill
@ 2015-01-29 16:46 ` Steven J. Hill
  2 siblings, 0 replies; 6+ messages in thread
From: Steven J. Hill @ 2015-01-29 16:46 UTC (permalink / raw)
  To: linux-mips; +Cc: ralf

From: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>

Make the I-cache flush pages while taking into account the
address color by using kmap_coherent() when there is
I-cache aliasing present.

Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
---
 arch/mips/include/asm/cpu-features.h |    3 +++
 arch/mips/mm/c-r4k.c                 |   17 ++++++++++++++---
 arch/mips/mm/init.c                  |    2 --
 3 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
index 23db770..92aa321 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -142,6 +142,9 @@
 #ifndef cpu_has_vtag_dcache
 #define cpu_has_vtag_dcache     (cpu_data[0].dcache.flags & MIPS_CACHE_VTAG)
 #endif
+#ifndef cpu_has_ic_aliases
+#define cpu_has_ic_aliases      (cpu_data[0].icache.flags & MIPS_CACHE_ALIASES)
+#endif
 #ifndef cpu_has_dc_aliases
 #define cpu_has_dc_aliases	(cpu_data[0].dcache.flags & MIPS_CACHE_ALIASES)
 #endif
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 9096c5f..d48da56a 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -552,6 +552,7 @@ static inline void local_r4k_flush_cache_page(void *args)
 	pmd_t *pmdp;
 	pte_t *ptep;
 	void *vaddr;
+	int noflush = 0;
 
 	/*
 	 * If ownes no valid ASID yet, cannot possibly have gotten
@@ -611,6 +612,7 @@ static inline void local_r4k_flush_cache_page(void *args)
 
 			if (cpu_context(cpu, mm) != 0)
 				drop_mmu_context(mm, cpu);
+			noflush = 1;
 		} else
 			vaddr ? r4k_blast_icache_page(addr) :
 				r4k_blast_icache_user_page(addr);
@@ -622,6 +624,13 @@ static inline void local_r4k_flush_cache_page(void *args)
 		else
 			kunmap_atomic(vaddr);
 	}
+
+	/*  If we have I-cache aliasing, then blast it via coherent page. */
+	if (exec && cpu_has_ic_aliases && !noflush && !map_coherent) {
+		vaddr = kmap_coherent(page, addr);
+		r4k_blast_icache_page((unsigned long)vaddr);
+		kunmap_coherent();
+	}
 }
 
 static void r4k_flush_cache_page(struct vm_area_struct *vma,
@@ -1317,10 +1326,12 @@ static void probe_pcache(void)
 		c->icache.ways = 1;
 	}
 
-	printk("Primary instruction cache %ldkB, %s, %s, linesize %d bytes.\n",
-	       icache_size >> 10,
+	printk("Primary instruction cache %ldkB, %s, %s, %slinesize %d bytes.\n",
+	       icache_size >> 10, way_string[c->icache.ways],
 	       c->icache.flags & MIPS_CACHE_VTAG ? "VIVT" : "VIPT",
-	       way_string[c->icache.ways], c->icache.linesz);
+	       (c->icache.flags & MIPS_CACHE_ALIASES) ?
+			"I-cache aliases, " : "",
+	       c->icache.linesz);
 
 	printk("Primary data cache %ldkB, %s, %s, %s, linesize %d bytes\n",
 	       dcache_size >> 10, way_string[c->dcache.ways],
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 0dc604a..b1584d6 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -88,8 +88,6 @@ static void *__kmap_pgprot(struct page *page, unsigned long addr, pgprot_t prot)
 	pte_t pte;
 	int tlbidx;
 
-	BUG_ON(Page_dcache_dirty(page));
-
 	pagefault_disable();
 	idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1);
 	idx += in_interrupt() ? FIX_N_COLOURS : 0;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [1/3] MIPS: Fix cache flushing for swap pages with non-DMA I/O.
@ 2015-02-04 14:54     ` Lars Persson
  0 siblings, 0 replies; 6+ messages in thread
From: Lars Persson @ 2015-02-04 14:54 UTC (permalink / raw)
  To: Steven J. Hill; +Cc: linux-mips, ralf

Hi

I NACK this patch series because AFAICT it re-introduces the race
condition that I fixed with 2a4a8b1e5d9d343e13ff22e19af7b353f7b52d6f
MIPS: Remove race window in page fault handling.

You need to address also the case of the file-system layer writing to an
executable page and calling flush_dcache_page().

With this patch series, the flush will be postponed by this check:
if (page_mapping(page) && !page_mapped(page)) {
 SetPageDcacheDirty(page);
 return;
}

Another thread can race with __update_cache() and execute garbage code.

Please consider adding a commit of the postponed cache flush in
flush_icache_page().

BR,
 Lars

On tor, 2015-01-29 at 10:46 -0600, Steven J. Hill wrote:
> From: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
> 
> Flush the D-cache before the page is given to a process
> as an executable (I-cache) page when the backing store
> is non-DMA I/O.
> 
> Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
> Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
> ---
>  arch/mips/include/asm/cacheflush.h   |    3 ++-
>  arch/mips/include/asm/cpu-features.h |    3 +++
>  arch/mips/include/asm/page.h         |    5 +++-
>  arch/mips/include/asm/pgtable.h      |    5 ++++
>  arch/mips/mm/c-r4k.c                 |   19 +++++++++++++-
>  arch/mips/mm/cache.c                 |   46 +++++++++++++++++-----------------
>  arch/mips/mm/init.c                  |   23 +++++++++++------
>  arch/mips/mm/sc-mips.c               |    1 +
>  8 files changed, 72 insertions(+), 33 deletions(-)
> 
> diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h
> index e08381a..3a4582a 100644
> --- a/arch/mips/include/asm/cacheflush.h
> +++ b/arch/mips/include/asm/cacheflush.h
> @@ -123,7 +123,8 @@ static inline void kunmap_noncoherent(void)
>  #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE
>  static inline void flush_kernel_dcache_page(struct page *page)
>  {
> -	BUG_ON(cpu_has_dc_aliases && PageHighMem(page));
> +	if (cpu_has_dc_aliases || !cpu_has_ic_fills_f_dc)
> +		__flush_dcache_page(page);
>  }
>  
>  /*
> diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
> index 2897cfa..23db770 100644
> --- a/arch/mips/include/asm/cpu-features.h
> +++ b/arch/mips/include/asm/cpu-features.h
> @@ -139,6 +139,9 @@
>  #ifndef cpu_has_vtag_icache
>  #define cpu_has_vtag_icache	(cpu_data[0].icache.flags & MIPS_CACHE_VTAG)
>  #endif
> +#ifndef cpu_has_vtag_dcache
> +#define cpu_has_vtag_dcache     (cpu_data[0].dcache.flags & MIPS_CACHE_VTAG)
> +#endif
>  #ifndef cpu_has_dc_aliases
>  #define cpu_has_dc_aliases	(cpu_data[0].dcache.flags & MIPS_CACHE_ALIASES)
>  #endif
> diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
> index 154b70a..fc7ab98 100644
> --- a/arch/mips/include/asm/page.h
> +++ b/arch/mips/include/asm/page.h
> @@ -95,13 +95,16 @@ static inline unsigned long pages_do_alias(unsigned long addr1,
>  
>  struct page;
>  
> +#include <asm/cpu-features.h>
> +
>  static inline void clear_user_page(void *addr, unsigned long vaddr,
>  	struct page *page)
>  {
>  	extern void (*flush_data_cache_page)(unsigned long addr);
>  
>  	clear_page(addr);
> -	if (pages_do_alias((unsigned long) addr, vaddr & PAGE_MASK))
> +	if (cpu_has_vtag_dcache || (cpu_has_dc_aliases &&
> +	     pages_do_alias((unsigned long) addr, vaddr & PAGE_MASK)))
>  		flush_data_cache_page((unsigned long)addr);
>  }
>  
> diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h
> index 62a6ba3..0a6a944 100644
> --- a/arch/mips/include/asm/pgtable.h
> +++ b/arch/mips/include/asm/pgtable.h
> @@ -148,6 +148,7 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
>  		}
>  	}
>  }
> +#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
>  
>  static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
>  {
> @@ -185,6 +186,7 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
>  	}
>  #endif
>  }
> +#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
>  
>  static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
>  {
> @@ -401,12 +403,15 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
>  
>  extern void __update_tlb(struct vm_area_struct *vma, unsigned long address,
>  	pte_t pte);
> +extern void __update_cache(struct vm_area_struct *vma, unsigned long address,
> +	pte_t pte);
>  
>  static inline void update_mmu_cache(struct vm_area_struct *vma,
>  	unsigned long address, pte_t *ptep)
>  {
>  	pte_t pte = *ptep;
>  	__update_tlb(vma, address, pte);
> +	__update_cache(vma, address, pte);
>  }
>  
>  static inline void update_mmu_cache_pmd(struct vm_area_struct *vma,
> diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
> index dd261df..e045116 100644
> --- a/arch/mips/mm/c-r4k.c
> +++ b/arch/mips/mm/c-r4k.c
> @@ -484,8 +484,11 @@ static inline void local_r4k_flush_cache_range(void * args)
>  		return;
>  
>  	r4k_blast_dcache();
> -	if (exec)
> +	if (exec) {
> +		if (!cpu_has_ic_fills_f_dc)
> +			wmb();
>  		r4k_blast_icache();
> +	}
>  }
>  
>  static void r4k_flush_cache_range(struct vm_area_struct *vma,
> @@ -570,6 +573,14 @@ static inline void local_r4k_flush_cache_page(void *args)
>  	if (!(pte_present(*ptep)))
>  		return;
>  
> +	/*
> +	 * If this page is not destined to be executable and the
> +	 * data cache does not have aliases, all of the mapping
> +	 * below can be skipped.
> +	 */
> +	if (!exec && !cpu_has_dc_aliases)
> +		return;
> +
>  	if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID))
>  		vaddr = NULL;
>  	else {
> @@ -589,6 +600,8 @@ static inline void local_r4k_flush_cache_page(void *args)
>  	if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) {
>  		vaddr ? r4k_blast_dcache_page(addr) :
>  			r4k_blast_dcache_user_page(addr);
> +		if (exec && !cpu_has_ic_fills_f_dc)
> +			wmb();
>  		if (exec && !cpu_icache_snoops_remote_store)
>  			r4k_blast_scache_page(addr);
>  	}
> @@ -621,6 +634,8 @@ static void r4k_flush_cache_page(struct vm_area_struct *vma,
>  	args.pfn = pfn;
>  
>  	r4k_on_each_cpu(local_r4k_flush_cache_page, &args);
> +	if (cpu_has_dc_aliases)
> +		ClearPageDcacheDirty(pfn_to_page(pfn));
>  }
>  
>  static inline void local_r4k_flush_data_cache_page(void * addr)
> @@ -652,6 +667,8 @@ static inline void local_r4k_flush_icache_range(unsigned long start, unsigned lo
>  		}
>  	}
>  
> +	wmb();
> +
>  	if (end - start > icache_size)
>  		r4k_blast_icache();
>  	else {
> diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
> index 7e3ea77..99db9e8 100644
> --- a/arch/mips/mm/cache.c
> +++ b/arch/mips/mm/cache.c
> @@ -19,6 +19,7 @@
>  #include <asm/processor.h>
>  #include <asm/cpu.h>
>  #include <asm/cpu-features.h>
> +#include <linux/highmem.h>
>  
>  /* Cache operations. */
>  void (*flush_cache_all)(void);
> @@ -105,48 +106,47 @@ void __flush_anon_page(struct page *page, unsigned long vmaddr)
>  {
>  	unsigned long addr = (unsigned long) page_address(page);
>  
> -	if (pages_do_alias(addr, vmaddr)) {
> +	if (pages_do_alias(addr, vmaddr & PAGE_MASK)) {
>  		if (page_mapped(page) && !Page_dcache_dirty(page)) {
>  			void *kaddr;
>  
>  			kaddr = kmap_coherent(page, vmaddr);
>  			flush_data_cache_page((unsigned long)kaddr);
>  			kunmap_coherent();
> -		} else
> -			flush_data_cache_page(addr);
> +		} else {
> +			void *kaddr;
> +
> +			kaddr = kmap_atomic(page);
> +			flush_data_cache_page((unsigned long)kaddr);
> +			kunmap_atomic(kaddr);
> +			ClearPageDcacheDirty(page);
> +		}
>  	}
>  }
>  
>  EXPORT_SYMBOL(__flush_anon_page);
>  
> -static void mips_flush_dcache_from_pte(pte_t pteval, unsigned long address)
> +void __update_cache(struct vm_area_struct *vma, unsigned long address,
> +	pte_t pte)
>  {
>  	struct page *page;
> -	unsigned long pfn = pte_pfn(pteval);
> +	unsigned long pfn = pte_pfn(pte);
> +	int exec = (vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc;
>  
> -	if (unlikely(!pfn_valid(pfn)))
> +	if (unlikely(!pfn_valid(pfn))) {
> +		wmb();
>  		return;
> -
> +	}
>  	page = pfn_to_page(pfn);
> -	if (page_mapping(page) && Page_dcache_dirty(page)) {
> +	if (page_mapped(page) && Page_dcache_dirty(page)) {
>  		unsigned long page_addr = (unsigned long) page_address(page);
> -
> -		if (!cpu_has_ic_fills_f_dc ||
> -		    pages_do_alias(page_addr, address & PAGE_MASK))
> +		if (exec || (cpu_has_dc_aliases &&
> +		    pages_do_alias(page_addr, address & PAGE_MASK))) {
>  			flush_data_cache_page(page_addr);
> -		ClearPageDcacheDirty(page);
> +			ClearPageDcacheDirty(page);
> +		}
>  	}
> -}
> -
> -void set_pte_at(struct mm_struct *mm, unsigned long addr,
> -        pte_t *ptep, pte_t pteval)
> -{
> -        if (cpu_has_dc_aliases || !cpu_has_ic_fills_f_dc) {
> -                if (pte_present(pteval))
> -                        mips_flush_dcache_from_pte(pteval, addr);
> -        }
> -
> -        set_pte(ptep, pteval);
> +	wmb();  /* finish any outstanding arch cache flushes before ret to user */
>  }
>  
>  unsigned long _page_cachable_default;
> diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
> index 448cde3..597bf7f 100644
> --- a/arch/mips/mm/init.c
> +++ b/arch/mips/mm/init.c
> @@ -165,9 +165,15 @@ void copy_user_highpage(struct page *to, struct page *from,
>  		copy_page(vto, vfrom);
>  		kunmap_atomic(vfrom);
>  	}
> -	if ((!cpu_has_ic_fills_f_dc) ||
> -	    pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
> +	if (cpu_has_dc_aliases)
> +		SetPageDcacheDirty(to);
> +	if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) ||
> +	    cpu_has_vtag_dcache || (cpu_has_dc_aliases &&
> +	     pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))) {
>  		flush_data_cache_page((unsigned long)vto);
> +		if (cpu_has_dc_aliases)
> +			ClearPageDcacheDirty(to);
> +	}
>  	kunmap_atomic(vto);
>  	/* Make sure this page is cleared on other CPU's too before using it */
>  	smp_wmb();
> @@ -187,8 +193,14 @@ void copy_to_user_page(struct vm_area_struct *vma,
>  		if (cpu_has_dc_aliases)
>  			SetPageDcacheDirty(page);
>  	}
> -	if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc)
> +	if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) ||
> +	    (Page_dcache_dirty(page) &&
> +	     pages_do_alias((unsigned long)dst & PAGE_MASK,
> +			    vaddr & PAGE_MASK))) {
>  		flush_cache_page(vma, vaddr, page_to_pfn(page));
> +		if (cpu_has_dc_aliases)
> +			ClearPageDcacheDirty(page);
> +	}
>  }
>  
>  void copy_from_user_page(struct vm_area_struct *vma,
> @@ -200,11 +212,8 @@ void copy_from_user_page(struct vm_area_struct *vma,
>  		void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
>  		memcpy(dst, vfrom, len);
>  		kunmap_coherent();
> -	} else {
> +	} else
>  		memcpy(dst, src, len);
> -		if (cpu_has_dc_aliases)
> -			SetPageDcacheDirty(page);
> -	}
>  }
>  EXPORT_SYMBOL_GPL(copy_from_user_page);
>  
> diff --git a/arch/mips/mm/sc-mips.c b/arch/mips/mm/sc-mips.c
> index 99eb8fa..e1db410 100644
> --- a/arch/mips/mm/sc-mips.c
> +++ b/arch/mips/mm/sc-mips.c
> @@ -24,6 +24,7 @@
>   */
>  static void mips_sc_wback_inv(unsigned long addr, unsigned long size)
>  {
> +	__sync();
>  	blast_scache_range(addr, addr + size);
>  }
>  

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [1/3] MIPS: Fix cache flushing for swap pages with non-DMA I/O.
@ 2015-02-04 14:54     ` Lars Persson
  0 siblings, 0 replies; 6+ messages in thread
From: Lars Persson @ 2015-02-04 14:54 UTC (permalink / raw)
  To: Steven J. Hill; +Cc: linux-mips, ralf

Hi

I NACK this patch series because AFAICT it re-introduces the race
condition that I fixed with 2a4a8b1e5d9d343e13ff22e19af7b353f7b52d6f
MIPS: Remove race window in page fault handling.

You need to address also the case of the file-system layer writing to an
executable page and calling flush_dcache_page().

With this patch series, the flush will be postponed by this check:
if (page_mapping(page) && !page_mapped(page)) {
 SetPageDcacheDirty(page);
 return;
}

Another thread can race with __update_cache() and execute garbage code.

Please consider adding a commit of the postponed cache flush in
flush_icache_page().

BR,
 Lars

On tor, 2015-01-29 at 10:46 -0600, Steven J. Hill wrote:
> From: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
> 
> Flush the D-cache before the page is given to a process
> as an executable (I-cache) page when the backing store
> is non-DMA I/O.
> 
> Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
> Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
> ---
>  arch/mips/include/asm/cacheflush.h   |    3 ++-
>  arch/mips/include/asm/cpu-features.h |    3 +++
>  arch/mips/include/asm/page.h         |    5 +++-
>  arch/mips/include/asm/pgtable.h      |    5 ++++
>  arch/mips/mm/c-r4k.c                 |   19 +++++++++++++-
>  arch/mips/mm/cache.c                 |   46 +++++++++++++++++-----------------
>  arch/mips/mm/init.c                  |   23 +++++++++++------
>  arch/mips/mm/sc-mips.c               |    1 +
>  8 files changed, 72 insertions(+), 33 deletions(-)
> 
> diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h
> index e08381a..3a4582a 100644
> --- a/arch/mips/include/asm/cacheflush.h
> +++ b/arch/mips/include/asm/cacheflush.h
> @@ -123,7 +123,8 @@ static inline void kunmap_noncoherent(void)
>  #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE
>  static inline void flush_kernel_dcache_page(struct page *page)
>  {
> -	BUG_ON(cpu_has_dc_aliases && PageHighMem(page));
> +	if (cpu_has_dc_aliases || !cpu_has_ic_fills_f_dc)
> +		__flush_dcache_page(page);
>  }
>  
>  /*
> diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
> index 2897cfa..23db770 100644
> --- a/arch/mips/include/asm/cpu-features.h
> +++ b/arch/mips/include/asm/cpu-features.h
> @@ -139,6 +139,9 @@
>  #ifndef cpu_has_vtag_icache
>  #define cpu_has_vtag_icache	(cpu_data[0].icache.flags & MIPS_CACHE_VTAG)
>  #endif
> +#ifndef cpu_has_vtag_dcache
> +#define cpu_has_vtag_dcache     (cpu_data[0].dcache.flags & MIPS_CACHE_VTAG)
> +#endif
>  #ifndef cpu_has_dc_aliases
>  #define cpu_has_dc_aliases	(cpu_data[0].dcache.flags & MIPS_CACHE_ALIASES)
>  #endif
> diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
> index 154b70a..fc7ab98 100644
> --- a/arch/mips/include/asm/page.h
> +++ b/arch/mips/include/asm/page.h
> @@ -95,13 +95,16 @@ static inline unsigned long pages_do_alias(unsigned long addr1,
>  
>  struct page;
>  
> +#include <asm/cpu-features.h>
> +
>  static inline void clear_user_page(void *addr, unsigned long vaddr,
>  	struct page *page)
>  {
>  	extern void (*flush_data_cache_page)(unsigned long addr);
>  
>  	clear_page(addr);
> -	if (pages_do_alias((unsigned long) addr, vaddr & PAGE_MASK))
> +	if (cpu_has_vtag_dcache || (cpu_has_dc_aliases &&
> +	     pages_do_alias((unsigned long) addr, vaddr & PAGE_MASK)))
>  		flush_data_cache_page((unsigned long)addr);
>  }
>  
> diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h
> index 62a6ba3..0a6a944 100644
> --- a/arch/mips/include/asm/pgtable.h
> +++ b/arch/mips/include/asm/pgtable.h
> @@ -148,6 +148,7 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
>  		}
>  	}
>  }
> +#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
>  
>  static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
>  {
> @@ -185,6 +186,7 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
>  	}
>  #endif
>  }
> +#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
>  
>  static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
>  {
> @@ -401,12 +403,15 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
>  
>  extern void __update_tlb(struct vm_area_struct *vma, unsigned long address,
>  	pte_t pte);
> +extern void __update_cache(struct vm_area_struct *vma, unsigned long address,
> +	pte_t pte);
>  
>  static inline void update_mmu_cache(struct vm_area_struct *vma,
>  	unsigned long address, pte_t *ptep)
>  {
>  	pte_t pte = *ptep;
>  	__update_tlb(vma, address, pte);
> +	__update_cache(vma, address, pte);
>  }
>  
>  static inline void update_mmu_cache_pmd(struct vm_area_struct *vma,
> diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
> index dd261df..e045116 100644
> --- a/arch/mips/mm/c-r4k.c
> +++ b/arch/mips/mm/c-r4k.c
> @@ -484,8 +484,11 @@ static inline void local_r4k_flush_cache_range(void * args)
>  		return;
>  
>  	r4k_blast_dcache();
> -	if (exec)
> +	if (exec) {
> +		if (!cpu_has_ic_fills_f_dc)
> +			wmb();
>  		r4k_blast_icache();
> +	}
>  }
>  
>  static void r4k_flush_cache_range(struct vm_area_struct *vma,
> @@ -570,6 +573,14 @@ static inline void local_r4k_flush_cache_page(void *args)
>  	if (!(pte_present(*ptep)))
>  		return;
>  
> +	/*
> +	 * If this page is not destined to be executable and the
> +	 * data cache does not have aliases, all of the mapping
> +	 * below can be skipped.
> +	 */
> +	if (!exec && !cpu_has_dc_aliases)
> +		return;
> +
>  	if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID))
>  		vaddr = NULL;
>  	else {
> @@ -589,6 +600,8 @@ static inline void local_r4k_flush_cache_page(void *args)
>  	if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) {
>  		vaddr ? r4k_blast_dcache_page(addr) :
>  			r4k_blast_dcache_user_page(addr);
> +		if (exec && !cpu_has_ic_fills_f_dc)
> +			wmb();
>  		if (exec && !cpu_icache_snoops_remote_store)
>  			r4k_blast_scache_page(addr);
>  	}
> @@ -621,6 +634,8 @@ static void r4k_flush_cache_page(struct vm_area_struct *vma,
>  	args.pfn = pfn;
>  
>  	r4k_on_each_cpu(local_r4k_flush_cache_page, &args);
> +	if (cpu_has_dc_aliases)
> +		ClearPageDcacheDirty(pfn_to_page(pfn));
>  }
>  
>  static inline void local_r4k_flush_data_cache_page(void * addr)
> @@ -652,6 +667,8 @@ static inline void local_r4k_flush_icache_range(unsigned long start, unsigned lo
>  		}
>  	}
>  
> +	wmb();
> +
>  	if (end - start > icache_size)
>  		r4k_blast_icache();
>  	else {
> diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
> index 7e3ea77..99db9e8 100644
> --- a/arch/mips/mm/cache.c
> +++ b/arch/mips/mm/cache.c
> @@ -19,6 +19,7 @@
>  #include <asm/processor.h>
>  #include <asm/cpu.h>
>  #include <asm/cpu-features.h>
> +#include <linux/highmem.h>
>  
>  /* Cache operations. */
>  void (*flush_cache_all)(void);
> @@ -105,48 +106,47 @@ void __flush_anon_page(struct page *page, unsigned long vmaddr)
>  {
>  	unsigned long addr = (unsigned long) page_address(page);
>  
> -	if (pages_do_alias(addr, vmaddr)) {
> +	if (pages_do_alias(addr, vmaddr & PAGE_MASK)) {
>  		if (page_mapped(page) && !Page_dcache_dirty(page)) {
>  			void *kaddr;
>  
>  			kaddr = kmap_coherent(page, vmaddr);
>  			flush_data_cache_page((unsigned long)kaddr);
>  			kunmap_coherent();
> -		} else
> -			flush_data_cache_page(addr);
> +		} else {
> +			void *kaddr;
> +
> +			kaddr = kmap_atomic(page);
> +			flush_data_cache_page((unsigned long)kaddr);
> +			kunmap_atomic(kaddr);
> +			ClearPageDcacheDirty(page);
> +		}
>  	}
>  }
>  
>  EXPORT_SYMBOL(__flush_anon_page);
>  
> -static void mips_flush_dcache_from_pte(pte_t pteval, unsigned long address)
> +void __update_cache(struct vm_area_struct *vma, unsigned long address,
> +	pte_t pte)
>  {
>  	struct page *page;
> -	unsigned long pfn = pte_pfn(pteval);
> +	unsigned long pfn = pte_pfn(pte);
> +	int exec = (vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc;
>  
> -	if (unlikely(!pfn_valid(pfn)))
> +	if (unlikely(!pfn_valid(pfn))) {
> +		wmb();
>  		return;
> -
> +	}
>  	page = pfn_to_page(pfn);
> -	if (page_mapping(page) && Page_dcache_dirty(page)) {
> +	if (page_mapped(page) && Page_dcache_dirty(page)) {
>  		unsigned long page_addr = (unsigned long) page_address(page);
> -
> -		if (!cpu_has_ic_fills_f_dc ||
> -		    pages_do_alias(page_addr, address & PAGE_MASK))
> +		if (exec || (cpu_has_dc_aliases &&
> +		    pages_do_alias(page_addr, address & PAGE_MASK))) {
>  			flush_data_cache_page(page_addr);
> -		ClearPageDcacheDirty(page);
> +			ClearPageDcacheDirty(page);
> +		}
>  	}
> -}
> -
> -void set_pte_at(struct mm_struct *mm, unsigned long addr,
> -        pte_t *ptep, pte_t pteval)
> -{
> -        if (cpu_has_dc_aliases || !cpu_has_ic_fills_f_dc) {
> -                if (pte_present(pteval))
> -                        mips_flush_dcache_from_pte(pteval, addr);
> -        }
> -
> -        set_pte(ptep, pteval);
> +	wmb();  /* finish any outstanding arch cache flushes before ret to user */
>  }
>  
>  unsigned long _page_cachable_default;
> diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
> index 448cde3..597bf7f 100644
> --- a/arch/mips/mm/init.c
> +++ b/arch/mips/mm/init.c
> @@ -165,9 +165,15 @@ void copy_user_highpage(struct page *to, struct page *from,
>  		copy_page(vto, vfrom);
>  		kunmap_atomic(vfrom);
>  	}
> -	if ((!cpu_has_ic_fills_f_dc) ||
> -	    pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
> +	if (cpu_has_dc_aliases)
> +		SetPageDcacheDirty(to);
> +	if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) ||
> +	    cpu_has_vtag_dcache || (cpu_has_dc_aliases &&
> +	     pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))) {
>  		flush_data_cache_page((unsigned long)vto);
> +		if (cpu_has_dc_aliases)
> +			ClearPageDcacheDirty(to);
> +	}
>  	kunmap_atomic(vto);
>  	/* Make sure this page is cleared on other CPU's too before using it */
>  	smp_wmb();
> @@ -187,8 +193,14 @@ void copy_to_user_page(struct vm_area_struct *vma,
>  		if (cpu_has_dc_aliases)
>  			SetPageDcacheDirty(page);
>  	}
> -	if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc)
> +	if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) ||
> +	    (Page_dcache_dirty(page) &&
> +	     pages_do_alias((unsigned long)dst & PAGE_MASK,
> +			    vaddr & PAGE_MASK))) {
>  		flush_cache_page(vma, vaddr, page_to_pfn(page));
> +		if (cpu_has_dc_aliases)
> +			ClearPageDcacheDirty(page);
> +	}
>  }
>  
>  void copy_from_user_page(struct vm_area_struct *vma,
> @@ -200,11 +212,8 @@ void copy_from_user_page(struct vm_area_struct *vma,
>  		void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
>  		memcpy(dst, vfrom, len);
>  		kunmap_coherent();
> -	} else {
> +	} else
>  		memcpy(dst, src, len);
> -		if (cpu_has_dc_aliases)
> -			SetPageDcacheDirty(page);
> -	}
>  }
>  EXPORT_SYMBOL_GPL(copy_from_user_page);
>  
> diff --git a/arch/mips/mm/sc-mips.c b/arch/mips/mm/sc-mips.c
> index 99eb8fa..e1db410 100644
> --- a/arch/mips/mm/sc-mips.c
> +++ b/arch/mips/mm/sc-mips.c
> @@ -24,6 +24,7 @@
>   */
>  static void mips_sc_wback_inv(unsigned long addr, unsigned long size)
>  {
> +	__sync();
>  	blast_scache_range(addr, addr + size);
>  }
>  

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-02-04 14:54 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-29 16:46 [PATCH 0/3] HIGHMEM and cache flush fixes Steven J. Hill
2015-01-29 16:46 ` [PATCH 1/3] MIPS: Fix cache flushing for swap pages with non-DMA I/O Steven J. Hill
2015-02-04 14:54   ` [1/3] " Lars Persson
2015-02-04 14:54     ` Lars Persson
2015-01-29 16:46 ` [PATCH 2/3] MIPS: Highmem: Fixes for cache aliasing and color Steven J. Hill
2015-01-29 16:46 ` [PATCH 3/3] MIPS: Fix I-cache flushing for kmap'd pages Steven J. Hill

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.