diff mbox series

MIPS: mm: Implement flush_cache_v(un)map using __flush_kernel_vmap_range

Message ID 20190529224659.27614-1-paul.burton@mips.com (mailing list archive)
State New
Headers show
Series MIPS: mm: Implement flush_cache_v(un)map using __flush_kernel_vmap_range | expand

Commit Message

Paul Burton May 29, 2019, 10:47 p.m. UTC
Our flush_cache_vmap() & flush_cache_vunmap() implementations for R4k
style systems simply call r4k_blast_dcache() to wipe out the whole L1
dcache if it suffers from aliases. This is unsafe on SMP for 2 reasons:

1) r4k_blast_dcache() relies upon preemption being disabled so that it
   can use current_cpu_data/smp_processor_id() to discover the
   properties of the current CPU's dcache & ensure the whole flush
   operation happens on one CPU. This may not be the case when
   flush_cache_vmap() or flush_cache_vunmap() are called.

2) It only flushes caches on one CPU, which means the caches for other
   CPUs may still contain stale data.

We already have an implementation of flush_kernel_vmap_range() which
does exactly what we need - it invalidates dcache entries on all CPUs
safely, and is better optimized to avoid wiping out the entire cache for
small flushes.

Reimplement flush_cache_vmap() & flush_cache_vunmap() using
__flush_kernel_vmap_range() which does what we need already.

For tx39 __flush_kernel_vmap_range() will simply BUG(), but so far as I
can see tx39 systems don't suffer from dcache aliasing so this should be
fine since it should never be called.

Signed-off-by: Paul Burton <paul.burton@mips.com>
Reported-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Chris, would you mind giving this a try?
---
 arch/mips/include/asm/cacheflush.h | 28 ++++++++++++----------------
 arch/mips/mm/c-r4k.c               | 15 ---------------
 arch/mips/mm/c-tx39.c              | 16 ----------------
 arch/mips/mm/cache.c               |  3 ---
 4 files changed, 12 insertions(+), 50 deletions(-)

Comments

Chris Packham May 29, 2019, 11:17 p.m. UTC | #1
Hi Paul,

On 30/05/19 10:47 AM, Paul Burton wrote:
> Our flush_cache_vmap() & flush_cache_vunmap() implementations for R4k
> style systems simply call r4k_blast_dcache() to wipe out the whole L1
> dcache if it suffers from aliases. This is unsafe on SMP for 2 reasons:
> 
> 1) r4k_blast_dcache() relies upon preemption being disabled so that it
>     can use current_cpu_data/smp_processor_id() to discover the
>     properties of the current CPU's dcache & ensure the whole flush
>     operation happens on one CPU. This may not be the case when
>     flush_cache_vmap() or flush_cache_vunmap() are called.
> 
> 2) It only flushes caches on one CPU, which means the caches for other
>     CPUs may still contain stale data.
> 
> We already have an implementation of flush_kernel_vmap_range() which
> does exactly what we need - it invalidates dcache entries on all CPUs
> safely, and is better optimized to avoid wiping out the entire cache for
> small flushes.
> 
> Reimplement flush_cache_vmap() & flush_cache_vunmap() using
> __flush_kernel_vmap_range() which does what we need already.
> 
> For tx39 __flush_kernel_vmap_range() will simply BUG(), but so far as I
> can see tx39 systems don't suffer from dcache aliasing so this should be
> fine since it should never be called.
> 
> Signed-off-by: Paul Burton <paul.burton@mips.com>
> Reported-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
> ---
> Chris, would you mind giving this a try?

This doesn't quite seem to work. It avoids the BUG() but I get other bad 
behavior. I can't discount it being something odd about my half finished 
port but I can replicate the badness without this patch.

One is a kernel panic in response to activity on the console (output 
below). The other seems to be a result of receiving garbage on the 
console. I wonder if the range needs to be rounded up to a cacheline 
boundary?

Run /sbin/init as init process
overlayfs: upper fs does not support xattr, falling back to index=off 
and metacopy=off.
overlayfs: upper fs does not support xattr, falling back to index=off 
and metacopy=off.
overlayfs: upper fs does not support xattr, falling back to index=off 
and metacopy=off.
random: crng init done
CPU 0 Unable to handle kernel paging request at virtual address 
00000000, epc == 80652e00, ra == 80652dc4
Oops[#1]:
CPU: 0 PID: 7 Comm: kworker/u4:0 Not tainted 5.1.0-at1 #15
Workqueue: events_unbound flush_to_ldisc
$ 0   : 00000000 00000001 00000000 00000000
$ 4   : 8f82aa00 fffffff8 fffffffd 00000001
$ 8   : 8da08818 00000000 00000001 00000018
$12   : fefefeff 7f7f7f7f 00000001 00000001
$16   : c00e1268 c00e126c 00000002 8f84bd18
$20   : 8da08819 83fa6e00 00000001 c00e1270
$24   : 0000c3b6 80336694
$28   : 8f84a000 8f84bcf0 0000000d 80652dc4
Hi    : d49c5168
Lo    : 7f29931c
epc   : 80652e00 __mutex_lock.isra.1+0xa8/0x464
ra    : 80652dc4 __mutex_lock.isra.1+0x6c/0x464
Status: 10008f03        KERNEL EXL IE
Cause : 0080000c (ExcCode 03)
BadVA : 00000000
PrId  : 0002a080 (Broadcom BMIPS4350)
Modules linked in:
Process kworker/u4:0 (pid: 7, threadinfo=f89f9771, task=a0a456a2, 
tls=00000000)
Stack : 00000002 8064f534 8f82aa80 8f82aa80 6b2dc12d 8006b2c8 8090fc78 
83fa6e6c
         83fa6e6c 8064f4a4 c00e1270 00000000 83fa6e6c 8064f534 c00df000 
83fa6e00
         c00e1268 00000000 8da08819 83fa6e00 00000001 c00df018 0000000d 
80334b50
         83fa6e6c 80654aa0 fffb8a00 80790000 83fa6e00 0000000a c00df000 
803358a0
         8f84bd70 00000000 00000000 8da08818 00000000 00000000 8da08818 
80336520
         ...
Call Trace:
[<80652e00>] __mutex_lock.isra.1+0xa8/0x464
[<80334b50>] commit_echoes+0x28/0xc4
[<803358a0>] n_tty_receive_char_special+0xa20/0xc04
[<80336520>] n_tty_receive_buf_common+0xa9c/0xc10
[<803366ac>] n_tty_receive_buf2+0x18/0x24
[<8033a6b8>] tty_port_default_receive_buf+0x50/0xa4
[<80339fc4>] flush_to_ldisc+0xb8/0x12c
[<80052098>] process_one_work+0x224/0x424
[<80052400>] worker_thread+0x168/0x5a0
[<8005882c>] kthread+0x13c/0x144
[<80015e6c>] ret_from_kernel_thread+0x14/0x1c
Code: afa2002c  ae13000c  afb70028 <ac530000> 8e020008  105300b6 
00000000  8f820000  afa20030



> ---
>   arch/mips/include/asm/cacheflush.h | 28 ++++++++++++----------------
>   arch/mips/mm/c-r4k.c               | 15 ---------------
>   arch/mips/mm/c-tx39.c              | 16 ----------------
>   arch/mips/mm/cache.c               |  3 ---
>   4 files changed, 12 insertions(+), 50 deletions(-)
> 
> diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h
> index d687b40b9fbb..6285c830c9f2 100644
> --- a/arch/mips/include/asm/cacheflush.h
> +++ b/arch/mips/include/asm/cacheflush.h
> @@ -85,22 +85,6 @@ extern void (*__flush_icache_user_range)(unsigned long start,
>   extern void (*__local_flush_icache_user_range)(unsigned long start,
>   					       unsigned long end);
>   
> -extern void (*__flush_cache_vmap)(void);
> -
> -static inline void flush_cache_vmap(unsigned long start, unsigned long end)
> -{
> -	if (cpu_has_dc_aliases)
> -		__flush_cache_vmap();
> -}
> -
> -extern void (*__flush_cache_vunmap)(void);
> -
> -static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
> -{
> -	if (cpu_has_dc_aliases)
> -		__flush_cache_vunmap();
> -}
> -
>   extern void copy_to_user_page(struct vm_area_struct *vma,
>   	struct page *page, unsigned long vaddr, void *dst, const void *src,
>   	unsigned long len);
> @@ -150,4 +134,16 @@ static inline void invalidate_kernel_vmap_range(void *vaddr, int size)
>   		__flush_kernel_vmap_range((unsigned long) vaddr, size);
>   }
>   
> +static inline void flush_cache_vmap(unsigned long start, unsigned long end)
> +{
> +	if (cpu_has_dc_aliases)
> +		__flush_kernel_vmap_range(start, end - start);
> +}
> +
> +static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
> +{
> +	if (cpu_has_dc_aliases)
> +		__flush_kernel_vmap_range(start, end - start);
> +}
> +
>   #endif /* _ASM_CACHEFLUSH_H */
> diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
> index 5166e38cd1c6..2b2953d3949d 100644
> --- a/arch/mips/mm/c-r4k.c
> +++ b/arch/mips/mm/c-r4k.c
> @@ -559,16 +559,6 @@ static inline int has_valid_asid(const struct mm_struct *mm, unsigned int type)
>   	return 0;
>   }
>   
> -static void r4k__flush_cache_vmap(void)
> -{
> -	r4k_blast_dcache();
> -}
> -
> -static void r4k__flush_cache_vunmap(void)
> -{
> -	r4k_blast_dcache();
> -}
> -
>   /*
>    * Note: flush_tlb_range() assumes flush_cache_range() sufficiently flushes
>    * whole caches when vma is executable.
> @@ -1854,9 +1844,6 @@ void r4k_cache_init(void)
>   	else
>   		shm_align_mask = PAGE_SIZE-1;
>   
> -	__flush_cache_vmap	= r4k__flush_cache_vmap;
> -	__flush_cache_vunmap	= r4k__flush_cache_vunmap;
> -
>   	flush_cache_all		= cache_noop;
>   	__flush_cache_all	= r4k___flush_cache_all;
>   	flush_cache_mm		= r4k_flush_cache_mm;
> @@ -1931,8 +1918,6 @@ void r4k_cache_init(void)
>   	case CPU_LOONGSON3:
>   		/* Loongson-3 maintains cache coherency by hardware */
>   		__flush_cache_all	= cache_noop;
> -		__flush_cache_vmap	= cache_noop;
> -		__flush_cache_vunmap	= cache_noop;
>   		__flush_kernel_vmap_range = (void *)cache_noop;
>   		flush_cache_mm		= (void *)cache_noop;
>   		flush_cache_page	= (void *)cache_noop;
> diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c
> index b7c8a9d79c35..6bf13a7db485 100644
> --- a/arch/mips/mm/c-tx39.c
> +++ b/arch/mips/mm/c-tx39.c
> @@ -121,16 +121,6 @@ static inline void tx39_blast_icache(void)
>   	local_irq_restore(flags);
>   }
>   
> -static void tx39__flush_cache_vmap(void)
> -{
> -	tx39_blast_dcache();
> -}
> -
> -static void tx39__flush_cache_vunmap(void)
> -{
> -	tx39_blast_dcache();
> -}
> -
>   static inline void tx39_flush_cache_all(void)
>   {
>   	if (!cpu_has_dc_aliases)
> @@ -339,8 +329,6 @@ void tx39_cache_init(void)
>   	switch (current_cpu_type()) {
>   	case CPU_TX3912:
>   		/* TX39/H core (writethru direct-map cache) */
> -		__flush_cache_vmap	= tx39__flush_cache_vmap;
> -		__flush_cache_vunmap	= tx39__flush_cache_vunmap;
>   		flush_cache_all = tx39h_flush_icache_all;
>   		__flush_cache_all	= tx39h_flush_icache_all;
>   		flush_cache_mm		= (void *) tx39h_flush_icache_all;
> @@ -363,10 +351,6 @@ void tx39_cache_init(void)
>   	default:
>   		/* TX39/H2,H3 core (writeback 2way-set-associative cache) */
>   		/* board-dependent init code may set WBON */
> -
> -		__flush_cache_vmap	= tx39__flush_cache_vmap;
> -		__flush_cache_vunmap	= tx39__flush_cache_vunmap;
> -
>   		flush_cache_all = tx39_flush_cache_all;
>   		__flush_cache_all = tx39___flush_cache_all;
>   		flush_cache_mm = tx39_flush_cache_mm;
> diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
> index 3da216988672..c6c0ef539d3a 100644
> --- a/arch/mips/mm/cache.c
> +++ b/arch/mips/mm/cache.c
> @@ -40,9 +40,6 @@ EXPORT_SYMBOL_GPL(__flush_icache_user_range);
>   void (*__local_flush_icache_user_range)(unsigned long start, unsigned long end);
>   EXPORT_SYMBOL_GPL(__local_flush_icache_user_range);
>   
> -void (*__flush_cache_vmap)(void);
> -void (*__flush_cache_vunmap)(void);
> -
>   void (*__flush_kernel_vmap_range)(unsigned long vaddr, int size);
>   EXPORT_SYMBOL_GPL(__flush_kernel_vmap_range);
>   
>
diff mbox series

Patch

diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h
index d687b40b9fbb..6285c830c9f2 100644
--- a/arch/mips/include/asm/cacheflush.h
+++ b/arch/mips/include/asm/cacheflush.h
@@ -85,22 +85,6 @@  extern void (*__flush_icache_user_range)(unsigned long start,
 extern void (*__local_flush_icache_user_range)(unsigned long start,
 					       unsigned long end);
 
-extern void (*__flush_cache_vmap)(void);
-
-static inline void flush_cache_vmap(unsigned long start, unsigned long end)
-{
-	if (cpu_has_dc_aliases)
-		__flush_cache_vmap();
-}
-
-extern void (*__flush_cache_vunmap)(void);
-
-static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
-{
-	if (cpu_has_dc_aliases)
-		__flush_cache_vunmap();
-}
-
 extern void copy_to_user_page(struct vm_area_struct *vma,
 	struct page *page, unsigned long vaddr, void *dst, const void *src,
 	unsigned long len);
@@ -150,4 +134,16 @@  static inline void invalidate_kernel_vmap_range(void *vaddr, int size)
 		__flush_kernel_vmap_range((unsigned long) vaddr, size);
 }
 
+static inline void flush_cache_vmap(unsigned long start, unsigned long end)
+{
+	if (cpu_has_dc_aliases)
+		__flush_kernel_vmap_range(start, end - start);
+}
+
+static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
+{
+	if (cpu_has_dc_aliases)
+		__flush_kernel_vmap_range(start, end - start);
+}
+
 #endif /* _ASM_CACHEFLUSH_H */
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 5166e38cd1c6..2b2953d3949d 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -559,16 +559,6 @@  static inline int has_valid_asid(const struct mm_struct *mm, unsigned int type)
 	return 0;
 }
 
-static void r4k__flush_cache_vmap(void)
-{
-	r4k_blast_dcache();
-}
-
-static void r4k__flush_cache_vunmap(void)
-{
-	r4k_blast_dcache();
-}
-
 /*
  * Note: flush_tlb_range() assumes flush_cache_range() sufficiently flushes
  * whole caches when vma is executable.
@@ -1854,9 +1844,6 @@  void r4k_cache_init(void)
 	else
 		shm_align_mask = PAGE_SIZE-1;
 
-	__flush_cache_vmap	= r4k__flush_cache_vmap;
-	__flush_cache_vunmap	= r4k__flush_cache_vunmap;
-
 	flush_cache_all		= cache_noop;
 	__flush_cache_all	= r4k___flush_cache_all;
 	flush_cache_mm		= r4k_flush_cache_mm;
@@ -1931,8 +1918,6 @@  void r4k_cache_init(void)
 	case CPU_LOONGSON3:
 		/* Loongson-3 maintains cache coherency by hardware */
 		__flush_cache_all	= cache_noop;
-		__flush_cache_vmap	= cache_noop;
-		__flush_cache_vunmap	= cache_noop;
 		__flush_kernel_vmap_range = (void *)cache_noop;
 		flush_cache_mm		= (void *)cache_noop;
 		flush_cache_page	= (void *)cache_noop;
diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c
index b7c8a9d79c35..6bf13a7db485 100644
--- a/arch/mips/mm/c-tx39.c
+++ b/arch/mips/mm/c-tx39.c
@@ -121,16 +121,6 @@  static inline void tx39_blast_icache(void)
 	local_irq_restore(flags);
 }
 
-static void tx39__flush_cache_vmap(void)
-{
-	tx39_blast_dcache();
-}
-
-static void tx39__flush_cache_vunmap(void)
-{
-	tx39_blast_dcache();
-}
-
 static inline void tx39_flush_cache_all(void)
 {
 	if (!cpu_has_dc_aliases)
@@ -339,8 +329,6 @@  void tx39_cache_init(void)
 	switch (current_cpu_type()) {
 	case CPU_TX3912:
 		/* TX39/H core (writethru direct-map cache) */
-		__flush_cache_vmap	= tx39__flush_cache_vmap;
-		__flush_cache_vunmap	= tx39__flush_cache_vunmap;
 		flush_cache_all = tx39h_flush_icache_all;
 		__flush_cache_all	= tx39h_flush_icache_all;
 		flush_cache_mm		= (void *) tx39h_flush_icache_all;
@@ -363,10 +351,6 @@  void tx39_cache_init(void)
 	default:
 		/* TX39/H2,H3 core (writeback 2way-set-associative cache) */
 		/* board-dependent init code may set WBON */
-
-		__flush_cache_vmap	= tx39__flush_cache_vmap;
-		__flush_cache_vunmap	= tx39__flush_cache_vunmap;
-
 		flush_cache_all = tx39_flush_cache_all;
 		__flush_cache_all = tx39___flush_cache_all;
 		flush_cache_mm = tx39_flush_cache_mm;
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index 3da216988672..c6c0ef539d3a 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -40,9 +40,6 @@  EXPORT_SYMBOL_GPL(__flush_icache_user_range);
 void (*__local_flush_icache_user_range)(unsigned long start, unsigned long end);
 EXPORT_SYMBOL_GPL(__local_flush_icache_user_range);
 
-void (*__flush_cache_vmap)(void);
-void (*__flush_cache_vunmap)(void);
-
 void (*__flush_kernel_vmap_range)(unsigned long vaddr, int size);
 EXPORT_SYMBOL_GPL(__flush_kernel_vmap_range);