123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- #include <linux/mm.h>
- #include <linux/highmem.h>
- #include <linux/sched.h>
- static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
- struct mm_walk *walk)
- {
- pte_t *pte;
- int err = 0;
- pte = pte_offset_map(pmd, addr);
- for (;;) {
- err = walk->pte_entry(pte, addr, addr + PAGE_SIZE, walk);
- if (err)
- break;
- addr += PAGE_SIZE;
- if (addr == end)
- break;
- pte++;
- }
- pte_unmap(pte);
- return err;
- }
- static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
- struct mm_walk *walk)
- {
- pmd_t *pmd;
- unsigned long next;
- int err = 0;
- pmd = pmd_offset(pud, addr);
- do {
- next = pmd_addr_end(addr, end);
- if (pmd_none_or_clear_bad(pmd)) {
- if (walk->pte_hole)
- err = walk->pte_hole(addr, next, walk);
- if (err)
- break;
- continue;
- }
- if (walk->pmd_entry)
- err = walk->pmd_entry(pmd, addr, next, walk);
- if (!err && walk->pte_entry)
- err = walk_pte_range(pmd, addr, next, walk);
- if (err)
- break;
- } while (pmd++, addr = next, addr != end);
- return err;
- }
- static int walk_pud_range(pgd_t *pgd, unsigned long addr, unsigned long end,
- struct mm_walk *walk)
- {
- pud_t *pud;
- unsigned long next;
- int err = 0;
- pud = pud_offset(pgd, addr);
- do {
- next = pud_addr_end(addr, end);
- if (pud_none_or_clear_bad(pud)) {
- if (walk->pte_hole)
- err = walk->pte_hole(addr, next, walk);
- if (err)
- break;
- continue;
- }
- if (walk->pud_entry)
- err = walk->pud_entry(pud, addr, next, walk);
- if (!err && (walk->pmd_entry || walk->pte_entry))
- err = walk_pmd_range(pud, addr, next, walk);
- if (err)
- break;
- } while (pud++, addr = next, addr != end);
- return err;
- }
- /**
- * walk_page_range - walk a memory map's page tables with a callback
- * @mm: memory map to walk
- * @addr: starting address
- * @end: ending address
- * @walk: set of callbacks to invoke for each level of the tree
- *
- * Recursively walk the page table for the memory area in a VMA,
- * calling supplied callbacks. Callbacks are called in-order (first
- * PGD, first PUD, first PMD, first PTE, second PTE... second PMD,
- * etc.). If lower-level callbacks are omitted, walking depth is reduced.
- *
- * Each callback receives an entry pointer and the start and end of the
- * associated range, and a copy of the original mm_walk for access to
- * the ->private or ->mm fields.
- *
- * No locks are taken, but the bottom level iterator will map PTE
- * directories from highmem if necessary.
- *
- * If any callback returns a non-zero value, the walk is aborted and
- * the return value is propagated back to the caller. Otherwise 0 is returned.
- */
- int walk_page_range(unsigned long addr, unsigned long end,
- struct mm_walk *walk)
- {
- pgd_t *pgd;
- unsigned long next;
- int err = 0;
- if (addr >= end)
- return err;
- if (!walk->mm)
- return -EINVAL;
- pgd = pgd_offset(walk->mm, addr);
- do {
- next = pgd_addr_end(addr, end);
- if (pgd_none_or_clear_bad(pgd)) {
- if (walk->pte_hole)
- err = walk->pte_hole(addr, next, walk);
- if (err)
- break;
- continue;
- }
- if (walk->pgd_entry)
- err = walk->pgd_entry(pgd, addr, next, walk);
- if (!err &&
- (walk->pud_entry || walk->pmd_entry || walk->pte_entry))
- err = walk_pud_range(pgd, addr, next, walk);
- if (err)
- break;
- } while (pgd++, addr = next, addr != end);
- return err;
- }
|