mirror of
https://github.com/joel16/android_kernel_sony_msm8994.git
synced 2024-11-27 14:11:04 +00:00
Merge "mm: Per process reclaim"
This commit is contained in:
commit
5eb8864819
@ -2860,6 +2860,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", S_IWUSR, 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),
|
||||
|
@ -204,6 +204,7 @@ struct pde_opener {
|
||||
};
|
||||
|
||||
extern const struct inode_operations proc_pid_link_inode_operations;
|
||||
extern const struct file_operations proc_reclaim_operations;
|
||||
|
||||
extern void proc_init_inodecache(void);
|
||||
extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/rmap.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/swapops.h>
|
||||
#include <linux/mm_inline.h>
|
||||
|
||||
#include <asm/elf.h>
|
||||
#include <asm/uaccess.h>
|
||||
@ -1170,6 +1171,126 @@ 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_page_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_zone_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);
|
||||
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[PROC_NUMBUF];
|
||||
struct mm_struct *mm;
|
||||
struct vm_area_struct *vma;
|
||||
enum reclaim_type type;
|
||||
char *type_buf;
|
||||
|
||||
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
|
||||
return -EINVAL;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
||||
reclaim_walk.private = vma;
|
||||
|
||||
if (is_vm_hugetlb_page(vma))
|
||||
continue;
|
||||
|
||||
if (type == RECLAIM_ANON && vma->vm_file)
|
||||
continue;
|
||||
if (type == RECLAIM_FILE && !vma->vm_file)
|
||||
continue;
|
||||
|
||||
walk_page_range(vma->vm_start, vma->vm_end,
|
||||
&reclaim_walk);
|
||||
}
|
||||
flush_tlb_mm(mm);
|
||||
up_read(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
}
|
||||
put_task_struct(task);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
const struct file_operations proc_reclaim_operations = {
|
||||
.write = reclaim_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
|
||||
struct numa_maps {
|
||||
|
@ -10,6 +10,10 @@
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/memcontrol.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);
|
||||
|
||||
/*
|
||||
* 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:
|
||||
|
13
mm/Kconfig
13
mm/Kconfig
@ -553,3 +553,16 @@ config KSWAPD_CPU_AFFINITY_MASK
|
||||
the resultant bitmask.
|
||||
For example to limit kswapd to the first 4 cores use the following:
|
||||
CONFIG_KSWAPD_CPU_AFFINITY_MASK="f"
|
||||
|
||||
config PROCESS_RECLAIM
|
||||
bool "Enable process reclaim"
|
||||
depends on PROC_FS
|
||||
default n
|
||||
help
|
||||
It allows to reclaim pages of the process by /proc/pid/reclaim.
|
||||
|
||||
(echo file > /proc/PID/reclaim) reclaims file-backed pages only.
|
||||
(echo anon > /proc/PID/reclaim) reclaims anonymous pages only.
|
||||
(echo all > /proc/PID/reclaim) reclaims all pages.
|
||||
|
||||
Any other vaule is ignored.
|
||||
|
60
mm/vmscan.c
60
mm/vmscan.c
@ -1167,6 +1167,66 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROCESS_RECLAIM
|
||||
static unsigned long shrink_page(struct page *page,
|
||||
struct zone *zone,
|
||||
struct scan_control *sc,
|
||||
enum ttu_flags ttu_flags,
|
||||
unsigned long *ret_nr_dirty,
|
||||
unsigned long *ret_nr_writeback,
|
||||
bool force_reclaim,
|
||||
struct list_head *ret_pages)
|
||||
{
|
||||
int reclaimed;
|
||||
LIST_HEAD(page_list);
|
||||
list_add(&page->lru, &page_list);
|
||||
|
||||
reclaimed = shrink_page_list(&page_list, zone, sc, ttu_flags,
|
||||
ret_nr_dirty, ret_nr_writeback,
|
||||
force_reclaim);
|
||||
if (!reclaimed)
|
||||
list_splice(&page_list, ret_pages);
|
||||
|
||||
return reclaimed;
|
||||
}
|
||||
|
||||
unsigned long reclaim_pages_from_list(struct list_head *page_list)
|
||||
{
|
||||
struct scan_control sc = {
|
||||
.gfp_mask = GFP_KERNEL,
|
||||
.priority = DEF_PRIORITY,
|
||||
.may_writepage = 1,
|
||||
.may_unmap = 1,
|
||||
.may_swap = 1,
|
||||
};
|
||||
|
||||
LIST_HEAD(ret_pages);
|
||||
struct page *page;
|
||||
unsigned long dummy1, dummy2, dummy3, dummy4, dummy5;
|
||||
unsigned long nr_reclaimed = 0;
|
||||
|
||||
while (!list_empty(page_list)) {
|
||||
page = lru_to_page(page_list);
|
||||
list_del(&page->lru);
|
||||
|
||||
ClearPageActive(page);
|
||||
nr_reclaimed += shrink_page(page, page_zone(page), &sc,
|
||||
TTU_UNMAP|TTU_IGNORE_ACCESS,
|
||||
&dummy1, &dummy2, &dummy3, &dummy4, &dummy5, true);
|
||||
}
|
||||
|
||||
while (!list_empty(&ret_pages)) {
|
||||
page = lru_to_page(&ret_pages);
|
||||
list_del(&page->lru);
|
||||
dec_zone_page_state(page, NR_ISOLATED_ANON +
|
||||
page_is_file_cache(page));
|
||||
putback_lru_page(page);
|
||||
}
|
||||
|
||||
return nr_reclaimed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Attempt to remove the specified page from its LRU. Only take this page
|
||||
* if it is of the appropriate PageActive status. Pages which are being
|
||||
|
Loading…
Reference in New Issue
Block a user