framework for fpu usage in kernel
RFC 1/1 framework for fpu usage in kernel
Currently there are 2 fpu users in the kernel:
raid5 checksumming and 3dnow memcpy/memset.
raid5 checksumming is not problematic, but _mmx_memcpy() has unexpected side effects if someone else is also using the fpu:
memcopy is a really generic function, and calling it saves the current fpu state into thread.i387.f{,x}save. IMHO that's wrong, memcopy must save into a local buffer like raid5 checksumming.
I've attached a proposal that supports arbitrary combinations of fpu users in kernel space.
* kernel threads can use the fpu freely. Nothing new.
* 2 sets of functions for fpu usage in "normal" threads:
- kfpu_acquire(), kfpu_try_acquire() + release functions.
- kfpu_full_begin(), kfpu_mmx_begin(), kfpu_nosave_begin(), kfpu_try_begin() + _end() functions.
The first set is only possible from normal process context. The caller can sleep between _acquire() and _release().
The second set can be called from arbitrary context, but the caller must not sleep between _begin() and _end().
The _try() functions check if the fpu is unused, and fail if the fpu is currently in use. That way the memcpy()/memset() functions can avoid fpu context saves/restores and it saves stack space.
Nesting is partially possible: _begin() within _acquire is possible, _acquire() within _begin() will BUG.
[_acquire()/_release() have similar restrictions as down() and up(), _begin()/_end() have similar restrictions as spinlocks]
The patch itself is alpha quality: only the sse functions are tested, it boots when compiled for Pentium III, the raid5 checksum _benchmark_ still works and distributed.net still cracks rc5.
I haven't yet checked that the exception handlers are still called properly.
What do you think?
--
Manfred
// $Header$
// Kernel Version:
// VERSION = 2 // PATCHLEVEL = 4 // SUBLEVEL = 1 // EXTRAVERSION =
include/asm-i386/i387.h +3 -4
@@ -16,6 +16,7 @@#include <asm/sigcontext.h> #include <asm/user.h> +#include <asm/kfpu.h> extern void init_fpu (void );/* * FPU lazy state save handling...
@@ -23,9 +24,7 @@extern void save_init_fpu (struct task_struct *tsk );extern void restore_fpu (struct task_struct *tsk ); -extern void kernel_fpu_begin (void ); -#define kernel_fpu_end () stts() - +#include <asm/kfpu.h> #define unlazy_fpu ( tsk ) do { \ if ( tsk->flags & PF_USEDFPU ) \
@@ -36,7 +35,7 @@if ( tsk->flags &PF_USEDFPU ) { \ asmvolatile ("fwait" ); \ tsk->flags &= ~PF_USEDFPU ; \ -stts (); \ +kfpu_leave (); \ } \ }while (0 )
include/asm-i386/kfpu.h +83 -0
83 lines not on this page · raw message
include/asm-i386/page.h +8 -1
@@ -11,7 +11,14 @@#include <linux/config.h> -#ifdef CONFIG_X86_USE_3DNOW +#ifdef CONFIG_X86_USE_SSE + +#include <asm/sse.h> + +#define clear_page (page) sse_clear_page(page) +#define copy_page (to,from) sse_copy_page(to,from) + +#elif defined(CONFIG_X86_USE_3DNOW )#include <asm/mmx.h>
include/asm-i386/sse.h +11 -0
@@ -0,0 +1,11 @@ +#ifndef _ASM_SSE_H +#define _ASM_SSE_H + +/* +* SSE helper operations +*/ + +extern void sse_clear_page (void *page); +extern void sse_copy_page (void *to,void *from); + +#endif
include/asm-i386/system.h +0 -2
@@ -100,7 +100,6 @@/* * Clear and set 'TS' bit respectively */ -#define clts () __asm__ __volatile__ ("clts")#define read_cr0 () ({ \ unsigned int __dummy; \ __asm__( \
@@ -110,7 +109,6 @@ })#define write_cr0 (x) \ __asm__("movl %0,%%cr0": :"r" (x)); -#define stts () write_cr0(8 | read_cr0())#endif /* __KERNEL__ */
include/asm-i386/xor.h +37 -36
167 lines not on this page · raw message
arch/i386/lib/Makefile +1 -0
@@ -12,6 +12,7 @@ memcpy.o obj-$(CONFIG_X86_USE_3DNOW) += mmx.o +obj-$(CONFIG_X86_USE_SSE) += sse.o obj-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o include $(TOPDIR)/Rules.make
arch/i386/lib/mmx.c +16 -17
@@ -28,7 +28,11 @@void *p=to;int i= len >>6 ;/* len/64 */ -kernel_fpu_begin (); + if(!kfpu_try_begin() { +/* FIXME: this belongs into string.h */ +__memcpy (to, from, len); +return ; + } __asm__ __volatile__ ( "1: prefetch (%0)\n" /* This set is 28 bytes */
@@ -84,7 +88,7 @@* Now do the tail of the block */ __memcpy (to, from, len&63 ); -kernel_fpu_end (); +kfpu_try_end ();return p; }
@@ -92,8 +96,6 @@ {int i; -kernel_fpu_begin (); - __asm__ __volatile__ (" pxor %%mm0, %%mm0\n" : : );
@@ -118,18 +120,12 @@ __asm__ __volatile__ (" sfence \n" : : ); -kernel_fpu_end (); }static void fast_copy_page (void *to,void *from) {int i; -kernel_fpu_begin (); - -/* maybe the prefetch stuff can go before the expensive fnsave... -* but that is for later. -AV -*/ __asm__ __volatile__ ("1: prefetch (%0)\n" " prefetch 64(%0)\n"
@@ -185,7 +181,6 @@ __asm__ __volatile__ (" sfence \n" : : ); -kernel_fpu_end (); }/*
@@ -205,10 +200,12 @@void mmx_clear_page (void * page) { -if (in_interrupt ()) -slow_zero_page (page); -else +if (kfpu_try_begin ()) {fast_clear_page (page); +kfpu_try_end (); + }else { +slow_zero_page (page); + } }static void slow_copy_page (void *to,void *from)
@@ -225,8 +222,10 @@void mmx_copy_page (void *to,void *from) { -if (in_interrupt ()) -slow_copy_page (to, from); -else +if (kfpu_try_begin ()) {fast_copy_page (to, from); +kfpu_try_end (); + }else { +slow_copy_page (to, from); + } }
arch/i386/lib/sse.c +107 -0
107 lines not on this page · raw message
arch/i386/kernel/i387.c +191 -7
225 lines not on this page · raw message
arch/i386/kernel/traps.c +1 -1
@@ -731,7 +731,7 @@*/ asmlinkage void math_state_restore(struct pt_regs regs) { - __asm__ __volatile__("clts" );/* Allow maths ops (or we recurse) */ +kfpu_enter ();/* Allow maths ops (or we recurse) */ if (current->used_math) {restore_fpu (current);
arch/i386/kernel/setup.c +2 -1
@@ -2274,7 +2274,8 @@*/ current->flags &= ~PF_USEDFPU ; current->used_math =0 ; -stts (); +kfpu_initialize (); + }/*
arch/i386/kernel/i386_ksyms.c +19 -0
@@ -117,6 +117,25 @@EXPORT_SYMBOL (mmx_copy_page);#endif +#ifdef CONFIG_X86_USE_SSE +EXPORT_SYMBOL (sse_clear_page); +EXPORT_SYMBOL (sse_copy_page); +#endif + +EXPORT_SYMBOL (kfpu_start); +EXPORT_SYMBOL (kfpu_acquire); +EXPORT_SYMBOL (kfpu_release); +EXPORT_SYMBOL (kfpu_try_acquire); +EXPORT_SYMBOL (kfpu_try_release); +EXPORT_SYMBOL (kfpu_full_begin); +EXPORT_SYMBOL (kfpu_mmx_begin); +EXPORT_SYMBOL (kfpu_nosave_begin); +EXPORT_SYMBOL (kfpu_full_end); +EXPORT_SYMBOL (kfpu_mmx_end); +EXPORT_SYMBOL (kfpu_nosave_end); +EXPORT_SYMBOL (kfpu_try_begin); +EXPORT_SYMBOL (kfpu_try_end); +#ifdef CONFIG_SMP EXPORT_SYMBOL (cpu_data);EXPORT_SYMBOL (kernel_flag);
arch/i386/config.in +2 -0
@@ -91,6 +91,7 @@ define_bool CONFIG_X86_GOOD_APIC y define_bool CONFIG_X86_PGE y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_USE_SSE y fi if [ "$CONFIG_MPENTIUM4" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 7
@@ -98,6 +99,7 @@ define_bool CONFIG_X86_GOOD_APIC y define_bool CONFIG_X86_PGE y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_USE_SSE y fi if [ "$CONFIG_MK6" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5
Quoted 3 lines
memcopy is a really generic function, and calling it saves the current fpu state into thread.i387.f{,x}save. IMHO that's wrong, memcopy must save into a local buffer like raid5 checksumming.
The mmx copy is only done in task context. There are a whole variety of _horrible_ problems doing it in interrupt space so based on the considerable number of problems with prior attempts to get it right on IRQ and copyuser cases I didnt bother
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/
Quoted 10 lines · Alan Cox
Quoted 4 lines
memcopy is a really generic function, and calling it saves the current fpu state into thread.i387.f{,x}save. IMHO that's wrong, memcopy must save into a local buffer like raid5 checksumming.
The mmx copy is only done in task context. There are a whole variety of _horrible_ problems doing it in interrupt space so based on the considerable number of problems with prior attempts to get it right on IRQ and copyuser cases I didnt bother
Even task context is dangerous:
What if a drm module wants to use the fpu and then uses memcpy() after modifying the ftp registers?
--
Manfred
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/
Quoted 2 lines
What if a drm module wants to use the fpu and then uses
memcpy()after modifying the ftp registers?
Interesting question. Right now the answer is dont do that. Point noted
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/