LCOV - code coverage report
Current view: top level - arch/arm/mm - flush.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 31 74 41.9 %
Date: 2014-04-16 Functions: 8 12 66.7 %
Branches: 26 38 68.4 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  *  linux/arch/arm/mm/flush.c
       3                 :            :  *
       4                 :            :  *  Copyright (C) 1995-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                 :            : #include <linux/module.h>
      11                 :            : #include <linux/mm.h>
      12                 :            : #include <linux/pagemap.h>
      13                 :            : #include <linux/highmem.h>
      14                 :            : 
      15                 :            : #include <asm/cacheflush.h>
      16                 :            : #include <asm/cachetype.h>
      17                 :            : #include <asm/highmem.h>
      18                 :            : #include <asm/smp_plat.h>
      19                 :            : #include <asm/tlbflush.h>
      20                 :            : #include <linux/hugetlb.h>
      21                 :            : 
      22                 :            : #include "mm.h"
      23                 :            : 
      24                 :            : #ifdef CONFIG_CPU_CACHE_VIPT
      25                 :            : 
      26                 :          0 : static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr)
      27                 :            : {
      28                 :          0 :         unsigned long to = FLUSH_ALIAS_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT);
      29                 :            :         const int zero = 0;
      30                 :            : 
      31                 :          0 :         set_top_pte(to, pfn_pte(pfn, PAGE_KERNEL));
      32                 :            : 
      33                 :          0 :         asm(    "mcrr      p15, 0, %1, %0, c14\n"
      34                 :            :         "  mcr     p15, 0, %2, c7, c10, 4"
      35                 :            :             :
      36                 :          0 :             : "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES), "r" (zero)
      37                 :            :             : "cc");
      38                 :          0 : }
      39                 :            : 
      40                 :          0 : static void flush_icache_alias(unsigned long pfn, unsigned long vaddr, unsigned long len)
      41                 :            : {
      42                 :          0 :         unsigned long va = FLUSH_ALIAS_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT);
      43                 :          0 :         unsigned long offset = vaddr & (PAGE_SIZE - 1);
      44                 :            :         unsigned long to;
      45                 :            : 
      46                 :          0 :         set_top_pte(va, pfn_pte(pfn, PAGE_KERNEL));
      47                 :          0 :         to = va + offset;
      48                 :          0 :         flush_icache_range(to, to + len);
      49                 :          0 : }
      50                 :            : 
      51                 :          0 : void flush_cache_mm(struct mm_struct *mm)
      52                 :            : {
      53                 :            :         if (cache_is_vivt()) {
      54                 :            :                 vivt_flush_cache_mm(mm);
      55                 :    2229874 :                 return;
      56                 :            :         }
      57                 :            : 
      58                 :            :         if (cache_is_vipt_aliasing()) {
      59                 :            :                 asm(    "mcr       p15, 0, %0, c7, c14, 0\n"
      60                 :            :                 "  mcr     p15, 0, %0, c7, c10, 4"
      61                 :            :                     :
      62                 :            :                     : "r" (0)
      63                 :            :                     : "cc");
      64                 :            :         }
      65                 :            : }
      66                 :            : 
      67                 :          0 : void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
      68                 :            : {
      69                 :            :         if (cache_is_vivt()) {
      70                 :            :                 vivt_flush_cache_range(vma, start, end);
      71                 :          4 :                 return;
      72                 :            :         }
      73                 :            : 
      74                 :            :         if (cache_is_vipt_aliasing()) {
      75                 :            :                 asm(    "mcr       p15, 0, %0, c7, c14, 0\n"
      76                 :            :                 "  mcr     p15, 0, %0, c7, c10, 4"
      77                 :            :                     :
      78                 :            :                     : "r" (0)
      79                 :            :                     : "cc");
      80                 :            :         }
      81                 :            : 
      82         [ +  + ]:     705841 :         if (vma->vm_flags & VM_EXEC)
      83                 :            :                 __flush_icache_all();
      84                 :            : }
      85                 :            : 
      86                 :          0 : void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn)
      87                 :            : {
      88                 :            :         if (cache_is_vivt()) {
      89                 :            :                 vivt_flush_cache_page(vma, user_addr, pfn);
      90                 :          0 :                 return;
      91                 :            :         }
      92                 :            : 
      93                 :            :         if (cache_is_vipt_aliasing()) {
      94                 :            :                 flush_pfn_alias(pfn, user_addr);
      95                 :            :                 __flush_icache_all();
      96                 :            :         }
      97                 :            : 
      98 [ +  + ][ -  + ]:   18479996 :         if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged())
      99                 :            :                 __flush_icache_all();
     100                 :            : }
     101                 :            : 
     102                 :            : #else
     103                 :            : #define flush_pfn_alias(pfn,vaddr)              do { } while (0)
     104                 :            : #define flush_icache_alias(pfn,vaddr,len)       do { } while (0)
     105                 :            : #endif
     106                 :            : 
     107                 :            : static void flush_ptrace_access_other(void *args)
     108                 :            : {
     109                 :            :         __flush_icache_all();
     110                 :            : }
     111                 :            : 
     112                 :            : static
     113                 :          0 : void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
     114                 :            :                          unsigned long uaddr, void *kaddr, unsigned long len)
     115                 :            : {
     116                 :            :         if (cache_is_vivt()) {
     117                 :            :                 if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) {
     118                 :            :                         unsigned long addr = (unsigned long)kaddr;
     119                 :            :                         __cpuc_coherent_kern_range(addr, addr + len);
     120                 :            :                 }
     121                 :            :                 return;
     122                 :            :         }
     123                 :            : 
     124                 :            :         if (cache_is_vipt_aliasing()) {
     125                 :            :                 flush_pfn_alias(page_to_pfn(page), uaddr);
     126                 :            :                 __flush_icache_all();
     127                 :            :                 return;
     128                 :            :         }
     129                 :            : 
     130                 :            :         /* VIPT non-aliasing D-cache */
     131         [ #  # ]:          0 :         if (vma->vm_flags & VM_EXEC) {
     132                 :          0 :                 unsigned long addr = (unsigned long)kaddr;
     133         [ #  # ]:          0 :                 if (icache_is_vipt_aliasing())
     134                 :          0 :                         flush_icache_alias(page_to_pfn(page), uaddr, len);
     135                 :            :                 else
     136                 :          0 :                         __cpuc_coherent_kern_range(addr, addr + len);
     137                 :            :                 if (cache_ops_need_broadcast())
     138                 :            :                         smp_call_function(flush_ptrace_access_other,
     139                 :            :                                           NULL, 1);
     140                 :            :         }
     141                 :            : }
     142                 :            : 
     143                 :            : /*
     144                 :            :  * Copy user data from/to a page which is mapped into a different
     145                 :            :  * processes address space.  Really, we want to allow our "user
     146                 :            :  * space" model to handle this.
     147                 :            :  *
     148                 :            :  * Note that this code needs to run on the current CPU.
     149                 :            :  */
     150                 :          0 : void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
     151                 :            :                        unsigned long uaddr, void *dst, const void *src,
     152                 :            :                        unsigned long len)
     153                 :            : {
     154                 :            : #ifdef CONFIG_SMP
     155                 :          0 :         preempt_disable();
     156                 :            : #endif
     157                 :          0 :         memcpy(dst, src, len);
     158                 :          0 :         flush_ptrace_access(vma, page, uaddr, dst, len);
     159                 :            : #ifdef CONFIG_SMP
     160                 :          0 :         preempt_enable();
     161                 :            : #endif
     162                 :          0 : }
     163                 :            : 
     164                 :          0 : void __flush_dcache_page(struct address_space *mapping, struct page *page)
     165                 :            : {
     166                 :            :         /*
     167                 :            :          * Writeback any data associated with the kernel mapping of this
     168                 :            :          * page.  This ensures that data in the physical page is mutually
     169                 :            :          * coherent with the kernels mapping.
     170                 :            :          */
     171         [ +  + ]:    9166076 :         if (!PageHighMem(page)) {
     172                 :    2792857 :                 size_t page_size = PAGE_SIZE << compound_order(page);
     173                 :    2792857 :                 __cpuc_flush_dcache_area(page_address(page), page_size);
     174                 :            :         } else {
     175                 :            :                 unsigned long i;
     176         [ -  + ]:    6373219 :                 if (cache_is_vipt_nonaliasing()) {
     177         [ +  + ]:   19119689 :                         for (i = 0; i < (1 << compound_order(page)); i++) {
     178                 :    6373204 :                                 void *addr = kmap_atomic(page + i);
     179                 :    6373253 :                                 __cpuc_flush_dcache_area(addr, PAGE_SIZE);
     180                 :    6373254 :                                 kunmap_atomic(addr);
     181                 :            :                         }
     182                 :            :                 } else {
     183         [ #  # ]:          0 :                         for (i = 0; i < (1 << compound_order(page)); i++) {
     184                 :          0 :                                 void *addr = kmap_high_get(page + i);
     185         [ #  # ]:          0 :                                 if (addr) {
     186                 :          0 :                                         __cpuc_flush_dcache_area(addr, PAGE_SIZE);
     187                 :          0 :                                         kunmap_high(page + i);
     188                 :            :                                 }
     189                 :            :                         }
     190                 :            :                 }
     191                 :            :         }
     192                 :            : 
     193                 :            :         /*
     194                 :            :          * If this is a page cache page, and we have an aliasing VIPT cache,
     195                 :            :          * we only need to do one flush - which would be at the relevant
     196                 :            :          * userspace colour, which is congruent with page->index.
     197                 :            :          */
     198                 :            :         if (mapping && cache_is_vipt_aliasing())
     199                 :            :                 flush_pfn_alias(page_to_pfn(page),
     200                 :            :                                 page->index << PAGE_CACHE_SHIFT);
     201                 :         35 : }
     202                 :            : 
     203                 :            : static void __flush_dcache_aliases(struct address_space *mapping, struct page *page)
     204                 :            : {
     205                 :            :         struct mm_struct *mm = current->active_mm;
     206                 :            :         struct vm_area_struct *mpnt;
     207                 :            :         pgoff_t pgoff;
     208                 :            : 
     209                 :            :         /*
     210                 :            :          * There are possible user space mappings of this page:
     211                 :            :          * - VIVT cache: we need to also write back and invalidate all user
     212                 :            :          *   data in the current VM view associated with this page.
     213                 :            :          * - aliasing VIPT: we only need to find one mapping of this page.
     214                 :            :          */
     215                 :            :         pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
     216                 :            : 
     217                 :            :         flush_dcache_mmap_lock(mapping);
     218                 :            :         vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) {
     219                 :            :                 unsigned long offset;
     220                 :            : 
     221                 :            :                 /*
     222                 :            :                  * If this VMA is not in our MM, we can ignore it.
     223                 :            :                  */
     224                 :            :                 if (mpnt->vm_mm != mm)
     225                 :            :                         continue;
     226                 :            :                 if (!(mpnt->vm_flags & VM_MAYSHARE))
     227                 :            :                         continue;
     228                 :            :                 offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
     229                 :            :                 flush_cache_page(mpnt, mpnt->vm_start + offset, page_to_pfn(page));
     230                 :            :         }
     231                 :            :         flush_dcache_mmap_unlock(mapping);
     232                 :            : }
     233                 :            : 
     234                 :            : #if __LINUX_ARM_ARCH__ >= 6
     235                 :          0 : void __sync_icache_dcache(pte_t pteval)
     236                 :            : {
     237                 :            :         unsigned long pfn;
     238                 :            :         struct page *page;
     239                 :            :         struct address_space *mapping;
     240                 :            : 
     241    [ + ][ +  + ]:  116252009 :         if (cache_is_vipt_nonaliasing() && !pte_exec(pteval))
     242                 :            :                 /* only flush non-aliasing VIPT caches for exec mappings */
     243                 :            :                 return;
     244                 :   29160800 :         pfn = pte_pfn(pteval);
     245         [ +  + ]:   29160800 :         if (!pfn_valid(pfn))
     246                 :            :                 return;
     247                 :            : 
     248                 :   29159275 :         page = pfn_to_page(pfn);
     249                 :            :         if (cache_is_vipt_aliasing())
     250                 :            :                 mapping = page_mapping(page);
     251                 :            :         else
     252                 :            :                 mapping = NULL;
     253                 :            : 
     254         [ +  + ]:   29159275 :         if (!test_and_set_bit(PG_dcache_clean, &page->flags))
     255                 :      11780 :                 __flush_dcache_page(mapping, page);
     256                 :            : 
     257         [ +  - ]:   29159880 :         if (pte_exec(pteval))
     258                 :            :                 __flush_icache_all();
     259                 :            : }
     260                 :            : #endif
     261                 :            : 
     262                 :            : /*
     263                 :            :  * Ensure cache coherency between kernel mapping and userspace mapping
     264                 :            :  * of this page.
     265                 :            :  *
     266                 :            :  * We have three cases to consider:
     267                 :            :  *  - VIPT non-aliasing cache: fully coherent so nothing required.
     268                 :            :  *  - VIVT: fully aliasing, so we need to handle every alias in our
     269                 :            :  *          current VM view.
     270                 :            :  *  - VIPT aliasing: need to handle one alias in our current VM view.
     271                 :            :  *
     272                 :            :  * If we need to handle aliasing:
     273                 :            :  *  If the page only exists in the page cache and there are no user
     274                 :            :  *  space mappings, we can be lazy and remember that we may have dirty
     275                 :            :  *  kernel cache lines for later.  Otherwise, we assume we have
     276                 :            :  *  aliasing mappings.
     277                 :            :  *
     278                 :            :  * Note that we disable the lazy flush for SMP configurations where
     279                 :            :  * the cache maintenance operations are not automatically broadcasted.
     280                 :            :  */
     281                 :          0 : void flush_dcache_page(struct page *page)
     282                 :            : {
     283                 :            :         struct address_space *mapping;
     284                 :            : 
     285                 :            :         /*
     286                 :            :          * The zero page is never written to, so never has any dirty
     287                 :            :          * cache lines, and therefore never needs to be flushed.
     288                 :            :          */
     289            [ + ]:   20935614 :         if (page == ZERO_PAGE(0))
     290                 :   20935842 :                 return;
     291                 :            : 
     292                 :   20935846 :         mapping = page_mapping(page);
     293                 :            : 
     294         [ +  + ]:   20935847 :         if (!cache_ops_need_broadcast() &&
     295         [ +  + ]:   17315498 :             mapping && !page_mapped(page))
     296                 :   11781826 :                 clear_bit(PG_dcache_clean, &page->flags);
     297                 :            :         else {
     298                 :    9154021 :                 __flush_dcache_page(mapping, page);
     299                 :            :                 if (mapping && cache_is_vivt())
     300                 :            :                         __flush_dcache_aliases(mapping, page);
     301         [ +  + ]:    9154293 :                 else if (mapping)
     302                 :            :                         __flush_icache_all();
     303                 :    9154311 :                 set_bit(PG_dcache_clean, &page->flags);
     304                 :            :         }
     305                 :            : }
     306                 :            : EXPORT_SYMBOL(flush_dcache_page);
     307                 :            : 
     308                 :            : /*
     309                 :            :  * Ensure cache coherency for the kernel mapping of this page. We can
     310                 :            :  * assume that the page is pinned via kmap.
     311                 :            :  *
     312                 :            :  * If the page only exists in the page cache and there are no user
     313                 :            :  * space mappings, this is a no-op since the page was already marked
     314                 :            :  * dirty at creation.  Otherwise, we need to flush the dirty kernel
     315                 :            :  * cache lines directly.
     316                 :            :  */
     317                 :          0 : void flush_kernel_dcache_page(struct page *page)
     318                 :            : {
     319                 :            :         if (cache_is_vivt() || cache_is_vipt_aliasing()) {
     320                 :            :                 struct address_space *mapping;
     321                 :            : 
     322                 :            :                 mapping = page_mapping(page);
     323                 :            : 
     324                 :            :                 if (!mapping || mapping_mapped(mapping)) {
     325                 :            :                         void *addr;
     326                 :            : 
     327                 :            :                         addr = page_address(page);
     328                 :            :                         /*
     329                 :            :                          * kmap_atomic() doesn't set the page virtual
     330                 :            :                          * address for highmem pages, and
     331                 :            :                          * kunmap_atomic() takes care of cache
     332                 :            :                          * flushing already.
     333                 :            :                          */
     334                 :            :                         if (!IS_ENABLED(CONFIG_HIGHMEM) || addr)
     335                 :            :                                 __cpuc_flush_dcache_area(addr, PAGE_SIZE);
     336                 :            :                 }
     337                 :            :         }
     338                 :    2041156 : }
     339                 :            : EXPORT_SYMBOL(flush_kernel_dcache_page);
     340                 :            : 
     341                 :            : /*
     342                 :            :  * Flush an anonymous page so that users of get_user_pages()
     343                 :            :  * can safely access the data.  The expected sequence is:
     344                 :            :  *
     345                 :            :  *  get_user_pages()
     346                 :            :  *    -> flush_anon_page
     347                 :            :  *  memcpy() to/from page
     348                 :            :  *  if written to page, flush_dcache_page()
     349                 :            :  */
     350                 :          0 : void __flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr)
     351                 :            : {
     352                 :            :         unsigned long pfn;
     353                 :            : 
     354                 :            :         /* VIPT non-aliasing caches need do nothing */
     355         [ -  + ]:    2498030 :         if (cache_is_vipt_nonaliasing())
     356                 :          0 :                 return;
     357                 :            : 
     358                 :            :         /*
     359                 :            :          * Write back and invalidate userspace mapping.
     360                 :            :          */
     361                 :          0 :         pfn = page_to_pfn(page);
     362                 :            :         if (cache_is_vivt()) {
     363                 :            :                 flush_cache_page(vma, vmaddr, pfn);
     364                 :            :         } else {
     365                 :            :                 /*
     366                 :            :                  * For aliasing VIPT, we can flush an alias of the
     367                 :            :                  * userspace address only.
     368                 :            :                  */
     369                 :          0 :                 flush_pfn_alias(pfn, vmaddr);
     370                 :            :                 __flush_icache_all();
     371                 :            :         }
     372                 :            : 
     373                 :            :         /*
     374                 :            :          * Invalidate kernel mapping.  No data should be contained
     375                 :            :          * in this mapping of the page.  FIXME: this is overkill
     376                 :            :          * since we actually ask for a write-back and invalidate.
     377                 :            :          */
     378                 :          0 :         __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
     379                 :            : }

Generated by: LCOV version 1.9