Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53dc8028 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle
Browse files

[MIPS] FPU ownership management & preemption fixes

parent c6a2f467
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -49,8 +49,7 @@ LEAF(resume)
#ifndef CONFIG_CPU_HAS_LLSC
	sw      zero, ll_bit
#endif
	mfc0	t1, CP0_STATUS
	sw	t1, THREAD_STATUS(a0)
	mfc0	t2, CP0_STATUS
	cpu_save_nonscratch a0
	sw	ra, THREAD_REG31(a0)

@@ -60,8 +59,8 @@ LEAF(resume)
	lw	t3, TASK_THREAD_INFO(a0)
	lw	t0, TI_FLAGS(t3)
	li	t1, _TIF_USEDFPU
	and	t2, t0, t1
	beqz	t2, 1f
	and	t1, t0
	beqz	t1, 1f
	nor	t1, zero, t1

	and	t0, t0, t1
@@ -74,10 +73,13 @@ LEAF(resume)
	li	t1, ~ST0_CU1
	and	t0, t0, t1
	sw	t0, ST_OFF(t3)
	/* clear thread_struct CU1 bit */
	and	t2, t1

	fpu_save_single a0, t0			# clobbers t0

1:
	sw	t2, THREAD_STATUS(a0)
	/*
	 * The order of restoring the registers takes care of the race
	 * updating $28, $29 and kernelsp without disabling ints.
+6 −4
Original line number Diff line number Diff line
@@ -48,8 +48,7 @@
#ifndef CONFIG_CPU_HAS_LLSC
	sw	zero, ll_bit
#endif
	mfc0	t1, CP0_STATUS
	LONG_S	t1, THREAD_STATUS(a0)
	mfc0	t2, CP0_STATUS
	cpu_save_nonscratch a0
	LONG_S	ra, THREAD_REG31(a0)

@@ -59,8 +58,8 @@
	PTR_L	t3, TASK_THREAD_INFO(a0)
	LONG_L	t0, TI_FLAGS(t3)
	li	t1, _TIF_USEDFPU
	and	t2, t0, t1
	beqz	t2, 1f
	and	t1, t0
	beqz	t1, 1f
	nor	t1, zero, t1

	and	t0, t0, t1
@@ -73,10 +72,13 @@
	li	t1, ~ST0_CU1
	and	t0, t0, t1
	LONG_S	t0, ST_OFF(t3)
	/* clear thread_struct CU1 bit */
	and	t2, t1

	fpu_save_double a0 t0 t1		# c0_status passed in t0
						# clobbers t1
1:
	LONG_S	t2, THREAD_STATUS(a0)

	/*
	 * The order of restoring the registers takes care of the race
+12 −17
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
	int err = 0;
	int i;
	unsigned int used_math;

	err |= __put_user(regs->cp0_epc, &sc->sc_pc);

@@ -104,22 +105,18 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
		err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
	}

	err |= __put_user(!!used_math(), &sc->sc_used_math);
	used_math = !!used_math();
	err |= __put_user(used_math, &sc->sc_used_math);

	if (used_math()) {
	if (used_math) {
		/*
		 * Save FPU state to signal context. Signal handler
		 * will "inherit" current FPU state.
		 */
		preempt_disable();

		if (!is_fpu_owner()) {
			own_fpu();
			restore_fp(current);
		}
		own_fpu(1);
		enable_fp_in_kernel();
		err |= save_fp_context(sc);

		preempt_enable();
		disable_fp_in_kernel();
	}
	return err;
}
@@ -188,20 +185,18 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
	err |= __get_user(used_math, &sc->sc_used_math);
	conditional_used_math(used_math);

	preempt_disable();

	if (used_math()) {
	if (used_math) {
		/* restore fpu context if we have used it before */
		own_fpu();
		own_fpu(0);
		enable_fp_in_kernel();
		if (!err)
			err = check_and_restore_fp_context(sc);
		disable_fp_in_kernel();
	} else {
		/* signal handler may have used FPU.  Give it up. */
		lose_fpu();
		lose_fpu(0);
	}

	preempt_enable();

	return err;
}

+12 −17
Original line number Diff line number Diff line
@@ -181,6 +181,7 @@ static int setup_sigcontext32(struct pt_regs *regs,
{
	int err = 0;
	int i;
	u32 used_math;

	err |= __put_user(regs->cp0_epc, &sc->sc_pc);

@@ -200,22 +201,18 @@ static int setup_sigcontext32(struct pt_regs *regs,
		err |= __put_user(mflo3(), &sc->sc_lo3);
	}

	err |= __put_user(!!used_math(), &sc->sc_used_math);
	used_math = !!used_math();
	err |= __put_user(used_math, &sc->sc_used_math);

	if (used_math()) {
	if (used_math) {
		/*
		 * Save FPU state to signal context.  Signal handler
		 * will "inherit" current FPU state.
		 */
		preempt_disable();

		if (!is_fpu_owner()) {
			own_fpu();
			restore_fp(current);
		}
		own_fpu(1);
		enable_fp_in_kernel();
		err |= save_fp_context32(sc);

		preempt_enable();
		disable_fp_in_kernel();
	}
	return err;
}
@@ -262,20 +259,18 @@ static int restore_sigcontext32(struct pt_regs *regs,
	err |= __get_user(used_math, &sc->sc_used_math);
	conditional_used_math(used_math);

	preempt_disable();

	if (used_math()) {
	if (used_math) {
		/* restore fpu context if we have used it before */
		own_fpu();
		own_fpu(0);
		enable_fp_in_kernel();
		if (!err)
			err = check_and_restore_fp_context32(sc);
		disable_fp_in_kernel();
	} else {
		/* signal handler may have used FPU.  Give it up. */
		lose_fpu();
		lose_fpu(0);
	}

	preempt_enable();

	return err;
}

+37 −47
Original line number Diff line number Diff line
@@ -610,16 +610,6 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
	if (fcr31 & FPU_CSR_UNI_X) {
		int sig;

		preempt_disable();

#ifdef CONFIG_PREEMPT
		if (!is_fpu_owner()) {
			/* We might lose fpu before disabling preempt... */
			own_fpu();
			BUG_ON(!used_math());
			restore_fp(current);
		}
#endif
		/*
		 * Unimplemented operation exception.  If we've got the full
		 * software emulator on-board, let's use it...
@@ -630,18 +620,12 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
		 * register operands before invoking the emulator, which seems
		 * a bit extreme for what should be an infrequent event.
		 */
		save_fp(current);
		/* Ensure 'resume' not overwrite saved fp context again. */
		lose_fpu();

		preempt_enable();
		lose_fpu(1);

		/* Run the emulator */
		sig = fpu_emulator_cop1Handler (regs, &current->thread.fpu, 1);

		preempt_disable();

		own_fpu();	/* Using the FPU again.  */
		/*
		 * We can't allow the emulated instruction to leave any of
		 * the cause bit set in $fcr31.
@@ -649,9 +633,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
		current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;

		/* Restore the hardware register state */
		restore_fp(current);

		preempt_enable();
		own_fpu(1);	/* Using the FPU again.  */

		/* If something went wrong, signal */
		if (sig)
@@ -775,12 +757,11 @@ asmlinkage void do_cpu(struct pt_regs *regs)
{
	unsigned int cpid;

	die_if_kernel("do_cpu invoked from kernel context!", regs);

	cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;

	switch (cpid) {
	case 0:
		die_if_kernel("do_cpu invoked from kernel context!", regs);
		if (!cpu_has_llsc)
			if (!simulate_llsc(regs))
				return;
@@ -791,21 +772,30 @@ asmlinkage void do_cpu(struct pt_regs *regs)
		break;

	case 1:
		preempt_disable();

		own_fpu();
		if (used_math()) {	/* Using the FPU again.  */
			restore_fp(current);
		} else {			/* First time FPU user.  */
		if (!test_thread_flag(TIF_ALLOW_FP_IN_KERNEL))
			die_if_kernel("do_cpu invoked from kernel context!",
				      regs);
		if (used_math())	/* Using the FPU again.  */
			own_fpu(1);
		else {			/* First time FPU user.  */
			init_fpu();
			set_used_math();
		}

		if (cpu_has_fpu) {
			preempt_enable();
		if (raw_cpu_has_fpu) {
			if (test_thread_flag(TIF_ALLOW_FP_IN_KERNEL)) {
				local_irq_disable();
				if (cpu_has_fpu)
					regs->cp0_status |= ST0_CU1;
				/*
				 * We must return without enabling
				 * interrupts to ensure keep FPU
				 * ownership until resume.
				 */
				return;
			}
		} else {
			int sig;
			preempt_enable();
			sig = fpu_emulator_cop1Handler(regs,
						&current->thread.fpu, 0);
			if (sig)
@@ -1259,26 +1249,26 @@ static inline void mips_srs_init(void)
/*
 * This is used by native signal handling
 */
asmlinkage int (*save_fp_context)(struct sigcontext *sc);
asmlinkage int (*restore_fp_context)(struct sigcontext *sc);
asmlinkage int (*save_fp_context)(struct sigcontext __user *sc);
asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc);

extern asmlinkage int _save_fp_context(struct sigcontext *sc);
extern asmlinkage int _restore_fp_context(struct sigcontext *sc);
extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);

extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc);
extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc);
extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc);
extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc);

#ifdef CONFIG_SMP
static int smp_save_fp_context(struct sigcontext *sc)
static int smp_save_fp_context(struct sigcontext __user *sc)
{
	return cpu_has_fpu
	return raw_cpu_has_fpu
	       ? _save_fp_context(sc)
	       : fpu_emulator_save_context(sc);
}

static int smp_restore_fp_context(struct sigcontext *sc)
static int smp_restore_fp_context(struct sigcontext __user *sc)
{
	return cpu_has_fpu
	return raw_cpu_has_fpu
	       ? _restore_fp_context(sc)
	       : fpu_emulator_restore_context(sc);
}
@@ -1306,14 +1296,14 @@ static inline void signal_init(void)
/*
 * This is used by 32-bit signal stuff on the 64-bit kernel
 */
asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc);
asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc);
asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc);
asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc);

extern asmlinkage int _save_fp_context32(struct sigcontext32 *sc);
extern asmlinkage int _restore_fp_context32(struct sigcontext32 *sc);
extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc);
extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc);

extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 *sc);
extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 *sc);
extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc);
extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc);

static inline void signal32_init(void)
{
Loading