diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 0ebced4b78c651c75f6cd0923b274372846806a8..55de0309b4b0ad53a8df4cd266b240eb589e86ae 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -16,6 +16,9 @@ #ifdef CONFIG_CMA #include #endif +#ifdef CONFIG_MEM_PURGEABLE +#include +#endif #include #include "internal.h" @@ -40,6 +43,11 @@ static int meminfo_proc_show(struct seq_file *m, void *v) int lru; unsigned long nr_purgeable_active = 0; unsigned long nr_purgeable_inactive = 0; +#ifdef CONFIG_MEM_PURGEABLE + unsigned long nr_purgeable_pined = 0; + + purg_pages_info(NULL, &nr_purgeable_pined); +#endif si_meminfo(&i); si_swapinfo(&i); @@ -81,6 +89,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) #ifdef CONFIG_MEM_PURGEABLE show_val_kb(m, "Active(purgeable): ", nr_purgeable_active); show_val_kb(m, "Inactive(purgeable): ", nr_purgeable_inactive); + show_val_kb(m, "Pined(purgeable): ", nr_purgeable_pined); #endif show_val_kb(m, "Unevictable: ", pages[LRU_UNEVICTABLE]); show_val_kb(m, "Mlocked: ", global_zone_page_state(NR_MLOCK)); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 46407d4e004daad3cd42193a35b03cb6c9920b0b..14617d886df8d28cef8be9f61ad9222942596b71 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -20,6 +20,9 @@ #include #include #include +#ifdef CONFIG_MEM_PURGEABLE +#include +#endif #include #include @@ -32,6 +35,11 @@ void task_mem(struct seq_file *m, struct mm_struct *mm) { unsigned long text, lib, swap, anon, file, shmem; unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss; +#ifdef CONFIG_MEM_PURGEABLE + unsigned long nr_purg_sum = 0, nr_purg_pin = 0; + + mm_purg_pages_info(mm, &nr_purg_sum, &nr_purg_pin); +#endif anon = get_mm_counter(mm, MM_ANONPAGES); file = get_mm_counter(mm, MM_FILEPAGES); @@ -75,6 +83,10 @@ void task_mem(struct seq_file *m, struct mm_struct *mm) seq_put_decimal_ull_width(m, " kB\nVmPTE:\t", mm_pgtables_bytes(mm) >> 10, 8); SEQ_PUT_DEC(" kB\nVmSwap:\t", swap); +#ifdef CONFIG_MEM_PURGEABLE + SEQ_PUT_DEC(" kB\nPurgSum:\t", nr_purg_sum); + SEQ_PUT_DEC(" kB\nPurgPin:\t", nr_purg_pin); +#endif seq_puts(m, " kB\n"); hugetlb_report_usage(m, mm); } diff --git a/include/linux/mm_purgeable.h b/include/linux/mm_purgeable.h index 11994d5cf59c06fe1cd8513f97853a33eb65866e..04d027cb1548d138e19ced1dcb1445a3b3af47b0 100644 --- a/include/linux/mm_purgeable.h +++ b/include/linux/mm_purgeable.h @@ -16,6 +16,27 @@ vm_fault_t do_uxpte_page_fault(struct vm_fault *vmf, pte_t *entry); bool uxpte_set_present(struct vm_area_struct *vma, unsigned long addr); void uxpte_clear_present(struct vm_area_struct *vma, unsigned long addr); +/* + * mm_purg_pages_info: get purgeable pages count of @mm + * @mm: [in] pointer to mm + * @total_purg_pages: [out] total purgeable pages of @mm + * @pined_purg_pages: [out] pined purgeable pages of @mm + * If @mm is NULL, return with doing nothing. + * If @total_purg_pages and @pined_purg_pages are both NULL, return with doing nothing. + * If one of @total_purg_pages and @pined_purg_pages is NULL, other one will be counted. + */ +void mm_purg_pages_info(struct mm_struct *mm, unsigned long *total_purg_pages, + unsigned long *pined_purg_pages); + +/* + * purg_pages_info: get global purgeable pages in system + * @total_purg_pages: [out] total purgeable pages in system + * @pined_purg_pages: [out] pined purgeable pages in system + * If @total_purg_pages and @pined_purg_pages are both NULL, return with doing nothing. + * If one of @total_purg_pages and @pined_purg_pages is NULL, other one will be counted. + */ +void purg_pages_info(unsigned long *total_purg_pages, unsigned long *pined_purg_pages); + #else /* CONFIG_MEM_PURGEABLE */ static inline void mm_init_uxpgd(struct mm_struct *mm) {} @@ -45,6 +66,12 @@ static inline bool uxpte_set_present(struct vm_area_struct *vma, static inline void uxpte_clear_present(struct vm_area_struct *vma, unsigned long addr) {} + +static inline void mm_purg_pages_info(struct mm_struct *mm, + unsigned long *total_purg_pages, unsigned long *pined_purg_pages) {} + +static inline void purg_pages_info(unsigned long *total_purg_pages, + unsigned long *pined_purg_pages) {} #endif /* CONFIG_MEM_PURGEABLE */ #endif /* __MM_PURGEABLE_MEM_H */ diff --git a/mm/purgeable.c b/mm/purgeable.c index ca46704efccbaea927f3edff621707a96e1bac4a..a4c71cf72c69ad19e4260ca969d888d5d153470b 100644 --- a/mm/purgeable.c +++ b/mm/purgeable.c @@ -9,6 +9,7 @@ #include #include #include +#include /* find_lock_task_mm */ #include @@ -23,7 +24,7 @@ struct uxpte_t { #define UXPTE_PER_PAGE (1 << UXPTE_PER_PAGE_SHIFT) #define UXPTE_PRESENT_BIT 1 -#define UXPTE_PRESENT_MASK (1 << UXPTE_PRESENT_BIT) +#define UXPTE_PRESENT_MASK ((1 << UXPTE_PRESENT_BIT) - 1) #define UXPTE_REFCNT_ONE (1 << UXPTE_PRESENT_BIT) #define UXPTE_UNDER_RECLAIM (-UXPTE_REFCNT_ONE) @@ -31,6 +32,8 @@ struct uxpte_t { #define uxpte_pn(vaddr) (vpn(vaddr) >> UXPTE_PER_PAGE_SHIFT) #define uxpte_off(vaddr) (vpn(vaddr) & (UXPTE_PER_PAGE - 1)) #define uxpn2addr(uxpn) ((uxpn) << (UXPTE_PER_PAGE_SHIFT + PAGE_SHIFT)) +#define uxpte_refcnt(uxpte) ((uxpte) >> UXPTE_PRESENT_BIT) +#define uxpte_present(uxpte) ((uxpte) & UXPTE_PRESENT_MASK) static inline long uxpte_read(struct uxpte_t *uxpte) { @@ -247,3 +250,98 @@ vm_fault_t do_uxpte_page_fault(struct vm_fault *vmf, pte_t *entry) *entry = pte_mkwrite(pte_mkdirty(*entry)); return 0; } + +static void __mm_purg_pages_info(struct mm_struct *mm, unsigned long *total_purg_pages, + unsigned long *pined_purg_pages) +{ + struct page *page = NULL; + void **slot = NULL; + struct radix_tree_iter iter; + struct uxpte_t *uxpte = NULL; + long pte_entry = 0; + int index = 0; + unsigned long nr_total = 0, nr_pined = 0; + + spin_lock(&mm->uxpgd_lock); + if (!mm->uxpgd) + goto out; + radix_tree_for_each_slot(slot, mm->uxpgd, &iter, 0) { + page = radix_tree_deref_slot(slot); + if (unlikely(!page)) + continue; + uxpte = page_to_virt(page); + for (index = 0; index < UXPTE_PER_PAGE; index++) { + pte_entry = uxpte_read(&(uxpte[index])); + if (uxpte_present(pte_entry) == 0) /* not present */ + continue; + nr_total++; + if (uxpte_refcnt(pte_entry) > 0) /* pined by user */ + nr_pined++; + } + } +out: + spin_unlock(&mm->uxpgd_lock); + + if (total_purg_pages) + *total_purg_pages = nr_total; + + if (pined_purg_pages) + *pined_purg_pages = nr_pined; +} + +void mm_purg_pages_info(struct mm_struct *mm, unsigned long *total_purg_pages, + unsigned long *pined_purg_pages) +{ + if (unlikely(!mm)) + return; + + if (!total_purg_pages && !pined_purg_pages) + return; + + __mm_purg_pages_info(mm, total_purg_pages, pined_purg_pages); +} + +void purg_pages_info(unsigned long *total_purg_pages, unsigned long *pined_purg_pages) +{ + struct task_struct *p = NULL; + struct task_struct *tsk = NULL; + unsigned long mm_nr_purge = 0, mm_nr_pined = 0; + unsigned long nr_total = 0, nr_pined = 0; + + if (!total_purg_pages && !pined_purg_pages) + return; + + if (total_purg_pages) + *total_purg_pages = 0; + + if (pined_purg_pages) + *pined_purg_pages = 0; + + rcu_read_lock(); + for_each_process(p) { + tsk = find_lock_task_mm(p); + if (!tsk) { + /* + * It is a kthread or all of p's threads have already + * detached their mm's. + */ + continue; + } + __mm_purg_pages_info(tsk->mm, &mm_nr_purge, &mm_nr_pined); + nr_total += mm_nr_purge; + nr_pined += mm_nr_pined; + task_unlock(tsk); + + if (mm_nr_purge > 0) { + pr_info("purgemm: tsk: %s %lu pined in %lu pages\n", tsk->comm ?: "NULL", + mm_nr_pined, mm_nr_purge); + } + } + rcu_read_unlock(); + if (total_purg_pages) + *total_purg_pages = nr_total; + + if (pined_purg_pages) + *pined_purg_pages = nr_pined; + pr_info("purgemm: Sum: %lu pined in %lu pages\n", nr_pined, nr_total); +}