[AVR32] Fix random segfault with preemption
authorPhilippe Rétornaz <philippe.retornaz@epfl.ch>
Wed, 10 Oct 2007 22:52:24 +0000 (18:52 -0400)
committerHaavard Skinnemoen <hskinnemoen@atmel.com>
Thu, 11 Oct 2007 11:32:56 +0000 (13:32 +0200)
As explained on:
http://www.avrfreaks.net/index.php?nameÿphpBB2&fileÿewtopic&tS307
If the current process is preempted before it can copy RAR_SUP and
RSR_SUP both register are lost and the process will segfault as soon
as it return from the syscall since the return adress will be
corrupted.

This patch disable IRQ as soon as we enter the syscall path and
reenable them when the copy is done.

In the interrupt handlers, check if we are interrupting the srrf
instruction, if so disable interrupts and return. The interrupt
handler will be re-called immediatly when the interrupts are
reenabled.

After some stressing workload:
 - find / > /dev/null in loop
 - top (in ssh)
 - ping -f avr32

The segfaults are not seen anymore.

Signed-off-by: Philippe Rétornaz <philippe.retornaz@epfl.ch>
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
arch/avr32/kernel/entry-avr32b.S

index 42657f1..ccadfd9 100644 (file)
@@ -159,11 +159,18 @@ handle_vmalloc_miss:
 
        .section .scall.text,"ax",@progbits
 system_call:
+#ifdef CONFIG_PREEMPT
+       mask_interrupts
+#endif
        pushm   r12             /* r12_orig */
        stmts   --sp, r0-lr
-       zero_fp
+
        mfsr    r0, SYSREG_RAR_SUP
        mfsr    r1, SYSREG_RSR_SUP
+#ifdef CONFIG_PREEMPT
+       unmask_interrupts
+#endif
+       zero_fp
        stm     --sp, r0-r1
 
        /* check for syscall tracing */
@@ -638,6 +645,13 @@ irq_level\level:
        stmts   --sp,r0-lr
        mfsr    r8, rar_int\level
        mfsr    r9, rsr_int\level
+
+#ifdef CONFIG_PREEMPT
+       sub     r11, pc, (. - system_call)
+       cp.w    r11, r8
+       breq    4f
+#endif
+
        pushm   r8-r9
 
        mov     r11, sp
@@ -668,6 +682,16 @@ irq_level\level:
        sub     sp, -4          /* ignore r12_orig */
        rete
 
+#ifdef CONFIG_PREEMPT
+4:     mask_interrupts
+       mfsr    r8, rsr_int\level
+       sbr     r8, 16
+       mtsr    rsr_int\level, r8
+       ldmts   sp++, r0-lr
+       sub     sp, -4          /* ignore r12_orig */
+       rete
+#endif
+
 2:     get_thread_info r0
        ld.w    r1, r0[TI_flags]
        bld     r1, TIF_CPU_GOING_TO_SLEEP