ax_task/sched/system/cpu/remote/
lifecycle.rs1use super::*;
2
3const CPU_LIFECYCLE_OFFLINE: usize = 1 << (usize::BITS - 1);
4const CPU_LIFECYCLE_INACTIVE: usize = 1 << (usize::BITS - 2);
5const CPU_LIFECYCLE_DRAINING: usize = CPU_LIFECYCLE_OFFLINE | CPU_LIFECYCLE_INACTIVE;
6const CPU_LIFECYCLE_MASK: usize = CPU_LIFECYCLE_DRAINING;
7const CPU_PUBLICATION_COUNT_MASK: usize = !CPU_LIFECYCLE_MASK;
8const CPU_PUBLICATION_OVERFLOW_INVARIANT: u32 = 0x4350_5542;
9const CPU_PUBLICATION_RELEASE_INVARIANT: u32 = 0x4350_5544;
10
11pub(super) const INITIAL_CPU_LIFECYCLE_STATE: usize = CPU_LIFECYCLE_OFFLINE;
12
13#[derive(Clone, Copy)]
14enum CpuPublicationClass {
15 Placement,
16 OwnerControl,
17}
18
19impl CpuPublicationClass {
20 const fn accepts(self, state: usize) -> bool {
21 match self {
22 Self::Placement => state & CPU_LIFECYCLE_MASK == 0,
23 Self::OwnerControl => state & CPU_LIFECYCLE_OFFLINE == 0,
24 }
25 }
26}
27
28#[derive(Debug)]
29pub(super) struct CpuPublicationState {
30 state: AtomicUsize,
31}
32
33impl CpuPublicationState {
34 pub(super) const fn new() -> Self {
35 Self {
36 state: AtomicUsize::new(INITIAL_CPU_LIFECYCLE_STATE),
37 }
38 }
39}
40
41#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub enum CpuLifecycleState {
44 Online,
46 Inactive,
48 Draining,
50 Offline,
52}
53
54impl CpuRemote {
55 pub fn lifecycle_state(&self) -> CpuLifecycleState {
57 match self.publication.state.load(Ordering::Acquire) & CPU_LIFECYCLE_MASK {
58 0 => CpuLifecycleState::Online,
59 CPU_LIFECYCLE_INACTIVE => CpuLifecycleState::Inactive,
60 CPU_LIFECYCLE_DRAINING => CpuLifecycleState::Draining,
61 CPU_LIFECYCLE_OFFLINE => CpuLifecycleState::Offline,
62 _ => unreachable!("CPU lifecycle mask has four encoded states"),
63 }
64 }
65
66 pub fn is_online(&self) -> bool {
68 matches!(
69 self.lifecycle_state(),
70 CpuLifecycleState::Online | CpuLifecycleState::Inactive
71 )
72 }
73
74 pub(crate) fn accepts_placement(&self) -> bool {
76 self.lifecycle_state() == CpuLifecycleState::Online
77 }
78
79 pub(crate) fn mark_online(&self) -> bool {
80 self.publication
81 .state
82 .compare_exchange(
83 CPU_LIFECYCLE_OFFLINE,
84 0,
85 Ordering::Release,
86 Ordering::Acquire,
87 )
88 .is_ok()
89 }
90
91 pub(crate) fn try_deactivate(&self) -> bool {
92 let inactive = self
96 .publication
97 .state
98 .compare_exchange(
99 0,
100 CPU_LIFECYCLE_INACTIVE,
101 Ordering::AcqRel,
102 Ordering::Acquire,
103 )
104 .is_ok();
105 if inactive {
106 self.cancel_idle_pull_if_uncommitted();
107 }
108 inactive
109 }
110
111 pub(crate) fn cancel_deactivation(&self) {
112 let mut current = self.publication.state.load(Ordering::Acquire);
113 loop {
114 if current & CPU_LIFECYCLE_MASK != CPU_LIFECYCLE_INACTIVE {
115 task_runtime::fatal_invariant(
116 CPU_PUBLICATION_RELEASE_INVARIANT,
117 self.owner.as_u32() as usize,
118 );
119 }
120 let online = current & !CPU_LIFECYCLE_INACTIVE;
121 match self.publication.state.compare_exchange_weak(
122 current,
123 online,
124 Ordering::Release,
125 Ordering::Acquire,
126 ) {
127 Ok(_) => return,
128 Err(actual) => current = actual,
129 }
130 }
131 }
132
133 pub(crate) fn try_begin_draining(&self) -> bool {
134 self.publication
137 .state
138 .compare_exchange(
139 CPU_LIFECYCLE_INACTIVE,
140 CPU_LIFECYCLE_DRAINING,
141 Ordering::AcqRel,
142 Ordering::Acquire,
143 )
144 .is_ok()
145 }
146
147 pub(crate) fn cancel_draining(&self) {
148 if self
149 .publication
150 .state
151 .compare_exchange(
152 CPU_LIFECYCLE_DRAINING,
153 0,
154 Ordering::Release,
155 Ordering::Acquire,
156 )
157 .is_err()
158 {
159 task_runtime::fatal_invariant(
160 CPU_PUBLICATION_RELEASE_INVARIANT,
161 self.owner.as_u32() as usize,
162 );
163 }
164 }
165
166 pub(crate) fn finish_offline(&self) {
167 self.reset_scheduler_for_offline();
168 if self
169 .publication
170 .state
171 .compare_exchange(
172 CPU_LIFECYCLE_DRAINING,
173 CPU_LIFECYCLE_OFFLINE,
174 Ordering::Release,
175 Ordering::Acquire,
176 )
177 .is_err()
178 {
179 task_runtime::fatal_invariant(
180 CPU_PUBLICATION_RELEASE_INVARIANT,
181 self.owner.as_u32() as usize,
182 );
183 }
184 }
185
186 pub(crate) fn begin_publication(&self) -> Option<CpuRemotePublication<'_>> {
187 self.try_acquire_publication(CpuPublicationClass::Placement)
188 .then(|| CpuRemotePublication { remote: self })
189 }
190
191 fn try_acquire_publication(&self, class: CpuPublicationClass) -> bool {
192 let mut current = self.publication.state.load(Ordering::Acquire);
193 loop {
194 if !class.accepts(current) {
195 return false;
196 }
197 let count = current & CPU_PUBLICATION_COUNT_MASK;
198 if count == CPU_PUBLICATION_COUNT_MASK {
199 task_runtime::fatal_invariant(
200 CPU_PUBLICATION_OVERFLOW_INVARIANT,
201 self.owner.as_u32() as usize,
202 );
203 }
204 match self.publication.state.compare_exchange_weak(
205 current,
206 current + 1,
207 Ordering::AcqRel,
208 Ordering::Acquire,
209 ) {
210 Ok(_) => {
211 #[cfg(feature = "qperf-metrics")]
212 match class {
213 CpuPublicationClass::Placement => {
214 crate::diagnostics::counters::record_cpu_placement_publication_acquire(
215 );
216 }
217 CpuPublicationClass::OwnerControl => {
218 crate::diagnostics::counters::record_cpu_owner_control_publication_acquire();
219 }
220 }
221 return true;
222 }
223 Err(actual) => current = actual,
224 }
225 }
226 }
227
228 pub(crate) fn begin_owned_publication(self: &Arc<Self>) -> Option<OwnedCpuRemotePublication> {
235 self.try_acquire_publication(CpuPublicationClass::Placement)
236 .then(|| OwnedCpuRemotePublication {
237 remote: Arc::clone(self),
238 })
239 }
240
241 pub(crate) fn begin_owner_delivery(&self) -> Option<CpuRemotePublication<'_>> {
242 self.try_acquire_publication(CpuPublicationClass::OwnerControl)
243 .then(|| CpuRemotePublication { remote: self })
244 }
245
246 pub(crate) fn is_quiescent_for_offline(&self) -> bool {
247 self.publication.state.load(Ordering::Acquire) == CPU_LIFECYCLE_DRAINING
248 && self.ktimer_is_quiescent_for_offline()
249 && self.deadline_is_quiescent_for_offline()
250 && !self.needs_reschedule()
251 && !self.has_remote_work()
252 && !self.is_idle_polling()
253 && self.idle_pull_is_quiescent()
254 }
255}
256
257pub(crate) struct CpuRemotePublication<'remote> {
258 remote: &'remote CpuRemote,
259}
260
261#[derive(Debug)]
262pub(crate) struct OwnedCpuRemotePublication {
263 remote: Arc<CpuRemote>,
264}
265
266impl OwnedCpuRemotePublication {
267 pub(crate) fn publish_owner_control(
268 self,
269 node: Pin<&'static InboxNode>,
270 message: InboxMessage,
271 ) -> PublishResult {
272 self.remote.publish_owner_control_owned(node, message)
273 }
274}
275
276impl CpuRemotePublication<'_> {
277 pub(crate) fn publish_owner_control(
278 self,
279 node: Pin<&'static InboxNode>,
280 message: InboxMessage,
281 ) -> PublishResult {
282 self.remote.publish_owner_control_owned(node, message)
283 }
284}
285
286impl Drop for CpuRemotePublication<'_> {
287 fn drop(&mut self) {
288 release_publication(self.remote);
289 }
290}
291
292impl Drop for OwnedCpuRemotePublication {
293 fn drop(&mut self) {
294 release_publication(&self.remote);
295 }
296}
297
298fn release_publication(remote: &CpuRemote) {
299 let mut current = remote.publication.state.load(Ordering::Acquire);
300 loop {
301 if current & CPU_LIFECYCLE_OFFLINE != 0 || current & CPU_PUBLICATION_COUNT_MASK == 0 {
302 task_runtime::fatal_invariant(
303 CPU_PUBLICATION_RELEASE_INVARIANT,
304 remote.owner.as_u32() as usize,
305 );
306 }
307 match remote.publication.state.compare_exchange_weak(
308 current,
309 current - 1,
310 Ordering::Release,
311 Ordering::Acquire,
312 ) {
313 Ok(_) => return,
314 Err(actual) => current = actual,
315 }
316 }
317}