@@ -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,21 @@ func handleInterrupt() {
9696 hartID := currentCPU ()
9797 switch code {
9898 case riscv .MachineSoftwareInterrupt :
99+ // Acknowledge the interrupt before checking its consumers. A new
100+ // request that arrives after this point must remain pending.
101+ aclintMSWI .MSIP [hartID ].Set (0 )
99102 if exitCodePlusOne .Load () != 0 {
100103 exitNow (exitCodePlusOne .Load () - 1 )
101104 }
102- if gcScanState . Load ( ) != 0 {
105+ if gcPauseRequest [ hartID ]. Swap ( 0 ) != 0 {
103106 // The GC needs to run.
104107 gcInterruptHandler (hartID )
105108 }
109+ if exitCodePlusOne .Load () != 0 {
110+ exitNow (exitCodePlusOne .Load () - 1 )
111+ }
106112 checkpoint := & schedulerWaitCheckpoints [hartID ]
107- if checkpoint .Saved () {
108- aclintMSWI .MSIP [hartID ].Set (0 )
113+ if schedulerWakePending [hartID ].Swap (0 ) != 0 && checkpoint .Saved () {
109114 riscv .MCAUSE .Set (0 )
110115 checkpoint .Jump ()
111116 }
@@ -132,6 +137,14 @@ func handleInterrupt() {
132137 riscv .MCAUSE .Set (0 )
133138}
134139
140+ var (
141+ // State used to request a GC pause on each hart.
142+ gcPauseRequest [numCPU ]atomic.Uint32
143+
144+ // State used to signal the next GC phase to each paused hart.
145+ gcSignalWait [numCPU ]atomic.Uint32
146+ )
147+
135148// The GC interrupted this core for the stop-the-world phase.
136149// This function handles that, and only returns after the stop-the-world phase
137150// ended.
@@ -140,21 +153,30 @@ func gcInterruptHandler(hartID uint32) {
140153 savedMIE := riscv .MIE .Get ()
141154 riscv .MIE .Set (riscv .MIE_MSIE )
142155
143- // Disable this interrupt (to be enabled again soon).
144- aclintMSWI .MSIP [hartID ].Set (0 )
145-
146156 // Let the GC know we're ready.
147157 gcScanState .Add (1 )
148158
149159 // Wait until we get a signal to start scanning.
150- for riscv .MIP .Get ()& riscv .MIP_MSIP == 0 {
151- riscv .Asm ("wfi" )
160+ for gcSignalWait [hartID ].Load () == 0 {
161+ if hartID == 0 && exitCodePlusOne .Load () != 0 {
162+ exitNow (exitCodePlusOne .Load () - 1 )
163+ }
164+ // Clear unrelated scheduler wakeups before waiting again. Check pending
165+ // requests after the clear so a concurrent signal is not lost.
166+ aclintMSWI .MSIP [hartID ].Set (0 )
167+ if hartID == 0 && exitCodePlusOne .Load () != 0 {
168+ exitNow (exitCodePlusOne .Load () - 1 )
169+ }
170+ if gcSignalWait [hartID ].Load () == 0 {
171+ riscv .Asm ("wfi" )
172+ }
152173 }
174+ gcSignalWait [hartID ].Store (0 )
153175 aclintMSWI .MSIP [hartID ].Set (0 )
154176
155177 // Scan the stack(s) of this core.
156178 scanCurrentStack ()
157- if ! task . OnSystemStack () {
179+ if ! gcOnSystemStack () {
158180 // Mark system stack.
159181 markRoots (task .SystemStack (), coreStackTop (hartID ))
160182 }
@@ -163,9 +185,19 @@ func gcInterruptHandler(hartID uint32) {
163185 gcScanState .Store (1 )
164186
165187 // 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" )
188+ for gcSignalWait [hartID ].Load () == 0 {
189+ if hartID == 0 && exitCodePlusOne .Load () != 0 {
190+ exitNow (exitCodePlusOne .Load () - 1 )
191+ }
192+ aclintMSWI .MSIP [hartID ].Set (0 )
193+ if hartID == 0 && exitCodePlusOne .Load () != 0 {
194+ exitNow (exitCodePlusOne .Load () - 1 )
195+ }
196+ if gcSignalWait [hartID ].Load () == 0 {
197+ riscv .Asm ("wfi" )
198+ }
168199 }
200+ gcSignalWait [hartID ].Store (0 )
169201 aclintMSWI .MSIP [hartID ].Set (0 )
170202
171203 // Restore MIE bits.
@@ -386,12 +418,16 @@ func currentCPU() uint32 {
386418 return uint32 (riscv .MHARTID .Get ())
387419}
388420
421+ func gcOnSystemStack () bool {
422+ return task .SystemStack () == 0
423+ }
424+
389425func startSecondaryCores () {
390426 // Start all the other cores besides hart 0.
391427 for hart := 1 ; hart < numCPU ; hart ++ {
392428 // Signal the given hart it is ready to start using a software
393429 // interrupt.
394- aclintMSWI . MSIP [ hart ]. Set ( 1 )
430+ signalHart ( uint32 ( hart ) )
395431 }
396432}
397433
@@ -403,6 +439,9 @@ var sleepingHarts uint8
403439// Checkpoints for cores waiting for runnable tasks.
404440var schedulerWaitCheckpoints [numCPU ]interrupt.Checkpoint
405441
442+ // State used to distinguish scheduler wakeups from other software interrupts.
443+ var schedulerWakePending [numCPU ]atomic.Uint32
444+
406445// Put the scheduler to sleep, since there are no tasks to run.
407446// This will unlock the scheduler lock, and must be called with the scheduler
408447// lock held.
@@ -449,21 +488,32 @@ func schedulerWake() {
449488
450489 if hart < 8 {
451490 // There is a sleeping hart. Wake it.
452- sleepingHarts &^= 1 << hart // clear the bit
453- aclintMSWI .MSIP [hart ].Set (1 ) // send software interrupt
491+ // Clear the sleeping bit before sending the wakeup.
492+ sleepingHarts &^= 1 << hart
493+ schedulerWakePending [hart ].Store (1 )
494+ signalHart (uint32 (hart ))
454495 }
455496}
456497
457498// Pause the given core by sending it an interrupt.
458499func gcPauseCore (core uint32 ) {
459- aclintMSWI .MSIP [core ].Set (1 ) // send software interrupt
500+ gcPauseRequest [core ].Store (1 )
501+ signalHart (core )
460502}
461503
462504// Signal the given core that it can resume one step.
463505// This is called twice after gcPauseCore: the first time to scan the stack of
464506// the core, and the second time to end the stop-the-world phase.
465507func gcSignalCore (core uint32 ) {
466- aclintMSWI .MSIP [core ].Set (1 ) // send software interrupt
508+ gcSignalWait [core ].Store (1 )
509+ signalHart (core )
510+ }
511+
512+ func signalHart (hart uint32 ) {
513+ // Order state writes before the interrupt notification.
514+ // See RISC-V Unprivileged ISA, section 2.7.
515+ riscv .Asm ("fence" )
516+ aclintMSWI .MSIP [hart ].Set (1 )
467517}
468518
469519func abort () {
@@ -485,7 +535,7 @@ func exit(code int) {
485535 if currentCPU () != 0 {
486536 // Signal hart 0 to exit.
487537 exitCodePlusOne .Store (uint32 (code ) + 1 )
488- aclintMSWI . MSIP [ 0 ]. Set ( 1 )
538+ signalHart ( 0 )
489539
490540 // Wait for the interrupt to happen. This should happen immediately.
491541 for {
@@ -519,6 +569,6 @@ func exitNow(code uint32) {
519569func handleException (code uint ) {
520570 // For a list of exception codes, see:
521571 // 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 " )
572+ print ("fatal error: exception with mcause=" , code , " pc=" , riscv .MEPC .Get (), " mtval=" , riscv . MTVAL . Get (), " hart=" , uint (riscv .MHARTID .Get ()), "\r \n " )
523573 abort ()
524574}
0 commit comments