Commit faea6234 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle

[MIPS] Retry {save,restore}_fp_context if failed in atomic context.

The save_fp_context()/restore_fp_context() might sleep on accessing
user stack and therefore might lose FPU ownership in middle of them.

If these function failed due to "in_atomic" test in do_page_fault,
touch the sigcontext area in non-atomic context and retry these
save/restore operation.

This is a replacement of a (broken) fix which was titled "Allow CpU
exception in kernel partially".
Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 5323180d
...@@ -34,4 +34,13 @@ extern int install_sigtramp(unsigned int __user *tramp, unsigned int syscall); ...@@ -34,4 +34,13 @@ extern int install_sigtramp(unsigned int __user *tramp, unsigned int syscall);
/* Check and clear pending FPU exceptions in saved CSR */ /* Check and clear pending FPU exceptions in saved CSR */
extern int fpcsr_pending(unsigned int __user *fpcsr); extern int fpcsr_pending(unsigned int __user *fpcsr);
/* Make sure we will not lose FPU ownership */
#ifdef CONFIG_PREEMPT
#define lock_fpu_owner() preempt_disable()
#define unlock_fpu_owner() preempt_enable()
#else
#define lock_fpu_owner() pagefault_disable()
#define unlock_fpu_owner() pagefault_enable()
#endif
#endif /* __SIGNAL_COMMON_H */ #endif /* __SIGNAL_COMMON_H */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/uaccess.h>
#include <asm/abi.h> #include <asm/abi.h>
#include <asm/asm.h> #include <asm/asm.h>
...@@ -27,7 +28,6 @@ ...@@ -27,7 +28,6 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/fpu.h> #include <asm/fpu.h>
#include <asm/sim.h> #include <asm/sim.h>
#include <asm/uaccess.h>
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/cpu-features.h> #include <asm/cpu-features.h>
#include <asm/war.h> #include <asm/war.h>
...@@ -78,6 +78,46 @@ struct rt_sigframe { ...@@ -78,6 +78,46 @@ struct rt_sigframe {
/* /*
* Helper routines * Helper routines
*/ */
static int protected_save_fp_context(struct sigcontext __user *sc)
{
int err;
while (1) {
lock_fpu_owner();
own_fpu_inatomic(1);
err = save_fp_context(sc); /* this might fail */
unlock_fpu_owner();
if (likely(!err))
break;
/* touch the sigcontext and try again */
err = __put_user(0, &sc->sc_fpregs[0]) |
__put_user(0, &sc->sc_fpregs[31]) |
__put_user(0, &sc->sc_fpc_csr);
if (err)
break; /* really bad sigcontext */
}
return err;
}
static int protected_restore_fp_context(struct sigcontext __user *sc)
{
int err, tmp;
while (1) {
lock_fpu_owner();
own_fpu_inatomic(0);
err = restore_fp_context(sc); /* this might fail */
unlock_fpu_owner();
if (likely(!err))
break;
/* touch the sigcontext and try again */
err = __get_user(tmp, &sc->sc_fpregs[0]) |
__get_user(tmp, &sc->sc_fpregs[31]) |
__get_user(tmp, &sc->sc_fpc_csr);
if (err)
break; /* really bad sigcontext */
}
return err;
}
int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{ {
int err = 0; int err = 0;
...@@ -113,10 +153,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) ...@@ -113,10 +153,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
* Save FPU state to signal context. Signal handler * Save FPU state to signal context. Signal handler
* will "inherit" current FPU state. * will "inherit" current FPU state.
*/ */
preempt_disable(); err |= protected_save_fp_context(sc);
own_fpu(1);
err |= save_fp_context(sc);
preempt_enable();
} }
return err; return err;
} }
...@@ -148,10 +185,7 @@ check_and_restore_fp_context(struct sigcontext __user *sc) ...@@ -148,10 +185,7 @@ check_and_restore_fp_context(struct sigcontext __user *sc)
err = sig = fpcsr_pending(&sc->sc_fpc_csr); err = sig = fpcsr_pending(&sc->sc_fpc_csr);
if (err > 0) if (err > 0)
err = 0; err = 0;
preempt_disable(); err |= protected_restore_fp_context(sc);
own_fpu(0);
err |= restore_fp_context(sc);
preempt_enable();
return err ?: sig; return err ?: sig;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/uaccess.h>
#include <asm/abi.h> #include <asm/abi.h>
#include <asm/asm.h> #include <asm/asm.h>
...@@ -29,7 +30,6 @@ ...@@ -29,7 +30,6 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/sim.h> #include <asm/sim.h>
#include <asm/uaccess.h>
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/fpu.h> #include <asm/fpu.h>
...@@ -176,6 +176,46 @@ struct rt_sigframe32 { ...@@ -176,6 +176,46 @@ struct rt_sigframe32 {
/* /*
* sigcontext handlers * sigcontext handlers
*/ */
static int protected_save_fp_context32(struct sigcontext32 __user *sc)
{
int err;
while (1) {
lock_fpu_owner();
own_fpu_inatomic(1);
err = save_fp_context32(sc); /* this might fail */
unlock_fpu_owner();
if (likely(!err))
break;
/* touch the sigcontext and try again */
err = __put_user(0, &sc->sc_fpregs[0]) |
__put_user(0, &sc->sc_fpregs[31]) |
__put_user(0, &sc->sc_fpc_csr);
if (err)
break; /* really bad sigcontext */
}
return err;
}
static int protected_restore_fp_context32(struct sigcontext32 __user *sc)
{
int err, tmp;
while (1) {
lock_fpu_owner();
own_fpu_inatomic(0);
err = restore_fp_context32(sc); /* this might fail */
unlock_fpu_owner();
if (likely(!err))
break;
/* touch the sigcontext and try again */
err = __get_user(tmp, &sc->sc_fpregs[0]) |
__get_user(tmp, &sc->sc_fpregs[31]) |
__get_user(tmp, &sc->sc_fpc_csr);
if (err)
break; /* really bad sigcontext */
}
return err;
}
static int setup_sigcontext32(struct pt_regs *regs, static int setup_sigcontext32(struct pt_regs *regs,
struct sigcontext32 __user *sc) struct sigcontext32 __user *sc)
{ {
...@@ -209,10 +249,7 @@ static int setup_sigcontext32(struct pt_regs *regs, ...@@ -209,10 +249,7 @@ static int setup_sigcontext32(struct pt_regs *regs,
* Save FPU state to signal context. Signal handler * Save FPU state to signal context. Signal handler
* will "inherit" current FPU state. * will "inherit" current FPU state.
*/ */
preempt_disable(); err |= protected_save_fp_context32(sc);
own_fpu(1);
err |= save_fp_context32(sc);
preempt_enable();
} }
return err; return err;
} }
...@@ -225,10 +262,7 @@ check_and_restore_fp_context32(struct sigcontext32 __user *sc) ...@@ -225,10 +262,7 @@ check_and_restore_fp_context32(struct sigcontext32 __user *sc)
err = sig = fpcsr_pending(&sc->sc_fpc_csr); err = sig = fpcsr_pending(&sc->sc_fpc_csr);
if (err > 0) if (err > 0)
err = 0; err = 0;
preempt_disable(); err |= protected_restore_fp_context32(sc);
own_fpu(0);
err |= restore_fp_context32(sc);
preempt_enable();
return err ?: sig; return err ?: sig;
} }
......
...@@ -100,14 +100,19 @@ static inline void __own_fpu(void) ...@@ -100,14 +100,19 @@ static inline void __own_fpu(void)
set_thread_flag(TIF_USEDFPU); set_thread_flag(TIF_USEDFPU);
} }
static inline void own_fpu(int restore) static inline void own_fpu_inatomic(int restore)
{ {
preempt_disable();
if (cpu_has_fpu && !__is_fpu_owner()) { if (cpu_has_fpu && !__is_fpu_owner()) {
__own_fpu(); __own_fpu();
if (restore) if (restore)
_restore_fp(current); _restore_fp(current);
} }
}
static inline void own_fpu(int restore)
{
preempt_disable();
own_fpu_inatomic(restore);
preempt_enable(); preempt_enable();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment