2005-04-17 02:20:36 +04:00
/*
* arch / sh / mm / cache - sh7705 . c
*
* Copyright ( C ) 1999 , 2000 Niibe Yutaka
* Copyright ( C ) 2004 Alex Song
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
2007-03-05 13:46:47 +03:00
*
2005-04-17 02:20:36 +04:00
*/
# include <linux/init.h>
# include <linux/mman.h>
# include <linux/mm.h>
2009-07-22 14:20:49 +04:00
# include <linux/fs.h>
2021-04-30 08:55:35 +03:00
# include <linux/pagemap.h>
2005-04-17 02:20:36 +04:00
# include <linux/threads.h>
# include <asm/addrspace.h>
# include <asm/page.h>
# include <asm/processor.h>
# include <asm/cache.h>
# include <asm/io.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <asm/mmu_context.h>
# include <asm/cacheflush.h>
2006-09-27 12:03:56 +04:00
/*
* The 32 KB cache on the SH7705 suffers from the same synonym problem
* as SH4 CPUs
*/
2005-04-17 02:20:36 +04:00
static inline void cache_wback_all ( void )
{
unsigned long ways , waysize , addrstart ;
2006-12-25 04:19:56 +03:00
ways = current_cpu_data . dcache . ways ;
waysize = current_cpu_data . dcache . sets ;
waysize < < = current_cpu_data . dcache . entry_shift ;
2005-04-17 02:20:36 +04:00
addrstart = CACHE_OC_ADDRESS_ARRAY ;
do {
unsigned long addr ;
for ( addr = addrstart ;
addr < addrstart + waysize ;
2006-12-25 04:19:56 +03:00
addr + = current_cpu_data . dcache . linesz ) {
2005-04-17 02:20:36 +04:00
unsigned long data ;
int v = SH_CACHE_UPDATED | SH_CACHE_VALID ;
2010-01-26 06:58:40 +03:00
data = __raw_readl ( addr ) ;
2005-04-17 02:20:36 +04:00
if ( ( data & v ) = = v )
2010-01-26 06:58:40 +03:00
__raw_writel ( data & ~ v , addr ) ;
2007-03-05 13:46:47 +03:00
2005-04-17 02:20:36 +04:00
}
2006-12-25 04:19:56 +03:00
addrstart + = current_cpu_data . dcache . way_incr ;
2005-04-17 02:20:36 +04:00
} while ( - - ways ) ;
}
/*
* Write back the range of D - cache , and purge the I - cache .
*
* Called from kernel / module . c : sys_init_module and routine for a . out format .
*/
2009-08-21 12:23:14 +04:00
static void sh7705_flush_icache_range ( void * args )
2005-04-17 02:20:36 +04:00
{
2009-08-21 12:23:14 +04:00
struct flusher_data * data = args ;
unsigned long start , end ;
start = data - > addr1 ;
end = data - > addr2 ;
2005-04-17 02:20:36 +04:00
__flush_wback_region ( ( void * ) start , end - start ) ;
}
/*
* Writeback & Invalidate the D - cache of the page
*/
2010-01-21 10:05:25 +03:00
static void __flush_dcache_page ( unsigned long phys )
2005-04-17 02:20:36 +04:00
{
unsigned long ways , waysize , addrstart ;
2009-09-01 16:12:55 +04:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
phys | = SH_CACHE_VALID ;
/*
* Here , phys is the physical address of the page . We check all the
* tags in the cache for those with the same page number as this page
* ( by masking off the lowest 2 bits of the 19 - bit tag ; these bits are
* derived from the offset within in the 4 k page ) . Matching valid
* entries are invalidated .
*
* Since 2 bits of the cache index are derived from the virtual page
* number , knowing this would reduce the number of cache entries to be
* searched by a factor of 4. However this function exists to deal with
* potential cache aliasing , therefore the optimisation is probably not
* possible .
*/
2009-09-01 16:12:55 +04:00
local_irq_save ( flags ) ;
2007-11-30 11:06:36 +03:00
jump_to_uncached ( ) ;
2005-04-17 02:20:36 +04:00
2006-12-25 04:19:56 +03:00
ways = current_cpu_data . dcache . ways ;
waysize = current_cpu_data . dcache . sets ;
waysize < < = current_cpu_data . dcache . entry_shift ;
2005-04-17 02:20:36 +04:00
addrstart = CACHE_OC_ADDRESS_ARRAY ;
do {
unsigned long addr ;
for ( addr = addrstart ;
addr < addrstart + waysize ;
2006-12-25 04:19:56 +03:00
addr + = current_cpu_data . dcache . linesz ) {
2005-04-17 02:20:36 +04:00
unsigned long data ;
2010-01-26 06:58:40 +03:00
data = __raw_readl ( addr ) & ( 0x1ffffC00 | SH_CACHE_VALID ) ;
2005-04-17 02:20:36 +04:00
if ( data = = phys ) {
data & = ~ ( SH_CACHE_VALID | SH_CACHE_UPDATED ) ;
2010-01-26 06:58:40 +03:00
__raw_writel ( data , addr ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-12-25 04:19:56 +03:00
addrstart + = current_cpu_data . dcache . way_incr ;
2005-04-17 02:20:36 +04:00
} while ( - - ways ) ;
2007-11-30 11:06:36 +03:00
back_to_cached ( ) ;
2009-09-01 16:12:55 +04:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Write back & invalidate the D - cache of the page .
* ( To avoid " alias " issues )
*/
2009-09-15 04:47:35 +04:00
static void sh7705_flush_dcache_page ( void * arg )
2005-04-17 02:20:36 +04:00
{
2009-09-15 04:47:35 +04:00
struct page * page = arg ;
mm: fix races between swapoff and flush dcache
Thanks to commit 4b3ef9daa4fc ("mm/swap: split swap cache into 64MB
trunks"), after swapoff the address_space associated with the swap
device will be freed. So page_mapping() users which may touch the
address_space need some kind of mechanism to prevent the address_space
from being freed during accessing.
The dcache flushing functions (flush_dcache_page(), etc) in architecture
specific code may access the address_space of swap device for anonymous
pages in swap cache via page_mapping() function. But in some cases
there are no mechanisms to prevent the swap device from being swapoff,
for example,
CPU1 CPU2
__get_user_pages() swapoff()
flush_dcache_page()
mapping = page_mapping()
... exit_swap_address_space()
... kvfree(spaces)
mapping_mapped(mapping)
The address space may be accessed after being freed.
But from cachetlb.txt and Russell King, flush_dcache_page() only care
about file cache pages, for anonymous pages, flush_anon_page() should be
used. The implementation of flush_dcache_page() in all architectures
follows this too. They will check whether page_mapping() is NULL and
whether mapping_mapped() is true to determine whether to flush the
dcache immediately. And they will use interval tree (mapping->i_mmap)
to find all user space mappings. While mapping_mapped() and
mapping->i_mmap isn't used by anonymous pages in swap cache at all.
So, to fix the race between swapoff and flush dcache, __page_mapping()
is add to return the address_space for file cache pages and NULL
otherwise. All page_mapping() invoking in flush dcache functions are
replaced with page_mapping_file().
[akpm@linux-foundation.org: simplify page_mapping_file(), per Mike]
Link: http://lkml.kernel.org/r/20180305083634.15174-1-ying.huang@intel.com
Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Chen Liqin <liqin.linux@gmail.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
Cc: Guan Xuetao <gxt@mprc.pku.edu.cn>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Chris Zankel <chris@zankel.net>
Cc: Vineet Gupta <vgupta@synopsys.com>
Cc: Ley Foon Tan <lftan@altera.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-06 02:24:39 +03:00
struct address_space * mapping = page_mapping_file ( page ) ;
2009-07-22 14:20:49 +04:00
if ( mapping & & ! mapping_mapped ( mapping ) )
2010-12-01 09:39:51 +03:00
clear_bit ( PG_dcache_clean , & page - > flags ) ;
2009-07-22 14:20:49 +04:00
else
2009-10-07 01:22:24 +04:00
__flush_dcache_page ( __pa ( page_address ( page ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-21 10:05:25 +03:00
static void sh7705_flush_cache_all ( void * args )
2005-04-17 02:20:36 +04:00
{
2009-09-01 16:12:55 +04:00
unsigned long flags ;
local_irq_save ( flags ) ;
2007-11-30 11:06:36 +03:00
jump_to_uncached ( ) ;
2009-09-01 16:12:55 +04:00
2005-04-17 02:20:36 +04:00
cache_wback_all ( ) ;
2007-11-30 11:06:36 +03:00
back_to_cached ( ) ;
2009-09-01 16:12:55 +04:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Write back and invalidate I / D - caches for the page .
*
* ADDRESS : Virtual Address ( U0 address )
*/
2009-08-21 12:23:14 +04:00
static void sh7705_flush_cache_page ( void * args )
2005-04-17 02:20:36 +04:00
{
2009-08-21 12:23:14 +04:00
struct flusher_data * data = args ;
unsigned long pfn = data - > addr2 ;
2005-04-17 02:20:36 +04:00
__flush_dcache_page ( pfn < < PAGE_SHIFT ) ;
}
/*
* This is called when a page - cache page is about to be mapped into a
* user process ' address space . It offers an opportunity for a
* port to ensure d - cache / i - cache coherency if necessary .
*
* Not entirely sure why this is necessary on SH3 with 32 K cache but
* without it we get occasional " Memory fault " when loading a program .
*/
2009-08-21 12:23:14 +04:00
static void sh7705_flush_icache_page ( void * page )
2005-04-17 02:20:36 +04:00
{
__flush_purge_region ( page_address ( page ) , PAGE_SIZE ) ;
}
2009-08-15 07:53:39 +04:00
void __init sh7705_cache_init ( void )
{
2009-08-21 12:23:14 +04:00
local_flush_icache_range = sh7705_flush_icache_range ;
local_flush_dcache_page = sh7705_flush_dcache_page ;
local_flush_cache_all = sh7705_flush_cache_all ;
local_flush_cache_mm = sh7705_flush_cache_all ;
local_flush_cache_dup_mm = sh7705_flush_cache_all ;
local_flush_cache_range = sh7705_flush_cache_all ;
local_flush_cache_page = sh7705_flush_cache_page ;
local_flush_icache_page = sh7705_flush_icache_page ;
2009-08-15 07:53:39 +04:00
}