LCOV - code coverage report
Current view: top level - mm - mincore.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 53 71 74.6 %
Date: 2014-02-18 Functions: 7 7 100.0 %
Branches: 28 44 63.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  *      linux/mm/mincore.c
       3                 :            :  *
       4                 :            :  * Copyright (C) 1994-2006  Linus Torvalds
       5                 :            :  */
       6                 :            : 
       7                 :            : /*
       8                 :            :  * The mincore() system call.
       9                 :            :  */
      10                 :            : #include <linux/pagemap.h>
      11                 :            : #include <linux/gfp.h>
      12                 :            : #include <linux/mm.h>
      13                 :            : #include <linux/mman.h>
      14                 :            : #include <linux/syscalls.h>
      15                 :            : #include <linux/swap.h>
      16                 :            : #include <linux/swapops.h>
      17                 :            : #include <linux/hugetlb.h>
      18                 :            : 
      19                 :            : #include <asm/uaccess.h>
      20                 :            : #include <asm/pgtable.h>
      21                 :            : 
      22                 :            : static void mincore_hugetlb_page_range(struct vm_area_struct *vma,
      23                 :            :                                 unsigned long addr, unsigned long end,
      24                 :            :                                 unsigned char *vec)
      25                 :            : {
      26                 :            : #ifdef CONFIG_HUGETLB_PAGE
      27                 :            :         struct hstate *h;
      28                 :            : 
      29                 :            :         h = hstate_vma(vma);
      30                 :            :         while (1) {
      31                 :            :                 unsigned char present;
      32                 :            :                 pte_t *ptep;
      33                 :            :                 /*
      34                 :            :                  * Huge pages are always in RAM for now, but
      35                 :            :                  * theoretically it needs to be checked.
      36                 :            :                  */
      37                 :            :                 ptep = huge_pte_offset(current->mm,
      38                 :            :                                        addr & huge_page_mask(h));
      39                 :            :                 present = ptep && !huge_pte_none(huge_ptep_get(ptep));
      40                 :            :                 while (1) {
      41                 :            :                         *vec = present;
      42                 :            :                         vec++;
      43                 :            :                         addr += PAGE_SIZE;
      44                 :            :                         if (addr == end)
      45                 :            :                                 return;
      46                 :            :                         /* check hugepage border */
      47                 :            :                         if (!(addr & ~huge_page_mask(h)))
      48                 :            :                                 break;
      49                 :            :                 }
      50                 :            :         }
      51                 :            : #else
      52                 :            :         BUG();
      53                 :            : #endif
      54                 :            : }
      55                 :            : 
      56                 :            : /*
      57                 :            :  * Later we can get more picky about what "in core" means precisely.
      58                 :            :  * For now, simply check to see if the page is in the page cache,
      59                 :            :  * and is up to date; i.e. that no page-in operation would be required
      60                 :            :  * at this time if an application were to map and access this page.
      61                 :            :  */
      62                 :          0 : static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff)
      63                 :            : {
      64                 :            :         unsigned char present = 0;
      65                 :            :         struct page *page;
      66                 :            : 
      67                 :            :         /*
      68                 :            :          * When tmpfs swaps out a page from a file, any process mapping that
      69                 :            :          * file will not get a swp_entry_t in its pte, but rather it is like
      70                 :            :          * any other file mapping (ie. marked !present and faulted in with
      71                 :            :          * tmpfs's .fault). So swapped out tmpfs mappings are tested here.
      72                 :            :          */
      73                 :          4 :         page = find_get_page(mapping, pgoff);
      74                 :            : #ifdef CONFIG_SWAP
      75                 :            :         /* shmem/tmpfs may return swap: account for swapcache page too. */
      76         [ -  + ]:          4 :         if (radix_tree_exceptional_entry(page)) {
      77                 :            :                 swp_entry_t swap = radix_to_swp_entry(page);
      78                 :          0 :                 page = find_get_page(swap_address_space(swap), swap.val);
      79                 :            :         }
      80                 :            : #endif
      81         [ +  - ]:          4 :         if (page) {
      82                 :          0 :                 present = PageUptodate(page);
      83                 :          4 :                 page_cache_release(page);
      84                 :            :         }
      85                 :            : 
      86                 :          4 :         return present;
      87                 :            : }
      88                 :            : 
      89                 :          0 : static void mincore_unmapped_range(struct vm_area_struct *vma,
      90                 :            :                                 unsigned long addr, unsigned long end,
      91                 :            :                                 unsigned char *vec)
      92                 :            : {
      93                 :          4 :         unsigned long nr = (end - addr) >> PAGE_SHIFT;
      94                 :            :         int i;
      95                 :            : 
      96         [ -  + ]:          4 :         if (vma->vm_file) {
      97                 :            :                 pgoff_t pgoff;
      98                 :            : 
      99                 :            :                 pgoff = linear_page_index(vma, addr);
     100         [ +  + ]:          8 :                 for (i = 0; i < nr; i++, pgoff++)
     101                 :          4 :                         vec[i] = mincore_page(vma->vm_file->f_mapping, pgoff);
     102                 :            :         } else {
     103         [ #  # ]:          0 :                 for (i = 0; i < nr; i++)
     104                 :          0 :                         vec[i] = 0;
     105                 :            :         }
     106                 :          0 : }
     107                 :            : 
     108                 :          0 : static void mincore_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
     109                 :            :                         unsigned long addr, unsigned long end,
     110                 :            :                         unsigned char *vec)
     111                 :            : {
     112                 :            :         unsigned long next;
     113                 :            :         spinlock_t *ptl;
     114                 :            :         pte_t *ptep;
     115                 :            : 
     116                 :          3 :         ptep = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
     117                 :            :         do {
     118                 :         11 :                 pte_t pte = *ptep;
     119                 :            :                 pgoff_t pgoff;
     120                 :            : 
     121                 :         11 :                 next = addr + PAGE_SIZE;
     122         [ +  + ]:         11 :                 if (pte_none(pte))
     123                 :          4 :                         mincore_unmapped_range(vma, addr, next, vec);
     124         [ +  + ]:          7 :                 else if (pte_present(pte))
     125                 :          4 :                         *vec = 1;
     126         [ -  - ]:          3 :                 else if (pte_file(pte)) {
     127                 :          0 :                         pgoff = pte_to_pgoff(pte);
     128                 :          0 :                         *vec = mincore_page(vma->vm_file->f_mapping, pgoff);
     129                 :            :                 } else { /* pte is a swap entry */
     130                 :            :                         swp_entry_t entry = pte_to_swp_entry(pte);
     131                 :            : 
     132         [ #  # ]:          0 :                         if (is_migration_entry(entry)) {
     133                 :            :                                 /* migration entries are always uptodate */
     134                 :          0 :                                 *vec = 1;
     135                 :            :                         } else {
     136                 :            : #ifdef CONFIG_SWAP
     137                 :            :                                 pgoff = entry.val;
     138                 :          0 :                                 *vec = mincore_page(swap_address_space(entry),
     139                 :            :                                         pgoff);
     140                 :            : #else
     141                 :            :                                 WARN_ON(1);
     142                 :            :                                 *vec = 1;
     143                 :            : #endif
     144                 :            :                         }
     145                 :            :                 }
     146                 :          8 :                 vec++;
     147         [ +  + ]:          8 :         } while (ptep++, addr = next, addr != end);
     148                 :          3 :         pte_unmap_unlock(ptep - 1, ptl);
     149                 :          3 : }
     150                 :            : 
     151                 :          0 : static void mincore_pmd_range(struct vm_area_struct *vma, pud_t *pud,
     152                 :            :                         unsigned long addr, unsigned long end,
     153                 :            :                         unsigned char *vec)
     154                 :            : {
     155                 :            :         unsigned long next;
     156                 :          3 :         pmd_t *pmd;
     157                 :            : 
     158                 :            :         pmd = pmd_offset(pud, addr);
     159                 :            :         do {
     160                 :            :                 next = pmd_addr_end(addr, end);
     161                 :            :                 if (pmd_trans_huge(*pmd)) {
     162                 :            :                         if (mincore_huge_pmd(vma, pmd, addr, next, vec)) {
     163                 :            :                                 vec += (next - addr) >> PAGE_SHIFT;
     164                 :            :                                 continue;
     165                 :            :                         }
     166                 :            :                         /* fall through */
     167                 :            :                 }
     168         [ -  + ]:          3 :                 if (pmd_none_or_trans_huge_or_clear_bad(pmd))
     169                 :          0 :                         mincore_unmapped_range(vma, addr, next, vec);
     170                 :            :                 else
     171                 :          3 :                         mincore_pte_range(vma, pmd, addr, next, vec);
     172                 :            :                 vec += (next - addr) >> PAGE_SHIFT;
     173                 :            :         } while (pmd++, addr = next, addr != end);
     174                 :          3 : }
     175                 :            : 
     176                 :            : static void mincore_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
     177                 :            :                         unsigned long addr, unsigned long end,
     178                 :            :                         unsigned char *vec)
     179                 :            : {
     180                 :            :         unsigned long next;
     181                 :            :         pud_t *pud;
     182                 :            : 
     183                 :            :         pud = pud_offset(pgd, addr);
     184                 :            :         do {
     185                 :            :                 next = pud_addr_end(addr, end);
     186                 :            :                 if (pud_none_or_clear_bad(pud))
     187                 :            :                         mincore_unmapped_range(vma, addr, next, vec);
     188                 :            :                 else
     189                 :          3 :                         mincore_pmd_range(vma, pud, addr, next, vec);
     190                 :            :                 vec += (next - addr) >> PAGE_SHIFT;
     191                 :            :         } while (pud++, addr = next, addr != end);
     192                 :            : }
     193                 :            : 
     194                 :          0 : static void mincore_page_range(struct vm_area_struct *vma,
     195                 :            :                         unsigned long addr, unsigned long end,
     196                 :            :                         unsigned char *vec)
     197                 :            : {
     198                 :            :         unsigned long next;
     199                 :            :         pgd_t *pgd;
     200                 :            : 
     201                 :          3 :         pgd = pgd_offset(vma->vm_mm, addr);
     202                 :            :         do {
     203         [ +  - ]:          3 :                 next = pgd_addr_end(addr, end);
     204                 :            :                 if (pgd_none_or_clear_bad(pgd))
     205                 :            :                         mincore_unmapped_range(vma, addr, next, vec);
     206                 :            :                 else
     207                 :            :                         mincore_pud_range(vma, pgd, addr, next, vec);
     208                 :          3 :                 vec += (next - addr) >> PAGE_SHIFT;
     209         [ -  + ]:          6 :         } while (pgd++, addr = next, addr != end);
     210                 :          3 : }
     211                 :            : 
     212                 :            : /*
     213                 :            :  * Do a chunk of "sys_mincore()". We've already checked
     214                 :            :  * all the arguments, we hold the mmap semaphore: we should
     215                 :            :  * just return the amount of info we're asked for.
     216                 :            :  */
     217                 :          0 : static long do_mincore(unsigned long addr, unsigned long pages, unsigned char *vec)
     218                 :            : {
     219                 :            :         struct vm_area_struct *vma;
     220                 :            :         unsigned long end;
     221                 :            : 
     222                 :          4 :         vma = find_vma(current->mm, addr);
     223 [ +  - ][ +  + ]:          4 :         if (!vma || addr < vma->vm_start)
     224                 :            :                 return -ENOMEM;
     225                 :            : 
     226                 :          3 :         end = min(vma->vm_end, addr + (pages << PAGE_SHIFT));
     227                 :            : 
     228                 :            :         if (is_vm_hugetlb_page(vma)) {
     229                 :            :                 mincore_hugetlb_page_range(vma, addr, end, vec);
     230                 :            :                 return (end - addr) >> PAGE_SHIFT;
     231                 :            :         }
     232                 :            : 
     233                 :            :         end = pmd_addr_end(addr, end);
     234                 :            : 
     235                 :            :         if (is_vm_hugetlb_page(vma))
     236                 :            :                 mincore_hugetlb_page_range(vma, addr, end, vec);
     237                 :            :         else
     238                 :          3 :                 mincore_page_range(vma, addr, end, vec);
     239                 :            : 
     240                 :          3 :         return (end - addr) >> PAGE_SHIFT;
     241                 :            : }
     242                 :            : 
     243                 :            : /*
     244                 :            :  * The mincore(2) system call.
     245                 :            :  *
     246                 :            :  * mincore() returns the memory residency status of the pages in the
     247                 :            :  * current process's address space specified by [addr, addr + len).
     248                 :            :  * The status is returned in a vector of bytes.  The least significant
     249                 :            :  * bit of each byte is 1 if the referenced page is in memory, otherwise
     250                 :            :  * it is zero.
     251                 :            :  *
     252                 :            :  * Because the status of a page can change after mincore() checks it
     253                 :            :  * but before it returns to the application, the returned vector may
     254                 :            :  * contain stale information.  Only locked pages are guaranteed to
     255                 :            :  * remain in memory.
     256                 :            :  *
     257                 :            :  * return values:
     258                 :            :  *  zero    - success
     259                 :            :  *  -EFAULT - vec points to an illegal address
     260                 :            :  *  -EINVAL - addr is not a multiple of PAGE_CACHE_SIZE
     261                 :            :  *  -ENOMEM - Addresses in the range [addr, addr + len] are
     262                 :            :  *              invalid for the address space of this process, or
     263                 :            :  *              specify one or more pages which are not currently
     264                 :            :  *              mapped
     265                 :            :  *  -EAGAIN - A kernel resource was temporarily unavailable.
     266                 :            :  */
     267                 :          0 : SYSCALL_DEFINE3(mincore, unsigned long, start, size_t, len,
     268                 :            :                 unsigned char __user *, vec)
     269                 :            : {
     270                 :            :         long retval;
     271                 :            :         unsigned long pages;
     272                 :            :         unsigned char *tmp;
     273                 :            : 
     274                 :            :         /* Check the start address: needs to be page-aligned.. */
     275         [ +  + ]:          4 :         if (start & ~PAGE_CACHE_MASK)
     276                 :            :                 return -EINVAL;
     277                 :            : 
     278                 :            :         /* ..and we need to be passed a valid user-space range */
     279         [ +  - ]:          3 :         if (!access_ok(VERIFY_READ, (void __user *) start, len))
     280                 :            :                 return -ENOMEM;
     281                 :            : 
     282                 :            :         /* This also avoids any overflows on PAGE_CACHE_ALIGN */
     283                 :          3 :         pages = len >> PAGE_SHIFT;
     284                 :          3 :         pages += (len & ~PAGE_MASK) != 0;
     285                 :            : 
     286         [ +  - ]:          3 :         if (!access_ok(VERIFY_WRITE, vec, pages))
     287                 :            :                 return -EFAULT;
     288                 :            : 
     289                 :          3 :         tmp = (void *) __get_free_page(GFP_USER);
     290         [ +  - ]:          3 :         if (!tmp)
     291                 :            :                 return -EAGAIN;
     292                 :            : 
     293                 :            :         retval = 0;
     294         [ +  + ]:          5 :         while (pages) {
     295                 :            :                 /*
     296                 :            :                  * Do at most PAGE_SIZE entries per iteration, due to
     297                 :            :                  * the temporary buffer size.
     298                 :            :                  */
     299                 :          4 :                 down_read(&current->mm->mmap_sem);
     300                 :          4 :                 retval = do_mincore(start, min(pages, PAGE_SIZE), tmp);
     301                 :          4 :                 up_read(&current->mm->mmap_sem);
     302                 :            : 
     303         [ +  + ]:          4 :                 if (retval <= 0)
     304                 :            :                         break;
     305         [ +  + ]:          3 :                 if (copy_to_user(vec, tmp, retval)) {
     306                 :            :                         retval = -EFAULT;
     307                 :            :                         break;
     308                 :            :                 }
     309                 :          2 :                 pages -= retval;
     310                 :          2 :                 vec += retval;
     311                 :          2 :                 start += retval << PAGE_SHIFT;
     312                 :            :                 retval = 0;
     313                 :            :         }
     314                 :          3 :         free_page((unsigned long) tmp);
     315                 :            :         return retval;
     316                 :            : }

Generated by: LCOV version 1.9