Extracts the page table entry walker from the smaps-specific code in fs/proc/task_mmu.c. This will be used later for clearing the reference bits on pages to measure the number of pages accessed over a time period through /proc/pid/smaps.
The new struct pte_walker includes the struct vm_area_struct of the memory to walk over. Iteration begins at the start address and completes at the the end address. A pointer to another data structure may be stored in the private field such as the struct mem_size_stats, which acts as the smaps accumulator. For each page table entry in the VMA, the func function is called with the corresponding struct pte_walker, the pte_t, and its address. Since the PTE walker is now extracted from the smaps code, smaps_pte_func() is invoked for each PTE in the VMA. Its behavior is identical to the existing implementation, except it is slightly slower because each PTE now invokes a function call. Cc: Hugh Dickins <[EMAIL PROTECTED]> Cc: Paul Mundt <[EMAIL PROTECTED]> Cc: Christoph Lameter <[EMAIL PROTECTED]> Signed-off-by: David Rientjes <[EMAIL PROTECTED]> --- fs/proc/task_mmu.c | 126 ++++++++++++++++++++++++++++++++++------------------ 1 files changed, 82 insertions(+), 44 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 55ade0d..e87824b 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -122,6 +122,15 @@ struct mem_size_stats unsigned long private_dirty; }; +struct pte_walker { + struct vm_area_struct *vma; /* VMA */ + unsigned long start; /* start address */ + unsigned long end; /* end address */ + void *private; /* private data */ + /* function to invoke for each pte in the above range */ + void (*func)(struct pte_walker*, pte_t*, unsigned long); +}; + static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats *mss) { struct proc_maps_private *priv = m->private; @@ -204,98 +213,127 @@ static int show_map(struct seq_file *m, void *v) return show_map_internal(m, v, NULL); } -static void smaps_pte_range(struct vm_area_struct *vma, pmd_t *pmd, - unsigned long addr, unsigned long end, - struct mem_size_stats *mss) +/* + * Walks each PTE in the struct pte_walker address range and calls + * walker->func() for each entry. + */ +static void walk_ptes(struct pte_walker *walker, pmd_t *pmd) { - pte_t *pte, ptent; + struct vm_area_struct *vma = walker->vma; + unsigned long addr = walker->start; spinlock_t *ptl; - struct page *page; + pte_t *pte; pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); do { - ptent = *pte; - if (!pte_present(ptent)) - continue; - - mss->resident += PAGE_SIZE; - - page = vm_normal_page(vma, addr, ptent); - if (!page) - continue; - - if (page_mapcount(page) >= 2) { - if (pte_dirty(ptent)) - mss->shared_dirty += PAGE_SIZE; - else - mss->shared_clean += PAGE_SIZE; - } else { - if (pte_dirty(ptent)) - mss->private_dirty += PAGE_SIZE; - else - mss->private_clean += PAGE_SIZE; - } - } while (pte++, addr += PAGE_SIZE, addr != end); + walker->func(walker, pte, addr); + } while (pte++, addr += PAGE_SIZE, addr != walker->end); pte_unmap_unlock(pte - 1, ptl); cond_resched(); } -static inline void smaps_pmd_range(struct vm_area_struct *vma, pud_t *pud, - unsigned long addr, unsigned long end, - struct mem_size_stats *mss) +static inline void walk_pmds(struct pte_walker *walker, pud_t *pud) { - pmd_t *pmd; + unsigned long addr = walker->start; + unsigned long end = walker->end; unsigned long next; + pmd_t *pmd; pmd = pmd_offset(pud, addr); do { next = pmd_addr_end(addr, end); if (pmd_none_or_clear_bad(pmd)) continue; - smaps_pte_range(vma, pmd, addr, next, mss); + walk_ptes(walker, pmd); } while (pmd++, addr = next, addr != end); } -static inline void smaps_pud_range(struct vm_area_struct *vma, pgd_t *pgd, - unsigned long addr, unsigned long end, - struct mem_size_stats *mss) +static inline void walk_puds(struct pte_walker *walker, pgd_t *pgd) { - pud_t *pud; + unsigned long addr = walker->start; + unsigned long end = walker->end; unsigned long next; + pud_t *pud; pud = pud_offset(pgd, addr); do { next = pud_addr_end(addr, end); if (pud_none_or_clear_bad(pud)) continue; - smaps_pmd_range(vma, pud, addr, next, mss); + walk_pmds(walker, pud); } while (pud++, addr = next, addr != end); } -static inline void smaps_pgd_range(struct vm_area_struct *vma, - unsigned long addr, unsigned long end, - struct mem_size_stats *mss) +static inline void walk_pgds(struct pte_walker *walker) { - pgd_t *pgd; + unsigned long addr = walker->start; + unsigned long end = walker->end; unsigned long next; + pgd_t *pgd; - pgd = pgd_offset(vma->vm_mm, addr); + pgd = pgd_offset(walker->vma->vm_mm, addr); do { next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(pgd)) continue; - smaps_pud_range(vma, pgd, addr, next, mss); + walk_puds(walker, pgd); } while (pgd++, addr = next, addr != end); } +/* + * Called for each PTE in the struct pte_walker address range. For all normal, + * present pages, we accumulate the size (in pages) grouped by shared and + * private attributes and dirty bits. + */ +static void smaps_pte_func(struct pte_walker *walker, pte_t *pte, + unsigned long addr) +{ + struct mem_size_stats *mss = walker->private; + struct page *page; + pte_t ptent; + + ptent = *pte; + if (!pte_present(ptent)) + return; + + mss->resident += PAGE_SIZE; + + page = vm_normal_page(walker->vma, addr, ptent); + if (!page) + return; + + if (page_mapcount(page) >= 2) { + if (pte_dirty(ptent)) + mss->shared_dirty += PAGE_SIZE; + else + mss->shared_clean += PAGE_SIZE; + } else { + if (pte_dirty(ptent)) + mss->private_dirty += PAGE_SIZE; + else + mss->private_clean += PAGE_SIZE; + } +} + +/* + * Displays the smap for the process. smaps_pte_func() is called for each PTE + * in the range from vma->vm_start to vma->vm_end. + */ static int show_smap(struct seq_file *m, void *v) { struct vm_area_struct *vma = v; struct mem_size_stats mss; + struct pte_walker walker = { + .vma = vma, + .start = vma->vm_start, + .end = vma->vm_end, + .private = &mss, + .func = smaps_pte_func, + }; memset(&mss, 0, sizeof mss); if (vma->vm_mm && !is_vm_hugetlb_page(vma)) - smaps_pgd_range(vma, vma->vm_start, vma->vm_end, &mss); + walk_pgds(&walker); return show_map_internal(m, v, &mss); } - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/