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. */
2008-09-12 11:06:58 +04:00
DEFINE_PER_CPU ( struct mmu_gather , mmu_gathers ) ;
2005-04-17 02:20:36 +04:00
void flush_tlb_pending ( void )
{
2008-03-28 02:51:44 +03:00
struct mmu_gather * mp = & get_cpu_var ( mmu_gathers ) ;
2005-04-17 02:20:36 +04:00
if ( mp - > tlb_nr ) {
2006-02-01 05:29:18 +03:00
flush_tsb_user ( mp ) ;
2005-04-17 02:20:36 +04:00
if ( CTX_VALID ( mp - > mm - > context ) ) {
# ifdef CONFIG_SMP
smp_flush_tlb_pending ( mp - > mm , mp - > tlb_nr ,
& mp - > vaddrs [ 0 ] ) ;
# else
__flush_tlb_pending ( CTX_HWBITS ( mp - > mm - > context ) ,
mp - > tlb_nr , & mp - > vaddrs [ 0 ] ) ;
# endif
}
mp - > tlb_nr = 0 ;
}
2006-05-01 09:54:27 +04:00
2008-03-28 02:51:44 +03:00
put_cpu_var ( mmu_gathers ) ;
2005-04-17 02:20:36 +04:00
}
void tlb_batch_add ( struct mm_struct * mm , unsigned long vaddr , pte_t * ptep , pte_t orig )
{
struct mmu_gather * mp = & __get_cpu_var ( mmu_gathers ) ;
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 :
2005-10-30 04:16:02 +03:00
if ( mp - > fullmm )
2005-04-17 02:20:36 +04:00
return ;
nr = mp - > tlb_nr ;
if ( unlikely ( nr ! = 0 & & mm ! = mp - > mm ) ) {
flush_tlb_pending ( ) ;
nr = 0 ;
}
if ( nr = = 0 )
mp - > mm = mm ;
mp - > vaddrs [ nr ] = vaddr ;
mp - > tlb_nr = + + nr ;
if ( nr > = TLB_BATCH_NR )
flush_tlb_pending ( ) ;
}