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

Commit d6551e88 authored by Russell King's avatar Russell King Committed by Russell King
Browse files

[ARM] Add thread_notify infrastructure



Some machine classes need to allow VFP support to be built into the
kernel, but still allow the kernel to run even though VFP isn't
present.  Unfortunately, the kernel hard-codes VFP instructions
into the thread switch, which prevents this being run-time selectable.

Solve this by introducing a notifier which things such as VFP can
hook into to be informed of events which affect the VFP subsystem
(eg, creation and destruction of threads, switches between threads.)

Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 52ab3f3d
Loading
Loading
Loading
Loading
+11 −13
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <asm/glue.h>
#include <asm/vfpmacros.h>
#include <asm/arch/entry-macro.S>
#include <asm/thread_notify.h>

#include "entry-header.S"

@@ -560,10 +561,8 @@ ENTRY(__switch_to)
	add	ip, r1, #TI_CPU_SAVE
	ldr	r3, [r2, #TI_TP_VALUE]
	stmia	ip!, {r4 - sl, fp, sp, lr}	@ Store most regs on stack
#ifndef CONFIG_MMU
	add	r2, r2, #TI_CPU_DOMAIN
#else
	ldr	r6, [r2, #TI_CPU_DOMAIN]!
#ifdef CONFIG_MMU
	ldr	r6, [r2, #TI_CPU_DOMAIN]
#endif
#if __LINUX_ARM_ARCH__ >= 6
#ifdef CONFIG_CPU_32v6K
@@ -585,21 +584,20 @@ ENTRY(__switch_to)
#ifdef CONFIG_MMU
	mcr	p15, 0, r6, c3, c0, 0		@ Set domain register
#endif
#ifdef CONFIG_VFP
	@ Always disable VFP so we can lazily save/restore the old
	@ state. This occurs in the context of the previous thread.
	VFPFMRX	r4, FPEXC
	bic	r4, r4, #FPEXC_ENABLE
	VFPFMXR	FPEXC, r4
#endif
#if defined(CONFIG_IWMMXT)
	bl	iwmmxt_task_switch
#elif defined(CONFIG_CPU_XSCALE)
	add	r4, r2, #40			@ cpu_context_save->extra
	add	r4, r2, #TI_CPU_DOMAIN + 40	@ cpu_context_save->extra
	ldmib	r4, {r4, r5}
	mar	acc0, r4, r5
#endif
	ldmib	r2, {r4 - sl, fp, sp, pc}	@ Load all regs saved previously
	mov	r5, r0
	add	r4, r2, #TI_CPU_SAVE
	ldr	r0, =thread_notify_head
	mov	r1, #THREAD_NOTIFY_SWITCH
	bl	atomic_notifier_call_chain
	mov	r0, r5
	ldmia	r4, {r4 - sl, fp, sp, pc}	@ Load all regs saved previously

	__INIT

+1 −1
Original line number Diff line number Diff line
@@ -285,7 +285,7 @@ ENTRY(iwmmxt_task_switch)
	bne	1f				@ yes: block them for next task

	ldr	r5, =concan_owner
	add	r6, r2, #(TI_IWMMXT_STATE - TI_CPU_DOMAIN) @ get next task Concan save area
	add	r6, r2, #TI_IWMMXT_STATE	@ get next task Concan save area
	ldr	r5, [r5]			@ get current Concan owner
	teq	r5, r6				@ next task owns it?
	movne	pc, lr				@ no: leave Concan disabled
+10 −14
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <asm/leds.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/thread_notify.h>
#include <asm/uaccess.h>
#include <asm/mach/time.h>

@@ -338,13 +339,9 @@ void exit_thread(void)
{
}

static void default_fp_init(union fp_state *fp)
{
	memset(fp, 0, sizeof(union fp_state));
}
ATOMIC_NOTIFIER_HEAD(thread_notify_head);

void (*fp_init)(union fp_state *) = default_fp_init;
EXPORT_SYMBOL(fp_init);
EXPORT_SYMBOL_GPL(thread_notify_head);

void flush_thread(void)
{
@@ -353,22 +350,21 @@ void flush_thread(void)

	memset(thread->used_cp, 0, sizeof(thread->used_cp));
	memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
	memset(&thread->fpstate, 0, sizeof(union fp_state));

	thread_notify(THREAD_NOTIFY_FLUSH, thread);
#if defined(CONFIG_IWMMXT)
	iwmmxt_task_release(thread);
#endif
	fp_init(&thread->fpstate);
#if defined(CONFIG_VFP)
	vfp_flush_thread(&thread->vfpstate);
#endif
}

void release_thread(struct task_struct *dead_task)
{
#if defined(CONFIG_VFP)
	vfp_release_thread(&task_thread_info(dead_task)->vfpstate);
#endif
	struct thread_info *thread = task_thread_info(dead_task);

	thread_notify(THREAD_NOTIFY_RELEASE, thread);
#if defined(CONFIG_IWMMXT)
	iwmmxt_task_release(task_thread_info(dead_task));
	iwmmxt_task_release(thread);
#endif
}

+19 −6
Original line number Diff line number Diff line
@@ -33,7 +33,8 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/init.h>
/* XXX */

#include <asm/thread_notify.h>

#include "softfloat.h"
#include "fpopcode.h"
@@ -56,16 +57,28 @@ void fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
extern char fpe_type[];
#endif

static int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v)
{
	struct thread_info *thread = v;

	if (cmd == THREAD_NOTIFY_FLUSH)
		nwfpe_init_fpa(&thread->fpstate);

	return NOTIFY_DONE;
}

static struct notifier_block nwfpe_notifier_block = {
	.notifier_call = nwfpe_notify,
};

/* kernel function prototypes required */
void fp_setup(void);

/* external declarations for saved kernel symbols */
extern void (*kern_fp_enter)(void);
extern void (*fp_init)(union fp_state *);

/* Original value of fp_enter from kernel before patched by fpe_init. */
static void (*orig_fp_enter)(void);
static void (*orig_fp_init)(union fp_state *);

/* forward declarations */
extern void nwfpe_enter(void);
@@ -88,20 +101,20 @@ static int __init fpe_init(void)
	printk(KERN_WARNING "NetWinder Floating Point Emulator V0.97 ("
	       NWFPE_BITS " precision)\n");

	thread_register_notifier(&nwfpe_notifier_block);

	/* Save pointer to the old FP handler and then patch ourselves in */
	orig_fp_enter = kern_fp_enter;
	orig_fp_init = fp_init;
	kern_fp_enter = nwfpe_enter;
	fp_init = nwfpe_init_fpa;

	return 0;
}

static void __exit fpe_exit(void)
{
	thread_unregister_notifier(&nwfpe_notifier_block);
	/* Restore the values we saved earlier. */
	kern_fp_enter = orig_fp_enter;
	fp_init = orig_fp_init;
}

/*
+46 −25
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/init.h>

#include <asm/thread_notify.h>
#include <asm/vfp.h>

#include "vfpinstr.h"
@@ -36,11 +38,16 @@ union vfp_state *last_VFP_context;
 */
unsigned int VFP_arch;

static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
{
	struct thread_info *thread = v;
	union vfp_state *vfp = &thread->vfpstate;

	switch (cmd) {
	case THREAD_NOTIFY_FLUSH:
		/*
		 * Per-thread VFP initialisation.
		 */
void vfp_flush_thread(union vfp_state *vfp)
{
		memset(vfp, 0, sizeof(union vfp_state));

		vfp->hard.fpexc = FPEXC_ENABLE;
@@ -52,22 +59,34 @@ void vfp_flush_thread(union vfp_state *vfp)
		fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);

		/*
	 * Ensure we don't try to overwrite our newly initialised
	 * state information on the first fault.
		 * FALLTHROUGH: Ensure we don't try to overwrite our newly
		 * initialised state information on the first fault.
		 */
	if (last_VFP_context == vfp)
		last_VFP_context = NULL;
}

	case THREAD_NOTIFY_RELEASE:
		/*
		 * Per-thread VFP cleanup.
		 */
void vfp_release_thread(union vfp_state *vfp)
{
		if (last_VFP_context == vfp)
			last_VFP_context = NULL;
		break;

	case THREAD_NOTIFY_SWITCH:
		/*
		 * Always disable VFP so we can lazily save/restore the
		 * old state.
		 */
		fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
		break;
	}

	return NOTIFY_DONE;
}

static struct notifier_block vfp_notifier_block = {
	.notifier_call	= vfp_notifier,
};

/*
 * Raise a SIGFPE for the current process.
 * sicode describes the signal being raised.
@@ -281,6 +300,8 @@ static int __init vfp_init(void)
			(vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT,
			(vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT);
		vfp_vector = vfp_support_entry;

		thread_register_notifier(&vfp_notifier_block);
	}
	return 0;
}
Loading