LCOV - code coverage report
Current view: top level - mm - mincore.c (source / functions) Hit Total Coverage
Test: Real Lines: 0 78 0.0 %
Date: 2020-10-17 15:46:16 Functions: 0 9 0.0 %
Legend: Neither, QEMU, Real, Both Branches: 0 0 -

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

Generated by: LCOV version 1.14