@@ -8,8 +8,6 @@ use alloc::sync::Arc;
88use alloc:: vec:: Vec ;
99use core:: cell:: RefCell ;
1010use core:: ptr;
11- #[ cfg( all( target_arch = "x86_64" , feature = "smp" , not( feature = "idle-poll" ) ) ) ]
12- use core:: sync:: atomic:: AtomicU8 ;
1311use core:: sync:: atomic:: { AtomicI32 , AtomicU32 , Ordering } ;
1412
1513use ahash:: RandomState ;
@@ -32,6 +30,8 @@ use crate::kernel::scheduler::TaskStacks;
3230use crate :: scheduler:: task:: * ;
3331use crate :: { arch, io} ;
3432
33+ #[ cfg( all( target_arch = "x86_64" , feature = "smp" , not( feature = "idle-poll" ) ) ) ]
34+ pub mod sleep_state;
3535pub mod task;
3636pub mod timer_interrupts;
3737
@@ -46,129 +46,6 @@ static WAITING_TASKS: InterruptTicketMutex<BTreeMap<TaskId, VecDeque<TaskHandle>
4646/// Map between Task ID and TaskHandle
4747static TASKS : InterruptTicketMutex < BTreeMap < TaskId , TaskHandle > > =
4848 InterruptTicketMutex :: new ( BTreeMap :: new ( ) ) ;
49- #[ cfg( all( target_arch = "x86_64" , feature = "smp" , not( feature = "idle-poll" ) ) ) ]
50- static CORE_HLT_STATE : RwSpinLock < Vec < CoreState > > = RwSpinLock :: new ( Vec :: new ( ) ) ;
51-
52- #[ cfg( all( target_arch = "x86_64" , feature = "smp" , not( feature = "idle-poll" ) ) ) ]
53- struct CoreState ( AtomicU8 ) ;
54-
55- /// # Race condition prevention
56- ///
57- /// Two methods matter here: [arch::kernel::interrupts::enable_and_wait] and [arch::kernel::apic::wakeup_core].
58- /// `enable_and_wait` checks the status, ensuring it is set to 0+setting it to 1, then sleeps.
59- /// `wakeup_core` checks the status, ensuring it is set to 1+setting it to 0, then issues interrupt.
60- ///
61- /// A race would happen if `go_to_sleep` returns true, and `wake_up` returns false.
62- /// The result of these two methods must therefore be atomically determined in a single operation.
63- /// No ordering should exist in which that condition can happen.
64- /// Any other variant is okay:
65- /// - if `go_to_sleep` is false and `wake_up` is true, we have a un-necessary core wakeup (which is okay)
66- /// - if both are false, the core is not entering sleep and is therefore not woken up
67- /// - if both are true, the core sleeps and is woken up
68- #[ cfg( all( target_arch = "x86_64" , feature = "smp" , not( feature = "idle-poll" ) ) ) ]
69- impl CoreState {
70- /// The core is currently busy and should not be interrupted for new tasks
71- const STATUS_ACTIVE : u8 = 0 ;
72-
73- /// The core is currently halted and can be interrupted for new tasks
74- const STATUS_IDLE : u8 = 1 ;
75-
76- /// Another core tried to wake up this core but it was already active.
77- const STATUS_DONT_SLEEP : u8 = 2 ;
78-
79- pub fn new ( ) -> Self {
80- Self ( AtomicU8 :: new ( CoreState :: STATUS_ACTIVE ) )
81- }
82-
83- #[ inline( always) ]
84- pub fn set_active ( & self ) {
85- self . 0 . store ( CoreState :: STATUS_ACTIVE , Ordering :: SeqCst ) ;
86- }
87-
88- /// Indicates that this core will go to HLT.
89- /// This must be called *before* entering a HLT loop, and *with interrupts disabled*.
90- /// Returns a boolean indicating if the core should enter the HLT loop or not
91- #[ inline( always) ]
92- pub fn go_to_sleep ( & self ) -> bool {
93- if self
94- . 0
95- . compare_exchange (
96- CoreState :: STATUS_ACTIVE ,
97- CoreState :: STATUS_IDLE ,
98- Ordering :: SeqCst ,
99- Ordering :: Relaxed ,
100- )
101- . is_err ( )
102- {
103- // There is a possible race condition here, because the two atomic operations can be
104- // interleaved, but this has no effect on the final result: we're not going to sleep
105- // anyway.
106- // If the state was already set to ACTIVE, this has no effect.
107- // Normally, nothing can set the state to IDLE except this method.
108- // So the race should have no effect.
109- // IN ANY CASE: as per top-level comments, returning false is the safe default here.
110- self . set_active ( ) ;
111- false
112- } else {
113- // We have correctly read status ACTIVE and set STATUS_IDLE. We go to sleep. This is
114- // safe because:
115- // - Another `wake_up` could be processing, either BEFORE or AFTER the atomic `swap`.
116- // * BEFORE:
117- // We have set the state to STATUS_IDLE, so `swap` will read that value and
118- // wake_up will return true ==> SAFE.
119- // * AFTER: **We cannot be here!** Indeed, `swap` has set the status to
120- // `STATUS_DONT_SLEEP`, so `compare_exchange` is in the `Err` case.
121- true
122- }
123- }
124-
125- /// Request to wake up this core.
126- /// Returns a boolean indicating if an interrupt should be sent, or if the core is already active
127- #[ inline( always) ]
128- pub fn wake_up ( & self ) -> bool {
129- // Ask the core not to sleep.
130- // This makes sure that if the two atomic operations become interleaved, the core will
131- // not go to sleep with us assuming it was running.
132- let previous_state = self . 0 . swap ( CoreState :: STATUS_DONT_SLEEP , Ordering :: SeqCst ) ;
133-
134- // If the core was idle, we can actually wake it up
135- if previous_state == CoreState :: STATUS_IDLE {
136- // Again, this operation is not necessarily ordered.
137- // - Another `go_to_sleep` could be processing, either BEFORE or AFTER the atomic op.
138- // * BEFORE:
139- // We have set the state to STATUS_DONT_SLEEP, so the atomic op will read that value
140- // and go_to_sleep will return false. We will send an interrupt which could have
141- // been avoided, but that's okay.
142- // * AFTER:
143- // The value read at the atomic OP does not matter here, in any case we WILL send
144- // the interrupt and wake the core up.
145- // IN ANY CASE: as per top-level comments, returning true is the safe default here.
146- self . set_active ( ) ;
147- true
148- } else {
149- // We don't wake up the core. This is safe, because:
150- // - Another `go_to_sleep` could be processing, either BEFORE or AFTER the atomic op.
151- // * BEFORE:
152- // We have set the state to STATUS_DONT_SLEEP, so the atomic op will read that value
153- // and go_to_sleep will return false ==> SAFE.
154- // * AFTER: **We cannot be here!** Indeed, the atomic operation in `go_to_sleep`
155- // also sets the state to STATUS_IDLE, so `previous_state` MUST BE STATUS_IDLE.
156- false
157- }
158- }
159- }
160-
161- #[ inline]
162- #[ cfg( all( target_arch = "x86_64" , feature = "smp" , not( feature = "idle-poll" ) ) ) ]
163- pub fn core_sleep ( ) -> bool {
164- CORE_HLT_STATE . read ( ) [ usize:: try_from ( core_id ( ) ) . unwrap ( ) ] . go_to_sleep ( )
165- }
166-
167- #[ inline]
168- #[ cfg( all( target_arch = "x86_64" , feature = "smp" , not( feature = "idle-poll" ) ) ) ]
169- pub fn core_wake_up ( core_id : CoreId ) -> bool {
170- CORE_HLT_STATE . read ( ) [ usize:: try_from ( core_id) . unwrap ( ) ] . wake_up ( )
171- }
17249
17350/// Unique identifier for a core.
17451pub type CoreId = u32 ;
@@ -1013,9 +890,7 @@ pub(crate) fn add_current_core() {
1013890 & CoreLocal :: get ( ) . scheduler_input ,
1014891 ) ;
1015892 #[ cfg( all( target_arch = "x86_64" , not( feature = "idle-poll" ) ) ) ]
1016- CORE_HLT_STATE
1017- . write ( )
1018- . insert ( core_id. try_into ( ) . unwrap ( ) , CoreState :: new ( ) ) ;
893+ sleep_state:: install_for_core ( core_id) ;
1019894 }
1020895}
1021896
0 commit comments