[PATCH v3 5/6] mm: Support address range reclaim
This patch adds address range reclaim of a process. The requirement is following as, Like webkit1, it uses a address space for handling multi tabs. IOW, it uses *one* process model so all tabs shares address space of the process. In such scenario, per-process reclaim is rather coarse-grained so this patch supports more fine-grained reclaim for being able to reclaim target address range of the process. For reclaim target range, you should use following format. echo [addr] [size-byte] > /proc/pid/reclaim The addr should be page-aligned. So now reclaim konb's interface is following as. echo file > /proc/pid/reclaim reclaim file-backed pages only echo anon > /proc/pid/reclaim reclaim anonymous pages only echo all > /proc/pid/reclaim reclaim all pages echo 0x10 8K > /proc/pid/reclaim reclaim pages in (0x10 - 0x102000) Signed-off-by: Minchan Kim --- fs/proc/task_mmu.c | 85 -- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 79b674e..9835028 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -1239,11 +1240,14 @@ static ssize_t reclaim_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct task_struct *task; - char buffer[PROC_NUMBUF]; + 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) @@ -1259,42 +1263,93 @@ static ssize_t reclaim_write(struct file *file, const char __user *buf, type = RECLAIM_ANON; else if (!strcmp(type_buf, "all")) type = RECLAIM_ALL; + else if (isdigit(*type_buf)) + type = RECLAIM_RANGE; else - return -EINVAL; + goto out_err; + + if (type == RECLAIM_RANGE) { + char *token; + unsigned long long len, len_in, tmp; + token = strsep(_buf, " "); + if (!token) + goto out_err; + tmp = memparse(token, ); + if (tmp & ~PAGE_MASK || tmp > ULONG_MAX) + goto out_err; + start = tmp; + + token = strsep(_buf, " "); + if (!token) + goto out_err; + len_in = memparse(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) { - struct mm_walk reclaim_walk = { - .pmd_entry = reclaim_pte_range, - .mm = mm, - }; + if (!mm) + goto out; - down_read(>mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) { - reclaim_walk.private = vma; + reclaim_walk.mm = mm; + reclaim_walk.pmd_entry = reclaim_pte_range; + down_read(>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), + _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, - _walk);
[PATCH v3 5/6] mm: Support address range reclaim
This patch adds address range reclaim of a process. The requirement is following as, Like webkit1, it uses a address space for handling multi tabs. IOW, it uses *one* process model so all tabs shares address space of the process. In such scenario, per-process reclaim is rather coarse-grained so this patch supports more fine-grained reclaim for being able to reclaim target address range of the process. For reclaim target range, you should use following format. echo [addr] [size-byte] /proc/pid/reclaim The addr should be page-aligned. So now reclaim konb's interface is following as. echo file /proc/pid/reclaim reclaim file-backed pages only echo anon /proc/pid/reclaim reclaim anonymous pages only echo all /proc/pid/reclaim reclaim all pages echo 0x10 8K /proc/pid/reclaim reclaim pages in (0x10 - 0x102000) Signed-off-by: Minchan Kim minc...@kernel.org --- fs/proc/task_mmu.c | 85 -- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 79b674e..9835028 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -12,6 +12,7 @@ #include linux/swap.h #include linux/swapops.h #include linux/mm_inline.h +#include linux/ctype.h #include asm/elf.h #include asm/uaccess.h @@ -1239,11 +1240,14 @@ static ssize_t reclaim_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct task_struct *task; - char buffer[PROC_NUMBUF]; + 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) @@ -1259,42 +1263,93 @@ static ssize_t reclaim_write(struct file *file, const char __user *buf, type = RECLAIM_ANON; else if (!strcmp(type_buf, all)) type = RECLAIM_ALL; + else if (isdigit(*type_buf)) + type = RECLAIM_RANGE; else - return -EINVAL; + 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) { - struct mm_walk reclaim_walk = { - .pmd_entry = reclaim_pte_range, - .mm = mm, - }; + if (!mm) + goto out; - down_read(mm-mmap_sem); - for (vma = mm-mmap; vma; vma = vma-vm_next) { - reclaim_walk.private = vma; + 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;