2005-04-17 02:20:36 +04:00
/* arch/sparc64/mm/tlb.c
*
* Copyright ( C ) 2004 David S . Miller < davem @ redhat . com >
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/percpu.h>
# include <linux/mm.h>
# include <linux/swap.h>
2006-05-01 09:54:27 +04:00
# include <linux/preempt.h>
2005-04-17 02:20:36 +04:00
# include <asm/pgtable.h>
# include <asm/pgalloc.h>
# include <asm/tlbflush.h>
# include <asm/cacheflush.h>
# include <asm/mmu_context.h>
# include <asm/tlb.h>
/* Heavily inspired by the ppc64 code. */
2011-05-25 04:11:50 +04:00
static DEFINE_PER_CPU ( struct tlb_batch , tlb_batch ) ;
2005-04-17 02:20:36 +04:00
void flush_tlb_pending ( void )
{
2011-05-25 04:11:50 +04:00
struct tlb_batch * tb = & get_cpu_var ( tlb_batch ) ;
2005-04-17 02:20:36 +04:00
2011-05-25 04:11:50 +04:00
if ( tb - > tlb_nr ) {
flush_tsb_user ( tb ) ;
2006-02-01 05:29:18 +03:00
2011-05-25 04:11:50 +04:00
if ( CTX_VALID ( tb - > mm - > context ) ) {
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SMP
2011-05-25 04:11:50 +04:00
smp_flush_tlb_pending ( tb - > mm , tb - > tlb_nr ,
& tb - > vaddrs [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
# else
2011-05-25 04:11:50 +04:00
__flush_tlb_pending ( CTX_HWBITS ( tb - > mm - > context ) ,
tb - > tlb_nr , & tb - > vaddrs [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
}
2011-05-25 04:11:50 +04:00
tb - > tlb_nr = 0 ;
2005-04-17 02:20:36 +04:00
}
2006-05-01 09:54:27 +04:00
2011-05-25 04:11:50 +04:00
put_cpu_var ( tlb_batch ) ;
2005-04-17 02:20:36 +04:00
}
2011-05-25 04:11:50 +04:00
void tlb_batch_add ( struct mm_struct * mm , unsigned long vaddr ,
pte_t * ptep , pte_t orig , int fullmm )
2005-04-17 02:20:36 +04:00
{
2011-05-25 04:11:50 +04:00
struct tlb_batch * tb = & get_cpu_var ( tlb_batch ) ;
2005-04-17 02:20:36 +04:00
unsigned long nr ;
vaddr & = PAGE_MASK ;
if ( pte_exec ( orig ) )
vaddr | = 0x1UL ;
2006-02-27 06:44:50 +03:00
if ( tlb_type ! = hypervisor & &
pte_dirty ( orig ) ) {
2005-04-17 02:20:36 +04:00
unsigned long paddr , pfn = pte_pfn ( orig ) ;
struct address_space * mapping ;
struct page * page ;
if ( ! pfn_valid ( pfn ) )
goto no_cache_flush ;
page = pfn_to_page ( pfn ) ;
if ( PageReserved ( page ) )
goto no_cache_flush ;
/* A real file page? */
mapping = page_mapping ( page ) ;
if ( ! mapping )
goto no_cache_flush ;
paddr = ( unsigned long ) page_address ( page ) ;
if ( ( paddr ^ vaddr ) & ( 1 < < 13 ) )
flush_dcache_page_all ( mm , page ) ;
}
no_cache_flush :
2011-05-25 04:11:50 +04:00
if ( fullmm ) {
put_cpu_var ( tlb_batch ) ;
2005-04-17 02:20:36 +04:00
return ;
2011-05-25 04:11:50 +04:00
}
2005-04-17 02:20:36 +04:00
2011-05-25 04:11:50 +04:00
nr = tb - > tlb_nr ;
2005-04-17 02:20:36 +04:00
2011-05-25 04:11:50 +04:00
if ( unlikely ( nr ! = 0 & & mm ! = tb - > mm ) ) {
2005-04-17 02:20:36 +04:00
flush_tlb_pending ( ) ;
nr = 0 ;
}
if ( nr = = 0 )
2011-05-25 04:11:50 +04:00
tb - > mm = mm ;
2005-04-17 02:20:36 +04:00
2011-05-25 04:11:50 +04:00
tb - > vaddrs [ nr ] = vaddr ;
tb - > tlb_nr = + + nr ;
2005-04-17 02:20:36 +04:00
if ( nr > = TLB_BATCH_NR )
flush_tlb_pending ( ) ;
2011-05-25 04:11:50 +04:00
put_cpu_var ( tlb_batch ) ;
2005-04-17 02:20:36 +04:00
}