pagewalk.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #include <linux/mm.h>
  2. #include <linux/highmem.h>
  3. #include <linux/sched.h>
  4. static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
  5. struct mm_walk *walk)
  6. {
  7. pte_t *pte;
  8. int err = 0;
  9. pte = pte_offset_map(pmd, addr);
  10. for (;;) {
  11. err = walk->pte_entry(pte, addr, addr + PAGE_SIZE, walk);
  12. if (err)
  13. break;
  14. addr += PAGE_SIZE;
  15. if (addr == end)
  16. break;
  17. pte++;
  18. }
  19. pte_unmap(pte);
  20. return err;
  21. }
  22. static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
  23. struct mm_walk *walk)
  24. {
  25. pmd_t *pmd;
  26. unsigned long next;
  27. int err = 0;
  28. pmd = pmd_offset(pud, addr);
  29. do {
  30. next = pmd_addr_end(addr, end);
  31. if (pmd_none_or_clear_bad(pmd)) {
  32. if (walk->pte_hole)
  33. err = walk->pte_hole(addr, next, walk);
  34. if (err)
  35. break;
  36. continue;
  37. }
  38. if (walk->pmd_entry)
  39. err = walk->pmd_entry(pmd, addr, next, walk);
  40. if (!err && walk->pte_entry)
  41. err = walk_pte_range(pmd, addr, next, walk);
  42. if (err)
  43. break;
  44. } while (pmd++, addr = next, addr != end);
  45. return err;
  46. }
  47. static int walk_pud_range(pgd_t *pgd, unsigned long addr, unsigned long end,
  48. struct mm_walk *walk)
  49. {
  50. pud_t *pud;
  51. unsigned long next;
  52. int err = 0;
  53. pud = pud_offset(pgd, addr);
  54. do {
  55. next = pud_addr_end(addr, end);
  56. if (pud_none_or_clear_bad(pud)) {
  57. if (walk->pte_hole)
  58. err = walk->pte_hole(addr, next, walk);
  59. if (err)
  60. break;
  61. continue;
  62. }
  63. if (walk->pud_entry)
  64. err = walk->pud_entry(pud, addr, next, walk);
  65. if (!err && (walk->pmd_entry || walk->pte_entry))
  66. err = walk_pmd_range(pud, addr, next, walk);
  67. if (err)
  68. break;
  69. } while (pud++, addr = next, addr != end);
  70. return err;
  71. }
  72. /**
  73. * walk_page_range - walk a memory map's page tables with a callback
  74. * @mm: memory map to walk
  75. * @addr: starting address
  76. * @end: ending address
  77. * @walk: set of callbacks to invoke for each level of the tree
  78. *
  79. * Recursively walk the page table for the memory area in a VMA,
  80. * calling supplied callbacks. Callbacks are called in-order (first
  81. * PGD, first PUD, first PMD, first PTE, second PTE... second PMD,
  82. * etc.). If lower-level callbacks are omitted, walking depth is reduced.
  83. *
  84. * Each callback receives an entry pointer and the start and end of the
  85. * associated range, and a copy of the original mm_walk for access to
  86. * the ->private or ->mm fields.
  87. *
  88. * No locks are taken, but the bottom level iterator will map PTE
  89. * directories from highmem if necessary.
  90. *
  91. * If any callback returns a non-zero value, the walk is aborted and
  92. * the return value is propagated back to the caller. Otherwise 0 is returned.
  93. */
  94. int walk_page_range(unsigned long addr, unsigned long end,
  95. struct mm_walk *walk)
  96. {
  97. pgd_t *pgd;
  98. unsigned long next;
  99. int err = 0;
  100. if (addr >= end)
  101. return err;
  102. if (!walk->mm)
  103. return -EINVAL;
  104. pgd = pgd_offset(walk->mm, addr);
  105. do {
  106. next = pgd_addr_end(addr, end);
  107. if (pgd_none_or_clear_bad(pgd)) {
  108. if (walk->pte_hole)
  109. err = walk->pte_hole(addr, next, walk);
  110. if (err)
  111. break;
  112. continue;
  113. }
  114. if (walk->pgd_entry)
  115. err = walk->pgd_entry(pgd, addr, next, walk);
  116. if (!err &&
  117. (walk->pud_entry || walk->pmd_entry || walk->pte_entry))
  118. err = walk_pud_range(pgd, addr, next, walk);
  119. if (err)
  120. break;
  121. } while (pgd++, addr = next, addr != end);
  122. return err;
  123. }