Skip to content

Commit

Permalink
x86, fpu: split FPU state from task struct - v5
Browse files Browse the repository at this point in the history
Split the FPU save area from the task struct. This allows easy migration
of FPU context, and it's generally cleaner. It also allows the following
two optimizations:

1) only allocate when the application actually uses FPU, so in the first
lazy FPU trap. This could save memory for non-fpu using apps. Next patch
does this lazy allocation.

2) allocate the right size for the actual cpu rather than 512 bytes always.
Patches enabling xsave/xrstor support (coming shortly) will take advantage
of this.

Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
  • Loading branch information
Suresh Siddha authored and Ingo Molnar committed Apr 19, 2008
1 parent fa5c463 commit 61c4628
Show file tree
Hide file tree
Showing 16 changed files with 161 additions and 90 deletions.
1 change: 1 addition & 0 deletions arch/x86/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ obj-$(CONFIG_X86_64) += pci-nommu_64.o bugs_64.o
obj-y += tsc_$(BITS).o io_delay.o rtc.o

obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
obj-y += process.o
obj-y += i387.o
obj-y += ptrace.o
obj-y += ds.o
Expand Down
80 changes: 46 additions & 34 deletions arch/x86/kernel/i387.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/regset.h>
#include <linux/sched.h>
#include <linux/bootmem.h>

#include <asm/sigcontext.h>
#include <asm/processor.h>
Expand Down Expand Up @@ -35,24 +36,36 @@
#endif

static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
unsigned int xstate_size;
static struct i387_fxsave_struct fx_scratch __cpuinitdata;

void mxcsr_feature_mask_init(void)
void __cpuinit mxcsr_feature_mask_init(void)
{
unsigned long mask = 0;

clts();
if (cpu_has_fxsr) {
memset(&current->thread.i387.fxsave, 0,
sizeof(struct i387_fxsave_struct));
asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave));
mask = current->thread.i387.fxsave.mxcsr_mask;
memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
asm volatile("fxsave %0" : : "m" (fx_scratch));
mask = fx_scratch.mxcsr_mask;
if (mask == 0)
mask = 0x0000ffbf;
}
mxcsr_feature_mask &= mask;
stts();
}

void __init init_thread_xstate(void)
{
if (cpu_has_fxsr)
xstate_size = sizeof(struct i387_fxsave_struct);
#ifdef CONFIG_X86_32
else
xstate_size = sizeof(struct i387_fsave_struct);
#endif
init_task.thread.xstate = alloc_bootmem(xstate_size);
}

#ifdef CONFIG_X86_64
/*
* Called at bootup to set up the initial FPU state that is later cloned
Expand All @@ -61,10 +74,6 @@ void mxcsr_feature_mask_init(void)
void __cpuinit fpu_init(void)
{
unsigned long oldcr0 = read_cr0();
extern void __bad_fxsave_alignment(void);

if (offsetof(struct task_struct, thread.i387.fxsave) & 15)
__bad_fxsave_alignment();

set_in_cr4(X86_CR4_OSFXSR);
set_in_cr4(X86_CR4_OSXMMEXCPT);
Expand Down Expand Up @@ -93,18 +102,19 @@ void init_fpu(struct task_struct *tsk)
}

if (cpu_has_fxsr) {
memset(&tsk->thread.i387.fxsave, 0,
sizeof(struct i387_fxsave_struct));
tsk->thread.i387.fxsave.cwd = 0x37f;
struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;

memset(fx, 0, xstate_size);
fx->cwd = 0x37f;
if (cpu_has_xmm)
tsk->thread.i387.fxsave.mxcsr = MXCSR_DEFAULT;
fx->mxcsr = MXCSR_DEFAULT;
} else {
memset(&tsk->thread.i387.fsave, 0,
sizeof(struct i387_fsave_struct));
tsk->thread.i387.fsave.cwd = 0xffff037fu;
tsk->thread.i387.fsave.swd = 0xffff0000u;
tsk->thread.i387.fsave.twd = 0xffffffffu;
tsk->thread.i387.fsave.fos = 0xffff0000u;
struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
memset(fp, 0, xstate_size);
fp->cwd = 0xffff037fu;
fp->swd = 0xffff0000u;
fp->twd = 0xffffffffu;
fp->fos = 0xffff0000u;
}
/*
* Only the device not available exception or ptrace can call init_fpu.
Expand Down Expand Up @@ -132,7 +142,7 @@ int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
init_fpu(target);

return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.i387.fxsave, 0, -1);
&target->thread.xstate->fxsave, 0, -1);
}

int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
Expand All @@ -148,12 +158,12 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
set_stopped_child_used_math(target);

ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.i387.fxsave, 0, -1);
&target->thread.xstate->fxsave, 0, -1);

/*
* mxcsr reserved bits must be masked to zero for security reasons.
*/
target->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;

return ret;
}
Expand Down Expand Up @@ -233,7 +243,7 @@ static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
static void
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
{
struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave;
struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
int i;
Expand Down Expand Up @@ -273,7 +283,7 @@ static void convert_to_fxsr(struct task_struct *tsk,
const struct user_i387_ia32_struct *env)

{
struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave;
struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
int i;
Expand Down Expand Up @@ -310,7 +320,8 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset,

if (!cpu_has_fxsr) {
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.i387.fsave, 0, -1);
&target->thread.xstate->fsave, 0,
-1);
}

if (kbuf && pos == 0 && count == sizeof(env)) {
Expand Down Expand Up @@ -338,7 +349,7 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,

if (!cpu_has_fxsr) {
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.i387.fsave, 0, -1);
&target->thread.xstate->fsave, 0, -1);
}

if (pos > 0 || count < sizeof(env))
Expand All @@ -358,18 +369,19 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,
static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
{
struct task_struct *tsk = current;
struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;

unlazy_fpu(tsk);
tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd;
if (__copy_to_user(buf, &tsk->thread.i387.fsave,
sizeof(struct i387_fsave_struct)))
fp->status = fp->swd;
if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
return -1;
return 1;
}

static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
{
struct task_struct *tsk = current;
struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;
struct user_i387_ia32_struct env;
int err = 0;

Expand All @@ -379,12 +391,12 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
if (__copy_to_user(buf, &env, sizeof(env)))
return -1;

err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status);
err |= __put_user(fx->swd, &buf->status);
err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
if (err)
return -1;

if (__copy_to_user(&buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
if (__copy_to_user(&buf->_fxsr_env[0], fx,
sizeof(struct i387_fxsave_struct)))
return -1;
return 1;
Expand Down Expand Up @@ -417,7 +429,7 @@ static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
struct task_struct *tsk = current;

clear_fpu(tsk);
return __copy_from_user(&tsk->thread.i387.fsave, buf,
return __copy_from_user(&tsk->thread.xstate->fsave, buf,
sizeof(struct i387_fsave_struct));
}

Expand All @@ -428,10 +440,10 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
int err;

clear_fpu(tsk);
err = __copy_from_user(&tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
sizeof(struct i387_fxsave_struct));
/* mxcsr reserved bits must be masked to zero for security reasons */
tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
if (err || __copy_from_user(&env, buf, sizeof(env)))
return 1;
convert_to_fxsr(tsk, &env);
Expand Down
35 changes: 35 additions & 0 deletions arch/x86/kernel/process.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <linux/sched.h>

static struct kmem_cache *task_xstate_cachep;

int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
*dst = *src;
dst->thread.xstate = kmem_cache_alloc(task_xstate_cachep, GFP_KERNEL);
if (!dst->thread.xstate)
return -ENOMEM;
WARN_ON((unsigned long)dst->thread.xstate & 15);
memcpy(dst->thread.xstate, src->thread.xstate, xstate_size);
return 0;
}

void free_thread_info(struct thread_info *ti)
{
kmem_cache_free(task_xstate_cachep, ti->task->thread.xstate);
ti->task->thread.xstate = NULL;

free_pages((unsigned long)(ti), get_order(THREAD_SIZE));
}

void arch_task_cache_init(void)
{
task_xstate_cachep =
kmem_cache_create("task_xstate", xstate_size,
__alignof__(union thread_xstate),
SLAB_PANIC, NULL);
}
2 changes: 1 addition & 1 deletion arch/x86/kernel/process_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ struct task_struct * __switch_to(struct task_struct *prev_p, struct task_struct

/* we're going to use this soon, after a few expensive things */
if (next_p->fpu_counter > 5)
prefetch(&next->i387.fxsave);
prefetch(next->xstate);

/*
* Reload esp0.
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/process_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)

/* we're going to use this soon, after a few expensive things */
if (next_p->fpu_counter>5)
prefetch(&next->i387.fxsave);
prefetch(next->xstate);

/*
* Reload esp0, LDT and the page table pointer:
Expand Down
6 changes: 1 addition & 5 deletions arch/x86/kernel/traps_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -1208,11 +1208,6 @@ void __init trap_init(void)
#endif
set_trap_gate(19, &simd_coprocessor_error);

/*
* Verify that the FXSAVE/FXRSTOR data will be 16-byte aligned.
* Generate a build-time error if the alignment is wrong.
*/
BUILD_BUG_ON(offsetof(struct task_struct, thread.i387.fxsave) & 15);
if (cpu_has_fxsr) {
printk(KERN_INFO "Enabling fast FPU save and restore... ");
set_in_cr4(X86_CR4_OSFXSR);
Expand All @@ -1233,6 +1228,7 @@ void __init trap_init(void)

set_bit(SYSCALL_VECTOR, used_vectors);

init_thread_xstate();
/*
* Should be a barrier for any external CPU state:
*/
Expand Down
6 changes: 5 additions & 1 deletion arch/x86/kernel/traps_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ asmlinkage void math_state_restore(void)

if (!used_math())
init_fpu(me);
restore_fpu_checking(&me->thread.i387.fxsave);
restore_fpu_checking(&me->thread.xstate->fxsave);
task_thread_info(me)->status |= TS_USEDFPU;
me->fpu_counter++;
}
Expand Down Expand Up @@ -1163,6 +1163,10 @@ void __init trap_init(void)
set_system_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
#endif

/*
* initialize the per thread extended state:
*/
init_thread_xstate();
/*
* Should be a barrier for any external CPU state.
*/
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/math-emu/fpu_entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ int fpregs_soft_set(struct task_struct *target,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct i387_soft_struct *s387 = &target->thread.i387.soft;
struct i387_soft_struct *s387 = &target->thread.xstate->soft;
void *space = s387->st_space;
int ret;
int offset, other, i, tags, regnr, tag, newtop;
Expand Down Expand Up @@ -730,7 +730,7 @@ int fpregs_soft_get(struct task_struct *target,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
struct i387_soft_struct *s387 = &target->thread.i387.soft;
struct i387_soft_struct *s387 = &target->thread.xstate->soft;
const void *space = s387->st_space;
int ret;
int offset = (S387->ftop & 7) * 10, other = 80 - offset;
Expand Down
26 changes: 13 additions & 13 deletions arch/x86/math-emu/fpu_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \
== (1 << 10))

#define I387 (current->thread.i387)
#define FPU_info (I387.soft.info)
#define I387 (current->thread.xstate)
#define FPU_info (I387->soft.info)

#define FPU_CS (*(unsigned short *) &(FPU_info->___cs))
#define FPU_SS (*(unsigned short *) &(FPU_info->___ss))
Expand All @@ -46,25 +46,25 @@
#define FPU_EIP (FPU_info->___eip)
#define FPU_ORIG_EIP (FPU_info->___orig_eip)

#define FPU_lookahead (I387.soft.lookahead)
#define FPU_lookahead (I387->soft.lookahead)

/* nz if ip_offset and cs_selector are not to be set for the current
instruction. */
#define no_ip_update (*(u_char *)&(I387.soft.no_update))
#define FPU_rm (*(u_char *)&(I387.soft.rm))
#define no_ip_update (*(u_char *)&(I387->soft.no_update))
#define FPU_rm (*(u_char *)&(I387->soft.rm))

/* Number of bytes of data which can be legally accessed by the current
instruction. This only needs to hold a number <= 108, so a byte will do. */
#define access_limit (*(u_char *)&(I387.soft.alimit))
#define access_limit (*(u_char *)&(I387->soft.alimit))

#define partial_status (I387.soft.swd)
#define control_word (I387.soft.cwd)
#define fpu_tag_word (I387.soft.twd)
#define registers (I387.soft.st_space)
#define top (I387.soft.ftop)
#define partial_status (I387->soft.swd)
#define control_word (I387->soft.cwd)
#define fpu_tag_word (I387->soft.twd)
#define registers (I387->soft.st_space)
#define top (I387->soft.ftop)

#define instruction_address (*(struct address *)&I387.soft.fip)
#define operand_address (*(struct address *)&I387.soft.foo)
#define instruction_address (*(struct address *)&I387->soft.fip)
#define operand_address (*(struct address *)&I387->soft.foo)

#define FPU_access_ok(x,y,z) if ( !access_ok(x,y,z) ) \
math_abort(FPU_info,SIGSEGV)
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/math-emu/reg_ld_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -1180,8 +1180,8 @@ u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d)
control_word |= 0xffff0040;
partial_status = status_word() | 0xffff0000;
fpu_tag_word |= 0xffff0000;
I387.soft.fcs &= ~0xf8000000;
I387.soft.fos |= 0xffff0000;
I387->soft.fcs &= ~0xf8000000;
I387->soft.fos |= 0xffff0000;
#endif /* PECULIAR_486 */
if (__copy_to_user(d, &control_word, 7 * 4))
FPU_abort;
Expand Down
Loading

0 comments on commit 61c4628

Please sign in to comment.