mincore.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. * linux/mm/mincore.c
  3. *
  4. * Copyright (C) 1994-2006 Linus Torvalds
  5. */
  6. /*
  7. * The mincore() system call.
  8. */
  9. #include <linux/slab.h>
  10. #include <linux/pagemap.h>
  11. #include <linux/mm.h>
  12. #include <linux/mman.h>
  13. #include <linux/syscalls.h>
  14. #include <asm/uaccess.h>
  15. #include <asm/pgtable.h>
  16. /*
  17. * Later we can get more picky about what "in core" means precisely.
  18. * For now, simply check to see if the page is in the page cache,
  19. * and is up to date; i.e. that no page-in operation would be required
  20. * at this time if an application were to map and access this page.
  21. */
  22. static unsigned char mincore_page(struct vm_area_struct * vma,
  23. unsigned long pgoff)
  24. {
  25. unsigned char present = 0;
  26. struct address_space * as = vma->vm_file->f_mapping;
  27. struct page * page;
  28. page = find_get_page(as, pgoff);
  29. if (page) {
  30. present = PageUptodate(page);
  31. page_cache_release(page);
  32. }
  33. return present;
  34. }
  35. /*
  36. * Do a chunk of "sys_mincore()". We've already checked
  37. * all the arguments, we hold the mmap semaphore: we should
  38. * just return the amount of info we're asked for.
  39. */
  40. static long do_mincore(unsigned long addr, unsigned char *vec, unsigned long pages)
  41. {
  42. unsigned long i, nr, pgoff;
  43. struct vm_area_struct *vma = find_vma(current->mm, addr);
  44. /*
  45. * find_vma() didn't find anything above us, or we're
  46. * in an unmapped hole in the address space: ENOMEM.
  47. */
  48. if (!vma || addr < vma->vm_start)
  49. return -ENOMEM;
  50. /*
  51. * Ok, got it. But check whether it's a segment we support
  52. * mincore() on. Right now, we don't do any anonymous mappings.
  53. *
  54. * FIXME: This is just stupid. And returning ENOMEM is
  55. * stupid too. We should just look at the page tables. But
  56. * this is what we've traditionally done, so we'll just
  57. * continue doing it.
  58. */
  59. if (!vma->vm_file)
  60. return -ENOMEM;
  61. /*
  62. * Calculate how many pages there are left in the vma, and
  63. * what the pgoff is for our address.
  64. */
  65. nr = (vma->vm_end - addr) >> PAGE_SHIFT;
  66. if (nr > pages)
  67. nr = pages;
  68. pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
  69. pgoff += vma->vm_pgoff;
  70. /* And then we just fill the sucker in.. */
  71. for (i = 0 ; i < nr; i++, pgoff++)
  72. vec[i] = mincore_page(vma, pgoff);
  73. return nr;
  74. }
  75. /*
  76. * The mincore(2) system call.
  77. *
  78. * mincore() returns the memory residency status of the pages in the
  79. * current process's address space specified by [addr, addr + len).
  80. * The status is returned in a vector of bytes. The least significant
  81. * bit of each byte is 1 if the referenced page is in memory, otherwise
  82. * it is zero.
  83. *
  84. * Because the status of a page can change after mincore() checks it
  85. * but before it returns to the application, the returned vector may
  86. * contain stale information. Only locked pages are guaranteed to
  87. * remain in memory.
  88. *
  89. * return values:
  90. * zero - success
  91. * -EFAULT - vec points to an illegal address
  92. * -EINVAL - addr is not a multiple of PAGE_CACHE_SIZE
  93. * -ENOMEM - Addresses in the range [addr, addr + len] are
  94. * invalid for the address space of this process, or
  95. * specify one or more pages which are not currently
  96. * mapped
  97. * -EAGAIN - A kernel resource was temporarily unavailable.
  98. */
  99. asmlinkage long sys_mincore(unsigned long start, size_t len,
  100. unsigned char __user * vec)
  101. {
  102. long retval;
  103. unsigned long pages;
  104. unsigned char *tmp;
  105. /* Check the start address: needs to be page-aligned.. */
  106. if (start & ~PAGE_CACHE_MASK)
  107. return -EINVAL;
  108. /* ..and we need to be passed a valid user-space range */
  109. if (!access_ok(VERIFY_READ, (void __user *) start, len))
  110. return -ENOMEM;
  111. /* This also avoids any overflows on PAGE_CACHE_ALIGN */
  112. pages = len >> PAGE_SHIFT;
  113. pages += (len & ~PAGE_MASK) != 0;
  114. if (!access_ok(VERIFY_WRITE, vec, pages))
  115. return -EFAULT;
  116. tmp = (void *) __get_free_page(GFP_USER);
  117. if (!tmp)
  118. return -EAGAIN;
  119. retval = 0;
  120. while (pages) {
  121. /*
  122. * Do at most PAGE_SIZE entries per iteration, due to
  123. * the temporary buffer size.
  124. */
  125. down_read(&current->mm->mmap_sem);
  126. retval = do_mincore(start, tmp, min(pages, PAGE_SIZE));
  127. up_read(&current->mm->mmap_sem);
  128. if (retval <= 0)
  129. break;
  130. if (copy_to_user(vec, tmp, retval)) {
  131. retval = -EFAULT;
  132. break;
  133. }
  134. pages -= retval;
  135. vec += retval;
  136. start += retval << PAGE_SHIFT;
  137. retval = 0;
  138. }
  139. free_page((unsigned long) tmp);
  140. return retval;
  141. }