lkml.org 
[lkml]   [2003]   [Aug]   [31]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject2.6.0-test4: bug WRT lazy FPU switching and CONFIG_PREEMPT
    Hi!

    There seems to be a bug with CONFIG_PREEMPT=y and lazy FPU switching. The following small program just unmasks math exceptions
    and then continously triggers a divide by zero. When CONFIG_PREEMPT is in effect this eventually leads to other processes
    inheriting the math-status of this brain-damaged but legal user level program. Especially, their math-exceptions are unmasked
    and so they might catch an unwanted SIGFPE.

    ######################################### snip ####################################
    #include <signal.h>
    #include <stdio.h>
    #include <fenv.h>

    double volatile foobar = 0.0;

    static void sigfpe(int sig)
    {
    static int cnt;

    if (++cnt % 100000 == 0) {
    fprintf(stderr, ".");
    }
    }

    int main(int argc, char *argv[])
    {
    signal(SIGFPE, sigfpe);
    feenableexcept(FE_ALL_EXCEPT);
    foobar = 1.0/foobar;
    }
    ######################################## snap ######################################

    The following patch seems to fix the problem. However, I must admit that I didn't fully understand why this happens. The problem
    seems to be a preemption occuring between the __save_init_fpu() (i.e. fsave/fxsave) and the stts(). But why should this be a
    problem?

    The patch touches two files:

    include/asm-i386/i387.h:
    here save_init_fpu() is defined and this seems to be the location of the problem

    arch/i386/kernel/traps.c:
    by checking for TS_USEDFPU we spare an additional "device not available" fault (with CONFIG_PREEMPT=y) and thereby a
    superflous math_state_restore(). There is not point in restoring the math-state just to save it again (but this didn't cause the
    shooting with wild SIGFPEs).

    Regards

    Claus

    ######################################### snip ##########################################################################
    diff -u --recursive --new-file linux-2.6.0-test4/arch/i386/kernel/traps.c linux-2.6.0-test4-mine/arch/i386/kernel/traps.c
    --- linux-2.6.0-test4/arch/i386/kernel/traps.c 2003-08-31 15:22:42.000000000 +0200
    +++ linux-2.6.0-test4-mine/arch/i386/kernel/traps.c 2003-08-31 15:35:49.000000000 +0200
    @@ -605,7 +605,10 @@
    * Save the info for the exception handler and clear the error.
    */
    task = current;
    - save_init_fpu(task);
    + /* don't trigger an unnecessary math_state_restore() */
    + if (task->thread_info->status & TS_USEDFPU) {
    + save_init_fpu(task);
    + }
    task->thread.trap_no = 16;
    task->thread.error_code = 0;
    info.si_signo = SIGFPE;
    @@ -667,7 +670,10 @@
    * Save the info for the exception handler and clear the error.
    */
    task = current;
    - save_init_fpu(task);
    + /* don't trigger an unnecessary math_state_restore() */
    + if (task->thread_info->status & TS_USEDFPU) {
    + save_init_fpu(task);
    + }
    task->thread.trap_no = 19;
    task->thread.error_code = 0;
    info.si_signo = SIGFPE;
    diff -u --recursive --new-file linux-2.6.0-test4/include/asm-i386/i387.h linux-2.6.0-test4-mine/include/asm-i386/i387.h
    --- linux-2.6.0-test4/include/asm-i386/i387.h 2003-07-27 19:06:54.000000000 +0200
    +++ linux-2.6.0-test4-mine/include/asm-i386/i387.h 2003-08-31 15:39:04.000000000 +0200
    @@ -30,7 +30,7 @@
    static inline void __save_init_fpu( struct task_struct *tsk )
    {
    if ( cpu_has_fxsr ) {
    - asm volatile( "fxsave %0 ; fnclex"
    + asm volatile( "fxsave %0 ; fclex"
    : "=m" (tsk->thread.i387.fxsave) );
    } else {
    asm volatile( "fnsave %0 ; fwait"
    @@ -41,8 +41,10 @@

    static inline void save_init_fpu( struct task_struct *tsk )
    {
    + preempt_disable();
    __save_init_fpu(tsk);
    stts();
    + preempt_enable_no_resched();
    }


    @@ -53,11 +55,13 @@

    #define clear_fpu( tsk ) \
    do { \
    + preempt_disable(); \
    if ((tsk)->thread_info->status & TS_USEDFPU) { \
    asm volatile("fwait"); \
    (tsk)->thread_info->status &= ~TS_USEDFPU; \
    stts(); \
    } \
    + preempt_enable_no_resched(); \
    } while (0)

    /*


    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2005-03-22 13:48    [W:0.044 / U:33.624 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site