@@ -56,7 +56,7 @@ func main() {
5656 // should exit immediately.
5757 // Signal hart 0 to exit.
5858 exitCodePlusOne .Store (0 + 1 ) // exit code 0
59- aclintMSWI . MSIP [ 0 ]. Set ( 1 )
59+ signalHart ( 0 )
6060
6161 // Unlock the scheduler to be sure. Shouldn't be needed.
6262 schedulerLock .Unlock ()
@@ -96,16 +96,24 @@ func handleInterrupt() {
9696 hartID := currentCPU ()
9797 switch code {
9898 case riscv .MachineSoftwareInterrupt :
99+ // Clear the interrupt before checking state so a new request stays pending.
100+ // See RISC-V Unprivileged ISA, section 2.7.
101+ aclintMSWI .MSIP [hartID ].Set (0 )
102+ riscv .Asm ("fence" )
99103 if exitCodePlusOne .Load () != 0 {
100104 exitNow (exitCodePlusOne .Load () - 1 )
101105 }
102- if gcScanState . Load ( ) != 0 {
106+ if gcPauseRequest [ hartID ]. Swap ( 0 ) != 0 {
103107 // The GC needs to run.
104108 gcInterruptHandler (hartID )
105109 }
110+ if exitCodePlusOne .Load () != 0 {
111+ exitNow (exitCodePlusOne .Load () - 1 )
112+ }
106113 checkpoint := & schedulerWaitCheckpoints [hartID ]
107- if checkpoint .Saved () {
108- aclintMSWI .MSIP [hartID ].Set (0 )
114+ // schedulerLock prevents this flag from being set before the
115+ // checkpoint is saved.
116+ if schedulerWakePending [hartID ].Swap (0 ) != 0 && checkpoint .Saved () {
109117 riscv .MCAUSE .Set (0 )
110118 checkpoint .Jump ()
111119 }
@@ -132,6 +140,14 @@ func handleInterrupt() {
132140 riscv .MCAUSE .Set (0 )
133141}
134142
143+ var (
144+ // State used to request a GC pause on each hart.
145+ gcPauseRequest [numCPU ]atomic.Uint32
146+
147+ // State used to signal the next GC phase to each paused hart.
148+ gcSignalWait [numCPU ]atomic.Uint32
149+ )
150+
135151// The GC interrupted this core for the stop-the-world phase.
136152// This function handles that, and only returns after the stop-the-world phase
137153// ended.
@@ -140,17 +156,11 @@ func gcInterruptHandler(hartID uint32) {
140156 savedMIE := riscv .MIE .Get ()
141157 riscv .MIE .Set (riscv .MIE_MSIE )
142158
143- // Disable this interrupt (to be enabled again soon).
144- aclintMSWI .MSIP [hartID ].Set (0 )
145-
146159 // Let the GC know we're ready.
147160 gcScanState .Add (1 )
148161
149162 // Wait until we get a signal to start scanning.
150- for riscv .MIP .Get ()& riscv .MIP_MSIP == 0 {
151- riscv .Asm ("wfi" )
152- }
153- aclintMSWI .MSIP [hartID ].Set (0 )
163+ gcWaitForSignal (hartID )
154164
155165 // Scan the stack(s) of this core.
156166 scanCurrentStack ()
@@ -163,10 +173,7 @@ func gcInterruptHandler(hartID uint32) {
163173 gcScanState .Store (1 )
164174
165175 // Wait until we get a signal that the stop-the-world phase has ended.
166- for riscv .MIP .Get ()& riscv .MIP_MSIP == 0 {
167- riscv .Asm ("wfi" )
168- }
169- aclintMSWI .MSIP [hartID ].Set (0 )
176+ gcWaitForSignal (hartID )
170177
171178 // Restore MIE bits.
172179 riscv .MIE .Set (savedMIE )
@@ -175,6 +182,24 @@ func gcInterruptHandler(hartID uint32) {
175182 gcScanState .Add (1 )
176183}
177184
185+ func gcWaitForSignal (hartID uint32 ) {
186+ for gcSignalWait [hartID ].Load () == 0 {
187+ // Clear unrelated wakeups before checking state to avoid losing a signal.
188+ // See RISC-V Unprivileged ISA, section 2.7.
189+ aclintMSWI .MSIP [hartID ].Set (0 )
190+ riscv .Asm ("fence" )
191+ if hartID == 0 && exitCodePlusOne .Load () != 0 {
192+ exitNow (exitCodePlusOne .Load () - 1 )
193+ }
194+ if gcSignalWait [hartID ].Load () == 0 {
195+ riscv .Asm ("wfi" )
196+ }
197+ }
198+ gcSignalWait [hartID ].Store (0 )
199+ aclintMSWI .MSIP [hartID ].Set (0 )
200+ riscv .Asm ("fence" )
201+ }
202+
178203//go:extern _stack_top
179204var stack0TopSymbol [0 ]byte
180205
@@ -391,7 +416,7 @@ func startSecondaryCores() {
391416 for hart := 1 ; hart < numCPU ; hart ++ {
392417 // Signal the given hart it is ready to start using a software
393418 // interrupt.
394- aclintMSWI . MSIP [ hart ]. Set ( 1 )
419+ signalHart ( uint32 ( hart ) )
395420 }
396421}
397422
@@ -403,6 +428,9 @@ var sleepingHarts uint8
403428// Checkpoints for cores waiting for runnable tasks.
404429var schedulerWaitCheckpoints [numCPU ]interrupt.Checkpoint
405430
431+ // State used to distinguish scheduler wakeups from other software interrupts.
432+ var schedulerWakePending [numCPU ]atomic.Uint32
433+
406434// Put the scheduler to sleep, since there are no tasks to run.
407435// This will unlock the scheduler lock, and must be called with the scheduler
408436// lock held.
@@ -449,21 +477,32 @@ func schedulerWake() {
449477
450478 if hart < 8 {
451479 // There is a sleeping hart. Wake it.
452- sleepingHarts &^= 1 << hart // clear the bit
453- aclintMSWI .MSIP [hart ].Set (1 ) // send software interrupt
480+ // Clear the sleeping bit before sending the wakeup.
481+ sleepingHarts &^= 1 << hart
482+ schedulerWakePending [hart ].Store (1 )
483+ signalHart (uint32 (hart ))
454484 }
455485}
456486
457487// Pause the given core by sending it an interrupt.
458488func gcPauseCore (core uint32 ) {
459- aclintMSWI .MSIP [core ].Set (1 ) // send software interrupt
489+ gcPauseRequest [core ].Store (1 )
490+ signalHart (core )
460491}
461492
462493// Signal the given core that it can resume one step.
463494// This is called twice after gcPauseCore: the first time to scan the stack of
464495// the core, and the second time to end the stop-the-world phase.
465496func gcSignalCore (core uint32 ) {
466- aclintMSWI .MSIP [core ].Set (1 ) // send software interrupt
497+ gcSignalWait [core ].Store (1 )
498+ signalHart (core )
499+ }
500+
501+ func signalHart (hart uint32 ) {
502+ // Order state writes before the interrupt notification.
503+ // See RISC-V Unprivileged ISA, section 2.7.
504+ riscv .Asm ("fence" )
505+ aclintMSWI .MSIP [hart ].Set (1 )
467506}
468507
469508func abort () {
@@ -485,7 +524,7 @@ func exit(code int) {
485524 if currentCPU () != 0 {
486525 // Signal hart 0 to exit.
487526 exitCodePlusOne .Store (uint32 (code ) + 1 )
488- aclintMSWI . MSIP [ 0 ]. Set ( 1 )
527+ signalHart ( 0 )
489528
490529 // Wait for the interrupt to happen. This should happen immediately.
491530 for {
@@ -519,6 +558,6 @@ func exitNow(code uint32) {
519558func handleException (code uint ) {
520559 // For a list of exception codes, see:
521560 // https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49
522- print ("fatal error: exception with mcause=" , code , " pc=" , riscv .MEPC .Get (), " hart=" , uint (riscv .MHARTID .Get ()), "\r \n " )
561+ print ("fatal error: exception with mcause=" , code , " pc=" , riscv .MEPC .Get (), " mtval=" , riscv . MTVAL . Get (), " hart=" , uint (riscv .MHARTID .Get ()), "\r \n " )
523562 abort ()
524563}
0 commit comments