Skip to content

Commit

Permalink
mm: memcg: enable memcg OOM killer only for user faults
Browse files Browse the repository at this point in the history
System calls and kernel faults (uaccess, gup) can handle an out of memory
situation gracefully and just return -ENOMEM.

Enable the memcg OOM killer only for user faults, where it's really the
only option available.

Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: David Rientjes <rientjes@google.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: azurIt <azurit@pobox.sk>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Johannes Weiner authored and Linus Torvalds committed Sep 12, 2013
1 parent 3a13c4d commit 519e524
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 12 deletions.
44 changes: 44 additions & 0 deletions include/linux/memcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,37 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
extern void mem_cgroup_replace_page_cache(struct page *oldpage,
struct page *newpage);

/**
* mem_cgroup_toggle_oom - toggle the memcg OOM killer for the current task
* @new: true to enable, false to disable
*
* Toggle whether a failed memcg charge should invoke the OOM killer
* or just return -ENOMEM. Returns the previous toggle state.
*/
static inline bool mem_cgroup_toggle_oom(bool new)
{
bool old;

old = current->memcg_oom.may_oom;
current->memcg_oom.may_oom = new;

return old;
}

static inline void mem_cgroup_enable_oom(void)
{
bool old = mem_cgroup_toggle_oom(true);

WARN_ON(old == true);
}

static inline void mem_cgroup_disable_oom(void)
{
bool old = mem_cgroup_toggle_oom(false);

WARN_ON(old == false);
}

#ifdef CONFIG_MEMCG_SWAP
extern int do_swap_account;
#endif
Expand Down Expand Up @@ -383,6 +414,19 @@ static inline void mem_cgroup_end_update_page_stat(struct page *page,
{
}

static inline bool mem_cgroup_toggle_oom(bool new)
{
return false;
}

static inline void mem_cgroup_enable_oom(void)
{
}

static inline void mem_cgroup_disable_oom(void)
{
}

static inline void mem_cgroup_inc_page_stat(struct page *page,
enum mem_cgroup_page_stat_item idx)
{
Expand Down
3 changes: 3 additions & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,9 @@ struct task_struct {
unsigned long memsw_nr_pages; /* uncharged mem+swap usage */
} memcg_batch;
unsigned int memcg_kmem_skip_account;
struct memcg_oom_info {
unsigned int may_oom:1;
} memcg_oom;
#endif
#ifdef CONFIG_UPROBES
struct uprobe_task *utask;
Expand Down
11 changes: 10 additions & 1 deletion mm/filemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,7 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
struct inode *inode = mapping->host;
pgoff_t offset = vmf->pgoff;
struct page *page;
bool memcg_oom;
pgoff_t size;
int ret = 0;

Expand All @@ -1622,18 +1623,26 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return VM_FAULT_SIGBUS;

/*
* Do we have something in the page cache already?
* Do we have something in the page cache already? Either
* way, try readahead, but disable the memcg OOM killer for it
* as readahead is optional and no errors are propagated up
* the fault stack. The OOM killer is enabled while trying to
* instantiate the faulting page individually below.
*/
page = find_get_page(mapping, offset);
if (likely(page) && !(vmf->flags & FAULT_FLAG_TRIED)) {
/*
* We found the page, so try async readahead before
* waiting for the lock.
*/
memcg_oom = mem_cgroup_toggle_oom(false);
do_async_mmap_readahead(vma, ra, file, page, offset);
mem_cgroup_toggle_oom(memcg_oom);
} else if (!page) {
/* No page in the page cache at all */
memcg_oom = mem_cgroup_toggle_oom(false);
do_sync_mmap_readahead(vma, ra, file, offset);
mem_cgroup_toggle_oom(memcg_oom);
count_vm_event(PGMAJFAULT);
mem_cgroup_count_vm_event(vma->vm_mm, PGMAJFAULT);
ret = VM_FAULT_MAJOR;
Expand Down
2 changes: 1 addition & 1 deletion mm/memcontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -2454,7 +2454,7 @@ static int mem_cgroup_do_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
return CHARGE_RETRY;

/* If we don't need to call oom-killer at el, return immediately */
if (!oom_check)
if (!oom_check || !current->memcg_oom.may_oom)
return CHARGE_NOMEM;
/* check OOM */
if (!mem_cgroup_handle_oom(mem_over_limit, gfp_mask, get_order(csize)))
Expand Down
40 changes: 30 additions & 10 deletions mm/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -3754,22 +3754,14 @@ int handle_pte_fault(struct mm_struct *mm,
/*
* By the time we get here, we already hold the mm semaphore
*/
int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
static int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;

__set_current_state(TASK_RUNNING);

count_vm_event(PGFAULT);
mem_cgroup_count_vm_event(mm, PGFAULT);

/* do counter updates before entering really critical section. */
check_sync_rss_stat(current);

if (unlikely(is_vm_hugetlb_page(vma)))
return hugetlb_fault(mm, vma, address, flags);

Expand Down Expand Up @@ -3850,6 +3842,34 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
return handle_pte_fault(mm, vma, address, pte, pmd, flags);
}

int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
{
int ret;

__set_current_state(TASK_RUNNING);

count_vm_event(PGFAULT);
mem_cgroup_count_vm_event(mm, PGFAULT);

/* do counter updates before entering really critical section. */
check_sync_rss_stat(current);

/*
* Enable the memcg OOM handling for faults triggered in user
* space. Kernel faults are handled more gracefully.
*/
if (flags & FAULT_FLAG_USER)
mem_cgroup_enable_oom();

ret = __handle_mm_fault(mm, vma, address, flags);

if (flags & FAULT_FLAG_USER)
mem_cgroup_disable_oom();

return ret;
}

#ifndef __PAGETABLE_PUD_FOLDED
/*
* Allocate page upper directory.
Expand Down

0 comments on commit 519e524

Please sign in to comment.