LCOV - code coverage report
Current view: top level - arch/arm/include/asm - tlb.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 40 40 100.0 %
Date: 2014-02-18 Functions: 0 0 -
Branches: 54 82 65.9 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  *  arch/arm/include/asm/tlb.h
       3                 :            :  *
       4                 :            :  *  Copyright (C) 2002 Russell King
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or modify
       7                 :            :  * it under the terms of the GNU General Public License version 2 as
       8                 :            :  * published by the Free Software Foundation.
       9                 :            :  *
      10                 :            :  *  Experimentation shows that on a StrongARM, it appears to be faster
      11                 :            :  *  to use the "invalidate whole tlb" rather than "invalidate single
      12                 :            :  *  tlb" for this.
      13                 :            :  *
      14                 :            :  *  This appears true for both the process fork+exit case, as well as
      15                 :            :  *  the munmap-large-area case.
      16                 :            :  */
      17                 :            : #ifndef __ASMARM_TLB_H
      18                 :            : #define __ASMARM_TLB_H
      19                 :            : 
      20                 :            : #include <asm/cacheflush.h>
      21                 :            : 
      22                 :            : #ifndef CONFIG_MMU
      23                 :            : 
      24                 :            : #include <linux/pagemap.h>
      25                 :            : 
      26                 :            : #define tlb_flush(tlb)  ((void) tlb)
      27                 :            : 
      28                 :            : #include <asm-generic/tlb.h>
      29                 :            : 
      30                 :            : #else /* !CONFIG_MMU */
      31                 :            : 
      32                 :            : #include <linux/swap.h>
      33                 :            : #include <asm/pgalloc.h>
      34                 :            : #include <asm/tlbflush.h>
      35                 :            : 
      36                 :            : #define MMU_GATHER_BUNDLE       8
      37                 :            : 
      38                 :            : /*
      39                 :            :  * TLB handling.  This allows us to remove pages from the page
      40                 :            :  * tables, and efficiently handle the TLB issues.
      41                 :            :  */
      42                 :            : struct mmu_gather {
      43                 :            :         struct mm_struct        *mm;
      44                 :            :         unsigned int            fullmm;
      45                 :            :         struct vm_area_struct   *vma;
      46                 :            :         unsigned long           start, end;
      47                 :            :         unsigned long           range_start;
      48                 :            :         unsigned long           range_end;
      49                 :            :         unsigned int            nr;
      50                 :            :         unsigned int            max;
      51                 :            :         struct page             **pages;
      52                 :            :         struct page             *local[MMU_GATHER_BUNDLE];
      53                 :            : };
      54                 :            : 
      55                 :            : DECLARE_PER_CPU(struct mmu_gather, mmu_gathers);
      56                 :            : 
      57                 :            : /*
      58                 :            :  * This is unnecessarily complex.  There's three ways the TLB shootdown
      59                 :            :  * code is used:
      60                 :            :  *  1. Unmapping a range of vmas.  See zap_page_range(), unmap_region().
      61                 :            :  *     tlb->fullmm = 0, and tlb_start_vma/tlb_end_vma will be called.
      62                 :            :  *     tlb->vma will be non-NULL.
      63                 :            :  *  2. Unmapping all vmas.  See exit_mmap().
      64                 :            :  *     tlb->fullmm = 1, and tlb_start_vma/tlb_end_vma will be called.
      65                 :            :  *     tlb->vma will be non-NULL.  Additionally, page tables will be freed.
      66                 :            :  *  3. Unmapping argument pages.  See shift_arg_pages().
      67                 :            :  *     tlb->fullmm = 0, but tlb_start_vma/tlb_end_vma will not be called.
      68                 :            :  *     tlb->vma will be NULL.
      69                 :            :  */
      70                 :            : static inline void tlb_flush(struct mmu_gather *tlb)
      71                 :            : {
      72 [ +  - ][ +  - ]:    3655425 :         if (tlb->fullmm || !tlb->vma)
         [ +  + ][ -  + ]
         [ +  + ][ -  + ]
            [ +  + ][ + ]
         [ +  + ][ -  + ]
         [ -  + ][ #  # ]
            [ + ][ +  + ]
      73                 :    1272220 :                 flush_tlb_mm(tlb->mm);
      74 [ -  + ][ +  + ]:    2383205 :         else if (tlb->range_end > 0) {
         [ +  + ][ +  - ]
                 [ #  # ]
      75                 :     764885 :                 flush_tlb_range(tlb->vma, tlb->range_start, tlb->range_end);
      76                 :     764890 :                 tlb->range_start = TASK_SIZE;
      77                 :     764890 :                 tlb->range_end = 0;
      78                 :            :         }
      79                 :            : }
      80                 :            : 
      81                 :            : static inline void tlb_add_flush(struct mmu_gather *tlb, unsigned long addr)
      82                 :            : {
      83   [ +  +  +  + ]:   87804393 :         if (!tlb->fullmm) {
                 [ +  + ]
      84 [ +  + ][ +  + ]:    3079787 :                 if (addr < tlb->range_start)
                 [ -  + ]
      85                 :     764853 :                         tlb->range_start = addr;
      86    [ + ][ +  + ]:    3079787 :                 if (addr + PAGE_SIZE > tlb->range_end)
                 [ +  + ]
      87                 :    3079788 :                         tlb->range_end = addr + PAGE_SIZE;
      88                 :            :         }
      89                 :            : }
      90                 :            : 
      91                 :            : static inline void __tlb_alloc_page(struct mmu_gather *tlb)
      92                 :            : {
      93                 :    2439569 :         unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0);
      94                 :            : 
      95   [ +  -  #  #  :    2444425 :         if (addr) {
          +  -  #  #  #  
                #  #  # ]
      96                 :    2439589 :                 tlb->pages = (void *)addr;
      97                 :    2439589 :                 tlb->max = PAGE_SIZE / sizeof(struct page *);
      98                 :            :         }
      99                 :            : }
     100                 :            : 
     101                 :            : static inline void tlb_flush_mmu(struct mmu_gather *tlb)
     102                 :            : {
     103                 :            :         tlb_flush(tlb);
     104                 :    2457243 :         free_pages_and_swap_cache(tlb->pages, tlb->nr);
     105                 :    2457276 :         tlb->nr = 0;
     106   [ -  +  -  +  :    2457276 :         if (tlb->pages == tlb->local)
             -  +  -  + ]
     107                 :            :                 __tlb_alloc_page(tlb);
     108                 :            : }
     109                 :            : 
     110                 :            : static inline void
     111                 :            : tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end)
     112                 :            : {
     113                 :    2439569 :         tlb->mm = mm;
     114                 :    2439569 :         tlb->fullmm = !(start | (end+1));
     115                 :    2439569 :         tlb->start = start;
     116                 :    2439569 :         tlb->end = end;
     117                 :    2439569 :         tlb->vma = NULL;
     118                 :    2439569 :         tlb->max = ARRAY_SIZE(tlb->local);
     119                 :    2439569 :         tlb->pages = tlb->local;
     120                 :    2439569 :         tlb->nr = 0;
     121                 :            :         __tlb_alloc_page(tlb);
     122                 :            : }
     123                 :            : 
     124                 :            : static inline void
     125                 :            : tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end)
     126                 :            : {
     127                 :            :         tlb_flush_mmu(tlb);
     128                 :            : 
     129                 :            :         /* keep the page table cache within bounds */
     130                 :            :         check_pgt_cache();
     131                 :            : 
     132 [ +  - ][ +  + ]:    2439582 :         if (tlb->pages != tlb->local)
     133                 :    2439576 :                 free_pages((unsigned long)tlb->pages, 0);
     134                 :            : }
     135                 :            : 
     136                 :            : /*
     137                 :            :  * Memorize the range for the TLB flush.
     138                 :            :  */
     139                 :            : static inline void
     140                 :            : tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, unsigned long addr)
     141                 :            : {
     142                 :            :         tlb_add_flush(tlb, addr);
     143                 :            : }
     144                 :            : 
     145                 :            : /*
     146                 :            :  * In the case of tlb vma handling, we can optimise these away in the
     147                 :            :  * case where we're doing a full MM flush.  When we're doing a munmap,
     148                 :            :  * the vmas are adjusted to only cover the region to be torn down.
     149                 :            :  */
     150                 :            : static inline void
     151                 :            : tlb_start_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
     152                 :            : {
     153         [ +  + ]:   23301591 :         if (!tlb->fullmm) {
     154                 :    1198201 :                 flush_cache_range(vma, vma->vm_start, vma->vm_end);
     155                 :    1198167 :                 tlb->vma = vma;
     156                 :    1198167 :                 tlb->range_start = TASK_SIZE;
     157                 :    1198167 :                 tlb->range_end = 0;
     158                 :            :         }
     159                 :            : }
     160                 :            : 
     161                 :            : static inline void
     162                 :            : tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
     163                 :            : {
     164         [ +  + ]:   23301929 :         if (!tlb->fullmm)
     165                 :            :                 tlb_flush(tlb);
     166                 :            : }
     167                 :            : 
     168                 :            : static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
     169                 :            : {
     170                 :   83901298 :         tlb->pages[tlb->nr++] = page;
     171                 :            :         VM_BUG_ON(tlb->nr > tlb->max);
     172                 :   83901298 :         return tlb->max - tlb->nr;
     173                 :            : }
     174                 :            : 
     175                 :            : static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
     176                 :            : {
     177            [ + ]:    3796987 :         if (!__tlb_remove_page(tlb, page))
     178                 :            :                 tlb_flush_mmu(tlb);
     179                 :            : }
     180                 :            : 
     181                 :            : static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
     182                 :            :         unsigned long addr)
     183                 :            : {
     184                 :            :         pgtable_page_dtor(pte);
     185                 :            : 
     186                 :            : #ifdef CONFIG_ARM_LPAE
     187                 :            :         tlb_add_flush(tlb, addr);
     188                 :            : #else
     189                 :            :         /*
     190                 :            :          * With the classic ARM MMU, a pte page has two corresponding pmd
     191                 :            :          * entries, each covering 1MB.
     192                 :            :          */
     193                 :    3796987 :         addr &= PMD_MASK;
     194                 :    3796987 :         tlb_add_flush(tlb, addr + SZ_1M - PAGE_SIZE);
     195                 :    3796987 :         tlb_add_flush(tlb, addr + SZ_1M);
     196                 :            : #endif
     197                 :            : 
     198                 :            :         tlb_remove_page(tlb, pte);
     199                 :            : }
     200                 :            : 
     201                 :            : static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
     202                 :            :                                   unsigned long addr)
     203                 :            : {
     204                 :            : #ifdef CONFIG_ARM_LPAE
     205                 :            :         tlb_add_flush(tlb, addr);
     206                 :            :         tlb_remove_page(tlb, virt_to_page(pmdp));
     207                 :            : #endif
     208                 :            : }
     209                 :            : 
     210                 :            : static inline void
     211                 :            : tlb_remove_pmd_tlb_entry(struct mmu_gather *tlb, pmd_t *pmdp, unsigned long addr)
     212                 :            : {
     213                 :            :         tlb_add_flush(tlb, addr);
     214                 :            : }
     215                 :            : 
     216                 :            : #define pte_free_tlb(tlb, ptep, addr)   __pte_free_tlb(tlb, ptep, addr)
     217                 :            : #define pmd_free_tlb(tlb, pmdp, addr)   __pmd_free_tlb(tlb, pmdp, addr)
     218                 :            : #define pud_free_tlb(tlb, pudp, addr)   pud_free((tlb)->mm, pudp)
     219                 :            : 
     220                 :            : #define tlb_migrate_finish(mm)          do { } while (0)
     221                 :            : 
     222                 :            : #endif /* CONFIG_MMU */
     223                 :            : #endif

Generated by: LCOV version 1.9