Loading include/linux/memcontrol.h +8 −8 Original line number Diff line number Diff line Loading @@ -461,16 +461,16 @@ unsigned long mem_cgroup_get_limit(struct mem_cgroup *memcg); void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p); static inline void mem_cgroup_oom_enable(void) static inline void mem_cgroup_enter_user_fault(void) { WARN_ON(current->memcg_may_oom); current->memcg_may_oom = 1; WARN_ON(current->in_user_fault); current->in_user_fault = 1; } static inline void mem_cgroup_oom_disable(void) static inline void mem_cgroup_exit_user_fault(void) { WARN_ON(!current->memcg_may_oom); current->memcg_may_oom = 0; WARN_ON(!current->in_user_fault); current->in_user_fault = 0; } static inline bool task_in_memcg_oom(struct task_struct *p) Loading Loading @@ -831,11 +831,11 @@ static inline void mem_cgroup_handle_over_high(void) { } static inline void mem_cgroup_oom_enable(void) static inline void mem_cgroup_enter_user_fault(void) { } static inline void mem_cgroup_oom_disable(void) static inline void mem_cgroup_exit_user_fault(void) { } Loading include/linux/sched.h +1 −1 Original line number Diff line number Diff line Loading @@ -876,7 +876,7 @@ struct task_struct { unsigned restore_sigmask:1; #endif #ifdef CONFIG_MEMCG unsigned memcg_may_oom:1; unsigned in_user_fault:1; #ifndef CONFIG_SLOB unsigned memcg_kmem_skip_account:1; #endif Loading mm/memcontrol.c +60 −15 Original line number Diff line number Diff line Loading @@ -1526,28 +1526,53 @@ static void memcg_oom_recover(struct mem_cgroup *memcg) __wake_up(&memcg_oom_waitq, TASK_NORMAL, 0, memcg); } static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) enum oom_status { OOM_SUCCESS, OOM_FAILED, OOM_ASYNC, OOM_SKIPPED }; static enum oom_status mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) { if (!current->memcg_may_oom) return; if (order > PAGE_ALLOC_COSTLY_ORDER) return OOM_SKIPPED; /* * We are in the middle of the charge context here, so we * don't want to block when potentially sitting on a callstack * that holds all kinds of filesystem and mm locks. * * Also, the caller may handle a failed allocation gracefully * (like optional page cache readahead) and so an OOM killer * invocation might not even be necessary. * cgroup1 allows disabling the OOM killer and waiting for outside * handling until the charge can succeed; remember the context and put * the task to sleep at the end of the page fault when all locks are * released. * * On the other hand, in-kernel OOM killer allows for an async victim * memory reclaim (oom_reaper) and that means that we are not solely * relying on the oom victim to make a forward progress and we can * invoke the oom killer here. * * That's why we don't do anything here except remember the * OOM context and then deal with it at the end of the page * fault when the stack is unwound, the locks are released, * and when we know whether the fault was overall successful. * Please note that mem_cgroup_out_of_memory might fail to find a * victim and then we have to bail out from the charge path. */ if (memcg->oom_kill_disable) { if (!current->in_user_fault) return OOM_SKIPPED; css_get(&memcg->css); current->memcg_in_oom = memcg; current->memcg_oom_gfp_mask = mask; current->memcg_oom_order = order; return OOM_ASYNC; } if (mem_cgroup_out_of_memory(memcg, mask, order)) return OOM_SUCCESS; WARN(1,"Memory cgroup charge failed because of no reclaimable memory! " "This looks like a misconfiguration or a kernel bug."); return OOM_FAILED; } /** Loading Loading @@ -1912,6 +1937,8 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, unsigned long nr_reclaimed; bool may_swap = true; bool drained = false; bool oomed = false; enum oom_status oom_status; if (mem_cgroup_is_root(memcg)) return 0; Loading Loading @@ -1999,6 +2026,9 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, if (nr_retries--) goto retry; if (gfp_mask & __GFP_RETRY_MAYFAIL && oomed) goto nomem; if (gfp_mask & __GFP_NOFAIL) goto force; Loading @@ -2007,8 +2037,23 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, mem_cgroup_event(mem_over_limit, MEMCG_OOM); mem_cgroup_oom(mem_over_limit, gfp_mask, /* * keep retrying as long as the memcg oom killer is able to make * a forward progress or bypass the charge if the oom killer * couldn't make any progress. */ oom_status = mem_cgroup_oom(mem_over_limit, gfp_mask, get_order(nr_pages * PAGE_SIZE)); switch (oom_status) { case OOM_SUCCESS: nr_retries = MEM_CGROUP_RECLAIM_RETRIES; oomed = true; goto retry; case OOM_FAILED: goto force; default: goto nomem; } nomem: if (!(gfp_mask & __GFP_NOFAIL)) return -ENOMEM; Loading mm/memory.c +4 −4 Original line number Diff line number Diff line Loading @@ -4554,9 +4554,9 @@ int __handle_speculative_fault(struct mm_struct *mm, unsigned long address, return VM_FAULT_RETRY; } mem_cgroup_oom_enable(); mem_cgroup_enter_user_fault(); ret = handle_pte_fault(&vmf); mem_cgroup_oom_disable(); mem_cgroup_exit_user_fault(); /* * If there is no need to retry, don't return the vma to the caller. Loading Loading @@ -4641,7 +4641,7 @@ int handle_mm_fault(struct vm_area_struct *vma, unsigned long address, * space. Kernel faults are handled more gracefully. */ if (flags & FAULT_FLAG_USER) mem_cgroup_oom_enable(); mem_cgroup_enter_user_fault(); if (unlikely(is_vm_hugetlb_page(vma))) ret = hugetlb_fault(vma->vm_mm, vma, address, flags); Loading @@ -4649,7 +4649,7 @@ int handle_mm_fault(struct vm_area_struct *vma, unsigned long address, ret = __handle_mm_fault(vma, address, flags); if (flags & FAULT_FLAG_USER) { mem_cgroup_oom_disable(); mem_cgroup_exit_user_fault(); /* * The task may have entered a memcg OOM situation but * if the allocation error was handled gracefully (no Loading Loading
include/linux/memcontrol.h +8 −8 Original line number Diff line number Diff line Loading @@ -461,16 +461,16 @@ unsigned long mem_cgroup_get_limit(struct mem_cgroup *memcg); void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p); static inline void mem_cgroup_oom_enable(void) static inline void mem_cgroup_enter_user_fault(void) { WARN_ON(current->memcg_may_oom); current->memcg_may_oom = 1; WARN_ON(current->in_user_fault); current->in_user_fault = 1; } static inline void mem_cgroup_oom_disable(void) static inline void mem_cgroup_exit_user_fault(void) { WARN_ON(!current->memcg_may_oom); current->memcg_may_oom = 0; WARN_ON(!current->in_user_fault); current->in_user_fault = 0; } static inline bool task_in_memcg_oom(struct task_struct *p) Loading Loading @@ -831,11 +831,11 @@ static inline void mem_cgroup_handle_over_high(void) { } static inline void mem_cgroup_oom_enable(void) static inline void mem_cgroup_enter_user_fault(void) { } static inline void mem_cgroup_oom_disable(void) static inline void mem_cgroup_exit_user_fault(void) { } Loading
include/linux/sched.h +1 −1 Original line number Diff line number Diff line Loading @@ -876,7 +876,7 @@ struct task_struct { unsigned restore_sigmask:1; #endif #ifdef CONFIG_MEMCG unsigned memcg_may_oom:1; unsigned in_user_fault:1; #ifndef CONFIG_SLOB unsigned memcg_kmem_skip_account:1; #endif Loading
mm/memcontrol.c +60 −15 Original line number Diff line number Diff line Loading @@ -1526,28 +1526,53 @@ static void memcg_oom_recover(struct mem_cgroup *memcg) __wake_up(&memcg_oom_waitq, TASK_NORMAL, 0, memcg); } static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) enum oom_status { OOM_SUCCESS, OOM_FAILED, OOM_ASYNC, OOM_SKIPPED }; static enum oom_status mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) { if (!current->memcg_may_oom) return; if (order > PAGE_ALLOC_COSTLY_ORDER) return OOM_SKIPPED; /* * We are in the middle of the charge context here, so we * don't want to block when potentially sitting on a callstack * that holds all kinds of filesystem and mm locks. * * Also, the caller may handle a failed allocation gracefully * (like optional page cache readahead) and so an OOM killer * invocation might not even be necessary. * cgroup1 allows disabling the OOM killer and waiting for outside * handling until the charge can succeed; remember the context and put * the task to sleep at the end of the page fault when all locks are * released. * * On the other hand, in-kernel OOM killer allows for an async victim * memory reclaim (oom_reaper) and that means that we are not solely * relying on the oom victim to make a forward progress and we can * invoke the oom killer here. * * That's why we don't do anything here except remember the * OOM context and then deal with it at the end of the page * fault when the stack is unwound, the locks are released, * and when we know whether the fault was overall successful. * Please note that mem_cgroup_out_of_memory might fail to find a * victim and then we have to bail out from the charge path. */ if (memcg->oom_kill_disable) { if (!current->in_user_fault) return OOM_SKIPPED; css_get(&memcg->css); current->memcg_in_oom = memcg; current->memcg_oom_gfp_mask = mask; current->memcg_oom_order = order; return OOM_ASYNC; } if (mem_cgroup_out_of_memory(memcg, mask, order)) return OOM_SUCCESS; WARN(1,"Memory cgroup charge failed because of no reclaimable memory! " "This looks like a misconfiguration or a kernel bug."); return OOM_FAILED; } /** Loading Loading @@ -1912,6 +1937,8 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, unsigned long nr_reclaimed; bool may_swap = true; bool drained = false; bool oomed = false; enum oom_status oom_status; if (mem_cgroup_is_root(memcg)) return 0; Loading Loading @@ -1999,6 +2026,9 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, if (nr_retries--) goto retry; if (gfp_mask & __GFP_RETRY_MAYFAIL && oomed) goto nomem; if (gfp_mask & __GFP_NOFAIL) goto force; Loading @@ -2007,8 +2037,23 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, mem_cgroup_event(mem_over_limit, MEMCG_OOM); mem_cgroup_oom(mem_over_limit, gfp_mask, /* * keep retrying as long as the memcg oom killer is able to make * a forward progress or bypass the charge if the oom killer * couldn't make any progress. */ oom_status = mem_cgroup_oom(mem_over_limit, gfp_mask, get_order(nr_pages * PAGE_SIZE)); switch (oom_status) { case OOM_SUCCESS: nr_retries = MEM_CGROUP_RECLAIM_RETRIES; oomed = true; goto retry; case OOM_FAILED: goto force; default: goto nomem; } nomem: if (!(gfp_mask & __GFP_NOFAIL)) return -ENOMEM; Loading
mm/memory.c +4 −4 Original line number Diff line number Diff line Loading @@ -4554,9 +4554,9 @@ int __handle_speculative_fault(struct mm_struct *mm, unsigned long address, return VM_FAULT_RETRY; } mem_cgroup_oom_enable(); mem_cgroup_enter_user_fault(); ret = handle_pte_fault(&vmf); mem_cgroup_oom_disable(); mem_cgroup_exit_user_fault(); /* * If there is no need to retry, don't return the vma to the caller. Loading Loading @@ -4641,7 +4641,7 @@ int handle_mm_fault(struct vm_area_struct *vma, unsigned long address, * space. Kernel faults are handled more gracefully. */ if (flags & FAULT_FLAG_USER) mem_cgroup_oom_enable(); mem_cgroup_enter_user_fault(); if (unlikely(is_vm_hugetlb_page(vma))) ret = hugetlb_fault(vma->vm_mm, vma, address, flags); Loading @@ -4649,7 +4649,7 @@ int handle_mm_fault(struct vm_area_struct *vma, unsigned long address, ret = __handle_mm_fault(vma, address, flags); if (flags & FAULT_FLAG_USER) { mem_cgroup_oom_disable(); mem_cgroup_exit_user_fault(); /* * The task may have entered a memcg OOM situation but * if the allocation error was handled gracefully (no Loading