Port-mips archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: SMP/preempt bug in matt-nb5-mips64



On Sun, Sep 05, 2010 at 11:35:41PM +0200, Manuel Bouyer wrote:
> Hello,
> I found a problem in matt-nb5-mips64's mips/mips/spl.S, regarding
> curcpu() and preemption. In both _splraise and _splsw_splhigh,
> curcpu() is loaded from L_CPU(MIPS_CURLWP) in a register early,
> especially before disabling interrupt. If the current IPL is 0,
> the current thread can be preempted and rescheduled on another CPU,
> and the new SPL is written back to the wrong cpu_info.
> >From there, bad things happens (what I've seen is an infinite loop
> from the interrupt handler on the victim CPU, because _splsw_splhigh
> thinks we're already at splhigh and do nothing, when interrupts are
> really enabled).
> The attached patch seems to fix it for me: it's enough to reload
> curcpu() before writing back the new IPL, as for the above senario to
> happen the old IPL of both CPUs has to be 0.

Well, unfortunably this is not enough.
If we get preempted just after loading L_CPU(MIPS_CURLWP) and before
checking the spl, we may be moved to another CPU and run at a later
time. When we get back running we check the spl of the previous CPU
which may not be 0 any more.
I couldn't find a better way than disabling the interrupts before
checking the spl (well, we could disable preemption but I suspect
it cost more than just disabling interrupts). The attached patch now seems to
DTRT for me.

-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
Index: spl.S
===================================================================
--- spl.S       (revision 100)
+++ spl.S       (working copy)
@@ -66,27 +66,32 @@
         * a1 = this IPL (IPL_*)
         * Can only use a0-a3 and v0-v1
         */
+       /*
+        * disable interrupt before messing with spl, or we could be
+        * preempted and moved to a different CPU
+        */
+       mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
+       mtc0    zero, MIPS_COP_0_STATUS         ## disable interrupts
+       COP0_SYNC
        PTR_L   a3, L_CPU(MIPS_CURLWP)
        INT_L   v0, CPU_INFO_CPL(a3)            # get current IPL from cpu_info
-       sltu    v1, a1, v0                      # newipl < curipl
-       bnez    v1, 1f                          # yes, don't change.
-       mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
+       sltu    a2, a1, v0                      # newipl < curipl
+       bnez    a2, 1f                          # yes, don't change.
+       nop
        or      v1, MIPS_INT_MASK               # enable all interrupts
        xor     a0, v1                          # disable ipl's masked bits
-       DYNAMIC_STATUS_MASK(a0,v0)              # machine dependent masking
-       mtc0    zero, MIPS_COP_0_STATUS         ## disable interrupts
-       COP0_SYNC
+       DYNAMIC_STATUS_MASK(a0,a2)              # machine dependent masking
        INT_S   a1, CPU_INFO_CPL(a3)            ## save IPL in cpu_info
        mtc0    a0, MIPS_COP_0_STATUS           ## store back
        COP0_SYNC
-#ifdef PARANOIA
        j       ra
         nop
-#endif /* PARANOIA */
 1:
+       mtc0    v1, MIPS_COP_0_STATUS           # restore status
+       COP0_SYNC
 #ifdef PARANOIA
        mfc0    v1, MIPS_COP_0_STATUS
-       and     a0, v1                          # a1 contains bit that MBZ
+       and     a0, v1                          # a0 contains bit that MBZ
 3:     bnez    a0, 3b                          # loop forever
         nop
 #endif /* PARANOIA */
@@ -98,7 +103,7 @@
        PTR_L   a3, L_CPU(MIPS_CURLWP)          # get cpu_info
        INT_L   a2, CPU_INFO_CPL(a3)            # get IPL from cpu_info
        beq     a0, a2, 2f                      # if same, nothing to do
-        nop
+        sync
 #ifdef PARANOIA
        sltu    v0, a0, a2                      # v0 = a0 < a2
 99:    beqz    v0, 99b                         # loop forever if false
@@ -210,30 +215,22 @@
 
 STATIC_LEAF(_splsw_splhigh)
 STATIC_XLEAF(_splsw_splhigh_noprof)
-       PTR_L   a3, L_CPU(MIPS_CURLWP)
-       INT_L   v0, CPU_INFO_CPL(a3)            # get current IPL from cpu_info
-       li      a1, IPL_HIGH                    # 
-       beq     v0, a1, 1f                      # don't do anything if IPL_HIGH
+       /*
+        * disable interrupt before messing with spl, or we could be
+        * preempted and moved to a different CPU
+        */
        mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
        and     a0, v1, MIPS_INT_MASK           # select all interrupts
        xor     a0, v1                          # clear all interrupts
-       DYNAMIC_STATUS_MASK(a0,a2)              # machine dependent masking
+       DYNAMIC_STATUS_MASK(a0,a1)              # machine dependent masking
        mtc0    a0, MIPS_COP_0_STATUS           ## store back
        COP0_SYNC
+       PTR_L   a3, L_CPU(MIPS_CURLWP)
+       INT_L   v0, CPU_INFO_CPL(a3)            # get current IPL from cpu_info
+       li      a1, IPL_HIGH                    # 
        INT_S   a1, CPU_INFO_CPL(a3)            ## save IPL in cpu_info
-#ifdef PARANOIA
        j       ra                              ## return
         nop
-#endif /* PARANOIA */
-1:
-#ifdef PARANOIA
-       mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
-       and     v1, MIPS_INT_MASK               # any int bits set?
-2:     bnez    v1, 2b                          # loop forever.
-        nop
-#endif /* PARANOIA */
-       j       ra                              ## return
-        nop
 END(_splsw_splhigh)
 
 STATIC_LEAF(_splsw_splddb)


Home | Main Index | Thread Index | Old Index