Skip to content

Commit

Permalink
sh: Move over to dynamically allocated FPU context.
Browse files Browse the repository at this point in the history
This follows the x86 xstate changes and implements a task_xstate slab
cache that is dynamically sized to match one of hard FP/soft FP/FPU-less.

This also tidies up and consolidates some of the SH-2A/SH-4 FPU
fragmentation. Now fpu state restorers are commonly defined, with the
init_fpu()/fpu_init() mess reworked to follow the x86 convention.
The fpu_init() register initialization has been replaced by xstate setup
followed by writing out to hardware via the standard restore path.

As init_fpu() now performs a slab allocation a secondary lighterweight
restorer is also introduced for the context switch.

In the future the DSP state will be rolled in here, too.

More work remains for math emulation and the SH-5 FPU, which presently
uses its own special (UP-only) interfaces.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
  • Loading branch information
Paul Mundt committed Jan 13, 2010
1 parent a370579 commit 0ea820c
Show file tree
Hide file tree
Showing 13 changed files with 292 additions and 285 deletions.
35 changes: 12 additions & 23 deletions arch/sh/include/asm/fpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#define __ASM_SH_FPU_H

#ifndef __ASSEMBLY__
#include <linux/preempt.h>
#include <asm/ptrace.h>

struct task_struct;

#ifdef CONFIG_SH_FPU
static inline void release_fpu(struct pt_regs *regs)
Expand All @@ -16,22 +16,23 @@ static inline void grab_fpu(struct pt_regs *regs)
regs->sr &= ~SR_FD;
}

struct task_struct;

extern void save_fpu(struct task_struct *__tsk);
void fpu_state_restore(struct pt_regs *regs);
extern void restore_fpu(struct task_struct *__tsk);
extern void fpu_state_restore(struct pt_regs *regs);
extern void __fpu_state_restore(void);
#else

#define save_fpu(tsk) do { } while (0)
#define release_fpu(regs) do { } while (0)
#define grab_fpu(regs) do { } while (0)
#define fpu_state_restore(regs) do { } while (0)

#define save_fpu(tsk) do { } while (0)
#define restore_fpu(tsk) do { } while (0)
#define release_fpu(regs) do { } while (0)
#define grab_fpu(regs) do { } while (0)
#define fpu_state_restore(regs) do { } while (0)
#define __fpu_state_restore(regs) do { } while (0)
#endif

struct user_regset;

extern int do_fpu_inst(unsigned short, struct pt_regs *);
extern int init_fpu(struct task_struct *);

extern int fpregs_get(struct task_struct *target,
const struct user_regset *regset,
Expand Down Expand Up @@ -65,18 +66,6 @@ static inline void clear_fpu(struct task_struct *tsk, struct pt_regs *regs)
preempt_enable();
}

static inline int init_fpu(struct task_struct *tsk)
{
if (tsk_used_math(tsk)) {
if ((boot_cpu_data.flags & CPU_HAS_FPU) && tsk == current)
unlazy_fpu(tsk, task_pt_regs(tsk));
return 0;
}

set_stopped_child_used_math(tsk);
return 0;
}

#endif /* __ASSEMBLY__ */

#endif /* __ASM_SH_FPU_H */
16 changes: 10 additions & 6 deletions arch/sh/include/asm/processor_32.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,15 @@ struct sh_fpu_soft_struct {
unsigned long entry_pc;
};

union sh_fpu_union {
struct sh_fpu_hard_struct hard;
struct sh_fpu_soft_struct soft;
union thread_xstate {
struct sh_fpu_hard_struct hardfpu;
struct sh_fpu_soft_struct softfpu;
};

extern unsigned int xstate_size;
extern void free_thread_xstate(struct task_struct *);
extern struct kmem_cache *task_xstate_cachep;

struct thread_struct {
/* Saved registers when thread is descheduled */
unsigned long sp;
Expand All @@ -103,13 +107,13 @@ struct thread_struct {
/* Hardware debugging registers */
unsigned long ubc_pc;

/* floating point info */
union sh_fpu_union fpu;

#ifdef CONFIG_SH_DSP
/* Dsp status information */
struct sh_dsp_struct dsp_status;
#endif

/* Extended processor state */
union thread_xstate *xstate;
};

/* Count of active tasks with UBC settings */
Expand Down
4 changes: 4 additions & 0 deletions arch/sh/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ static inline struct thread_info *current_thread_info(void)

extern struct thread_info *alloc_thread_info(struct task_struct *tsk);
extern void free_thread_info(struct thread_info *ti);
extern void arch_task_cache_init(void);
#define arch_task_cache_init arch_task_cache_init
extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
extern void init_thread_xstate(void);

#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR

Expand Down
2 changes: 2 additions & 0 deletions arch/sh/kernel/cpu/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ obj-$(CONFIG_ARCH_SHMOBILE) += shmobile/

obj-$(CONFIG_SH_ADC) += adc.o
obj-$(CONFIG_SH_CLK_CPG) += clock-cpg.o
obj-$(CONFIG_SH_FPU) += fpu.o
obj-$(CONFIG_SH_FPU_EMU) += fpu.o

obj-y += irq/ init.o clock.o hwblk.o
82 changes: 82 additions & 0 deletions arch/sh/kernel/cpu/fpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include <linux/sched.h>
#include <asm/processor.h>
#include <asm/fpu.h>

int init_fpu(struct task_struct *tsk)
{
if (tsk_used_math(tsk)) {
if ((boot_cpu_data.flags & CPU_HAS_FPU) && tsk == current)
unlazy_fpu(tsk, task_pt_regs(tsk));
return 0;
}

/*
* Memory allocation at the first usage of the FPU and other state.
*/
if (!tsk->thread.xstate) {
tsk->thread.xstate = kmem_cache_alloc(task_xstate_cachep,
GFP_KERNEL);
if (!tsk->thread.xstate)
return -ENOMEM;
}

if (boot_cpu_data.flags & CPU_HAS_FPU) {
struct sh_fpu_hard_struct *fp = &tsk->thread.xstate->hardfpu;
memset(fp, 0, xstate_size);
fp->fpscr = FPSCR_INIT;
} else {
struct sh_fpu_soft_struct *fp = &tsk->thread.xstate->softfpu;
memset(fp, 0, xstate_size);
fp->fpscr = FPSCR_INIT;
}

set_stopped_child_used_math(tsk);
return 0;
}

#ifdef CONFIG_SH_FPU
void __fpu_state_restore(void)
{
struct task_struct *tsk = current;

restore_fpu(tsk);

task_thread_info(tsk)->status |= TS_USEDFPU;
tsk->fpu_counter++;
}

void fpu_state_restore(struct pt_regs *regs)
{
struct task_struct *tsk = current;

if (unlikely(!user_mode(regs))) {
printk(KERN_ERR "BUG: FPU is used in kernel mode.\n");
BUG();
return;
}

if (!tsk_used_math(tsk)) {
/*
* does a slab alloc which can sleep
*/
if (init_fpu(tsk)) {
/*
* ran out of memory!
*/
do_group_exit(SIGKILL);
return;
}
}

grab_fpu(regs);

__fpu_state_restore();
}

BUILD_TRAP_HANDLER(fpu_state_restore)
{
TRAP_HANDLER_DECL;

fpu_state_restore(regs);
}
#endif /* CONFIG_SH_FPU */
80 changes: 48 additions & 32 deletions arch/sh/kernel/cpu/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,30 @@
#include <asm/ubc.h>
#endif

#ifdef CONFIG_SH_FPU
#define cpu_has_fpu 1
#else
#define cpu_has_fpu 0
#endif

#ifdef CONFIG_SH_DSP
#define cpu_has_dsp 1
#else
#define cpu_has_dsp 0
#endif

/*
* Generic wrapper for command line arguments to disable on-chip
* peripherals (nofpu, nodsp, and so forth).
*/
#define onchip_setup(x) \
static int x##_disabled __initdata = 0; \
\
static int __init x##_setup(char *opts) \
{ \
x##_disabled = 1; \
return 1; \
} \
#define onchip_setup(x) \
static int x##_disabled __initdata = !cpu_has_##x; \
\
static int __init x##_setup(char *opts) \
{ \
x##_disabled = 1; \
return 1; \
} \
__setup("no" __stringify(x), x##_setup);

onchip_setup(fpu);
Expand Down Expand Up @@ -207,6 +219,18 @@ static void detect_cache_shape(void)
l2_cache_shape = -1; /* No S-cache */
}

static void __init fpu_init(void)
{
/* Disable the FPU */
if (fpu_disabled && (current_cpu_data.flags & CPU_HAS_FPU)) {
printk("FPU Disabled\n");
current_cpu_data.flags &= ~CPU_HAS_FPU;
}

disable_fpu();
clear_used_math();
}

#ifdef CONFIG_SH_DSP
static void __init release_dsp(void)
{
Expand Down Expand Up @@ -244,9 +268,17 @@ static void __init dsp_init(void)
if (sr & SR_DSP)
current_cpu_data.flags |= CPU_HAS_DSP;

/* Disable the DSP */
if (dsp_disabled && (current_cpu_data.flags & CPU_HAS_DSP)) {
printk("DSP Disabled\n");
current_cpu_data.flags &= ~CPU_HAS_DSP;
}

/* Now that we've determined the DSP status, clear the DSP bit. */
release_dsp();
}
#else
static inline void __init dsp_init(void) { }
#endif /* CONFIG_SH_DSP */

/**
Expand Down Expand Up @@ -302,37 +334,21 @@ asmlinkage void __init sh_cpu_init(void)
detect_cache_shape();
}

/* Disable the FPU */
if (fpu_disabled) {
printk("FPU Disabled\n");
current_cpu_data.flags &= ~CPU_HAS_FPU;
}

/* FPU initialization */
disable_fpu();
if ((current_cpu_data.flags & CPU_HAS_FPU)) {
current_thread_info()->status &= ~TS_USEDFPU;
clear_used_math();
}
fpu_init();
dsp_init();

/*
* Initialize the per-CPU ASID cache very early, since the
* TLB flushing routines depend on this being setup.
*/
current_cpu_data.asid_cache = NO_CONTEXT;

#ifdef CONFIG_SH_DSP
/* Probe for DSP */
dsp_init();

/* Disable the DSP */
if (dsp_disabled) {
printk("DSP Disabled\n");
current_cpu_data.flags &= ~CPU_HAS_DSP;
release_dsp();
}
#endif

speculative_execution_init();
expmask_init();

/*
* Boot processor to setup the FP and extended state context info.
*/
if (raw_smp_processor_id() == 0)
init_thread_xstate();
}
Loading

0 comments on commit 0ea820c

Please sign in to comment.