MIPS: Handle __put_user() sleeping.

do_signal() does __put_user() which can fault, resulting in a might_sleep()
warning in down_read(&mm->mmap_sem) and a "scheduling while atomic" warning
when mmap_sem is contented. On Swarm this also results in:

WARNING: at kernel/smp.c:459 smp_call_function_many+0x148/0x398()
Modules linked in:
Call Trace:

[<ffffffff804b48a4>] dump_stack+0x1c/0x50
[<ffffffff8013dc94>] warn_slowpath_common+0x8c/0xc8
[<ffffffff8013dcfc>] warn_slowpath_null+0x2c/0x40
[<ffffffff801864a0>] smp_call_function_many+0x148/0x398
[<ffffffff80186748>] smp_call_function+0x58/0xa8
[<ffffffff80119b5c>] r4k_flush_data_cache_page+0x54/0xd8
[<ffffffff801f39bc>] handle_pte_fault+0xa9c/0xad0
[<ffffffff801f40d0>] handle_mm_fault+0x158/0x200
[<ffffffff80115548>] do_page_fault+0x218/0x3b0
[<ffffffff80102744>] ret_from_exception+0x0/0x10
[<ffffffff8010eb18>] copy_siginfo_to_user32+0x50/0x298
[<ffffffff8010edf0>] setup_rt_frame_32+0x90/0x250
[<ffffffff80106414>] do_notify_resume+0x154/0x358
[<ffffffff80102930>] work_notifysig+0xc/0x14

Fixed by enabling interrupts in do_notify_resume before delivering signals.

[ralf@linux-mips.org: Reported and original fix by tglx but I wanted to
minimize the amount of code being run with interrupts disabled so I moved
the local_irq_disable() call right into do_notify_resume.  Which is saner
than doing it in entry.S.]

Reported-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Ralf Baechle 2011-07-27 11:44:47 +01:00
parent 0f462e3c12
commit 1f717929e9
1 changed files with 3 additions and 0 deletions

View File

@ -8,6 +8,7 @@
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
#include <linux/cache.h>
#include <linux/irqflags.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/personality.h>
@ -658,6 +659,8 @@ static void do_signal(struct pt_regs *regs)
asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
__u32 thread_info_flags)
{
local_irq_enable();
/* deal with pending signal delivery */
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
do_signal(regs);