Loading Documentation/filesystems/proc.txt +20 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,7 @@ Table 1-1: Process specific entries in /proc maps Memory maps to executables and library files (2.4) mem Memory held by this process root Link to the root directory of this process reclaim Reclaim pages in this process stat Process status statm Process memory status information status Process status in human readable form Loading Loading @@ -538,6 +539,25 @@ current value: Any other value written to /proc/PID/clear_refs will have no effect. The file /proc/PID/reclaim is used to reclaim pages in this process. To reclaim file-backed pages, > echo file > /proc/PID/reclaim To reclaim anonymous pages, > echo anon > /proc/PID/reclaim To reclaim all pages, > echo all > /proc/PID/reclaim Also, you can specify address range of process so part of address space will be reclaimed. The format is following as > echo addr size-byte > /proc/PID/reclaim NOTE: addr should be page-aligned. Below is example which try to reclaim 2M from 0x100000. > echo 0x100000 2M > /proc/PID/reclaim The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags using /proc/kpageflags and number of times a page is mapped using /proc/kpagecount. For detailed explanation, see Documentation/vm/pagemap.txt. Loading fs/proc/base.c +3 −0 Original line number Diff line number Diff line Loading @@ -3162,6 +3162,9 @@ static const struct pid_entry tgid_base_stuff[] = { REG("mounts", S_IRUGO, proc_mounts_operations), REG("mountinfo", S_IRUGO, proc_mountinfo_operations), REG("mountstats", S_IRUSR, proc_mountstats_operations), #ifdef CONFIG_PROCESS_RECLAIM REG("reclaim", 0200, proc_reclaim_operations), #endif #ifdef CONFIG_PROC_PAGE_MONITOR REG("clear_refs", S_IWUSR, proc_clear_refs_operations), REG("smaps", S_IRUGO, proc_pid_smaps_operations), Loading fs/proc/internal.h +1 −0 Original line number Diff line number Diff line Loading @@ -199,6 +199,7 @@ struct pde_opener { extern const struct inode_operations proc_link_inode_operations; extern const struct inode_operations proc_pid_link_inode_operations; extern const struct file_operations proc_reclaim_operations; extern void proc_init_inodecache(void); void set_proc_pid_nlink(void); Loading fs/proc/task_mmu.c +177 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <linux/page_idle.h> #include <linux/shmem_fs.h> #include <linux/uaccess.h> #include <linux/mm_inline.h> #include <linux/ctype.h> #include <asm/elf.h> #include <asm/tlb.h> Loading Loading @@ -1642,6 +1644,181 @@ const struct file_operations proc_pagemap_operations = { }; #endif /* CONFIG_PROC_PAGE_MONITOR */ #ifdef CONFIG_PROCESS_RECLAIM static int reclaim_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk) { struct vm_area_struct *vma = walk->private; pte_t *pte, ptent; spinlock_t *ptl; struct page *page; LIST_HEAD(page_list); int isolated; split_huge_pmd(vma, addr, pmd); if (pmd_trans_unstable(pmd)) return 0; cont: isolated = 0; pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); for (; addr != end; pte++, addr += PAGE_SIZE) { ptent = *pte; if (!pte_present(ptent)) continue; page = vm_normal_page(vma, addr, ptent); if (!page) continue; if (isolate_lru_page(page)) continue; list_add(&page->lru, &page_list); inc_node_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page)); isolated++; if (isolated >= SWAP_CLUSTER_MAX) break; } pte_unmap_unlock(pte - 1, ptl); reclaim_pages_from_list(&page_list, vma); if (addr != end) goto cont; cond_resched(); return 0; } enum reclaim_type { RECLAIM_FILE, RECLAIM_ANON, RECLAIM_ALL, RECLAIM_RANGE, }; static ssize_t reclaim_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct task_struct *task; char buffer[200]; struct mm_struct *mm; struct vm_area_struct *vma; enum reclaim_type type; char *type_buf; struct mm_walk reclaim_walk = {}; unsigned long start = 0; unsigned long end = 0; memset(buffer, 0, sizeof(buffer)); if (count > sizeof(buffer) - 1) count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAULT; type_buf = strstrip(buffer); if (!strcmp(type_buf, "file")) type = RECLAIM_FILE; else if (!strcmp(type_buf, "anon")) type = RECLAIM_ANON; else if (!strcmp(type_buf, "all")) type = RECLAIM_ALL; else if (isdigit(*type_buf)) type = RECLAIM_RANGE; else goto out_err; if (type == RECLAIM_RANGE) { char *token; unsigned long long len, len_in, tmp; token = strsep(&type_buf, " "); if (!token) goto out_err; tmp = memparse(token, &token); if (tmp & ~PAGE_MASK || tmp > ULONG_MAX) goto out_err; start = tmp; token = strsep(&type_buf, " "); if (!token) goto out_err; len_in = memparse(token, &token); len = (len_in + ~PAGE_MASK) & PAGE_MASK; if (len > ULONG_MAX) goto out_err; /* * Check to see whether len was rounded up from small -ve * to zero. */ if (len_in && !len) goto out_err; end = start + len; if (end < start) goto out_err; } task = get_proc_task(file->f_path.dentry->d_inode); if (!task) return -ESRCH; mm = get_task_mm(task); if (!mm) goto out; reclaim_walk.mm = mm; reclaim_walk.pmd_entry = reclaim_pte_range; down_read(&mm->mmap_sem); if (type == RECLAIM_RANGE) { vma = find_vma(mm, start); while (vma) { if (vma->vm_start > end) break; if (is_vm_hugetlb_page(vma)) continue; reclaim_walk.private = vma; walk_page_range(max(vma->vm_start, start), min(vma->vm_end, end), &reclaim_walk); vma = vma->vm_next; } } else { for (vma = mm->mmap; vma; vma = vma->vm_next) { if (is_vm_hugetlb_page(vma)) continue; if (type == RECLAIM_ANON && vma->vm_file) continue; if (type == RECLAIM_FILE && !vma->vm_file) continue; reclaim_walk.private = vma; walk_page_range(vma->vm_start, vma->vm_end, &reclaim_walk); } } flush_tlb_mm(mm); up_read(&mm->mmap_sem); mmput(mm); out: put_task_struct(task); return count; out_err: return -EINVAL; } const struct file_operations proc_reclaim_operations = { .write = reclaim_write, .llseek = noop_llseek, }; #endif #ifdef CONFIG_NUMA struct numa_maps { Loading include/linux/rmap.h +9 −2 Original line number Diff line number Diff line Loading @@ -12,6 +12,11 @@ #include <linux/memcontrol.h> #include <linux/highmem.h> extern int isolate_lru_page(struct page *page); extern void putback_lru_page(struct page *page); extern unsigned long reclaim_pages_from_list(struct list_head *page_list, struct vm_area_struct *vma); /* * The anon_vma heads a list of private "related" vmas, to scan if * an anonymous page pointing to this anon_vma needs to be unmapped: Loading Loading @@ -203,7 +208,8 @@ static inline void page_dup_rmap(struct page *page, bool compound) int page_referenced(struct page *, int is_locked, struct mem_cgroup *memcg, unsigned long *vm_flags); bool try_to_unmap(struct page *, enum ttu_flags flags); bool try_to_unmap(struct page *page, enum ttu_flags flags, struct vm_area_struct *vma); /* Avoid racy checks */ #define PVMW_SYNC (1 << 0) Loading Loading @@ -269,6 +275,7 @@ int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma); */ struct rmap_walk_control { void *arg; struct vm_area_struct *target_vma; /* * Return false if page table scanning in rmap_walk should be stopped. * Otherwise, return true. Loading Loading @@ -297,7 +304,7 @@ static inline int page_referenced(struct page *page, int is_locked, return 0; } #define try_to_unmap(page, refs) false #define try_to_unmap(page, refs, vma) false static inline int page_mkclean(struct page *page) { Loading Loading
Documentation/filesystems/proc.txt +20 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,7 @@ Table 1-1: Process specific entries in /proc maps Memory maps to executables and library files (2.4) mem Memory held by this process root Link to the root directory of this process reclaim Reclaim pages in this process stat Process status statm Process memory status information status Process status in human readable form Loading Loading @@ -538,6 +539,25 @@ current value: Any other value written to /proc/PID/clear_refs will have no effect. The file /proc/PID/reclaim is used to reclaim pages in this process. To reclaim file-backed pages, > echo file > /proc/PID/reclaim To reclaim anonymous pages, > echo anon > /proc/PID/reclaim To reclaim all pages, > echo all > /proc/PID/reclaim Also, you can specify address range of process so part of address space will be reclaimed. The format is following as > echo addr size-byte > /proc/PID/reclaim NOTE: addr should be page-aligned. Below is example which try to reclaim 2M from 0x100000. > echo 0x100000 2M > /proc/PID/reclaim The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags using /proc/kpageflags and number of times a page is mapped using /proc/kpagecount. For detailed explanation, see Documentation/vm/pagemap.txt. Loading
fs/proc/base.c +3 −0 Original line number Diff line number Diff line Loading @@ -3162,6 +3162,9 @@ static const struct pid_entry tgid_base_stuff[] = { REG("mounts", S_IRUGO, proc_mounts_operations), REG("mountinfo", S_IRUGO, proc_mountinfo_operations), REG("mountstats", S_IRUSR, proc_mountstats_operations), #ifdef CONFIG_PROCESS_RECLAIM REG("reclaim", 0200, proc_reclaim_operations), #endif #ifdef CONFIG_PROC_PAGE_MONITOR REG("clear_refs", S_IWUSR, proc_clear_refs_operations), REG("smaps", S_IRUGO, proc_pid_smaps_operations), Loading
fs/proc/internal.h +1 −0 Original line number Diff line number Diff line Loading @@ -199,6 +199,7 @@ struct pde_opener { extern const struct inode_operations proc_link_inode_operations; extern const struct inode_operations proc_pid_link_inode_operations; extern const struct file_operations proc_reclaim_operations; extern void proc_init_inodecache(void); void set_proc_pid_nlink(void); Loading
fs/proc/task_mmu.c +177 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <linux/page_idle.h> #include <linux/shmem_fs.h> #include <linux/uaccess.h> #include <linux/mm_inline.h> #include <linux/ctype.h> #include <asm/elf.h> #include <asm/tlb.h> Loading Loading @@ -1642,6 +1644,181 @@ const struct file_operations proc_pagemap_operations = { }; #endif /* CONFIG_PROC_PAGE_MONITOR */ #ifdef CONFIG_PROCESS_RECLAIM static int reclaim_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk) { struct vm_area_struct *vma = walk->private; pte_t *pte, ptent; spinlock_t *ptl; struct page *page; LIST_HEAD(page_list); int isolated; split_huge_pmd(vma, addr, pmd); if (pmd_trans_unstable(pmd)) return 0; cont: isolated = 0; pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); for (; addr != end; pte++, addr += PAGE_SIZE) { ptent = *pte; if (!pte_present(ptent)) continue; page = vm_normal_page(vma, addr, ptent); if (!page) continue; if (isolate_lru_page(page)) continue; list_add(&page->lru, &page_list); inc_node_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page)); isolated++; if (isolated >= SWAP_CLUSTER_MAX) break; } pte_unmap_unlock(pte - 1, ptl); reclaim_pages_from_list(&page_list, vma); if (addr != end) goto cont; cond_resched(); return 0; } enum reclaim_type { RECLAIM_FILE, RECLAIM_ANON, RECLAIM_ALL, RECLAIM_RANGE, }; static ssize_t reclaim_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct task_struct *task; char buffer[200]; struct mm_struct *mm; struct vm_area_struct *vma; enum reclaim_type type; char *type_buf; struct mm_walk reclaim_walk = {}; unsigned long start = 0; unsigned long end = 0; memset(buffer, 0, sizeof(buffer)); if (count > sizeof(buffer) - 1) count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAULT; type_buf = strstrip(buffer); if (!strcmp(type_buf, "file")) type = RECLAIM_FILE; else if (!strcmp(type_buf, "anon")) type = RECLAIM_ANON; else if (!strcmp(type_buf, "all")) type = RECLAIM_ALL; else if (isdigit(*type_buf)) type = RECLAIM_RANGE; else goto out_err; if (type == RECLAIM_RANGE) { char *token; unsigned long long len, len_in, tmp; token = strsep(&type_buf, " "); if (!token) goto out_err; tmp = memparse(token, &token); if (tmp & ~PAGE_MASK || tmp > ULONG_MAX) goto out_err; start = tmp; token = strsep(&type_buf, " "); if (!token) goto out_err; len_in = memparse(token, &token); len = (len_in + ~PAGE_MASK) & PAGE_MASK; if (len > ULONG_MAX) goto out_err; /* * Check to see whether len was rounded up from small -ve * to zero. */ if (len_in && !len) goto out_err; end = start + len; if (end < start) goto out_err; } task = get_proc_task(file->f_path.dentry->d_inode); if (!task) return -ESRCH; mm = get_task_mm(task); if (!mm) goto out; reclaim_walk.mm = mm; reclaim_walk.pmd_entry = reclaim_pte_range; down_read(&mm->mmap_sem); if (type == RECLAIM_RANGE) { vma = find_vma(mm, start); while (vma) { if (vma->vm_start > end) break; if (is_vm_hugetlb_page(vma)) continue; reclaim_walk.private = vma; walk_page_range(max(vma->vm_start, start), min(vma->vm_end, end), &reclaim_walk); vma = vma->vm_next; } } else { for (vma = mm->mmap; vma; vma = vma->vm_next) { if (is_vm_hugetlb_page(vma)) continue; if (type == RECLAIM_ANON && vma->vm_file) continue; if (type == RECLAIM_FILE && !vma->vm_file) continue; reclaim_walk.private = vma; walk_page_range(vma->vm_start, vma->vm_end, &reclaim_walk); } } flush_tlb_mm(mm); up_read(&mm->mmap_sem); mmput(mm); out: put_task_struct(task); return count; out_err: return -EINVAL; } const struct file_operations proc_reclaim_operations = { .write = reclaim_write, .llseek = noop_llseek, }; #endif #ifdef CONFIG_NUMA struct numa_maps { Loading
include/linux/rmap.h +9 −2 Original line number Diff line number Diff line Loading @@ -12,6 +12,11 @@ #include <linux/memcontrol.h> #include <linux/highmem.h> extern int isolate_lru_page(struct page *page); extern void putback_lru_page(struct page *page); extern unsigned long reclaim_pages_from_list(struct list_head *page_list, struct vm_area_struct *vma); /* * The anon_vma heads a list of private "related" vmas, to scan if * an anonymous page pointing to this anon_vma needs to be unmapped: Loading Loading @@ -203,7 +208,8 @@ static inline void page_dup_rmap(struct page *page, bool compound) int page_referenced(struct page *, int is_locked, struct mem_cgroup *memcg, unsigned long *vm_flags); bool try_to_unmap(struct page *, enum ttu_flags flags); bool try_to_unmap(struct page *page, enum ttu_flags flags, struct vm_area_struct *vma); /* Avoid racy checks */ #define PVMW_SYNC (1 << 0) Loading Loading @@ -269,6 +275,7 @@ int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma); */ struct rmap_walk_control { void *arg; struct vm_area_struct *target_vma; /* * Return false if page table scanning in rmap_walk should be stopped. * Otherwise, return true. Loading Loading @@ -297,7 +304,7 @@ static inline int page_referenced(struct page *page, int is_locked, return 0; } #define try_to_unmap(page, refs) false #define try_to_unmap(page, refs, vma) false static inline int page_mkclean(struct page *page) { Loading