improve memcg oom killer robustness
Subsystems (27)
Johannes Weiner
maintainercover improve memcg oom killer robustness
The memcg code can trap tasks in the context of the failing allocation until an OOM situation is resolved. They can hold all kinds of locks (fs, mm) at this point, which makes it prone to deadlocking.
This series converts memcg OOM handling into a two step process that is started in the charge context, but any waiting is done after the fault stack is fully unwound.
Patches 1-4 prepare architecture handlers to support the new memcg requirements, but in doing so they also remove old cruft and unify out-of-memory behavior across architectures.
Patch 5 disables the memcg OOM handling for syscalls, readahead, kernel faults, because they can gracefully unwind the stack with -ENOMEM. OOM handling is restricted to user triggered faults that have no other option.
Patch 6 implements the two-part OOM handling such that tasks are never trapped with the full charge stack in an OOM situation.
arch/alpha/mm/fault.c | 7 ++++--- arch/arc/mm/fault.c | 11 ++++------- arch/arm/mm/fault.c | 23 +++++++++++++---------- arch/arm64/mm/fault.c | 23 +++++++++++++---------- arch/avr32/mm/fault.c | 4 +++- arch/cris/mm/fault.c | 6 ++++-- arch/frv/mm/fault.c | 10 ++++++---- arch/hexagon/mm/vm_fault.c | 6 ++++-- arch/ia64/mm/fault.c | 6 ++++-- arch/m32r/mm/fault.c | 10 ++++++---- arch/m68k/mm/fault.c | 2 ++ arch/metag/mm/fault.c | 6 ++++-- arch/microblaze/mm/fault.c | 7 +++++-- arch/mips/mm/fault.c | 8 ++++++-- arch/mn10300/mm/fault.c | 2 ++ arch/openrisc/mm/fault.c | 1 + arch/parisc/mm/fault.c | 7 +++++-- arch/powerpc/mm/fault.c | 7 ++++--- arch/s390/mm/fault.c | 2 ++ arch/score/mm/fault.c | 13 ++++++------- arch/sh/mm/fault.c | 9 ++++++--- arch/sparc/mm/fault_32.c | 12 +++++++++--- arch/sparc/mm/fault_64.c | 8 +++++--- arch/tile/mm/fault.c | 13 +++++-------- arch/um/kernel/trap.c | 22 ++++++++++++++-------- arch/unicore32/mm/fault.c | 22 +++++++++++++--------- arch/x86/mm/fault.c | 43 ++++++++++++++++++++++--------------------- arch/xtensa/mm/fault.c | 2 ++ include/linux/memcontrol.h | 40 ++++++++++++++++++++++++++++++++++++++++ include/linux/mm.h | 1 + include/linux/sched.h | 6 ++++++ mm/filemap.c | 11 ++++++++++- mm/memcontrol.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------- mm/memory.c | 43 +++++++++++++++++++++++++++++++++---------- mm/oom_kill.c | 7 +++++-- 35 files changed, 373 insertions(+), 183 deletions(-)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Johannes Weiner
maintainerPATCH 1/6 arch: mm: remove obsolete init OOM protection
Back before smart OOM killing, when faulting tasks where killed directly on allocation failures, the arch-specific fault handlers needed special protection for the init process.
Now that all fault handlers call into the generic OOM killer (609838c "mm: invoke oom-killer from remaining unconverted page fault handlers"), which already provides init protection, the arch-specific leftovers can be removed.
--- arch/arc/mm/fault.c | 5 ----- arch/score/mm/fault.c | 6 ------ arch/tile/mm/fault.c | 6 ------ 3 files changed, 17 deletions(-)
arch/arc/mm/fault.c +0 -5
@@ -122,7 +122,6 @@ good_area: goto bad_area; } -survive:/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo
@@ -201,10 +200,6 @@ no_context:die ("Oops" , regs, address); out_of_memory: -if (is_global_init (tsk)) { -yield (); - goto survive; - }up_read (&mm->mmap_sem);if (user_mode (regs)) {
arch/score/mm/fault.c +0 -6
@@ -100,7 +100,6 @@ good_area: goto bad_area; } -survive:/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo
@@ -167,11 +166,6 @@ no_context:*/ out_of_memory:up_read (&mm->mmap_sem); -if (is_global_init (tsk)) { -yield (); -down_read (&mm->mmap_sem); - goto survive; - }if (!user_mode (regs)) goto no_context;pagefault_out_of_memory ();
arch/tile/mm/fault.c +0 -6
@@ -430,7 +430,6 @@ good_area: goto bad_area; } - survive:/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo
@@ -568,11 +567,6 @@ no_context:*/ out_of_memory:up_read (&mm->mmap_sem); -if (is_global_init (tsk)) { -yield (); -down_read (&mm->mmap_sem); - goto survive; - }if (is_kernel_mode) goto no_context;pagefault_out_of_memory ();
Signature · 7 lines
1.8.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 1/6] arch: mm: remove obsolete init OOM protection
Quoted 10 lines · Johannes Weiner
Back before smart OOM killing, when faulting tasks where killed directly on allocation failures, the arch-specific fault handlers needed special protection for the init process.
Now that all fault handlers call into the generic OOM killer (609838c "mm: invoke oom-killer from remaining unconverted page fault handlers"), which already provides init protection, the arch-specific leftovers can be removed.
Signed-off-by Johannes Weiner <[email protected]>
Looks good to me
Quoted 80 lines
--- arch/arc/mm/fault.c | 5 ----- arch/score/mm/fault.c | 6 ------ arch/tile/mm/fault.c | 6 ------ 3 files changed, 17 deletions(-)arch/arc/mm/fault.c +0
-5@@ -122,7 +122,6 @@ good_area: goto bad_area; } -survive:/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -201,10 +200,6 @@ no_context:die ("Oops" , regs, address); out_of_memory: -if (is_global_init (tsk)) { -yield (); - goto survive; - }up_read (&mm->mmap_sem);if (user_mode (regs)) {arch/score/mm/fault.c +0
-6@@ -100,7 +100,6 @@ good_area: goto bad_area; } -survive:/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -167,11 +166,6 @@ no_context:*/ out_of_memory:up_read (&mm->mmap_sem); -if (is_global_init (tsk)) { -yield (); -down_read (&mm->mmap_sem); - goto survive; - }if (!user_mode (regs)) goto no_context;pagefault_out_of_memory ();arch/tile/mm/fault.c +0
-6@@ -430,7 +430,6 @@ good_area: goto bad_area; } - survive:/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -568,11 +567,6 @@ no_context:*/ out_of_memory:up_read (&mm->mmap_sem); -if (is_global_init (tsk)) { -yield (); -down_read (&mm->mmap_sem); - goto survive; - }if (is_kernel_mode) goto no_context;pagefault_out_of_memory ();Signature · 1 line
1.8.3.2
Signature · 8 lines
Michal Hocko SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 1/6] arch: mm: remove obsolete init OOM protection
In reply to Johannes WeinerQuoted 10 lines · Johannes Weiner
Back before smart OOM killing, when faulting tasks where killed directly on allocation failures, the arch-specific fault handlers needed special protection for the init process.
Now that all fault handlers call into the generic OOM killer (609838c "mm: invoke oom-killer from remaining unconverted page fault handlers"), which already provides init protection, the arch-specific leftovers can be removed.
Signed-off-by Johannes Weiner <[email protected]>
Looks good to me.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Johannes Weiner
maintainerPATCH 2/6 arch: mm: do not invoke OOM killer on kernel fault OOM
In reply to Johannes WeinerKernel faults are expected to handle OOM conditions gracefully (gup, uaccess etc.), so they should never invoke the OOM killer. Reserve this for faults triggered in user context when it is the only option.
Most architectures already do this, fix up the remaining few.
--- arch/arm/mm/fault.c | 14 +++++++------- arch/arm64/mm/fault.c | 14 +++++++------- arch/avr32/mm/fault.c | 2 +- arch/mips/mm/fault.c | 2 ++ arch/um/kernel/trap.c | 2 ++ arch/unicore32/mm/fault.c | 14 +++++++------- 6 files changed, 26 insertions(+), 22 deletions(-)
arch/arm/mm/fault.c +7 -7
@@ -349,6 +349,13 @@ retry:if (likely (!(fault & (VM_FAULT_ERROR |VM_FAULT_BADMAP |VM_FAULT_BADACCESS ))))return 0 ; +/* +* If we are in kernel mode at this point, we +* have no context to handle this fault with. +*/ +if (!user_mode (regs)) + goto no_context; +if (fault &VM_FAULT_OOM ) {/* * We ran out of memory, call the OOM killer, and return to
@@ -359,13 +366,6 @@ retry:return 0 ; } -/* -* If we are in kernel mode at this point, we -* have no context to handle this fault with. -*/ -if (!user_mode (regs)) - goto no_context; -if (fault &VM_FAULT_SIGBUS ) {/* * We had some memory, but were unable to
arch/arm64/mm/fault.c +7 -7
@@ -294,6 +294,13 @@ retry: VM_FAULT_BADACCESS))))return 0 ; +/* +* If we are in kernel mode at this point, we have no context to +* handle this fault with. +*/ +if (!user_mode (regs)) + goto no_context; +if (fault &VM_FAULT_OOM ) {/* * We ran out of memory, call the OOM killer, and return to
@@ -304,13 +311,6 @@ retry:return 0 ; } -/* -* If we are in kernel mode at this point, we have no context to -* handle this fault with. -*/ -if (!user_mode (regs)) - goto no_context; -if (fault &VM_FAULT_SIGBUS ) {/* * We had some memory, but were unable to successfully fix up
arch/avr32/mm/fault.c +1 -1
@@ -228,9 +228,9 @@ no_context:*/ out_of_memory:up_read (&mm->mmap_sem); -pagefault_out_of_memory ();if (!user_mode (regs)) goto no_context; +pagefault_out_of_memory ();return ; do_sigbus:
arch/mips/mm/fault.c +2 -0
@@ -241,6 +241,8 @@ out_of_memory:* (which will retry the fault, or kill us if we got oom-killed). */ up_read (&mm->mmap_sem); +if (!user_mode (regs)) + goto no_context;pagefault_out_of_memory ();return ;
arch/um/kernel/trap.c +2 -0
@@ -124,6 +124,8 @@ out_of_memory:* (which will retry the fault, or kill us if we got oom-killed). */ up_read (&mm->mmap_sem); +if (!is_user) + goto out_nosemaphore;pagefault_out_of_memory ();return 0 ; }
arch/unicore32/mm/fault.c +7 -7
@@ -278,6 +278,13 @@ retry: (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS))))return 0 ; +/* +* If we are in kernel mode at this point, we +* have no context to handle this fault with. +*/ +if (!user_mode (regs)) + goto no_context; +if (fault &VM_FAULT_OOM ) {/* * We ran out of memory, call the OOM killer, and return to
@@ -288,13 +295,6 @@ retry:return 0 ; } -/* -* If we are in kernel mode at this point, we -* have no context to handle this fault with. -*/ -if (!user_mode (regs)) - goto no_context; -if (fault &VM_FAULT_SIGBUS ) {/* * We had some memory, but were unable to
Signature · 7 lines
1.8.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 2/6] arch: mm: do not invoke OOM killer on kernel fault OOM
Quoted 7 lines · Johannes Weiner
Kernel faults are expected to handle OOM conditions gracefully (gup, uaccess etc.), so they should never invoke the OOM killer. Reserve this for faults triggered in user context when it is the only option.
Most architectures already do this, fix up the remaining few.
Signed-off-by Johannes Weiner <[email protected]>
I didn't go over all architectures to check whether something slipped through but the converted ones look OK to me.
Quoted 149 lines
--- arch/arm/mm/fault.c | 14 +++++++------- arch/arm64/mm/fault.c | 14 +++++++------- arch/avr32/mm/fault.c | 2 +- arch/mips/mm/fault.c | 2 ++ arch/um/kernel/trap.c | 2 ++ arch/unicore32/mm/fault.c | 14 +++++++------- 6 files changed, 26 insertions(+), 22 deletions(-)arch/arm/mm/fault.c +7
-7@@ -349,6 +349,13 @@ retry:if (likely (!(fault & (VM_FAULT_ERROR |VM_FAULT_BADMAP |VM_FAULT_BADACCESS ))))return 0 ; +/* +* If we are in kernel mode at this point, we +* have no context to handle this fault with. +*/ +if (!user_mode (regs)) + goto no_context; +if (fault &VM_FAULT_OOM ) {/* * We ran out of memory, call the OOM killer, and return to @@ -359,13 +366,6 @@ retry:return 0 ; } -/* -* If we are in kernel mode at this point, we -* have no context to handle this fault with. -*/ -if (!user_mode (regs)) - goto no_context; -if (fault &VM_FAULT_SIGBUS ) {/* * We had some memory, but were unable to arch/arm64/mm/fault.c +7
-7@@ -294,6 +294,13 @@ retry: VM_FAULT_BADACCESS))))return 0 ; +/* +* If we are in kernel mode at this point, we have no context to +* handle this fault with. +*/ +if (!user_mode (regs)) + goto no_context; +if (fault &VM_FAULT_OOM ) {/* * We ran out of memory, call the OOM killer, and return to @@ -304,13 +311,6 @@ retry:return 0 ; } -/* -* If we are in kernel mode at this point, we have no context to -* handle this fault with. -*/ -if (!user_mode (regs)) - goto no_context; -if (fault &VM_FAULT_SIGBUS ) {/* * We had some memory, but were unable to successfully fix up arch/avr32/mm/fault.c +1
-1@@ -228,9 +228,9 @@ no_context:*/ out_of_memory:up_read (&mm->mmap_sem); -pagefault_out_of_memory ();if (!user_mode (regs)) goto no_context; +pagefault_out_of_memory ();return ; do_sigbus:arch/mips/mm/fault.c +2
-0@@ -241,6 +241,8 @@ out_of_memory:* (which will retry the fault, or kill us if we got oom-killed). */ up_read (&mm->mmap_sem); +if (!user_mode (regs)) + goto no_context;pagefault_out_of_memory ();return ;arch/um/kernel/trap.c +2
-0@@ -124,6 +124,8 @@ out_of_memory:* (which will retry the fault, or kill us if we got oom-killed). */ up_read (&mm->mmap_sem); +if (!is_user) + goto out_nosemaphore;pagefault_out_of_memory ();return 0 ; }arch/unicore32/mm/fault.c +7
-7@@ -278,6 +278,13 @@ retry: (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS))))return 0 ; +/* +* If we are in kernel mode at this point, we +* have no context to handle this fault with. +*/ +if (!user_mode (regs)) + goto no_context; +if (fault &VM_FAULT_OOM ) {/* * We ran out of memory, call the OOM killer, and return to @@ -288,13 +295,6 @@ retry:return 0 ; } -/* -* If we are in kernel mode at this point, we -* have no context to handle this fault with. -*/ -if (!user_mode (regs)) - goto no_context; -if (fault &VM_FAULT_SIGBUS ) {/* * We had some memory, but were unable to Signature · 1 line
1.8.3.2
Signature · 8 lines
Michal Hocko SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 2/6] arch: mm: do not invoke OOM killer on kernel fault OOM
In reply to Johannes WeinerQuoted 7 lines · Johannes Weiner
Kernel faults are expected to handle OOM conditions gracefully (gup, uaccess etc.), so they should never invoke the OOM killer. Reserve this for faults triggered in user context when it is the only option.
Most architectures already do this, fix up the remaining few.
Signed-off-by Johannes Weiner <[email protected]>
OK. but now almost all arch have the same page fault handler. So, I think we can implement arch generic page fault handler in future. Ah, ok, never mind if you are not interest.
Johannes Weiner
maintainerRe: [patch 2/6] arch: mm: do not invoke OOM killer on kernel fault OOM
Quoted 5 lines · KOSAKI Motohiro
Quoted 4 lines · Johannes Weiner
Kernel faults are expected to handle OOM conditions gracefully (gup, uaccess etc.), so they should never invoke the OOM killer. Reserve this for faults triggered in user context when it is the only option.
Quoted 7 lines
Quoted 3 lines
Most architectures already do this, fix up the remaining few.
Signed-off-by Johannes Weiner <[email protected]>OK. but now almost all arch have the same page fault handler. So, I think we can implement arch generic page fault handler in future. Ah, ok, never mind if you are not interest.
Well, I'm already working towards it ;-) Still a long way to go, though to fully replace them with generic code...
Quoted 1 line
Acked-by KOSAKI Motohiro <[email protected]>
Thanks!
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Johannes Weiner
maintainerPATCH 3/6 arch: mm: pass userspace fault flag to generic fault handler
In reply to Johannes WeinerUnlike global OOM handling, memory cgroup code will invoke the OOM killer in any OOM situation because it has no way of telling faults occuring in kernel context - which could be handled more gracefully - from user-triggered faults.
Pass a flag that identifies faults originating in user space from the architecture-specific fault handlers to generic code so that memcg OOM handling can be improved.
--- arch/alpha/mm/fault.c | 7 ++++--- arch/arc/mm/fault.c | 6 ++++-- arch/arm/mm/fault.c | 9 ++++++--- arch/arm64/mm/fault.c | 9 ++++++--- arch/avr32/mm/fault.c | 2 ++ arch/cris/mm/fault.c | 6 ++++-- arch/frv/mm/fault.c | 10 ++++++---- arch/hexagon/mm/vm_fault.c | 6 ++++-- arch/ia64/mm/fault.c | 6 ++++-- arch/m32r/mm/fault.c | 10 ++++++---- arch/m68k/mm/fault.c | 2 ++ arch/metag/mm/fault.c | 6 ++++-- arch/microblaze/mm/fault.c | 7 +++++-- arch/mips/mm/fault.c | 6 ++++-- arch/mn10300/mm/fault.c | 2 ++ arch/openrisc/mm/fault.c | 1 + arch/parisc/mm/fault.c | 7 +++++-- arch/powerpc/mm/fault.c | 7 ++++--- arch/s390/mm/fault.c | 2 ++ arch/score/mm/fault.c | 7 ++++++- arch/sh/mm/fault.c | 9 ++++++--- arch/sparc/mm/fault_32.c | 12 +++++++++--- arch/sparc/mm/fault_64.c | 8 +++++--- arch/tile/mm/fault.c | 7 +++++-- arch/um/kernel/trap.c | 20 ++++++++++++-------- arch/unicore32/mm/fault.c | 8 ++++++-- arch/x86/mm/fault.c | 8 +++++--- arch/xtensa/mm/fault.c | 2 ++ include/linux/mm.h | 1 + 29 files changed, 132 insertions(+), 61 deletions(-)
arch/alpha/mm/fault.c +4 -3
@@ -89,8 +89,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,const struct exception_table_entry *fixup;int fault, si_code =SEGV_MAPERR ;siginfo_t info; -unsigned int flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (cause >0 ?FAULT_FLAG_WRITE :0 )); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;/* As of EV6, a load into $31/$f31 is a prefetch, and never faults (or is suppressed by the PALcode). Support that for older CPUs
@@ -115,7 +114,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr,if (address >=TASK_SIZE ) goto vmalloc_fault;#endif - +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);
@@ -142,6 +142,7 @@ retry: }else {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }/* If for any reason at all we couldn't handle the fault,
arch/arc/mm/fault.c +4 -2
@@ -60,8 +60,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address)siginfo_t info;int fault, ret;int write = regs->ecr_cause &ECR_C_PROTV_STORE ;/* ST/EX */ -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;/* * We fault-in kernel-space virtual memory on-demand. The
@@ -89,6 +88,8 @@ void do_page_fault(struct pt_regs *regs, unsigned long address)if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);
@@ -117,6 +118,7 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area;
arch/arm/mm/fault.c +6 -3
@@ -261,9 +261,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)struct task_struct *tsk;struct mm_struct *mm;int fault, sig, code; -int write = fsr &FSR_WRITE ; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;if (notify_page_fault (regs, fsr))return 0 ;
@@ -282,6 +280,11 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (fsr &FSR_WRITE ) + flags |=FAULT_FLAG_WRITE ; +/* * As per x86, we may deadlock here. However, since the kernel only * validly references user space from well defined areas of the code,
arch/arm64/mm/fault.c +6 -3
@@ -208,9 +208,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,struct task_struct *tsk;struct mm_struct *mm;int fault, sig, code; -bool write = (esr &ESR_WRITE ) && !(esr &ESR_CM ); -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;
@@ -226,6 +224,11 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if ((esr &ESR_WRITE ) && !(esr &ESR_CM )) + flags |=FAULT_FLAG_WRITE ; +/* * As per x86, we may deadlock here. However, since the kernel only * validly references user space from well defined areas of the code,
arch/avr32/mm/fault.c +2 -0
@@ -86,6 +86,8 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)local_irq_enable (); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);
arch/cris/mm/fault.c +4 -2
@@ -58,8 +58,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs,struct vm_area_struct * vma;siginfo_t info;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - ((writeaccess &1 ) ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; D(printk(KERN_DEBUG "Page fault for %lX on %X at %lX, prot %d write %d\n",
@@ -117,6 +116,8 @@ do_page_fault(unsigned long address, struct pt_regs *regs,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);
@@ -155,6 +156,7 @@ retry: }else if (writeaccess ==1 ) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area;
arch/frv/mm/fault.c +6 -4
@@ -34,11 +34,11 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long earstruct vm_area_struct *vma;struct mm_struct *mm;unsigned long _pme, lrai, lrad, fixup; +unsigned long flags =0 ;siginfo_t info;pgd_t *pge;pud_t *pue;pte_t *pte; -int write;int fault;#if 0
@@ -81,6 +81,9 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long earif (in_atomic () || !mm) goto no_context; +if (user_mode (__frame)) + flags |=FAULT_FLAG_USER ; +down_read (&mm->mmap_sem); vma =find_vma (mm, ear0);
@@ -129,7 +132,6 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear*/ good_area: info.si_code =SEGV_ACCERR ; - write =0 ;switch (esr0 &ESR0_ATXC ) {default :/* handle write to write protected page */
@@ -140,7 +142,7 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear#endif if (!(vma->vm_flags &VM_WRITE )) goto bad_area; - write =1 ; + flags |=FAULT_FLAG_WRITE ;break ;/* handle read from protected page */
@@ -162,7 +164,7 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear* make sure we exit gracefully rather than endlessly redo * the fault. */ - fault =handle_mm_fault (mm, vma, ear0, write ?FAULT_FLAG_WRITE :0 ); + fault =handle_mm_fault (mm, vma, ear0, flags);if (unlikely (fault &VM_FAULT_ERROR )) {if (fault &VM_FAULT_OOM ) goto out_of_memory;
arch/hexagon/mm/vm_fault.c +4 -2
@@ -53,8 +53,7 @@ void do_page_fault(unsigned long address, long cause, struct pt_regs *regs)int si_code =SEGV_MAPERR ;int fault;const struct exception_table_entry *fixup; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (cause >0 ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;/* * If we're in an interrupt or have no user context,
@@ -65,6 +64,8 @@ void do_page_fault(unsigned long address, long cause, struct pt_regs *regs)local_irq_enable (); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);
@@ -96,6 +97,7 @@ good_area:case FLT_STORE :if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ;break ; }
arch/ia64/mm/fault.c +4 -2
@@ -90,8 +90,6 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re mask = ((((isr >>IA64_ISR_X_BIT ) &1UL ) <<VM_EXEC_BIT ) | (((isr >>IA64_ISR_W_BIT ) &1UL ) <<VM_WRITE_BIT )); - flags |= ((mask &VM_WRITE ) ?FAULT_FLAG_WRITE :0 ); -/* mmap_sem is performance critical.... */ prefetchw (&mm->mmap_sem);
@@ -119,6 +117,10 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *reif (notify_page_fault (regs,TRAP_BRKPT ))return ; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (mask &VM_WRITE ) + flags |=FAULT_FLAG_WRITE ; retry:down_read (&mm->mmap_sem);
arch/m32r/mm/fault.c +6 -4
@@ -78,7 +78,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code,struct mm_struct *mm;struct vm_area_struct * vma;unsigned long page, addr; -int write; +unsigned long flags =0 ;int fault;siginfo_t info;
@@ -117,6 +117,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code,if (in_atomic () || !mm) goto bad_area_nosemaphore; +if (error_code &ACE_USERMODE ) + flags |=FAULT_FLAG_USER ; +/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an
@@ -166,14 +169,13 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code,*/ good_area: info.si_code =SEGV_ACCERR ; - write =0 ;switch (error_code & (ACE_WRITE |ACE_PROTECTION )) {default :/* 3: write, present */ /* fall through */ case ACE_WRITE :/* write, not present */ if (!(vma->vm_flags &VM_WRITE )) goto bad_area; - write++; + flags |=FAULT_FLAG_WRITE ;break ;case ACE_PROTECTION :/* read, present */ case 0 :/* read, not present */
@@ -194,7 +196,7 @@ good_area:*/ addr = (address &PAGE_MASK );set_thread_fault_code (error_code); - fault =handle_mm_fault (mm, vma, addr, write ?FAULT_FLAG_WRITE :0 ); + fault =handle_mm_fault (mm, vma, addr, flags);if (unlikely (fault &VM_FAULT_ERROR )) {if (fault &VM_FAULT_OOM ) goto out_of_memory;
arch/m68k/mm/fault.c +2 -0
@@ -88,6 +88,8 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);
arch/metag/mm/fault.c +4 -2
@@ -53,8 +53,7 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,struct vm_area_struct *vma, *prev_vma;siginfo_t info;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write_access ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current;
@@ -109,6 +108,8 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);
@@ -121,6 +122,7 @@ good_area:if (write_access) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC |VM_WRITE ))) goto bad_area;
arch/microblaze/mm/fault.c +5 -2
@@ -92,8 +92,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,int code =SEGV_MAPERR ;int is_write = error_code &ESR_S ;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (is_write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; regs->ear = address; regs->esr = error_code;
@@ -121,6 +120,9 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,die ("Weird page fault" , regs,SIGSEGV ); } +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an
@@ -199,6 +201,7 @@ good_area:if (unlikely (is_write)) {if (unlikely (!(vma->vm_flags &VM_WRITE ))) goto bad_area; + flags |=FAULT_FLAG_WRITE ;/* a read */ }else {/* protection fault */
arch/mips/mm/fault.c +4 -2
@@ -42,8 +42,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,const int field =sizeof (unsigned long ) *2 ;siginfo_t info;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;#if 0 printk("Cpu%d[%s:%d:%0*lx:%ld:%0*lx]\n", raw_smp_processor_id(),
@@ -93,6 +92,8 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,if (in_atomic () || !mm) goto bad_area_nosemaphore; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);
@@ -114,6 +115,7 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (cpu_has_rixi) {if (address == regs->cp0_epc && !(vma->vm_flags &VM_EXEC )) {
arch/mn10300/mm/fault.c +2 -0
@@ -171,6 +171,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long fault_code,if (in_atomic () || !mm) goto no_context; +if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);
arch/openrisc/mm/fault.c +1 -0
@@ -86,6 +86,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,if (user_mode (regs)) {/* Exception was in userspace: reenable interrupts */ local_irq_enable (); + flags |=FAULT_FLAG_USER ; }else {/* If exception was in a syscall, then IRQ's may have * been enabled or disabled. If they were enabled,
arch/parisc/mm/fault.c +5 -2
@@ -180,6 +180,10 @@ void do_page_fault(struct pt_regs *regs, unsigned long code,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (acc_type &VM_WRITE ) + flags |=FAULT_FLAG_WRITE ; retry:down_read (&mm->mmap_sem); vma =find_vma_prev (mm, address, &prev_vma);
@@ -203,8 +207,7 @@ good_area:* fault. */ - fault =handle_mm_fault (mm, vma, address, - flags | ((acc_type &VM_WRITE ) ?FAULT_FLAG_WRITE :0 )); + fault =handle_mm_fault (mm, vma, address, flags);if ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current))return ;
arch/powerpc/mm/fault.c +4 -3
@@ -223,9 +223,6 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, is_write = error_code &ESR_DST ;#endif /* CONFIG_4xx || CONFIG_BOOKE */ -if (is_write) - flags |=FAULT_FLAG_WRITE ; -#ifdef CONFIG_PPC_ICSWX /* * we need to do this early because this "data storage
@@ -280,6 +277,9 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS ,1 , regs, address); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an
@@ -408,6 +408,7 @@ good_area: }else if (is_write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ;/* a read */ }else {/* protection fault */
arch/s390/mm/fault.c +2 -0
@@ -302,6 +302,8 @@ static inline int do_exception(struct pt_regs *regs, int access) address = trans_exc_code & __FAIL_ADDR_MASK;perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS ,1 , regs, address); flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; +if (regs->psw.mask &PSW_MASK_PSTATE ) + flags |=FAULT_FLAG_USER ;if (access ==VM_WRITE || (trans_exc_code & store_indication) ==0x400 ) flags |=FAULT_FLAG_WRITE ;down_read (&mm->mmap_sem);
arch/score/mm/fault.c +6 -1
@@ -47,6 +47,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,struct task_struct *tsk = current;struct mm_struct *mm = tsk->mm;const int field =sizeof (unsigned long ) *2 ; +unsigned long flags =0 ;siginfo_t info;int fault;
@@ -75,6 +76,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,if (in_atomic () || !mm) goto bad_area_nosemaphore; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +down_read (&mm->mmap_sem); vma =find_vma (mm, address);if (!vma)
@@ -95,6 +99,7 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_WRITE |VM_EXEC ))) goto bad_area;
@@ -105,7 +110,7 @@ good_area:* make sure we exit gracefully rather than endlessly redo * the fault. */ - fault =handle_mm_fault (mm, vma, address, write); + fault =handle_mm_fault (mm, vma, address, flags);if (unlikely (fault &VM_FAULT_ERROR )) {if (fault &VM_FAULT_OOM ) goto out_of_memory;
arch/sh/mm/fault.c +6 -3
@@ -400,9 +400,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,struct mm_struct *mm;struct vm_area_struct * vma;int fault; -int write = error_code &FAULT_CODE_WRITE ; -unsigned int flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 )); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;
@@ -476,6 +474,11 @@ good_area:set_thread_fault_code (error_code); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (error_code &FAULT_CODE_WRITE ) + flags |=FAULT_FLAG_WRITE ; +/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo
arch/sparc/mm/fault_32.c +9 -3
@@ -177,8 +177,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,unsigned long g2;int from_user = !(regs->psr &PSR_PS );int fault, code; -unsigned int flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 )); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;if (text_fault) address = regs->pc;
@@ -235,6 +234,11 @@ good_area: goto bad_area; } +if (from_user) + flags |=FAULT_FLAG_USER ; +if (write) + flags |=FAULT_FLAG_WRITE ; +/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo
@@ -383,6 +387,7 @@ static void force_user_fault(unsigned long address, int write)struct vm_area_struct *vma;struct task_struct *tsk = current;struct mm_struct *mm = tsk->mm; +unsigned int flags =FAULT_FLAG_USER ;int code; code =SEGV_MAPERR ;
@@ -402,11 +407,12 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area; } -switch (handle_mm_fault (mm, vma, address, write ?FAULT_FLAG_WRITE :0 )) { +switch (handle_mm_fault (mm, vma, address, flags)) {case VM_FAULT_SIGBUS :case VM_FAULT_OOM : goto do_sigbus;
arch/sparc/mm/fault_64.c +5 -3
@@ -314,8 +314,9 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) }else {bad_kernel_pc (regs, address);return ; - } - } + } + }else + flags |=FAULT_FLAG_USER ;/* * If we're in an interrupt or have no user
@@ -418,13 +419,14 @@ good_area: vma->vm_file != NULL)set_thread_fault_code (fault_code |FAULT_CODE_BLKCOMMIT ); + + flags |=FAULT_FLAG_WRITE ; }else {/* Allow reads even for write-only mappings */ if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area; } - flags |= ((fault_code &FAULT_CODE_WRITE ) ?FAULT_FLAG_WRITE :0 ); fault =handle_mm_fault (mm, vma, address, flags);if ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current))
arch/tile/mm/fault.c +5 -2
@@ -280,8 +280,7 @@ static int handle_page_fault(struct pt_regs *regs,if (!is_page_fault) write =1 ; - flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 )); + flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; is_kernel_mode = (EX1_PL (regs->ex1) !=USER_PL );
@@ -365,6 +364,9 @@ static int handle_page_fault(struct pt_regs *regs, goto bad_area_nosemaphore; } +if (!is_kernel_mode) + flags |=FAULT_FLAG_USER ; +/* * When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the
@@ -425,6 +427,7 @@ good_area:#endif if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!is_page_fault || !(vma->vm_flags &VM_READ )) goto bad_area;
arch/um/kernel/trap.c +12 -8
@@ -30,8 +30,7 @@ int handle_page_fault(unsigned long address, unsigned long ip,pmd_t *pmd;pte_t *pte;int err = -EFAULT ; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (is_write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; *code_out =SEGV_MAPERR ;
@@ -42,6 +41,8 @@ int handle_page_fault(unsigned long address, unsigned long ip,if (in_atomic ()) goto out_nosemaphore; +if (is_user) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);
@@ -58,12 +59,15 @@ retry: good_area: *code_out =SEGV_ACCERR ; -if (is_write && !(vma->vm_flags &VM_WRITE )) - goto out; - -/* Don't require VM_READ|VM_EXEC for write faults! */ -if (!is_write && !(vma->vm_flags & (VM_READ |VM_EXEC ))) - goto out; +if (is_write) { +if (!(vma->vm_flags &VM_WRITE )) + goto out; + flags |=FAULT_FLAG_WRITE ; + }else { +/* Don't require VM_READ|VM_EXEC for write faults! */ +if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) + goto out; + } do { int fault;
arch/unicore32/mm/fault.c +6 -2
@@ -209,8 +209,7 @@ static int do_pf(unsigned long addr, unsigned int fsr, struct pt_regs *regs)struct task_struct *tsk;struct mm_struct *mm;int fault, sig, code; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - ((!(fsr ^0x12 )) ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;
@@ -222,6 +221,11 @@ static int do_pf(unsigned long addr, unsigned int fsr, struct pt_regs *regs)if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (!(fsr ^0x12 )) + flags |=FAULT_FLAG_WRITE ; +/* * As per x86, we may deadlock here. However, since the kernel only * validly references user space from well defined areas of the code,
arch/x86/mm/fault.c +5 -3
@@ -1011,9 +1011,7 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code)unsigned long address;struct mm_struct *mm;int fault; -int write = error_code &PF_WRITE ; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;
@@ -1083,6 +1081,7 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code)if (user_mode_vm (regs)) {local_irq_enable (); error_code |=PF_USER ; + flags |=FAULT_FLAG_USER ; }else {if (regs->flags &X86_EFLAGS_IF )local_irq_enable ();
@@ -1109,6 +1108,9 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code)return ; } +if (error_code &PF_WRITE ) + flags |=FAULT_FLAG_WRITE ; +/* * When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in
arch/xtensa/mm/fault.c +2 -0
@@ -72,6 +72,8 @@ void do_page_fault(struct pt_regs *regs) address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");#endif +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);
include/linux/mm.h +1 -0
@@ -170,6 +170,7 @@ extern pgprot_t protection_map[16];#define FAULT_FLAG_RETRY_NOWAIT 0x10/* Don't drop mmap_sem and wait when retrying */ #define FAULT_FLAG_KILLABLE 0x20/* The fault task is in SIGKILL killable region */ #define FAULT_FLAG_TRIED 0x40/* second try */ +#define FAULT_FLAG_USER 0x80/* The fault originated in userspace */ /* * vm_fault is filled by the the pagefault handler and passed to the vma's
Signature · 7 lines
1.8.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 3/6] arch: mm: pass userspace fault flag to generic fault handler
Quoted 10 lines · Johannes Weiner
Unlike global OOM handling, memory cgroup code will invoke the OOM killer in any OOM situation because it has no way of telling faults occuring in kernel context - which could be handled more gracefully - from user-triggered faults.
Pass a flag that identifies faults originating in user space from the architecture-specific fault handlers to generic code so that memcg OOM handling can be improved.
Signed-off-by Johannes Weiner <[email protected]>
Looks good to me but I guess maintainers of the affected archs should be CCed
Quoted 861 lines
--- arch/alpha/mm/fault.c | 7 ++++--- arch/arc/mm/fault.c | 6 ++++-- arch/arm/mm/fault.c | 9 ++++++--- arch/arm64/mm/fault.c | 9 ++++++--- arch/avr32/mm/fault.c | 2 ++ arch/cris/mm/fault.c | 6 ++++-- arch/frv/mm/fault.c | 10 ++++++---- arch/hexagon/mm/vm_fault.c | 6 ++++-- arch/ia64/mm/fault.c | 6 ++++-- arch/m32r/mm/fault.c | 10 ++++++---- arch/m68k/mm/fault.c | 2 ++ arch/metag/mm/fault.c | 6 ++++-- arch/microblaze/mm/fault.c | 7 +++++-- arch/mips/mm/fault.c | 6 ++++-- arch/mn10300/mm/fault.c | 2 ++ arch/openrisc/mm/fault.c | 1 + arch/parisc/mm/fault.c | 7 +++++-- arch/powerpc/mm/fault.c | 7 ++++--- arch/s390/mm/fault.c | 2 ++ arch/score/mm/fault.c | 7 ++++++- arch/sh/mm/fault.c | 9 ++++++--- arch/sparc/mm/fault_32.c | 12 +++++++++--- arch/sparc/mm/fault_64.c | 8 +++++--- arch/tile/mm/fault.c | 7 +++++-- arch/um/kernel/trap.c | 20 ++++++++++++-------- arch/unicore32/mm/fault.c | 8 ++++++-- arch/x86/mm/fault.c | 8 +++++--- arch/xtensa/mm/fault.c | 2 ++ include/linux/mm.h | 1 + 29 files changed, 132 insertions(+), 61 deletions(-)arch/alpha/mm/fault.c +4
-3@@ -89,8 +89,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,const struct exception_table_entry *fixup;int fault, si_code =SEGV_MAPERR ;siginfo_t info; -unsigned int flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (cause >0 ?FAULT_FLAG_WRITE :0 )); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;/* As of EV6, a load into $31/$f31 is a prefetch, and never faults (or is suppressed by the PALcode). Support that for older CPUs @@ -115,7 +114,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr,if (address >=TASK_SIZE ) goto vmalloc_fault;#endif - +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);@@ -142,6 +142,7 @@ retry: }else {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }/* If for any reason at all we couldn't handle the fault, arch/arc/mm/fault.c +4
-2@@ -60,8 +60,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address)siginfo_t info;int fault, ret;int write = regs->ecr_cause &ECR_C_PROTV_STORE ;/* ST/EX */ -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;/* * We fault-in kernel-space virtual memory on-demand. The @@ -89,6 +88,8 @@ void do_page_fault(struct pt_regs *regs, unsigned long address)if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);@@ -117,6 +118,7 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area;arch/arm/mm/fault.c +6
-3@@ -261,9 +261,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)struct task_struct *tsk;struct mm_struct *mm;int fault, sig, code; -int write = fsr &FSR_WRITE ; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;if (notify_page_fault (regs, fsr))return 0 ;@@ -282,6 +280,11 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (fsr &FSR_WRITE ) + flags |=FAULT_FLAG_WRITE ; +/* * As per x86, we may deadlock here. However, since the kernel only * validly references user space from well defined areas of the code, arch/arm64/mm/fault.c +6
-3@@ -208,9 +208,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,struct task_struct *tsk;struct mm_struct *mm;int fault, sig, code; -bool write = (esr &ESR_WRITE ) && !(esr &ESR_CM ); -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;@@ -226,6 +224,11 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if ((esr &ESR_WRITE ) && !(esr &ESR_CM )) + flags |=FAULT_FLAG_WRITE ; +/* * As per x86, we may deadlock here. However, since the kernel only * validly references user space from well defined areas of the code, arch/avr32/mm/fault.c +2
-0@@ -86,6 +86,8 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)local_irq_enable (); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);arch/cris/mm/fault.c +4
-2@@ -58,8 +58,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs,struct vm_area_struct * vma;siginfo_t info;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - ((writeaccess &1 ) ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; D(printk(KERN_DEBUG "Page fault for %lX on %X at %lX, prot %d write %d\n",@@ -117,6 +116,8 @@ do_page_fault(unsigned long address, struct pt_regs *regs,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);@@ -155,6 +156,7 @@ retry: }else if (writeaccess ==1 ) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area;arch/frv/mm/fault.c +6
-4@@ -34,11 +34,11 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long earstruct vm_area_struct *vma;struct mm_struct *mm;unsigned long _pme, lrai, lrad, fixup; +unsigned long flags =0 ;siginfo_t info;pgd_t *pge;pud_t *pue;pte_t *pte; -int write;int fault;#if 0 @@ -81,6 +81,9 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long earif (in_atomic () || !mm) goto no_context; +if (user_mode (__frame)) + flags |=FAULT_FLAG_USER ; +down_read (&mm->mmap_sem); vma =find_vma (mm, ear0);@@ -129,7 +132,6 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear*/ good_area: info.si_code =SEGV_ACCERR ; - write =0 ;switch (esr0 &ESR0_ATXC ) {default :/* handle write to write protected page */ @@ -140,7 +142,7 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear#endif if (!(vma->vm_flags &VM_WRITE )) goto bad_area; - write =1 ; + flags |=FAULT_FLAG_WRITE ;break ;/* handle read from protected page */ @@ -162,7 +164,7 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear* make sure we exit gracefully rather than endlessly redo * the fault. */ - fault =handle_mm_fault (mm, vma, ear0, write ?FAULT_FLAG_WRITE :0 ); + fault =handle_mm_fault (mm, vma, ear0, flags);if (unlikely (fault &VM_FAULT_ERROR )) {if (fault &VM_FAULT_OOM ) goto out_of_memory;arch/hexagon/mm/vm_fault.c +4
-2@@ -53,8 +53,7 @@ void do_page_fault(unsigned long address, long cause, struct pt_regs *regs)int si_code =SEGV_MAPERR ;int fault;const struct exception_table_entry *fixup; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (cause >0 ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;/* * If we're in an interrupt or have no user context, @@ -65,6 +64,8 @@ void do_page_fault(unsigned long address, long cause, struct pt_regs *regs)local_irq_enable (); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);@@ -96,6 +97,7 @@ good_area:case FLT_STORE :if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ;break ; }arch/ia64/mm/fault.c +4
-2@@ -90,8 +90,6 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re mask = ((((isr >>IA64_ISR_X_BIT ) &1UL ) <<VM_EXEC_BIT ) | (((isr >>IA64_ISR_W_BIT ) &1UL ) <<VM_WRITE_BIT )); - flags |= ((mask &VM_WRITE ) ?FAULT_FLAG_WRITE :0 ); -/* mmap_sem is performance critical.... */ prefetchw (&mm->mmap_sem);@@ -119,6 +117,10 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *reif (notify_page_fault (regs,TRAP_BRKPT ))return ; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (mask &VM_WRITE ) + flags |=FAULT_FLAG_WRITE ; retry:down_read (&mm->mmap_sem);arch/m32r/mm/fault.c +6
-4@@ -78,7 +78,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code,struct mm_struct *mm;struct vm_area_struct * vma;unsigned long page, addr; -int write; +unsigned long flags =0 ;int fault;siginfo_t info;@@ -117,6 +117,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code,if (in_atomic () || !mm) goto bad_area_nosemaphore; +if (error_code &ACE_USERMODE ) + flags |=FAULT_FLAG_USER ; +/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an @@ -166,14 +169,13 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code,*/ good_area: info.si_code =SEGV_ACCERR ; - write =0 ;switch (error_code & (ACE_WRITE |ACE_PROTECTION )) {default :/* 3: write, present */ /* fall through */ case ACE_WRITE :/* write, not present */ if (!(vma->vm_flags &VM_WRITE )) goto bad_area; - write++; + flags |=FAULT_FLAG_WRITE ;break ;case ACE_PROTECTION :/* read, present */ case 0 :/* read, not present */ @@ -194,7 +196,7 @@ good_area:*/ addr = (address &PAGE_MASK );set_thread_fault_code (error_code); - fault =handle_mm_fault (mm, vma, addr, write ?FAULT_FLAG_WRITE :0 ); + fault =handle_mm_fault (mm, vma, addr, flags);if (unlikely (fault &VM_FAULT_ERROR )) {if (fault &VM_FAULT_OOM ) goto out_of_memory;arch/m68k/mm/fault.c +2
-0@@ -88,6 +88,8 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);arch/metag/mm/fault.c +4
-2@@ -53,8 +53,7 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,struct vm_area_struct *vma, *prev_vma;siginfo_t info;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write_access ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current;@@ -109,6 +108,8 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);@@ -121,6 +122,7 @@ good_area:if (write_access) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC |VM_WRITE ))) goto bad_area;arch/microblaze/mm/fault.c +5
-2@@ -92,8 +92,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,int code =SEGV_MAPERR ;int is_write = error_code &ESR_S ;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (is_write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; regs->ear = address; regs->esr = error_code;@@ -121,6 +120,9 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,die ("Weird page fault" , regs,SIGSEGV ); } +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an @@ -199,6 +201,7 @@ good_area:if (unlikely (is_write)) {if (unlikely (!(vma->vm_flags &VM_WRITE ))) goto bad_area; + flags |=FAULT_FLAG_WRITE ;/* a read */ }else {/* protection fault */ arch/mips/mm/fault.c +4
-2@@ -42,8 +42,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,const int field =sizeof (unsigned long ) *2 ;siginfo_t info;int fault; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;#if 0 printk("Cpu%d[%s:%d:%0*lx:%ld:%0*lx]\n", raw_smp_processor_id(),@@ -93,6 +92,8 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,if (in_atomic () || !mm) goto bad_area_nosemaphore; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);@@ -114,6 +115,7 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (cpu_has_rixi) {if (address == regs->cp0_epc && !(vma->vm_flags &VM_EXEC )) {arch/mn10300/mm/fault.c +2
-0@@ -171,6 +171,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long fault_code,if (in_atomic () || !mm) goto no_context; +if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem);arch/openrisc/mm/fault.c +1
-0@@ -86,6 +86,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,if (user_mode (regs)) {/* Exception was in userspace: reenable interrupts */ local_irq_enable (); + flags |=FAULT_FLAG_USER ; }else {/* If exception was in a syscall, then IRQ's may have * been enabled or disabled. If they were enabled, arch/parisc/mm/fault.c +5
-2@@ -180,6 +180,10 @@ void do_page_fault(struct pt_regs *regs, unsigned long code,if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (acc_type &VM_WRITE ) + flags |=FAULT_FLAG_WRITE ; retry:down_read (&mm->mmap_sem); vma =find_vma_prev (mm, address, &prev_vma);@@ -203,8 +207,7 @@ good_area:* fault. */ - fault =handle_mm_fault (mm, vma, address, - flags | ((acc_type &VM_WRITE ) ?FAULT_FLAG_WRITE :0 )); + fault =handle_mm_fault (mm, vma, address, flags);if ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current))return ;arch/powerpc/mm/fault.c +4
-3@@ -223,9 +223,6 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, is_write = error_code &ESR_DST ;#endif /* CONFIG_4xx || CONFIG_BOOKE */ -if (is_write) - flags |=FAULT_FLAG_WRITE ; -#ifdef CONFIG_PPC_ICSWX /* * we need to do this early because this "data storage @@ -280,6 +277,9 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS ,1 , regs, address); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an @@ -408,6 +408,7 @@ good_area: }else if (is_write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ;/* a read */ }else {/* protection fault */ arch/s390/mm/fault.c +2
-0@@ -302,6 +302,8 @@ static inline int do_exception(struct pt_regs *regs, int access) address = trans_exc_code & __FAIL_ADDR_MASK;perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS ,1 , regs, address); flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; +if (regs->psw.mask &PSW_MASK_PSTATE ) + flags |=FAULT_FLAG_USER ;if (access ==VM_WRITE || (trans_exc_code & store_indication) ==0x400 ) flags |=FAULT_FLAG_WRITE ;down_read (&mm->mmap_sem);arch/score/mm/fault.c +6
-1@@ -47,6 +47,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,struct task_struct *tsk = current;struct mm_struct *mm = tsk->mm;const int field =sizeof (unsigned long ) *2 ; +unsigned long flags =0 ;siginfo_t info;int fault;@@ -75,6 +76,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,if (in_atomic () || !mm) goto bad_area_nosemaphore; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +down_read (&mm->mmap_sem); vma =find_vma (mm, address);if (!vma)@@ -95,6 +99,7 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_WRITE |VM_EXEC ))) goto bad_area;@@ -105,7 +110,7 @@ good_area:* make sure we exit gracefully rather than endlessly redo * the fault. */ - fault =handle_mm_fault (mm, vma, address, write); + fault =handle_mm_fault (mm, vma, address, flags);if (unlikely (fault &VM_FAULT_ERROR )) {if (fault &VM_FAULT_OOM ) goto out_of_memory;arch/sh/mm/fault.c +6
-3@@ -400,9 +400,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,struct mm_struct *mm;struct vm_area_struct * vma;int fault; -int write = error_code &FAULT_CODE_WRITE ; -unsigned int flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 )); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;@@ -476,6 +474,11 @@ good_area:set_thread_fault_code (error_code); +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (error_code &FAULT_CODE_WRITE ) + flags |=FAULT_FLAG_WRITE ; +/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo arch/sparc/mm/fault_32.c +9
-3@@ -177,8 +177,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,unsigned long g2;int from_user = !(regs->psr &PSR_PS );int fault, code; -unsigned int flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 )); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ;if (text_fault) address = regs->pc;@@ -235,6 +234,11 @@ good_area: goto bad_area; } +if (from_user) + flags |=FAULT_FLAG_USER ; +if (write) + flags |=FAULT_FLAG_WRITE ; +/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -383,6 +387,7 @@ static void force_user_fault(unsigned long address, int write)struct vm_area_struct *vma;struct task_struct *tsk = current;struct mm_struct *mm = tsk->mm; +unsigned int flags =FAULT_FLAG_USER ;int code; code =SEGV_MAPERR ;@@ -402,11 +407,12 @@ good_area:if (write) {if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area; } -switch (handle_mm_fault (mm, vma, address, write ?FAULT_FLAG_WRITE :0 )) { +switch (handle_mm_fault (mm, vma, address, flags)) {case VM_FAULT_SIGBUS :case VM_FAULT_OOM : goto do_sigbus;arch/sparc/mm/fault_64.c +5
-3@@ -314,8 +314,9 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) }else {bad_kernel_pc (regs, address);return ; - } - } + } + }else + flags |=FAULT_FLAG_USER ;/* * If we're in an interrupt or have no user @@ -418,13 +419,14 @@ good_area: vma->vm_file != NULL)set_thread_fault_code (fault_code |FAULT_CODE_BLKCOMMIT ); + + flags |=FAULT_FLAG_WRITE ; }else {/* Allow reads even for write-only mappings */ if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) goto bad_area; } - flags |= ((fault_code &FAULT_CODE_WRITE ) ?FAULT_FLAG_WRITE :0 ); fault =handle_mm_fault (mm, vma, address, flags);if ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current))arch/tile/mm/fault.c +5
-2@@ -280,8 +280,7 @@ static int handle_page_fault(struct pt_regs *regs,if (!is_page_fault) write =1 ; - flags = (FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 )); + flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; is_kernel_mode = (EX1_PL (regs->ex1) !=USER_PL );@@ -365,6 +364,9 @@ static int handle_page_fault(struct pt_regs *regs, goto bad_area_nosemaphore; } +if (!is_kernel_mode) + flags |=FAULT_FLAG_USER ; +/* * When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the @@ -425,6 +427,7 @@ good_area:#endif if (!(vma->vm_flags &VM_WRITE )) goto bad_area; + flags |=FAULT_FLAG_WRITE ; }else {if (!is_page_fault || !(vma->vm_flags &VM_READ )) goto bad_area;arch/um/kernel/trap.c +12
-8@@ -30,8 +30,7 @@ int handle_page_fault(unsigned long address, unsigned long ip,pmd_t *pmd;pte_t *pte;int err = -EFAULT ; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (is_write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; *code_out =SEGV_MAPERR ;@@ -42,6 +41,8 @@ int handle_page_fault(unsigned long address, unsigned long ip,if (in_atomic ()) goto out_nosemaphore; +if (is_user) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);@@ -58,12 +59,15 @@ retry: good_area: *code_out =SEGV_ACCERR ; -if (is_write && !(vma->vm_flags &VM_WRITE )) - goto out; - -/* Don't require VM_READ|VM_EXEC for write faults! */ -if (!is_write && !(vma->vm_flags & (VM_READ |VM_EXEC ))) - goto out; +if (is_write) { +if (!(vma->vm_flags &VM_WRITE )) + goto out; + flags |=FAULT_FLAG_WRITE ; + }else { +/* Don't require VM_READ|VM_EXEC for write faults! */ +if (!(vma->vm_flags & (VM_READ |VM_EXEC ))) + goto out; + } do { int fault;arch/unicore32/mm/fault.c +6
-2@@ -209,8 +209,7 @@ static int do_pf(unsigned long addr, unsigned int fsr, struct pt_regs *regs)struct task_struct *tsk;struct mm_struct *mm;int fault, sig, code; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - ((!(fsr ^0x12 )) ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;@@ -222,6 +221,11 @@ static int do_pf(unsigned long addr, unsigned int fsr, struct pt_regs *regs)if (in_atomic () || !mm) goto no_context; +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; +if (!(fsr ^0x12 )) + flags |=FAULT_FLAG_WRITE ; +/* * As per x86, we may deadlock here. However, since the kernel only * validly references user space from well defined areas of the code, arch/x86/mm/fault.c +5
-3@@ -1011,9 +1011,7 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code)unsigned long address;struct mm_struct *mm;int fault; -int write = error_code &PF_WRITE ; -unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE | - (write ?FAULT_FLAG_WRITE :0 ); +unsigned int flags =FAULT_FLAG_ALLOW_RETRY |FAULT_FLAG_KILLABLE ; tsk = current; mm = tsk->mm;@@ -1083,6 +1081,7 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code)if (user_mode_vm (regs)) {local_irq_enable (); error_code |=PF_USER ; + flags |=FAULT_FLAG_USER ; }else {if (regs->flags &X86_EFLAGS_IF )local_irq_enable ();@@ -1109,6 +1108,9 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code)return ; } +if (error_code &PF_WRITE ) + flags |=FAULT_FLAG_WRITE ; +/* * When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in arch/xtensa/mm/fault.c +2
-0@@ -72,6 +72,8 @@ void do_page_fault(struct pt_regs *regs) address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");#endif +if (user_mode (regs)) + flags |=FAULT_FLAG_USER ; retry:down_read (&mm->mmap_sem); vma =find_vma (mm, address);include/linux/mm.h +1
-0@@ -170,6 +170,7 @@ extern pgprot_t protection_map[16];#define FAULT_FLAG_RETRY_NOWAIT 0x10/* Don't drop mmap_sem and wait when retrying */ #define FAULT_FLAG_KILLABLE 0x20/* The fault task is in SIGKILL killable region */ #define FAULT_FLAG_TRIED 0x40/* second try */ +#define FAULT_FLAG_USER 0x80/* The fault originated in userspace */ /* * vm_fault is filled by the the pagefault handler and passed to the vma's Signature · 1 line
1.8.3.2
Signature · 2 lines
Michal Hocko SUSE Labs
Johannes Weiner
maintainerRe: [patch 3/6] arch: mm: pass userspace fault flag to generic fault handler
Quoted 6 lines · Michal Hocko
Quoted 5 lines · Johannes Weiner
Unlike global OOM handling, memory cgroup code will invoke the OOM killer in any OOM situation because it has no way of telling faults occuring in kernel context - which could be handled more gracefully - from user-triggered faults.
Quoted 8 lines
Quoted 5 lines
Pass a flag that identifies faults originating in user space from the architecture-specific fault handlers to generic code so that memcg OOM handling can be improved.
Signed-off-by Johannes Weiner <[email protected]>Looks good to me but I guess maintainers of the affected archs should be CCed
linux-arch is on CC, that should do the trick :)
Quoted 1 line
Reviewed-by Michal Hocko <[email protected]>
Thanks!
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Johannes Weiner
maintainerPATCH 4/6 x86: finish user fault error path with fatal signal
In reply to Johannes WeinerThe x86 fault handler bails in the middle of error handling when the task has a fatal signal pending. For a subsequent patch this is a problem in OOM situations because it relies on pagefault_out_of_memory() being called even when the task has been killed, to perform proper per-task OOM state unwinding.
Shortcutting the fault like this is a rather minor optimization that saves a few instructions in rare cases. Just remove it for user-triggered faults.
Use the opportunity to split the fault retry handling from actual fault errors and add locking documentation that reads suprisingly similar to ARM's.
--- arch/x86/mm/fault.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-)
arch/x86/mm/fault.c +17 -18
@@ -842,23 +842,15 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,force_sig_info_fault (SIGBUS , code, address, tsk, fault); } -static noinline int +static noinline voidmm_fault_error (struct pt_regs *regs,unsigned long error_code,unsigned long address,unsigned int fault) { -/* -* Pagefault was interrupted by SIGKILL. We have no reason to -* continue pagefault. -*/ -if (fatal_signal_pending (current)) { -if (!(fault &VM_FAULT_RETRY )) -up_read (¤t->mm->mmap_sem); -if (!(error_code &PF_USER )) -no_context (regs, error_code, address,0 ,0 ); -return 1 ; +if (fatal_signal_pending (current) && !(error_code &PF_USER )) { +up_read (¤t->mm->mmap_sem); +no_context (regs, error_code, address,0 ,0 ); +return ; } -if (!(fault &VM_FAULT_ERROR )) -return 0 ;if (fault &VM_FAULT_OOM ) {/* Kernel mode? Handle exceptions or die: */
@@ -866,7 +858,7 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,up_read (¤t->mm->mmap_sem);no_context (regs, error_code, address,SIGSEGV ,SEGV_MAPERR ); -return 1 ; +return ; }up_read (¤t->mm->mmap_sem);
@@ -884,7 +876,6 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,else BUG (); } -return 1 ; }static int spurious_fault_check (unsigned long error_code,pte_t *pte)
@@ -1189,9 +1180,17 @@ good_area:*/ fault =handle_mm_fault (mm, vma, address, flags); -if (unlikely (fault & (VM_FAULT_RETRY |VM_FAULT_ERROR ))) { -if (mm_fault_error (regs, error_code, address, fault)) -return ; +/* +* If we need to retry but a fatal signal is pending, handle the +* signal first. We do not need to release the mmap_sem because it +* would already be released in __lock_page_or_retry in mm/filemap.c. +*/ +if (unlikely ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current))) +return ; + +if (unlikely (fault &VM_FAULT_ERROR )) { +mm_fault_error (regs, error_code, address, fault); +return ; }/*
Signature · 7 lines
1.8.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 4/6] x86: finish user fault error path with fatal signal
Quoted 9 lines · Johannes Weiner
The x86 fault handler bails in the middle of error handling when the task has a fatal signal pending. For a subsequent patch this is a problem in OOM situations because it relies on
pagefault_out_of_memory()being called even when the task has been killed, to perform proper per-task OOM state unwinding.Shortcutting the fault like this is a rather minor optimization that saves a few instructions in rare cases. Just remove it for user-triggered faults.
OK, I thought that this optimization tries to prevent calling OOM because the current might release some memory but that wasn't the intention of b80ef10e8 (x86: Move do_page_fault()'s error path under unlikely()).
Quoted 5 lines
Use the opportunity to split the fault retry handling from actual fault errors and add locking documentation that reads suprisingly similar to ARM's.
Signed-off-by Johannes Weiner <[email protected]>
Quoted 78 lines
--- arch/x86/mm/fault.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-)arch/x86/mm/fault.c +17
-18@@ -842,23 +842,15 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,force_sig_info_fault (SIGBUS , code, address, tsk, fault); } -static noinline int +static noinline voidmm_fault_error (struct pt_regs *regs,unsigned long error_code,unsigned long address,unsigned int fault) { -/* -* Pagefault was interrupted by SIGKILL. We have no reason to -* continue pagefault. -*/ -if (fatal_signal_pending (current)) { -if (!(fault &VM_FAULT_RETRY )) -up_read (¤t->mm->mmap_sem); -if (!(error_code &PF_USER )) -no_context (regs, error_code, address,0 ,0 ); -return 1 ; +if (fatal_signal_pending (current) && !(error_code &PF_USER )) { +up_read (¤t->mm->mmap_sem); +no_context (regs, error_code, address,0 ,0 ); +return ; } -if (!(fault &VM_FAULT_ERROR )) -return 0 ;if (fault &VM_FAULT_OOM ) {/* Kernel mode? Handle exceptions or die: */ @@ -866,7 +858,7 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,up_read (¤t->mm->mmap_sem);no_context (regs, error_code, address,SIGSEGV ,SEGV_MAPERR ); -return 1 ; +return ; }up_read (¤t->mm->mmap_sem);@@ -884,7 +876,6 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,else BUG (); } -return 1 ; }static int spurious_fault_check (unsigned long error_code,pte_t *pte)@@ -1189,9 +1180,17 @@ good_area:*/ fault =handle_mm_fault (mm, vma, address, flags); -if (unlikely (fault & (VM_FAULT_RETRY |VM_FAULT_ERROR ))) { -if (mm_fault_error (regs, error_code, address, fault)) -return ; +/* +* If we need to retry but a fatal signal is pending, handle the +* signal first. We do not need to release the mmap_sem because it +* would already be released in __lock_page_or_retry in mm/filemap.c. +*/ +if (unlikely ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current))) +return ; + +if (unlikely (fault &VM_FAULT_ERROR )) { +mm_fault_error (regs, error_code, address, fault); +return ; }/* Signature · 1 line
1.8.3.2
Signature · 8 lines
Michal Hocko SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Johannes Weiner
maintainerRe: [patch 4/6] x86: finish user fault error path with fatal signal
Quoted 7 lines · Michal Hocko
Quoted 6 lines · Johannes Weiner
The x86 fault handler bails in the middle of error handling when the task has a fatal signal pending. For a subsequent patch this is a problem in OOM situations because it relies on
pagefault_out_of_memory()being called even when the task has been killed, to perform proper per-task OOM state unwinding.
Quoted 8 lines
Quoted 3 lines
Shortcutting the fault like this is a rather minor optimization that saves a few instructions in rare cases. Just remove it for user-triggered faults.
OK, I thought that this optimization tries to prevent calling OOM because the current might release some memory but that wasn't the intention of
b80ef10e8(x86: Move do_page_fault()'s error path underunlikely()).
out_of_memory() also checks the caller for pending signals, so it would not actually invoke the OOM killer if the caller is already dying.
Quoted 7 lines
Quoted 5 lines
Use the opportunity to split the fault retry handling from actual fault errors and add locking documentation that reads suprisingly similar to ARM's.
Signed-off-by Johannes Weiner <[email protected]>Reviewed-by Michal Hocko <[email protected]>
Thanks!
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 4/6] x86: finish user fault error path with fatal signal
Quoted 12 lines · Johannes Weiner
Quoted 11 lines · Michal Hocko
Quoted 9 lines · Johannes Weiner
The x86 fault handler bails in the middle of error handling when the task has a fatal signal pending. For a subsequent patch this is a problem in OOM situations because it relies on
pagefault_out_of_memory()being called even when the task has been killed, to perform proper per-task OOM state unwinding.Shortcutting the fault like this is a rather minor optimization that saves a few instructions in rare cases. Just remove it for user-triggered faults.
Quoted 8 lines
Quoted 4 lines
OK, I thought that this optimization tries to prevent calling OOM because the current might release some memory but that wasn't the intention of
b80ef10e8(x86: Move do_page_fault()'s error path underunlikely()).
out_of_memory()also checks the caller for pending signals, so it would not actually invoke the OOM killer if the caller is already dying.
Ohh, right you are! I should have checked deeper in the call chain.
Quoted 9 lines
Quoted 7 lines
Quoted 5 lines
Use the opportunity to split the fault retry handling from actual fault errors and add locking documentation that reads suprisingly similar to ARM's.
Signed-off-by Johannes Weiner <[email protected]>Reviewed-by Michal Hocko <[email protected]>Thanks!
Signature · 8 lines
Michal Hocko SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 4/6] x86: finish user fault error path with fatal signal
In reply to Johannes WeinerQuoted 10 lines · Johannes Weiner
The x86 fault handler bails in the middle of error handling when the task has a fatal signal pending. For a subsequent patch this is a problem in OOM situations because it relies on
pagefault_out_of_memory()being called even when the task has been killed, to perform proper per-task OOM state unwinding.Shortcutting the fault like this is a rather minor optimization that saves a few instructions in rare cases. Just remove it for user-triggered faults.
Quoted 5 lines
Use the opportunity to split the fault retry handling from actual fault errors and add locking documentation that reads suprisingly similar to ARM's.
Signed-off-by Johannes Weiner <[email protected]>
Johannes Weiner
maintainerPATCH 5/6 mm: memcg: enable memcg OOM killer only for user faults
In reply to Johannes WeinerSystem 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.
--- include/linux/memcontrol.h | 23 +++++++++++++++++++++++ include/linux/sched.h | 3 +++ mm/filemap.c | 11 ++++++++++- mm/memcontrol.c | 2 +- mm/memory.c | 40 ++++++++++++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 12 deletions(-)
include/linux/memcontrol.h +23 -0
@@ -125,6 +125,24 @@ 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_xchg_may_oom - toggle the memcg OOM killer for a task +* @p: 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_xchg_may_oom (struct task_struct *p,bool new) +{ +bool old; + + old = p->memcg_oom.may_oom; + p->memcg_oom.may_oom = new; + +return old; +} +#ifdef CONFIG_MEMCG_SWAP extern int do_swap_account;#endif
@@ -348,6 +366,11 @@ static inline void mem_cgroup_end_update_page_stat(struct page *page, { } +static inline bool mem_cgroup_xchg_may_oom (struct task_struct *p,bool new) +{ +return !new; +} +static inline void mem_cgroup_inc_page_stat (struct page *page,enum mem_cgroup_page_stat_item idx) {
include/linux/sched.h +3 -0
@@ -1398,6 +1398,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;
mm/filemap.c +10 -1
@@ -1617,6 +1617,7 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)struct file_ra_state *ra = &file->f_ra;struct inode *inode = mapping->host;pgoff_t offset = vmf->pgoff; +unsigned int may_oom;struct page *page;pgoff_t size;int ret =0 ;
@@ -1626,7 +1627,11 @@ 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 )) {
@@ -1634,10 +1639,14 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)* We found the page, so try async readahead before * waiting for the lock. */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_async_mmap_readahead (vma, ra, file, page, offset); +mem_cgroup_xchg_may_oom (current, may_oom); }else if (!page) {/* No page in the page cache at all */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_sync_mmap_readahead (vma, ra, file, offset); +mem_cgroup_xchg_may_oom (current, may_oom);count_vm_event (PGMAJFAULT );mem_cgroup_count_vm_event (vma->vm_mm,PGMAJFAULT ); ret =VM_FAULT_MAJOR ;
mm/memcontrol.c +1 -1
@@ -2614,7 +2614,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)))
mm/memory.c +30 -10
@@ -3752,22 +3752,14 @@ unlock:/* * 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);
@@ -3851,6 +3843,34 @@ retry: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 ) +WARN_ON (mem_cgroup_xchg_may_oom (current, true) == true); + + ret =__handle_mm_fault (mm, vma, address, flags); + +if (flags &FAULT_FLAG_USER ) +WARN_ON (mem_cgroup_xchg_may_oom (current, false) == false); + +return ret; +} +#ifndef __PAGETABLE_PUD_FOLDED/* * Allocate page upper directory.
Signature · 7 lines
1.8.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 5/6] mm: memcg: enable memcg OOM killer only for user faults
Quoted 7 lines · Johannes Weiner
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 <[email protected]>
It looks OK to me, but I have few comments bellow. Nothing really huge but I do not like mem_cgroup_xchg_may_oom for !MEMCG.
Quoted 13 lines
--- include/linux/memcontrol.h | 23 +++++++++++++++++++++++ include/linux/sched.h | 3 +++ mm/filemap.c | 11 ++++++++++- mm/memcontrol.c | 2 +- mm/memory.c | 40 ++++++++++++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 12 deletions(-)include/linux/memcontrol.h +0
-0
include/linux/memcontrol.h extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, · line 130
Quoted 6 lines
include/linux/memcontrol.h +3
-0@@ -125,6 +125,24 @@ 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_xchg_may_oom - toggle the memcg OOM killer for a task +* @p: task
Is this ever safe to call on !current? If not then I wouldn't allow to give p as a parameter.
Quoted 19 lines
include/linux/memcontrol.h +15
-0+* @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_xchg_may_oom (struct task_struct *p,bool new) +{ +bool old; + + old = p->memcg_oom.may_oom; + p->memcg_oom.may_oom = new; + +return old; +} +#ifdef CONFIG_MEMCG_SWAP extern int do_swap_account;#endif
include/linux/memcontrol.h static inline void mem_cgroup_end_update_page_stat(struct page *page, · line 372
Quoted 7 lines
include/linux/memcontrol.h +4
-0@@ -348,6 +366,11 @@ static inline void mem_cgroup_end_update_page_stat(struct page *page, { } +static inline bool mem_cgroup_xchg_may_oom (struct task_struct *p,bool new) +{ +return !new; +}
This looks a bit weird. MEMCG is not compiled in and yet we return that the previous may_oom could be true. This is calling for troubles if somebody tries to do any following decisions on the returned value. Why not simply return false unconditionally?
[...]
Quoted 4 lines
mm/filemap.c +0
-0
[...]
mm/filemap.c int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) · line 1642
Quoted 5 lines
mm/filemap.c +1
-0@@ -1634,10 +1639,14 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)* We found the page, so try async readahead before * waiting for the lock. */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );
s/0/false/
below ditto
Quoted 10 lines
mm/filemap.c +3
-0do_async_mmap_readahead (vma, ra, file, page, offset); +mem_cgroup_xchg_may_oom (current, may_oom); }else if (!page) {/* No page in the page cache at all */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_sync_mmap_readahead (vma, ra, file, offset); +mem_cgroup_xchg_may_oom (current, may_oom);count_vm_event (PGMAJFAULT );mem_cgroup_count_vm_event (vma->vm_mm,PGMAJFAULT ); ret =VM_FAULT_MAJOR ;
[...]
Quoted 4 lines
mm/memory.c +0
-0
[...]
Quoted 18 lines
mm/memory.c +14
-0@@ -3851,6 +3843,34 @@ retry: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); + +/*
mm/memory.c retry: · line 3869
Quoted 10 lines
mm/memory.c +10
-0+* Enable the memcg OOM handling for faults triggered in user +* space. Kernel faults are handled more gracefully. +*/ +if (flags &FAULT_FLAG_USER ) +WARN_ON (mem_cgroup_xchg_may_oom (current, true) == true); + + ret =__handle_mm_fault (mm, vma, address, flags); + +if (flags &FAULT_FLAG_USER ) +WARN_ON (mem_cgroup_xchg_may_oom (current, false) == false);
Ohh, I see why you used !new in mem_cgroup_xchg_may_oom for !MEMCG case above. This could be fixed easily if you add mem_cgroup_{enable,disable}_oom which would be empty for !MEMCG.
Quoted 10 lines
mm/memory.c +4
-0+ +return ret; +} +#ifndef __PAGETABLE_PUD_FOLDED/* * Allocate page upper directory. Signature · 1 line
1.8.3.2
Signature · 2 lines
Michal Hocko SUSE Labs
Johannes Weiner
maintainerRe: [patch 5/6] mm: memcg: enable memcg OOM killer only for user faults
Quoted 11 lines · Michal Hocko
Quoted 7 lines · Johannes Weiner
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 <[email protected]>It looks OK to me, but I have few comments bellow. Nothing really huge but I do not like
mem_cgroup_xchg_may_oomfor !MEMCG.
:-)
Quoted 16 lines
Quoted 16 lines
--- include/linux/memcontrol.h | 23 +++++++++++++++++++++++ include/linux/sched.h | 3 +++ mm/filemap.c | 11 ++++++++++- mm/memcontrol.c | 2 +- mm/memory.c | 40 ++++++++++++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 12 deletions(-)include/linux/memcontrol.h +0
-0@@ -125,6 +125,24 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,extern void mem_cgroup_replace_page_cache (struct page *oldpage,struct page *newpage);
Quoted 6 lines
include/linux/memcontrol.h extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, · line 130
Quoted 3 lines
include/linux/memcontrol.h +3
-0+/** +* mem_cgroup_xchg_may_oom - toggle the memcg OOM killer for a task +* @p: task Is this ever safe to call on !current? If not then I wouldn't allow to give p as a parameter.
Makes sense, I removed the parameter.
Quoted 9 lines
include/linux/memcontrol.h int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) · line 1642
Quoted 5 lines
include/linux/memcontrol.h +1
-0@@ -1634,10 +1639,14 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)* We found the page, so try async readahead before * waiting for the lock. */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );s/0/false/
below ditto
Oops, updated both sites.
Quoted 40 lines
Quoted 10 lines
include/linux/memcontrol.h +3
-0do_async_mmap_readahead (vma, ra, file, page, offset); +mem_cgroup_xchg_may_oom (current, may_oom); }else if (!page) {/* No page in the page cache at all */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_sync_mmap_readahead (vma, ra, file, offset); +mem_cgroup_xchg_may_oom (current, may_oom);count_vm_event (PGMAJFAULT );mem_cgroup_count_vm_event (vma->vm_mm,PGMAJFAULT ); ret =VM_FAULT_MAJOR ;[...]
Quoted 4 lines
mm/memory.c +0
-0[...]
Quoted 24 lines
mm/memory.c +20
-0@@ -3851,6 +3843,34 @@ retry: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 ) +WARN_ON (mem_cgroup_xchg_may_oom (current, true) == true); +
Quoted 8 lines
mm/memory.c retry: · line 3869
Quoted 4 lines
mm/memory.c +4
-0+ ret =__handle_mm_fault (mm, vma, address, flags); + +if (flags &FAULT_FLAG_USER ) +WARN_ON (mem_cgroup_xchg_may_oom (current, false) == false);Ohh, I see why you used !new in
mem_cgroup_xchg_may_oomfor !MEMCG case above. This could be fixed easily if you add mem_cgroup_{enable,disable}_oom which would be empty for !MEMCG.
You're right, it's much cleaner this way. I added the enable/disable functions, which has the advantage that the memcg-specific WARN_ON is also not in generic code but encapsulated nicely.
Thanks for your feedback!
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 5/6] mm: memcg: enable memcg OOM killer only for user faults
In reply to Johannes WeinerQuoted 30 lines · Johannes Weiner
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 <[email protected]>--- include/linux/memcontrol.h | 23 +++++++++++++++++++++++ include/linux/sched.h | 3 +++ mm/filemap.c | 11 ++++++++++- mm/memcontrol.c | 2 +- mm/memory.c | 40 ++++++++++++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 12 deletions(-)include/linux/memcontrol.h +7
-0@@ -125,6 +125,24 @@ 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_xchg_may_oom - toggle the memcg OOM killer for a task +* @p: 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.
include/linux/memcontrol.h extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, · line 144
Quoted 10 lines
include/linux/memcontrol.h +10
-0+*/ +static inline bool mem_cgroup_xchg_may_oom (struct task_struct *p,bool new) +{ +bool old; + + old = p->memcg_oom.may_oom; + p->memcg_oom.may_oom = new; + +return old; +}
The name of xchg strongly suggest the function use compare-swap op. So, it seems misleading name. I suggest just use "set_*" or something else. In linux kernel, many setter functions already return old value. Don't mind.
include/linux/sched.h struct task_struct { · line 1403
Quoted 11 lines
include/linux/sched.h +3
-0@@ -1398,6 +1398,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;
This ":1" makes slower but doesn't diet any memory space, right? I suggest to use bool. If anybody need to diet in future, he may change it to bit field. That's ok, let's stop too early and questionable micro optimization.
mm/filemap.c int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) · line 1620
Quoted 9 lines
mm/filemap.c +1
-0@@ -1617,6 +1617,7 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)struct file_ra_state *ra = &file->f_ra;struct inode *inode = mapping->host;pgoff_t offset = vmf->pgoff; +unsigned int may_oom;
Why don't you use bool? your mem_cgroup_xchg_may_oom() uses bool and it seems cleaner more.
Quoted 83 lines
mm/filemap.c +9
-1@@ -1626,7 +1627,11 @@ 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 )) {@@ -1634,10 +1639,14 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)* We found the page, so try async readahead before * waiting for the lock. */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_async_mmap_readahead (vma, ra, file, page, offset); +mem_cgroup_xchg_may_oom (current, may_oom); }else if (!page) {/* No page in the page cache at all */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_sync_mmap_readahead (vma, ra, file, offset); +mem_cgroup_xchg_may_oom (current, may_oom);count_vm_event (PGMAJFAULT );mem_cgroup_count_vm_event (vma->vm_mm,PGMAJFAULT ); ret =VM_FAULT_MAJOR ;mm/memcontrol.c +1
-1@@ -2614,7 +2614,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)))mm/memory.c +11
-10@@ -3752,22 +3752,14 @@ unlock:/* * 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);@@ -3851,6 +3843,34 @@ retry: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 );
mm/memory.c retry: · line 3864
Quoted 10 lines
mm/memory.c +10
-0+ +/* 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 ) +WARN_ON (mem_cgroup_xchg_may_oom (current, true) == true);
Please don't assume WARN_ON never erase any code. I'm not surprised if embedded guys replace WARN_ON with nop in future.
Thanks.
Johannes Weiner
maintainerRe: [patch 5/6] mm: memcg: enable memcg OOM killer only for user faults
Quoted 37 lines · KOSAKI Motohiro
Quoted 36 lines · Johannes Weiner
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 <[email protected]>--- include/linux/memcontrol.h | 23 +++++++++++++++++++++++ include/linux/sched.h | 3 +++ mm/filemap.c | 11 ++++++++++- mm/memcontrol.c | 2 +- mm/memory.c | 40 ++++++++++++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 12 deletions(-)include/linux/memcontrol.h +13
-0@@ -125,6 +125,24 @@ 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_xchg_may_oom - toggle the memcg OOM killer for a task +* @p: 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_xchg_may_oom (struct task_struct *p,bool new) +{ +bool old; + + old = p->memcg_oom.may_oom;
Quoted 8 lines
include/linux/memcontrol.h extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, · line 144
Quoted 4 lines
include/linux/memcontrol.h +4
-0+ p->memcg_oom.may_oom = new; + +return old; +}The name of xchg strongly suggest the function use compare-swap op. So, it seems misleading name. I suggest just use "set_*" or something else. In linux kernel, many setter functions already return old value. Don't mind.
I renamed it to bool mem_cgroup_toggle_oom(bool onoff) when I incorporated Michal's feedback, would you be okay with that?
Quoted 7 lines
Quoted 7 lines
include/linux/sched.h +0
-0@@ -1398,6 +1398,9 @@ struct task_struct {unsigned long memsw_nr_pages;/* uncharged mem+swap usage */ } memcg_batch;
Quoted 8 lines
include/linux/sched.h struct task_struct { · line 1403
Quoted 4 lines
include/linux/sched.h +3
-0unsigned int memcg_kmem_skip_account; +struct memcg_oom_info { +unsigned int may_oom:1 ; + } memcg_oom;This ":1" makes slower but doesn't diet any memory space, right? I suggest to use bool. If anybody need to diet in future, he may change it to bit field. That's ok, let's stop too early and questionable micro optimization.
It should sit in the same word as the memcg_kmem_skip_account, plus I'm adding another bit in the next patch (in_memcg_oom), so we save space. It's also the OOM path, so anything but performance critical.
Quoted 11 lines
mm/filemap.c int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) · line 1620
Quoted 9 lines
mm/filemap.c +1
-0@@ -1617,6 +1617,7 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)struct file_ra_state *ra = &file->f_ra;struct inode *inode = mapping->host;pgoff_t offset = vmf->pgoff; +unsigned int may_oom;Why don't you use bool? your
mem_cgroup_xchg_may_oom()uses bool and it seems cleaner more.
Yup, forgot to convert it with the interface, I changed it to bool.
Quoted 18 lines
Quoted 18 lines
mm/filemap.c +14
-0@@ -3851,6 +3843,34 @@ retry: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); + +/*
Quoted 8 lines
mm/filemap.c retry: · line 3864
Quoted 5 lines
mm/filemap.c +5
-0+* Enable the memcg OOM handling for faults triggered in user +* space. Kernel faults are handled more gracefully. +*/ +if (flags &FAULT_FLAG_USER ) +WARN_ON (mem_cgroup_xchg_may_oom (current, true) == true);Please don't assume
WARN_ONnever erase any code. I'm not surprised if embedded guys replaceWARN_ONwith nop in future.
That would be really messed up.
But at the same time, the WARN_ON() obfuscates what's going on a little bit, so putting it separately should make the code more readable. I'll change it.
Thanks for your input!
Re: [patch 5/6] mm: memcg: enable memcg OOM killer only for user faults
Quoted 47 lines · Johannes Weiner
Quoted 37 lines · KOSAKI Motohiro
Quoted 36 lines · Johannes Weiner
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 <[email protected]>--- include/linux/memcontrol.h | 23 +++++++++++++++++++++++ include/linux/sched.h | 3 +++ mm/filemap.c | 11 ++++++++++- mm/memcontrol.c | 2 +- mm/memory.c | 40 ++++++++++++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 12 deletions(-)include/linux/memcontrol.h +13
-0@@ -125,6 +125,24 @@ 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_xchg_may_oom - toggle the memcg OOM killer for a task +* @p: 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_xchg_may_oom (struct task_struct *p,bool new) +{ +bool old; + + old = p->memcg_oom.may_oom;Quoted 8 lines
include/linux/memcontrol.h extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, · line 144
Quoted 4 lines
include/linux/memcontrol.h +4
-0+ p->memcg_oom.may_oom = new; + +return old; +}The name of xchg strongly suggest the function use compare-swap op. So, it seems misleading name. I suggest just use "set_*" or something else. In linux kernel, many setter functions already return old value. Don't mind.
Quoted 2 lines
I renamed it to bool
mem_cgroup_toggle_oom(bool onoff)when I incorporated Michal's feedback, would you be okay with that?
Yes, thank you.
Quoted 17 lines
Quoted 16 lines
include/linux/sched.h struct task_struct { · line 1403
Quoted 12 lines
include/linux/sched.h +3
-0@@ -1398,6 +1398,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;This ":1" makes slower but doesn't diet any memory space, right? I suggest to use bool. If anybody need to diet in future, he may change it to bit field. That's ok, let's stop too early and questionable micro optimization.
Quoted 3 lines
It should sit in the same word as the
memcg_kmem_skip_account, plus I'm adding another bit in the next patch (in_memcg_oom), so we save space. It's also the OOM path, so anything but performance critical.
Oh, if you added another bit too, it's ok, of course.
Quoted 12 lines
Quoted 11 lines
mm/filemap.c int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) · line 1620
Quoted 9 lines
mm/filemap.c +1
-0@@ -1617,6 +1617,7 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)struct file_ra_state *ra = &file->f_ra;struct inode *inode = mapping->host;pgoff_t offset = vmf->pgoff; +unsigned int may_oom;Why don't you use bool? your
mem_cgroup_xchg_may_oom()uses bool and it seems cleaner more.
Quoted 1 line
Yup, forgot to convert it with the interface, I changed it to bool.
thx.
Quoted 10 lines
Quoted 9 lines
mm/filemap.c int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
Quoted 6 lines
mm/filemap.c +6
-0+/* +* Enable the memcg OOM handling for faults triggered in user +* space. Kernel faults are handled more gracefully. +*/ +if (flags &FAULT_FLAG_USER ) +WARN_ON (mem_cgroup_xchg_may_oom (current, true) == true);Please don't assume
WARN_ONnever erase any code. I'm not surprised if embedded guys replaceWARN_ONwith nop in future.
Quoted 7 lines
That would be really messed up.
But at the same time, the
WARN_ON()obfuscates what's going on a little bit, so putting it separately should make the code more readable. I'll change it.Thanks for your input!
No problem. :)
Johannes Weiner
maintainerPATCH 6/6 mm: memcg: do not trap chargers with full callstack on OOM
In reply to Johannes WeinerThe memcg OOM handling is incredibly fragile and can deadlock. When a task fails to charge memory, it invokes the OOM killer and loops right there in the charge code until it succeeds. Comparably, any other task that enters the charge path at this point will go to a waitqueue right then and there and sleep until the OOM situation is resolved. The problem is that these tasks may hold filesystem locks and the mmap_sem; locks that the selected OOM victim may need to exit.
For example, in one reported case, the task invoking the OOM killer was about to charge a page cache page during a write(), which holds the i_mutex. The OOM killer selected a task that was just entering truncate() and trying to acquire the i_mutex:
OOM invoking task:
[<ffffffff8110a9c1>] mem_cgroup_handle_oom+0x241/0x3b0 [<ffffffff8110b5ab>] T.1146+0x5ab/0x5c0 [<ffffffff8110c22e>] mem_cgroup_cache_charge+0xbe/0xe0 [<ffffffff810ca28c>] add_to_page_cache_locked+0x4c/0x140 [<ffffffff810ca3a2>] add_to_page_cache_lru+0x22/0x50 [<ffffffff810ca45b>] grab_cache_page_write_begin+0x8b/0xe0 [<ffffffff81193a18>] ext3_write_begin+0x88/0x270 [<ffffffff810c8fc6>] generic_file_buffered_write+0x116/0x290 [<ffffffff810cb3cc>] __generic_file_aio_write+0x27c/0x480 [<ffffffff810cb646>] generic_file_aio_write+0x76/0xf0 # takes ->i_mutex [<ffffffff8111156a>] do_sync_write+0xea/0x130 [<ffffffff81112183>] vfs_write+0xf3/0x1f0 [<ffffffff81112381>] sys_write+0x51/0x90 [<ffffffff815b5926>] system_call_fastpath+0x18/0x1d
[<ffffffffffffffff>] 0xffffffffffffffff
OOM kill victim:
[<ffffffff811109b8>] do_truncate+0x58/0xa0 # takes i_mutex [<ffffffff81121c90>] do_last+0x250/0xa30 [<ffffffff81122547>] path_openat+0xd7/0x440 [<ffffffff811229c9>] do_filp_open+0x49/0xa0 [<ffffffff8110f7d6>] do_sys_open+0x106/0x240 [<ffffffff8110f950>] sys_open+0x20/0x30 [<ffffffff815b5926>] system_call_fastpath+0x18/0x1d
[<ffffffffffffffff>] 0xffffffffffffffff
The OOM handling task will retry the charge indefinitely while the OOM killed task is not releasing any resources.
A similar scenario can happen when the kernel OOM killer for a memcg is disabled and a userspace task is in charge of resolving OOM situations. In this case, ALL tasks that enter the OOM path will be made to sleep on the OOM waitqueue and wait for userspace to free resources or increase the group's limit. But a userspace OOM handler is prone to deadlock itself on the locks held by the waiting tasks. For example one of the sleeping tasks may be stuck in a brk() call with the mmap_sem held for writing but the userspace handler, in order to pick an optimal victim, may need to read files from /proc/<pid>, which tries to acquire the same mmap_sem for reading and deadlocks.
This patch changes the way tasks behave after detecting a memcg OOM and makes sure nobody loops or sleeps with locks held:
1. When OOMing in a user fault, invoke the OOM killer and restart the fault instead of looping on the charge attempt. This way, the OOM victim can not get stuck on locks the looping task may hold.
2. When OOMing in a user fault but somebody else is handling it (either the kernel OOM killer or a userspace handler), don't go to sleep in the charge context. Instead, remember the OOMing memcg in the task struct and then fully unwind the page fault stack with -ENOMEM. pagefault_out_of_memory() will then call back into the memcg code to check if the -ENOMEM came from the memcg, and then either put the task to sleep on the memcg's OOM waitqueue or just restart the fault. The OOM victim can no longer get stuck on any lock a sleeping task may hold.
This relies on the memcg OOM killer only being enabled when an allocation failure will result in a call to pagefault_out_of_memory().
While reworking the OOM routine, also remove a needless OOM waitqueue wakeup when invoking the killer. In addition to the wakeup implied in the kill signal delivery, only uncharges and limit increases, things that actually change the memory situation, should poke the waitqueue.
--- include/linux/memcontrol.h | 17 +++++ include/linux/sched.h | 3 + mm/memcontrol.c | 156 ++++++++++++++++++++++++++++++--------------- mm/memory.c | 3 + mm/oom_kill.c | 7 +- 5 files changed, 132 insertions(+), 54 deletions(-)
include/linux/memcontrol.h +17 -0
@@ -143,6 +143,13 @@ static inline bool mem_cgroup_xchg_may_oom(struct task_struct *p, bool new)return old; } +static inline bool task_in_mem_cgroup_oom (struct task_struct *p) +{ +return p->memcg_oom.in_memcg_oom; +} + +bool mem_cgroup_oom_synchronize (void ); +#ifdef CONFIG_MEMCG_SWAP extern int do_swap_account;#endif
@@ -371,6 +378,16 @@ static inline bool mem_cgroup_xchg_may_oom(struct task_struct *p, bool new)return !new; } +static inline bool task_in_mem_cgroup_oom (struct task_struct *p) +{ +return false; +} + +static inline bool mem_cgroup_oom_synchronize (void ) +{ +return false; +} +static inline void mem_cgroup_inc_page_stat (struct page *page,enum mem_cgroup_page_stat_item idx) {
include/linux/sched.h +3 -0
@@ -1400,6 +1400,9 @@ struct task_struct {unsigned int memcg_kmem_skip_account;struct memcg_oom_info {unsigned int may_oom:1 ; +unsigned int in_memcg_oom:1 ; +int wakeups; +struct mem_cgroup *wait_on_memcg; } memcg_oom;#endif #ifdef CONFIG_UPROBES
mm/memcontrol.c +104 -52
234 lines not on this page · raw message
mm/memory.c +3 -0
@@ -3868,6 +3868,9 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,if (flags &FAULT_FLAG_USER )WARN_ON (mem_cgroup_xchg_may_oom (current, false) == false); +if (WARN_ON (task_in_mem_cgroup_oom (current) && !(ret &VM_FAULT_OOM ))) +mem_cgroup_oom_synchronize (); +return ret; }
mm/oom_kill.c +5 -2
@@ -678,9 +678,12 @@ out:*/ void pagefault_out_of_memory (void ) { -struct zonelist *zonelist =node_zonelist (first_online_node, -GFP_KERNEL ); +struct zonelist *zonelist; +if (mem_cgroup_oom_synchronize ()) +return ; + + zonelist =node_zonelist (first_online_node,GFP_KERNEL );if (try_set_zonelist_oom (zonelist,GFP_KERNEL )) {out_of_memory (NULL ,0 ,0 ,NULL , false);clear_zonelist_oom (zonelist,GFP_KERNEL );
Signature · 7 lines
1.8.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
Quoted 74 lines · Johannes Weiner
The memcg OOM handling is incredibly fragile and can deadlock. When a task fails to charge memory, it invokes the OOM killer and loops right there in the charge code until it succeeds. Comparably, any other task that enters the charge path at this point will go to a waitqueue right then and there and sleep until the OOM situation is resolved. The problem is that these tasks may hold filesystem locks and the
mmap_sem; locks that the selected OOM victim may need to exit.For example, in one reported case, the task invoking the OOM killer was about to charge a page cache page during a
write(), which holds thei_mutex. The OOM killer selected a task that was just enteringtruncate()and trying to acquire thei_mutex:OOM invoking task:
[<ffffffff8110a9c1>] mem_cgroup_handle_oom+0x241/0x3b0 [<ffffffff8110b5ab>] T.1146+0x5ab/0x5c0 [<ffffffff8110c22e>] mem_cgroup_cache_charge+0xbe/0xe0 [<ffffffff810ca28c>] add_to_page_cache_locked+0x4c/0x140 [<ffffffff810ca3a2>] add_to_page_cache_lru+0x22/0x50 [<ffffffff810ca45b>] grab_cache_page_write_begin+0x8b/0xe0 [<ffffffff81193a18>] ext3_write_begin+0x88/0x270 [<ffffffff810c8fc6>] generic_file_buffered_write+0x116/0x290 [<ffffffff810cb3cc>] __generic_file_aio_write+0x27c/0x480 [<ffffffff810cb646>] generic_file_aio_write+0x76/0xf0 # takes ->i_mutex [<ffffffff8111156a>] do_sync_write+0xea/0x130 [<ffffffff81112183>] vfs_write+0xf3/0x1f0 [<ffffffff81112381>] sys_write+0x51/0x90 [<ffffffff815b5926>] system_call_fastpath+0x18/0x1d[<ffffffffffffffff>]
0xffffffffffffffffOOM kill victim:
[<ffffffff811109b8>] do_truncate+0x58/0xa0 # takes i_mutex [<ffffffff81121c90>] do_last+0x250/0xa30 [<ffffffff81122547>] path_openat+0xd7/0x440 [<ffffffff811229c9>] do_filp_open+0x49/0xa0 [<ffffffff8110f7d6>] do_sys_open+0x106/0x240 [<ffffffff8110f950>] sys_open+0x20/0x30 [<ffffffff815b5926>] system_call_fastpath+0x18/0x1d[<ffffffffffffffff>]
0xffffffffffffffffThe OOM handling task will retry the charge indefinitely while the OOM killed task is not releasing any resources.
A similar scenario can happen when the kernel OOM killer for a memcg is disabled and a userspace task is in charge of resolving OOM situations. In this case, ALL tasks that enter the OOM path will be made to sleep on the OOM waitqueue and wait for userspace to free resources or increase the group's limit. But a userspace OOM handler is prone to deadlock itself on the locks held by the waiting tasks. For example one of the sleeping tasks may be stuck in a
brk()call with themmap_semheld for writing but the userspace handler, in order to pick an optimal victim, may need to read files from /proc/<pid>, which tries to acquire the samemmap_semfor reading and deadlocks.This patch changes the way tasks behave after detecting a memcg OOM and makes sure nobody loops or sleeps with locks held:
1. When OOMing in a user fault, invoke the OOM killer and restart the fault instead of looping on the charge attempt. This way, the OOM victim can not get stuck on locks the looping task may hold.
2. When OOMing in a user fault but somebody else is handling it (either the kernel OOM killer or a userspace handler), don't go to sleep in the charge context. Instead, remember the OOMing memcg in the task struct and then fully unwind the page fault stack with
-ENOMEM.pagefault_out_of_memory()will then call back into the memcg code to check if the-ENOMEMcame from the memcg, and then either put the task to sleep on the memcg's OOM waitqueue or just restart the fault. The OOM victim can no longer get stuck on any lock a sleeping task may hold.This relies on the memcg OOM killer only being enabled when an allocation failure will result in a call to
pagefault_out_of_memory().
Quoted 8 lines
While reworking the OOM routine, also remove a needless OOM waitqueue wakeup when invoking the killer. In addition to the wakeup implied in the kill signal delivery, only uncharges and limit increases, things that actually change the memory situation, should poke the waitqueue.
Reported-by Reported-by: azurIt <[email protected]>Debugged-by Michal Hocko <[email protected]>Signed-off-by Johannes Weiner <[email protected]>
Looks good just one remark bellow.
[...]
Quoted 4 lines
mm/memcontrol.c +0
-0
[...]
Quoted 12 lines
mm/memcontrol.c +2
-4@@ -2189,31 +2191,20 @@ static void memcg_oom_recover(struct mem_cgroup *memcg) }/* -* try to call OOM killer. returns false if we should exit memory-reclaim loop. +* try to call OOM killer */ -static bool mem_cgroup_handle_oom (struct mem_cgroup *memcg,gfp_t mask, -int order) +static void mem_cgroup_oom (struct mem_cgroup *memcg,gfp_t mask,int order) { -struct oom_wait_info owait;
mm/memcontrol.c static void memcg_oom_recover(struct mem_cgroup *memcg) · line 2206
Quoted 10 lines
mm/memcontrol.c +1
-8-bool locked, need_to_kill; +bool locked, need_to_kill = true; - owait.memcg = memcg; - owait.wait.flags =0 ; - owait.wait.func = memcg_oom_wake_function; - owait.wait.private = current; -INIT_LIST_HEAD (&owait.wait.task_list); - need_to_kill = true; -mem_cgroup_mark_under_oom (memcg);
You are marking memcg under_oom only for the sleepers. So if we have no sleepers then the memcg will never report it is under oom which is a behavior change. On the other hand who-ever relies on under_oom under such conditions (it would basically mean a busy loop reading memory.oom_control) would be racy anyway so it is questionable it matters at all. At least now when we do not have any active notification that under_oom has changed.
Anyway, this shouldn't be a part of this patch so if you want it because it saves a pointless hierarchy traversal then make it a separate patch with explanation why the new behavior is still OK.
Quoted 70 lines
mm/memcontrol.c +40
-14+if (!current->memcg_oom.may_oom) +return ; + + current->memcg_oom.in_memcg_oom =1 ;/* At first, try to OOM lock hierarchy under memcg.*/ spin_lock (&memcg_oom_lock); locked =mem_cgroup_oom_lock (memcg); -/* -* Even if signal_pending(), we can't quit charge() loop without -* accounting. So, UNINTERRUPTIBLE is appropriate. But SIGKILL -* under OOM is always welcomed, use TASK_KILLABLE here. -*/ -prepare_to_wait (&memcg_oom_waitq, &owait.wait,TASK_KILLABLE );if (!locked || memcg->oom_kill_disable) need_to_kill = false;if (locked)@@ -2221,24 +2212,100 @@ static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask,spin_unlock (&memcg_oom_lock);if (need_to_kill) { -finish_wait (&memcg_oom_waitq, &owait.wait);mem_cgroup_out_of_memory (memcg, mask, order); }else { -schedule (); -finish_wait (&memcg_oom_waitq, &owait.wait); +/* +* A system call can just return -ENOMEM, but if this +* is a page fault and somebody else is handling the +* OOM already, we need to sleep on the OOM waitqueue +* for this memcg until the situation is resolved. +* Which can take some time because it might be +* handled by a userspace task. +* +* However, this is the charge context, which means +* that we may sit on a large call stack and hold +* various filesystem locks, the mmap_sem etc. and we +* don't want the OOM handler to deadlock on them +* while we sit here and wait. Store the current OOM +* context in the task_struct, then return -ENOMEM. +* At the end of the page fault handler, with the +* stack unwound, pagefault_out_of_memory() will check +* back with us by calling +* mem_cgroup_oom_synchronize(), possibly putting the +* task to sleep. +*/ +mem_cgroup_mark_under_oom (memcg); + current->memcg_oom.wakeups =atomic_read (&memcg->oom_wakeups); +css_get (&memcg->css); + current->memcg_oom.wait_on_memcg = memcg; } -spin_lock (&memcg_oom_lock); -if (locked) + +if (locked) { +spin_lock (&memcg_oom_lock);mem_cgroup_oom_unlock (memcg); -memcg_wakeup_oom (memcg); -spin_unlock (&memcg_oom_lock); +/* +* Sleeping tasks might have been killed, make sure +* they get scheduled so they can exit. +*/ +if (need_to_kill) +memcg_oom_recover (memcg); +spin_unlock (&memcg_oom_lock); + } +} -mem_cgroup_unmark_under_oom (memcg);
[...]
Signature · 2 lines
Michal Hocko SUSE Labs
Johannes Weiner
maintainerRe: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
Quoted 32 lines · Michal Hocko
Quoted 12 lines · Johannes Weiner
(quoted diff) +2
-4@@ -2189,31 +2191,20 @@ static void memcg_oom_recover(struct mem_cgroup *memcg) } /* - * try to call OOM killer. returns false if we should exit memory-reclaim loop. + * try to call OOM killer */ -static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, - int order) +static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) { - struct oom_wait_info owait;Review static void memcg_oom_recover(struct mem_cgroup *memcg) · line 2206
Quoted 10 lines
(quoted diff) +1
-8- bool locked, need_to_kill; + bool locked, need_to_kill = true; - owait.memcg = memcg; - owait.wait.flags = 0; - owait.wait.func = memcg_oom_wake_function; - owait.wait.private = current; - INIT_LIST_HEAD(&owait.wait.task_list); - need_to_kill = true; - mem_cgroup_mark_under_oom(memcg);You are marking memcg
under_oomonly for the sleepers. So if we have no sleepers then the memcg will never report it is under oom which is a behavior change. On the other hand who-ever relies onunder_oomunder such conditions (it would basically mean a busy loop reading memory.oom_control) would be racy anyway so it is questionable it matters at all. At least now when we do not have any active notification thatunder_oomhas changed.
Quoted 3 lines
Anyway, this shouldn't be a part of this patch so if you want it because it saves a pointless hierarchy traversal then make it a separate patch with explanation why the new behavior is still OK.
This made me think again about how the locking and waking in there works and I found a bug in this patch.
Basically, we have an open-coded sleeping lock in there and it's all obfuscated by having way too much stuffed into the memcg_oom_lock section.
Removing all the clutter, it becomes clear that I can't remove that (undocumented) final wakeup at the end of the function. As with any lock, a contender has to be woken up after unlock. We can't rely on the lock holder's OOM kill to trigger uncharges and wakeups, because a contender for the OOM lock could show up after the OOM kill but before the lock is released. If there weren't any more wakeups, the contender would sleep indefinitely.
It also becomes clear that I can't remove the mem_cgroup_mark_under_oom() like that because it is key in receiving wakeups. And as with any sleeping lock, we need to listen to wakeups before attempting the trylock, or we might miss the wakeup from the unlock.
It definitely became a separate patch, which cleans up this unholy mess first before putting new things on top:
---
From: Johannes Weiner <[email protected]>
Subject: [patch] mm: memcg: rework and document OOM serialization
1. Remove the return value of mem_cgroup_oom_unlock().
2. Rename mem_cgroup_oom_lock() to mem_cgroup_oom_trylock().
3. Pull the prepare_to_wait() out of the memcg_oom_lock scope. This makes it more obvious that the task has to be on the waitqueue before attempting to OOM-trylock the hierarchy, to not miss any wakeups before going to sleep. It just didn't matter until now because it was all lumped together into the global memcg_oom_lock spinlock section.
4. Pull the mem_cgroup_oom_notify() out of the memcg_oom_lock scope. It is proctected by the hierarchical OOM-lock.
5. The memcg_oom_lock spinlock is only required to propagate the OOM lock in any given hierarchy atomically. Restrict its scope to mem_cgroup_oom_(trylock|unlock).
6. Do not wake up the waitqueue unconditionally at the end of the function. Only the lockholder has to wake up the next in line after releasing the lock.
Note that the lockholder kicks off the OOM-killer, which in turn leads to wakeups from the uncharges of the exiting task. But any contender is not guaranteed to see them if it enters the OOM path after the OOM kills but before the lockholder releases the lock. Thus the wakeup has to be explicitely after releasing the lock.
7. Put the OOM task on the waitqueue before marking the hierarchy as under OOM as that is the point where we start to receive wakeups. No point in listening before being on the waitqueue.
8. Likewise, unmark the hierarchy before finishing the sleep, for symmetry.
--- mm/memcontrol.c | 85 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 38 deletions(-)
mm/memcontrol.c +47 -38
@@ -2076,15 +2076,18 @@ static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,return total; } +static DEFINE_SPINLOCK (memcg_oom_lock); +/* * Check OOM-Killer is already running under our hierarchy. * If someone is running, return false. -* Has to be called with memcg_oom_lock */ -static bool mem_cgroup_oom_lock (struct mem_cgroup *memcg) +static bool mem_cgroup_oom_trylock (struct mem_cgroup *memcg) {struct mem_cgroup *iter, *failed =NULL ; +spin_lock (&memcg_oom_lock); +for_each_mem_cgroup_tree (iter, memcg) {if (iter->oom_lock) {/*
@@ -2098,33 +2101,33 @@ static bool mem_cgroup_oom_lock(struct mem_cgroup *memcg) iter->oom_lock = true; } -if (!failed) -return true; - -/* -* OK, we failed to lock the whole subtree so we have to clean up -* what we set up to the failing subtree -*/ -for_each_mem_cgroup_tree (iter, memcg) { -if (iter == failed) { -mem_cgroup_iter_break (memcg, iter); -break ; +if (failed) { +/* +* OK, we failed to lock the whole subtree so we have +* to clean up what we set up to the failing subtree +*/ +for_each_mem_cgroup_tree (iter, memcg) { +if (iter == failed) { +mem_cgroup_iter_break (memcg, iter); +break ; + } + iter->oom_lock = false; } - iter->oom_lock = false; - } -return false; + } + +spin_unlock (&memcg_oom_lock); + +return !failed; } -/* -* Has to be called with memcg_oom_lock -*/ -static int mem_cgroup_oom_unlock (struct mem_cgroup *memcg) +static void mem_cgroup_oom_unlock (struct mem_cgroup *memcg) {struct mem_cgroup *iter; +spin_lock (&memcg_oom_lock);for_each_mem_cgroup_tree (iter, memcg) iter->oom_lock = false; -return 0 ; +spin_unlock (&memcg_oom_lock); }static void mem_cgroup_mark_under_oom (struct mem_cgroup *memcg)
@@ -2148,7 +2151,6 @@ static void mem_cgroup_unmark_under_oom(struct mem_cgroup *memcg)atomic_add_unless (&iter->under_oom,-1 ,0 ); } -static DEFINE_SPINLOCK (memcg_oom_lock);static DECLARE_WAIT_QUEUE_HEAD (memcg_oom_waitq);struct oom_wait_info {
@@ -2195,45 +2197,52 @@ static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, int order) {struct oom_wait_info owait; -bool locked, need_to_kill; +bool locked; owait.memcg = memcg; owait.wait.flags =0 ; owait.wait.func = memcg_oom_wake_function; owait.wait.private = current;INIT_LIST_HEAD (&owait.wait.task_list); - need_to_kill = true; -mem_cgroup_mark_under_oom (memcg); -/* At first, try to OOM lock hierarchy under memcg.*/ -spin_lock (&memcg_oom_lock); - locked =mem_cgroup_oom_lock (memcg);/* +* As with any blocking lock, a contender needs to start +* listening for wakeups before attempting the trylock, +* otherwise it can miss the wakeup from the unlock and sleep +* indefinitely. This is just open-coded because our locking +* is so particular to memcg hierarchies. +* * Even if signal_pending(), we can't quit charge() loop without * accounting. So, UNINTERRUPTIBLE is appropriate. But SIGKILL * under OOM is always welcomed, use TASK_KILLABLE here. */ prepare_to_wait (&memcg_oom_waitq, &owait.wait,TASK_KILLABLE ); -if (!locked || memcg->oom_kill_disable) - need_to_kill = false; +mem_cgroup_mark_under_oom (memcg); + + locked =mem_cgroup_oom_trylock (memcg); +if (locked)mem_cgroup_oom_notify (memcg); -spin_unlock (&memcg_oom_lock); -if (need_to_kill) { +if (locked && !memcg->oom_kill_disable) { +mem_cgroup_unmark_under_oom (memcg);finish_wait (&memcg_oom_waitq, &owait.wait);mem_cgroup_out_of_memory (memcg, mask, order); }else {schedule (); +mem_cgroup_unmark_under_oom (memcg);finish_wait (&memcg_oom_waitq, &owait.wait); } -spin_lock (&memcg_oom_lock); -if (locked) -mem_cgroup_oom_unlock (memcg); -memcg_wakeup_oom (memcg); -spin_unlock (&memcg_oom_lock); -mem_cgroup_unmark_under_oom (memcg); +if (locked) { +mem_cgroup_oom_unlock (memcg); +/* +* There is no guarantee that a OOM-lock contender +* sees the wakeups triggered by the OOM kill +* uncharges. Wake any sleepers explicitely. +*/ +memcg_oom_recover (memcg); + }if (test_thread_flag (TIF_MEMDIE ) ||fatal_signal_pending (current))return false;
Signature · 7 lines
1.8.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
Quoted 44 lines · Johannes Weiner
Quoted 32 lines · Michal Hocko
Quoted 12 lines · Johannes Weiner
(quoted diff) +2
-4@@ -2189,31 +2191,20 @@ static void memcg_oom_recover(struct mem_cgroup *memcg) } /* - * try to call OOM killer. returns false if we should exit memory-reclaim loop. + * try to call OOM killer */ -static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, - int order) +static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) { - struct oom_wait_info owait;Review static void memcg_oom_recover(struct mem_cgroup *memcg) · line 2206
Quoted 10 lines
(quoted diff) +1
-8- bool locked, need_to_kill; + bool locked, need_to_kill = true; - owait.memcg = memcg; - owait.wait.flags = 0; - owait.wait.func = memcg_oom_wake_function; - owait.wait.private = current; - INIT_LIST_HEAD(&owait.wait.task_list); - need_to_kill = true; - mem_cgroup_mark_under_oom(memcg);You are marking memcg
under_oomonly for the sleepers. So if we have no sleepers then the memcg will never report it is under oom which is a behavior change. On the other hand who-ever relies onunder_oomunder such conditions (it would basically mean a busy loop reading memory.oom_control) would be racy anyway so it is questionable it matters at all. At least now when we do not have any active notification thatunder_oomhas changed.Quoted 3 lines
Anyway, this shouldn't be a part of this patch so if you want it because it saves a pointless hierarchy traversal then make it a separate patch with explanation why the new behavior is still OK.
This made me think again about how the locking and waking in there works and I found a bug in this patch.
Basically, we have an open-coded sleeping lock in there and it's all obfuscated by having way too much stuffed into the
memcg_oom_locksection.
Quoted 7 lines
Removing all the clutter, it becomes clear that I can't remove that (undocumented) final wakeup at the end of the function. As with any lock, a contender has to be woken up after unlock. We can't rely on the lock holder's OOM kill to trigger uncharges and wakeups, because a contender for the OOM lock could show up after the OOM kill but before the lock is released. If there weren't any more wakeups, the contender would sleep indefinitely.
I have checked that path again and I still do not see how wakeup_oom helps here. What prevents us from the following race then?
spin_lock(&memcg_oom_lock)
locked = mem_cgroup_oom_lock(memcg) # truespin_unlock(&memcg_oom_lock)
spin_lock(&memcg_oom_lock) locked = mem_cgroup_oom_lock(memcg) # false spin_unlock(&memcg_oom_lock) <resched>mem_cgroup_out_of_memory()
<uncharge & memcg_oom_recover>spin_lock(&memcg_oom_lock)mem_cgroup_oom_unlock(memcg)memcg_wakeup_oom(memcg)
schedule()spin_unlock(&memcg_oom_lock)mem_cgroup_unmark_under_oom(memcg)
Quoted 8 lines
It also becomes clear that I can't remove the
mem_cgroup_mark_under_oom()like that because it is key in receiving wakeups. And as with any sleeping lock, we need to listen to wakeups before attempting the trylock, or we might miss the wakeup from the unlock.It definitely became a separate patch, which cleans up this unholy mess first before putting new things on top:
I will check the patch tomorrow with a clean head. [...]
Signature · 8 lines
Michal Hocko SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Johannes Weiner
maintainerRe: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
Quoted 53 lines · Michal Hocko
Quoted 44 lines · Johannes Weiner
Quoted 32 lines · Michal Hocko
Quoted 12 lines · Johannes Weiner
(quoted diff) +2
-4@@ -2189,31 +2191,20 @@ static void memcg_oom_recover(struct mem_cgroup *memcg) } /* - * try to call OOM killer. returns false if we should exit memory-reclaim loop. + * try to call OOM killer */ -static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, - int order) +static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) { - struct oom_wait_info owait;Review static void memcg_oom_recover(struct mem_cgroup *memcg) · line 2206
Quoted 10 lines
(quoted diff) +1
-8- bool locked, need_to_kill; + bool locked, need_to_kill = true; - owait.memcg = memcg; - owait.wait.flags = 0; - owait.wait.func = memcg_oom_wake_function; - owait.wait.private = current; - INIT_LIST_HEAD(&owait.wait.task_list); - need_to_kill = true; - mem_cgroup_mark_under_oom(memcg);You are marking memcg
under_oomonly for the sleepers. So if we have no sleepers then the memcg will never report it is under oom which is a behavior change. On the other hand who-ever relies onunder_oomunder such conditions (it would basically mean a busy loop reading memory.oom_control) would be racy anyway so it is questionable it matters at all. At least now when we do not have any active notification thatunder_oomhas changed.Quoted 3 lines
Anyway, this shouldn't be a part of this patch so if you want it because it saves a pointless hierarchy traversal then make it a separate patch with explanation why the new behavior is still OK.
This made me think again about how the locking and waking in there works and I found a bug in this patch.
Basically, we have an open-coded sleeping lock in there and it's all obfuscated by having way too much stuffed into the
memcg_oom_locksection.Quoted 7 lines
Removing all the clutter, it becomes clear that I can't remove that (undocumented) final wakeup at the end of the function. As with any lock, a contender has to be woken up after unlock. We can't rely on the lock holder's OOM kill to trigger uncharges and wakeups, because a contender for the OOM lock could show up after the OOM kill but before the lock is released. If there weren't any more wakeups, the contender would sleep indefinitely.
Quoted 6 lines
I have checked that path again and I still do not see how
wakeup_oomhelps here. What prevents us from the following race then?
spin_lock(&memcg_oom_lock)
locked =mem_cgroup_oom_lock(memcg)# truespin_unlock(&memcg_oom_lock)
prepare_to_wait()
Quoted 12 lines
spin_lock(&memcg_oom_lock)locked =mem_cgroup_oom_lock(memcg)# falsespin_unlock(&memcg_oom_lock)<resched>mem_cgroup_out_of_memory()
<uncharge &memcg_oom_recover>spin_lock(&memcg_oom_lock)mem_cgroup_oom_unlock(memcg)memcg_wakeup_oom(memcg)
schedule()spin_unlock(&memcg_oom_lock)mem_cgroup_unmark_under_oom(memcg)
Re: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
Quoted 54 lines · Johannes Weiner
Quoted 53 lines · Michal Hocko
Quoted 51 lines · Johannes Weiner
Quoted 32 lines · Michal Hocko
Quoted 12 lines · Johannes Weiner
(quoted diff) +2
-4@@ -2189,31 +2191,20 @@ static void memcg_oom_recover(struct mem_cgroup *memcg) } /* - * try to call OOM killer. returns false if we should exit memory-reclaim loop. + * try to call OOM killer */ -static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, - int order) +static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order) { - struct oom_wait_info owait;Review static void memcg_oom_recover(struct mem_cgroup *memcg) · line 2206
Quoted 10 lines
(quoted diff) +1
-8- bool locked, need_to_kill; + bool locked, need_to_kill = true; - owait.memcg = memcg; - owait.wait.flags = 0; - owait.wait.func = memcg_oom_wake_function; - owait.wait.private = current; - INIT_LIST_HEAD(&owait.wait.task_list); - need_to_kill = true; - mem_cgroup_mark_under_oom(memcg);You are marking memcg
under_oomonly for the sleepers. So if we have no sleepers then the memcg will never report it is under oom which is a behavior change. On the other hand who-ever relies onunder_oomunder such conditions (it would basically mean a busy loop reading memory.oom_control) would be racy anyway so it is questionable it matters at all. At least now when we do not have any active notification thatunder_oomhas changed.Quoted 3 lines
Anyway, this shouldn't be a part of this patch so if you want it because it saves a pointless hierarchy traversal then make it a separate patch with explanation why the new behavior is still OK.
This made me think again about how the locking and waking in there works and I found a bug in this patch.
Basically, we have an open-coded sleeping lock in there and it's all obfuscated by having way too much stuffed into the
memcg_oom_locksection.Removing all the clutter, it becomes clear that I can't remove that (undocumented) final wakeup at the end of the function. As with any lock, a contender has to be woken up after unlock. We can't rely on the lock holder's OOM kill to trigger uncharges and wakeups, because a contender for the OOM lock could show up after the OOM kill but before the lock is released. If there weren't any more wakeups, the contender would sleep indefinitely.
Quoted 8 lines
Quoted 6 lines
I have checked that path again and I still do not see how
wakeup_oomhelps here. What prevents us from the following race then?
spin_lock(&memcg_oom_lock)
locked =mem_cgroup_oom_lock(memcg)# truespin_unlock(&memcg_oom_lock)
prepare_to_wait()
For some reason that one disappeared from my screen ;)
Quoted 12 lines
Quoted 12 lines
spin_lock(&memcg_oom_lock)locked =mem_cgroup_oom_lock(memcg)# falsespin_unlock(&memcg_oom_lock)<resched>mem_cgroup_out_of_memory()
<uncharge &memcg_oom_recover>spin_lock(&memcg_oom_lock)mem_cgroup_oom_unlock(memcg)memcg_wakeup_oom(memcg)
schedule()spin_unlock(&memcg_oom_lock)mem_cgroup_unmark_under_oom(memcg)
Signature · 2 lines
Michal Hocko SUSE Labs
Re: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
In reply to Johannes WeinerOn Fri 26-07-13 17:28:09, Johannes Weiner wrote:
[...]
Quoted 31 lines
From: Johannes Weiner <[email protected]>
Subject: [patch] mm: memcg: rework and document OOM serialization1. Remove the return value of
mem_cgroup_oom_unlock().2. Rename
mem_cgroup_oom_lock()tomem_cgroup_oom_trylock().3. Pull the
prepare_to_wait()out of thememcg_oom_lockscope. This makes it more obvious that the task has to be on the waitqueue before attempting to OOM-trylock the hierarchy, to not miss any wakeups before going to sleep. It just didn't matter until now because it was all lumped together into the globalmemcg_oom_lockspinlock section.4. Pull the
mem_cgroup_oom_notify()out of thememcg_oom_lockscope. It is proctected by the hierarchical OOM-lock.5. The
memcg_oom_lockspinlock is only required to propagate the OOM lock in any given hierarchy atomically. Restrict its scope tomem_cgroup_oom_(trylock|unlock).6. Do not wake up the waitqueue unconditionally at the end of the function. Only the lockholder has to wake up the next in line after releasing the lock.
Note that the lockholder kicks off the OOM-killer, which in turn leads to wakeups from the uncharges of the exiting task. But any contender is not guaranteed to see them if it enters the OOM path after the OOM kills but before the lockholder releases the lock. Thus the wakeup has to be explicitely after releasing the lock.
Quoted 7 lines
7. Put the OOM task on the waitqueue before marking the hierarchy as under OOM as that is the point where we start to receive wakeups. No point in listening before being on the waitqueue.
8. Likewise, unmark the hierarchy before finishing the sleep, for symmetry.
OK, this looks better than what we have today, but still could be done better IMO ;)
mm/memcontrol.c static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg, · line 2078
Quoted 13 lines
Signed-off-by Johannes Weiner <[email protected]>--- mm/memcontrol.c | 85 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 38 deletions(-)mm/memcontrol.c +0
-0@@ -2076,15 +2076,18 @@ static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,return total; }
/* Protects oom_lock hierarchy consistent state and oom_notify chain */
Quoted 7 lines
mm/memcontrol.c +2
-1+static DEFINE_SPINLOCK (memcg_oom_lock); +/* * Check OOM-Killer is already running under our hierarchy. * If someone is running, return false. -* Has to be called with memcg_oom_lock */
[...]
Quoted 18 lines
mm/memcontrol.c +1
-6@@ -2195,45 +2197,52 @@ static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, int order) {struct oom_wait_info owait; -bool locked, need_to_kill; +bool locked; owait.memcg = memcg; owait.wait.flags =0 ; owait.wait.func = memcg_oom_wake_function; owait.wait.private = current;INIT_LIST_HEAD (&owait.wait.task_list); - need_to_kill = true; -mem_cgroup_mark_under_oom (memcg); -/* At first, try to OOM lock hierarchy under memcg.*/ -spin_lock (&memcg_oom_lock); - locked =mem_cgroup_oom_lock (memcg);
mm/memcontrol.c static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, · line 2217
Quoted 10 lines
mm/memcontrol.c +6
-0/* +* As with any blocking lock, a contender needs to start +* listening for wakeups before attempting the trylock, +* otherwise it can miss the wakeup from the unlock and sleep +* indefinitely. This is just open-coded because our locking +* is so particular to memcg hierarchies. +* * Even if signal_pending(), we can't quit charge() loop without * accounting. So, UNINTERRUPTIBLE is appropriate. But SIGKILL * under OOM is always welcomed, use TASK_KILLABLE here.
Could you take care of this paragraph as well, while you are at it, please? I've always found it it confusing. I would remove it completely I would remove it completely.
Quoted 7 lines
mm/memcontrol.c +3
-2*/ prepare_to_wait (&memcg_oom_waitq, &owait.wait,TASK_KILLABLE ); -if (!locked || memcg->oom_kill_disable) - need_to_kill = false; +mem_cgroup_mark_under_oom (memcg); + + locked =mem_cgroup_oom_trylock (memcg);
mm/memcontrol.c static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, · line 2230
Quoted 10 lines
mm/memcontrol.c +3
-2+if (locked)mem_cgroup_oom_notify (memcg); -spin_unlock (&memcg_oom_lock); -if (need_to_kill) { +if (locked && !memcg->oom_kill_disable) { +mem_cgroup_unmark_under_oom (memcg);finish_wait (&memcg_oom_waitq, &owait.wait);mem_cgroup_out_of_memory (memcg, mask, order);
Killing under hierarchy which is not under_oom sounds strange to me. Cannot we just move finish_wait & unmark down after unlock? It would also take care about incorrect memcg_oom_recover you have in oom_unlock path. The ordering would also be more natural
prepare_wait
mark_under_oom
trylock
unlock
unmark_under_oom
finish_wait
Quoted 10 lines
mm/memcontrol.c +1
-5}else {schedule (); +mem_cgroup_unmark_under_oom (memcg);finish_wait (&memcg_oom_waitq, &owait.wait); } -spin_lock (&memcg_oom_lock); -if (locked) -mem_cgroup_oom_unlock (memcg); -memcg_wakeup_oom (memcg); -spin_unlock (&memcg_oom_lock);
mm/memcontrol.c static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, · line 2244
Quoted 10 lines
mm/memcontrol.c +8
-1-mem_cgroup_unmark_under_oom (memcg); +if (locked) { +mem_cgroup_oom_unlock (memcg); +/* +* There is no guarantee that a OOM-lock contender +* sees the wakeups triggered by the OOM kill +* uncharges. Wake any sleepers explicitely. +*/ +memcg_oom_recover (memcg);
This will be a noop because memcg is no longer under_oom (you wanted memcg_wakeup_oom here I guess). Moreover, even the killed wouldn't wake up anybody for the same reason.
Quoted 7 lines
mm/memcontrol.c +1
-0+ }if (test_thread_flag (TIF_MEMDIE ) ||fatal_signal_pending (current))return false;Signature · 1 line
1.8.3.2
Signature · 2 lines
Michal Hocko SUSE Labs
Johannes Weiner
maintainerRe: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
Quoted 49 lines · Michal Hocko
On Fri 26-07-13 17:28:09, Johannes Weiner wrote:
[...]Quoted 31 lines
From: Johannes Weiner <[email protected]>
Subject: [patch] mm: memcg: rework and document OOM serialization1. Remove the return value of
mem_cgroup_oom_unlock().2. Rename
mem_cgroup_oom_lock()tomem_cgroup_oom_trylock().3. Pull the
prepare_to_wait()out of thememcg_oom_lockscope. This makes it more obvious that the task has to be on the waitqueue before attempting to OOM-trylock the hierarchy, to not miss any wakeups before going to sleep. It just didn't matter until now because it was all lumped together into the globalmemcg_oom_lockspinlock section.4. Pull the
mem_cgroup_oom_notify()out of thememcg_oom_lockscope. It is proctected by the hierarchical OOM-lock.5. The
memcg_oom_lockspinlock is only required to propagate the OOM lock in any given hierarchy atomically. Restrict its scope tomem_cgroup_oom_(trylock|unlock).6. Do not wake up the waitqueue unconditionally at the end of the function. Only the lockholder has to wake up the next in line after releasing the lock.
Note that the lockholder kicks off the OOM-killer, which in turn leads to wakeups from the uncharges of the exiting task. But any contender is not guaranteed to see them if it enters the OOM path after the OOM kills but before the lockholder releases the lock. Thus the wakeup has to be explicitely after releasing the lock.
Quoted 7 lines
7. Put the OOM task on the waitqueue before marking the hierarchy as under OOM as that is the point where we start to receive wakeups. No point in listening before being on the waitqueue.
8. Likewise, unmark the hierarchy before finishing the sleep, for symmetry.
OK, this looks better than what we have today, but still could be done better IMO ;)
Quoted 5 lines
Signed-off-by Johannes Weiner <[email protected]>--- mm/memcontrol.c | 85 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 38 deletions(-)
Quoted 10 lines
mm/memcontrol.c static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg, · line 2078
Quoted 8 lines
mm/memcontrol.c +0
-0@@ -2076,15 +2076,18 @@ static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,return total; }/* Protects
oom_lockhierarchy consistent state andoom_notifychain */
See 4. :-)
Quoted 32 lines
Quoted 7 lines
mm/memcontrol.c +2
-1+static DEFINE_SPINLOCK (memcg_oom_lock); +/* * Check OOM-Killer is already running under our hierarchy. * If someone is running, return false. -* Has to be called with memcg_oom_lock */ [...]
Quoted 24 lines
mm/memcontrol.c +6
-6@@ -2195,45 +2197,52 @@ static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, int order) {struct oom_wait_info owait; -bool locked, need_to_kill; +bool locked; owait.memcg = memcg; owait.wait.flags =0 ; owait.wait.func = memcg_oom_wake_function; owait.wait.private = current;INIT_LIST_HEAD (&owait.wait.task_list); - need_to_kill = true; -mem_cgroup_mark_under_oom (memcg); -/* At first, try to OOM lock hierarchy under memcg.*/ -spin_lock (&memcg_oom_lock); - locked =mem_cgroup_oom_lock (memcg);/* +* As with any blocking lock, a contender needs to start +* listening for wakeups before attempting the trylock, +* otherwise it can miss the wakeup from the unlock and sleep +* indefinitely. This is just open-coded because our locking +* is so particular to memcg hierarchies.
Quoted 8 lines
mm/memcontrol.c static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, · line 2217
Quoted 4 lines
mm/memcontrol.c +1
-0+* * Even if signal_pending(), we can't quit charge() loop without * accounting. So, UNINTERRUPTIBLE is appropriate. But SIGKILL * under OOM is always welcomed, use TASK_KILLABLE here. Could you take care of this paragraph as well, while you are at it, please? I've always found it it confusing. I would remove it completely I would remove it completely.
It's removed completely in the same series, that's why I didn't bother.
Quoted 12 lines
Quoted 12 lines
mm/memcontrol.c +4
-3*/ prepare_to_wait (&memcg_oom_waitq, &owait.wait,TASK_KILLABLE ); -if (!locked || memcg->oom_kill_disable) - need_to_kill = false; +mem_cgroup_mark_under_oom (memcg); + + locked =mem_cgroup_oom_trylock (memcg); +if (locked)mem_cgroup_oom_notify (memcg); -spin_unlock (&memcg_oom_lock);
Quoted 16 lines
mm/memcontrol.c static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, · line 2230
Quoted 5 lines
mm/memcontrol.c +2
-1-if (need_to_kill) { +if (locked && !memcg->oom_kill_disable) { +mem_cgroup_unmark_under_oom (memcg);finish_wait (&memcg_oom_waitq, &owait.wait);mem_cgroup_out_of_memory (memcg, mask, order);Killing under hierarchy which is not
under_oomsounds strange to me. Cannot we just movefinish_wait& unmark down after unlock? It would also take care about incorrectmemcg_oom_recoveryou have inoom_unlockpath. The ordering would also be more natural
prepare_wait
mark_under_oom
trylock
unlock
unmark_under_oom
finish_wait
That makes no sense. The waitqueue is for when you failed the trylock and need to sleep until the holder unlocks. If you acquire the lock, what's the point in staying on the waitqueue?
Look at __mutex_lock_common() in kernel/mutex.c.
There is also no point in leaving the under_oom set during the kill, this flag is so transient if the in-kernel OOM killer is enabled. Whether we leave it set during mem_cgroup_out_of_memory() or not, how would you possibly read it and base any meaningful decisions on it from userspace?
Quoted 11 lines
Quoted 11 lines
mm/memcontrol.c +1
-5}else {schedule (); +mem_cgroup_unmark_under_oom (memcg);finish_wait (&memcg_oom_waitq, &owait.wait); } -spin_lock (&memcg_oom_lock); -if (locked) -mem_cgroup_oom_unlock (memcg); -memcg_wakeup_oom (memcg); -spin_unlock (&memcg_oom_lock);
Quoted 13 lines
mm/memcontrol.c static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, · line 2244
Quoted 9 lines
mm/memcontrol.c +8
-1-mem_cgroup_unmark_under_oom (memcg); +if (locked) { +mem_cgroup_oom_unlock (memcg); +/* +* There is no guarantee that a OOM-lock contender +* sees the wakeups triggered by the OOM kill +* uncharges. Wake any sleepers explicitely. +*/ +memcg_oom_recover (memcg);This will be a noop because memcg is no longer
under_oom(you wantedmemcg_wakeup_oomhere I guess). Moreover, even the killed wouldn't wake up anybody for the same reason.
Anybody entering this path will increase the under_oom counter. The killer decreases it again, but everybody who is sleeping because they failed the trylock still hasn't unmarked the hierarchy (they call schedule() before unmark_under_oom()). So we issue wakeups when there is somebody waiting for the lock.
Michal Hocko
Acked-byRe: [patch 6/6] mm: memcg: do not trap chargers with full callstack on OOM
Quoted 2 lines · Johannes Weiner
Quoted 1 line · Michal Hocko
On Fri 26-07-13 17:28:09, Johannes Weiner wrote:
[...]
Quoted 25 lines
Quoted 11 lines
Quoted 11 lines
mm/memcontrol.c +1
-5}else {schedule (); +mem_cgroup_unmark_under_oom (memcg);finish_wait (&memcg_oom_waitq, &owait.wait); } -spin_lock (&memcg_oom_lock); -if (locked) -mem_cgroup_oom_unlock (memcg); -memcg_wakeup_oom (memcg); -spin_unlock (&memcg_oom_lock);Quoted 13 lines
mm/memcontrol.c static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, · line 2244
Quoted 9 lines
mm/memcontrol.c +8
-1-mem_cgroup_unmark_under_oom (memcg); +if (locked) { +mem_cgroup_oom_unlock (memcg); +/* +* There is no guarantee that a OOM-lock contender +* sees the wakeups triggered by the OOM kill +* uncharges. Wake any sleepers explicitely. +*/ +memcg_oom_recover (memcg);This will be a noop because memcg is no longer
under_oom(you wantedmemcg_wakeup_oomhere I guess). Moreover, even the killed wouldn't wake up anybody for the same reason.
Quoted 5 lines
Anybody entering this path will increase the
under_oomcounter. The killer decreases it again, but everybody who is sleeping because they failed the trylock still hasn't unmarked the hierarchy (they callschedule()beforeunmark_under_oom()). So we issue wakeups when there is somebody waiting for the lock.
True, sorry for the noise. Feel free to add
Signature · 2 lines
Michal Hocko SUSE Labs
Johannes Weiner
maintainerPATCH 1/1 memcg OOM robustness (x86 only)
In reply to Johannes WeinerazurIt, this is the combined backport for 3.2, x86 + generic bits + debugging. It would be fantastic if you could give this another shot once you get back from vacation. Thanks!
Johannes
---
arch/x86/mm/fault.c +18 -18
@@ -842,30 +842,22 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,force_sig_info_fault (SIGBUS , code, address, tsk, fault); } -static noinline int +static noinline voidmm_fault_error (struct pt_regs *regs,unsigned long error_code,unsigned long address,unsigned int fault) { -/* -* Pagefault was interrupted by SIGKILL. We have no reason to -* continue pagefault. -*/ -if (fatal_signal_pending (current)) { -if (!(fault &VM_FAULT_RETRY )) -up_read (¤t->mm->mmap_sem); -if (!(error_code &PF_USER )) -no_context (regs, error_code, address); -return 1 ; +if (fatal_signal_pending (current) && !(error_code &PF_USER )) { +up_read (¤t->mm->mmap_sem); +no_context (regs, error_code, address); +return ; } -if (!(fault &VM_FAULT_ERROR )) -return 0 ;if (fault &VM_FAULT_OOM ) {/* Kernel mode? Handle exceptions or die: */ if (!(error_code &PF_USER )) {up_read (¤t->mm->mmap_sem);no_context (regs, error_code, address); -return 1 ; +return ; }out_of_memory (regs, error_code, address);
@@ -876,7 +868,6 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,else BUG (); } -return 1 ; }static int spurious_fault_check (unsigned long error_code,pte_t *pte)
@@ -1070,6 +1061,7 @@ do_page_fault(struct pt_regs *regs, unsigned long error_code)if (user_mode_vm (regs)) {local_irq_enable (); error_code |=PF_USER ; + flags |=FAULT_FLAG_USER ; }else {if (regs->flags &X86_EFLAGS_IF )local_irq_enable ();
@@ -1167,9 +1159,17 @@ good_area:*/ fault =handle_mm_fault (mm, vma, address, flags); -if (unlikely (fault & (VM_FAULT_RETRY |VM_FAULT_ERROR ))) { -if (mm_fault_error (regs, error_code, address, fault)) -return ; +/* +* If we need to retry but a fatal signal is pending, handle the +* signal first. We do not need to release the mmap_sem because it +* would already be released in __lock_page_or_retry in mm/filemap.c. +*/ +if (unlikely ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current))) +return ; + +if (unlikely (fault &VM_FAULT_ERROR )) { +mm_fault_error (regs, error_code, address, fault); +return ; }/*
include/linux/memcontrol.h +40 -0
@@ -120,6 +120,31 @@ mem_cgroup_get_reclaim_stat_from_page(struct page *page);extern void mem_cgroup_print_oom_info (struct mem_cgroup *memcg,struct task_struct *p); +/** +* mem_cgroup_xchg_may_oom - toggle the memcg OOM killer for a task +* @p: 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_xchg_may_oom (struct task_struct *p,bool new) +{ +bool old; + + old = p->memcg_oom.may_oom; + p->memcg_oom.may_oom = new; + +return old; +} + +static inline bool task_in_mem_cgroup_oom (struct task_struct *p) +{ +return p->memcg_oom.in_memcg_oom; +} + +bool mem_cgroup_oom_synchronize (void ); +#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP extern int do_swap_account;#endif
@@ -333,6 +358,21 @@ mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p) { } +static inline bool mem_cgroup_xchg_may_oom (struct task_struct *p,bool new) +{ +return !new; +} + +static inline bool task_in_mem_cgroup_oom (struct task_struct *p) +{ +return false; +} + +static inline bool mem_cgroup_oom_synchronize (void ) +{ +return false; +} +static inline void mem_cgroup_inc_page_stat (struct page *page,enum mem_cgroup_page_stat_item idx) {
include/linux/mm.h +1 -0
@@ -156,6 +156,7 @@ extern pgprot_t protection_map[16];#define FAULT_FLAG_ALLOW_RETRY 0x08/* Retry fault if blocking */ #define FAULT_FLAG_RETRY_NOWAIT 0x10/* Don't drop mmap_sem and wait when retrying */ #define FAULT_FLAG_KILLABLE 0x20/* The fault task is in SIGKILL killable region */ +#define FAULT_FLAG_USER 0x40/* The fault originated in userspace */ /* * This interface is used by x86 PAT code to identify a pfn mapping that is
include/linux/sched.h +9 -0
@@ -91,6 +91,7 @@ struct sched_param {#include <linux/latencytop.h> #include <linux/cred.h> #include <linux/llist.h> +#include <linux/stacktrace.h> #include <asm/processor.h>
@@ -1568,6 +1569,14 @@ struct task_struct {unsigned long nr_pages;/* uncharged usage */ unsigned long memsw_nr_pages; /* uncharged mem+swap usage */ } memcg_batch; +struct memcg_oom_info { +unsigned int may_oom:1 ; +unsigned int in_memcg_oom:1 ; +struct stack_trace trace; +unsigned long trace_entries[16 ]; +int wakeups; +struct mem_cgroup *wait_on_memcg; + } memcg_oom;#endif #ifdef CONFIG_HAVE_HW_BREAKPOINT atomic_t ptrace_bp_refcnt;
mm/filemap.c +10 -1
@@ -1660,6 +1660,7 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)struct file_ra_state *ra = &file->f_ra;struct inode *inode = mapping->host;pgoff_t offset = vmf->pgoff; +unsigned int may_oom;struct page *page;pgoff_t size;int ret =0 ;
@@ -1669,7 +1670,11 @@ 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)) {
@@ -1677,10 +1682,14 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)* We found the page, so try async readahead before * waiting for the lock. */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_async_mmap_readahead (vma, ra, file, page, offset); +mem_cgroup_xchg_may_oom (current, may_oom); }else {/* No page in the page cache at all */ + may_oom =mem_cgroup_xchg_may_oom (current,0 );do_sync_mmap_readahead (vma, ra, file, offset); +mem_cgroup_xchg_may_oom (current, may_oom);count_vm_event (PGMAJFAULT );mem_cgroup_count_vm_event (vma->vm_mm,PGMAJFAULT ); ret =VM_FAULT_MAJOR ;
mm/memcontrol.c +110 -50
243 lines not on this page · raw message
mm/memory.c +37 -10
71 lines not on this page · raw message
mm/oom_kill.c +2 -0
@@ -785,6 +785,8 @@ out:*/ void pagefault_out_of_memory (void ) { +if (mem_cgroup_oom_synchronize ()) +return ;if (try_set_system_oom ()) {out_of_memory (NULL ,0 ,0 ,NULL );clear_system_oom ();
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 3.2] memcg OOM robustness (x86 only)
Quoted 5 lines
azurIt, this is the combined backport for 3.2, x86 + generic bits + debugging. It would be fantastic if you could give this another shot once you get back from vacation. Thanks!
Johannes
Hi Johannes,
is this still up to date? Thank you.
azur
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Johannes Weiner
maintainerRe: [patch 3.2] memcg OOM robustness (x86 only)
Hey azur,
Quoted 1 line · azurIt
is this still up to date? Thank you.
no, I'll send an updated version in a bit.
Thanks!
Johannes
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>