1use crate::task::{RoleOverrides, Task};
6use fidl_fuchsia_scheduler::{
7 RoleManagerGetProfileForRoleRequest, RoleManagerMarker, RoleManagerSetRoleRequest,
8 RoleManagerSynchronousProxy, RoleName, RoleTarget, RoleType,
9};
10use fuchsia_component::client::connect_to_protocol_sync;
11use starnix_logging::{impossible_error, log_debug, log_error, log_warn, track_stub};
12use starnix_sync::{LockDepMutex, ProfileHandleCacheLock};
13use starnix_task_command::TaskCommand;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::{
16 SCHED_BATCH, SCHED_DEADLINE, SCHED_FIFO, SCHED_IDLE, SCHED_NORMAL, SCHED_RESET_ON_FORK,
17 SCHED_RR, errno, error, sched_param,
18};
19use std::collections::HashMap;
20
21pub struct SchedulerManager {
22 role_manager: Option<RoleManagerSynchronousProxy>,
23 role_overrides: RoleOverrides,
24 profile_handle_cache: LockDepMutex<HashMap<String, zx::Profile>, ProfileHandleCacheLock>,
25}
26
27impl SchedulerManager {
28 pub fn new(role_overrides: RoleOverrides) -> SchedulerManager {
31 let profile_handle_cache = Default::default();
32 let role_manager = fuchsia_runtime::with_thread_self(|thread| {
33 let role_manager = connect_to_protocol_sync::<RoleManagerMarker>().unwrap();
34 if let Err(e) = Self::set_thread_role_inner(
35 &role_manager,
36 thread,
37 SchedulerState::default().role_name(),
38 &profile_handle_cache,
39 ) {
40 log_debug!("Setting thread role failed ({e:?}), will not set thread priority.");
41 None
42 } else {
43 log_debug!("Thread role set successfully, scheduler manager initialized.");
44 Some(role_manager)
45 }
46 });
47
48 SchedulerManager { role_manager, role_overrides, profile_handle_cache }
49 }
50
51 pub fn empty_for_tests() -> Self {
53 Self {
54 role_manager: None,
55 role_overrides: RoleOverrides::new().build().unwrap(),
56 profile_handle_cache: Default::default(),
57 }
58 }
59
60 pub fn new_for_tests(
62 role_manager: Option<RoleManagerSynchronousProxy>,
63 role_overrides: RoleOverrides,
64 ) -> Self {
65 Self { role_manager, role_overrides, profile_handle_cache: Default::default() }
66 }
67
68 pub fn role_name(&self, task: &Task) -> Result<&str, Errno> {
71 let scheduler_state = task.read().scheduler_state;
72 self.role_name_inner(task, scheduler_state)
73 }
74
75 pub fn role_name_inner(
76 &self,
77 task: &Task,
78 scheduler_state: SchedulerState,
79 ) -> Result<&str, Errno> {
80 let thread_group = task.thread_group();
81 let thread_group_state = thread_group.read();
82 if thread_group_state.did_exec {
86 let process_name = if task.tid == thread_group.leader {
87 task.command()
88 } else {
89 thread_group_state
90 .get_task(thread_group.leader)
91 .ok_or_else(|| errno!(EINVAL))?
92 .command()
93 };
94 let thread_name = task.command();
95
96 let cpuset_path = task.read().cpuset_path.clone();
97
98 return Ok(self.resolve_role_name(
99 &process_name,
100 &thread_name,
101 &cpuset_path,
102 scheduler_state,
103 ));
104 }
105 Ok(scheduler_state.role_name())
106 }
107
108 pub fn resolve_role_name(
109 &self,
110 process_name: &TaskCommand,
111 thread_name: &TaskCommand,
112 cgroup_path: &str,
113 scheduler_state: SchedulerState,
114 ) -> &str {
115 if let Some(name) =
116 self.role_overrides.get_role_name(process_name, thread_name, cgroup_path)
117 {
118 return name;
119 }
120 scheduler_state.role_name()
121 }
122
123 pub fn set_thread_role(&self, task: &Task, role_name: &str) -> Result<(), Errno> {
125 let Some(role_manager) = self.role_manager.as_ref() else {
126 log_debug!("no role manager for setting role");
127 return Ok(());
128 };
129
130 let zircon_thread = {
131 let running_state = match task.running_state() {
132 Ok(live) => live,
133 Err(_) => {
134 log_debug!(
135 "thread role update requested for task without live state, skipping"
136 );
137 return Ok(());
138 }
139 };
140 let Some(zircon_thread) = running_state.thread.get() else {
141 log_debug!("thread role update requested for task without thread, skipping");
142 return Ok(());
143 };
144 zircon_thread.clone()
145 };
146 Self::set_thread_role_inner(
147 role_manager,
148 &zircon_thread.thread,
149 role_name,
150 &self.profile_handle_cache,
151 )?;
152
153 Ok(())
154 }
155
156 fn set_thread_role_inner(
157 role_manager: &RoleManagerSynchronousProxy,
158 thread: &zx::Thread,
159 role_name: &str,
160 cache: &LockDepMutex<HashMap<String, zx::Profile>, ProfileHandleCacheLock>,
161 ) -> Result<(), Errno> {
162 log_debug!(role_name; "setting thread role");
163
164 {
165 let params = cache.lock();
166 if let Some(profile) = params.get(role_name) {
167 match thread.set_profile(&profile, 0) {
168 Ok(_) => return Ok(()),
169 Err(e) => log_error!("Failed to set role profile {:?}", e),
170 }
171 }
172 }
173
174 let request = RoleManagerGetProfileForRoleRequest {
175 role: Some(RoleName { role: role_name.to_string() }),
176 target: Some(RoleType::Task),
177 ..Default::default()
178 };
179 match role_manager.get_profile_for_role(request, zx::MonotonicInstant::INFINITE) {
180 Ok(Ok(response)) => {
181 let Some(profile) = response.profile else {
182 log_warn!("GetRole returned success but no profile handle");
183 return error!(EINVAL);
184 };
185
186 if let Err(e) = thread.set_profile(&profile, 0) {
187 log_warn!(e:%; "Failed to set thread profile from handle");
188 return error!(EINVAL);
189 }
190 cache.lock().insert(role_name.to_string(), profile);
191 Ok(())
192 }
193 Ok(Err(e)) => {
194 log_warn!(e:%; "GetRole returned error");
195 Self::set_thread_role_legacy(role_manager, thread, role_name)
196 }
197 Err(e) => {
198 log_warn!(e:%; "GetRole FIDL call failed");
199 Self::set_thread_role_legacy(role_manager, thread, role_name)
200 }
201 }
202 }
203
204 fn set_thread_role_legacy(
205 role_manager: &RoleManagerSynchronousProxy,
206 thread: &zx::Thread,
207 role_name: &str,
208 ) -> Result<(), Errno> {
209 let thread = thread.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?;
210 let request = RoleManagerSetRoleRequest {
211 target: Some(RoleTarget::Thread(thread)),
212 role: Some(RoleName { role: role_name.to_string() }),
213 ..Default::default()
214 };
215 let _ = role_manager.set_role(request, zx::MonotonicInstant::INFINITE).map_err(|err| {
216 log_warn!(err:%; "Unable to set thread role.");
217 errno!(EINVAL)
218 })?;
219 Ok(())
220 }
221}
222
223#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
228pub struct NormalPriority {
229 value: u8,
232}
233
234impl NormalPriority {
235 const MIN_VALUE: u8 = 1;
236 const DEFAULT_VALUE: u8 = 20;
237 const MAX_VALUE: u8 = 40;
238
239 pub(crate) fn from_setpriority_syscall(user_nice: i32) -> Self {
246 Self {
247 value: (Self::DEFAULT_VALUE as i32)
248 .saturating_sub(user_nice)
249 .clamp(Self::MIN_VALUE as i32, Self::MAX_VALUE as i32) as u8,
250 }
251 }
252
253 pub fn from_binder(user_nice: i8) -> Result<Self, Errno> {
260 let value = (Self::DEFAULT_VALUE as i8).saturating_sub(user_nice);
261 if value < (Self::MIN_VALUE as i8) || value > (Self::MAX_VALUE as i8) {
262 return error!(EINVAL);
263 }
264 Ok(Self { value: u8::try_from(value).expect("normal priority should fit in a u8") })
265 }
266
267 pub fn as_nice(&self) -> i8 {
270 (Self::DEFAULT_VALUE as i8) - (self.value as i8)
271 }
272
273 pub(crate) fn raw_priority(&self) -> u8 {
276 self.value
277 }
278
279 pub(crate) fn exceeds(&self, limit: u64) -> bool {
281 limit < (self.value as u64)
282 }
283}
284
285impl std::default::Default for NormalPriority {
286 fn default() -> Self {
287 Self { value: Self::DEFAULT_VALUE }
288 }
289}
290
291#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
295pub(crate) struct RealtimePriority {
296 value: u8,
300}
301
302impl RealtimePriority {
303 const NON_REAL_TIME_VALUE: u8 = 0;
304 const MIN_VALUE: u8 = 1;
305 const MAX_VALUE: u8 = 99;
306
307 const NON_REAL_TIME: RealtimePriority = RealtimePriority { value: Self::NON_REAL_TIME_VALUE };
308
309 pub(crate) fn exceeds(&self, limit: u64) -> bool {
310 limit < (self.value as u64)
311 }
312}
313
314#[derive(Clone, Copy, Debug, Eq, PartialEq)]
316pub(crate) enum SchedulingPolicy {
317 Normal,
318 Batch,
319 Idle,
320 Fifo,
321 RoundRobin,
322}
323
324impl SchedulingPolicy {
325 fn realtime_priority_min(&self) -> u8 {
326 match self {
327 Self::Normal | Self::Batch | Self::Idle => RealtimePriority::NON_REAL_TIME_VALUE,
328 Self::Fifo | Self::RoundRobin => RealtimePriority::MIN_VALUE,
329 }
330 }
331
332 fn realtime_priority_max(&self) -> u8 {
333 match self {
334 Self::Normal | Self::Batch | Self::Idle => RealtimePriority::NON_REAL_TIME_VALUE,
335 Self::Fifo | Self::RoundRobin => RealtimePriority::MAX_VALUE,
336 }
337 }
338
339 pub(crate) fn realtime_priority_from(&self, priority: i32) -> Result<RealtimePriority, Errno> {
340 let priority = u8::try_from(priority).map_err(|_| errno!(EINVAL))?;
341 if priority < self.realtime_priority_min() || priority > self.realtime_priority_max() {
342 return error!(EINVAL);
343 }
344 Ok(RealtimePriority { value: priority })
345 }
346}
347
348impl TryFrom<u32> for SchedulingPolicy {
349 type Error = Errno;
350
351 fn try_from(value: u32) -> Result<Self, Errno> {
352 Ok(match value {
353 SCHED_NORMAL => Self::Normal,
354 SCHED_BATCH => Self::Batch,
355 SCHED_IDLE => Self::Idle,
356 SCHED_FIFO => Self::Fifo,
357 SCHED_RR => Self::RoundRobin,
358 _ => {
359 return error!(EINVAL);
360 }
361 })
362 }
363}
364
365#[derive(Clone, Copy, Debug, Eq, PartialEq)]
366pub struct SchedulerState {
367 pub(crate) policy: SchedulingPolicy,
368 pub(crate) normal_priority: NormalPriority,
372 pub(crate) realtime_priority: RealtimePriority,
375 pub(crate) reset_on_fork: bool,
376}
377
378impl SchedulerState {
379 pub fn is_default(&self) -> bool {
380 self == &Self::default()
381 }
382
383 pub fn from_binder(policy: u8, priority_or_nice: u8) -> Result<Self, Errno> {
389 let (policy, normal_priority, realtime_priority) = match policy as u32 {
390 SCHED_NORMAL => (
391 SchedulingPolicy::Normal,
392 NormalPriority::from_binder(priority_or_nice as i8)?,
393 RealtimePriority::NON_REAL_TIME,
394 ),
395 SCHED_BATCH => (
396 SchedulingPolicy::Batch,
397 NormalPriority::from_binder(priority_or_nice as i8)?,
398 RealtimePriority::NON_REAL_TIME,
399 ),
400 SCHED_FIFO => (
401 SchedulingPolicy::Fifo,
402 NormalPriority::default(),
403 SchedulingPolicy::Fifo.realtime_priority_from(priority_or_nice as i32)?,
404 ),
405 SCHED_RR => (
406 SchedulingPolicy::RoundRobin,
407 NormalPriority::default(),
408 SchedulingPolicy::RoundRobin.realtime_priority_from(priority_or_nice as i32)?,
409 ),
410 _ => return error!(EINVAL),
411 };
412 Ok(Self { policy, normal_priority, realtime_priority, reset_on_fork: false })
413 }
414
415 pub fn fork(self) -> Self {
416 if self.reset_on_fork {
417 let (policy, normal_priority, realtime_priority) = if self.is_realtime() {
418 (
424 SchedulingPolicy::Normal,
425 NormalPriority::default(),
426 RealtimePriority::NON_REAL_TIME,
427 )
428 } else {
429 (
434 self.policy,
435 std::cmp::min(self.normal_priority, NormalPriority::default()),
436 RealtimePriority::NON_REAL_TIME,
437 )
438 };
439 Self {
440 policy,
441 normal_priority,
442 realtime_priority,
443 reset_on_fork: false,
445 }
446 } else {
447 self
448 }
449 }
450
451 pub fn policy_for_sched_getscheduler(&self) -> u32 {
457 let mut base = match self.policy {
458 SchedulingPolicy::Normal => SCHED_NORMAL,
459 SchedulingPolicy::Batch => SCHED_BATCH,
460 SchedulingPolicy::Idle => SCHED_IDLE,
461 SchedulingPolicy::Fifo => SCHED_FIFO,
462 SchedulingPolicy::RoundRobin => SCHED_RR,
463 };
464 if self.reset_on_fork {
465 base |= SCHED_RESET_ON_FORK;
466 }
467 base
468 }
469
470 pub fn get_sched_param(&self) -> sched_param {
475 sched_param {
476 sched_priority: (if self.is_realtime() {
477 self.realtime_priority.value
478 } else {
479 RealtimePriority::NON_REAL_TIME_VALUE
480 }) as i32,
481 }
482 }
483
484 pub fn normal_priority(&self) -> NormalPriority {
485 self.normal_priority
486 }
487
488 pub fn is_realtime(&self) -> bool {
489 match self.policy {
490 SchedulingPolicy::Normal | SchedulingPolicy::Batch | SchedulingPolicy::Idle => false,
491 SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => true,
492 }
493 }
494
495 pub(crate) fn role_name(&self) -> &'static str {
508 match self.policy {
509 SchedulingPolicy::Idle => FAIR_PRIORITY_ROLE_NAMES[0],
512
513 SchedulingPolicy::Normal => {
516 FAIR_PRIORITY_ROLE_NAMES[(self.normal_priority.value as usize / 2) + 6]
517 }
518 SchedulingPolicy::Batch => {
519 track_stub!(TODO("https://fxbug.dev/308055542"), "SCHED_BATCH hinting");
520 FAIR_PRIORITY_ROLE_NAMES[(self.normal_priority.value as usize / 2) + 6]
521 }
522
523 SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => REALTIME_ROLE_NAME,
529 }
530 }
531
532 pub fn is_less_than_for_binder(&self, other: Self) -> bool {
535 match self.policy {
536 SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => match other.policy {
537 SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => {
538 self.realtime_priority < other.realtime_priority
539 }
540 SchedulingPolicy::Normal | SchedulingPolicy::Batch | SchedulingPolicy::Idle => {
541 false
542 }
543 },
544 SchedulingPolicy::Normal => match other.policy {
545 SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => true,
546 SchedulingPolicy::Normal => {
547 self.normal_priority.value < other.normal_priority.value
548 }
549 SchedulingPolicy::Batch | SchedulingPolicy::Idle => false,
550 },
551 SchedulingPolicy::Batch => match other.policy {
552 SchedulingPolicy::Fifo
553 | SchedulingPolicy::RoundRobin
554 | SchedulingPolicy::Normal => true,
555 SchedulingPolicy::Batch => self.normal_priority.value < other.normal_priority.value,
556 SchedulingPolicy::Idle => false,
557 },
558 SchedulingPolicy::Idle => match other.policy {
560 SchedulingPolicy::Fifo
561 | SchedulingPolicy::RoundRobin
562 | SchedulingPolicy::Normal
563 | SchedulingPolicy::Batch => true,
564 SchedulingPolicy::Idle => false,
565 },
566 }
567 }
568}
569
570impl std::default::Default for SchedulerState {
571 fn default() -> Self {
572 Self {
573 policy: SchedulingPolicy::Normal,
574 normal_priority: NormalPriority::default(),
575 realtime_priority: RealtimePriority::NON_REAL_TIME,
576 reset_on_fork: false,
577 }
578 }
579}
580
581pub fn min_priority_for_sched_policy(policy: u32) -> Result<u8, Errno> {
582 Ok(match policy {
583 SCHED_DEADLINE => RealtimePriority::NON_REAL_TIME_VALUE,
584 _ => SchedulingPolicy::try_from(policy)?.realtime_priority_min(),
585 })
586}
587
588pub fn max_priority_for_sched_policy(policy: u32) -> Result<u8, Errno> {
589 Ok(match policy {
590 SCHED_DEADLINE => RealtimePriority::NON_REAL_TIME_VALUE,
591 _ => SchedulingPolicy::try_from(policy)?.realtime_priority_max(),
592 })
593}
594
595const FAIR_PRIORITY_ROLE_NAMES: [&str; 32] = [
599 "fuchsia.starnix.fair.0",
600 "fuchsia.starnix.fair.1",
601 "fuchsia.starnix.fair.2",
602 "fuchsia.starnix.fair.3",
603 "fuchsia.starnix.fair.4",
604 "fuchsia.starnix.fair.5",
605 "fuchsia.starnix.fair.6",
606 "fuchsia.starnix.fair.7",
607 "fuchsia.starnix.fair.8",
608 "fuchsia.starnix.fair.9",
609 "fuchsia.starnix.fair.10",
610 "fuchsia.starnix.fair.11",
611 "fuchsia.starnix.fair.12",
612 "fuchsia.starnix.fair.13",
613 "fuchsia.starnix.fair.14",
614 "fuchsia.starnix.fair.15",
615 "fuchsia.starnix.fair.16",
616 "fuchsia.starnix.fair.17",
617 "fuchsia.starnix.fair.18",
618 "fuchsia.starnix.fair.19",
619 "fuchsia.starnix.fair.20",
620 "fuchsia.starnix.fair.21",
621 "fuchsia.starnix.fair.22",
622 "fuchsia.starnix.fair.23",
623 "fuchsia.starnix.fair.24",
624 "fuchsia.starnix.fair.25",
625 "fuchsia.starnix.fair.26",
626 "fuchsia.starnix.fair.27",
627 "fuchsia.starnix.fair.28",
628 "fuchsia.starnix.fair.29",
629 "fuchsia.starnix.fair.30",
630 "fuchsia.starnix.fair.31",
631];
632const REALTIME_ROLE_NAME: &str = "fuchsia.starnix.realtime";
633#[cfg(test)]
636mod tests {
637 use super::*;
638 use assert_matches::assert_matches;
639
640 #[fuchsia::test]
641 fn default_role_name() {
642 assert_eq!(SchedulerState::default().role_name(), "fuchsia.starnix.fair.16");
643 }
644
645 #[fuchsia::test]
646 fn normal_with_non_default_nice_role_name() {
647 assert_eq!(
648 SchedulerState {
649 policy: SchedulingPolicy::Normal,
650 normal_priority: NormalPriority { value: 10 },
651 realtime_priority: RealtimePriority::NON_REAL_TIME,
652 reset_on_fork: false
653 }
654 .role_name(),
655 "fuchsia.starnix.fair.11"
656 );
657 assert_eq!(
658 SchedulerState {
659 policy: SchedulingPolicy::Normal,
660 normal_priority: NormalPriority { value: 27 },
661 realtime_priority: RealtimePriority::NON_REAL_TIME,
662 reset_on_fork: false
663 }
664 .role_name(),
665 "fuchsia.starnix.fair.19"
666 );
667 }
668
669 #[fuchsia::test]
670 fn fifo_role_name() {
671 assert_eq!(
672 SchedulerState {
673 policy: SchedulingPolicy::Fifo,
674 normal_priority: NormalPriority::default(),
675 realtime_priority: RealtimePriority { value: 1 },
676 reset_on_fork: false
677 }
678 .role_name(),
679 "fuchsia.starnix.realtime",
680 );
681 assert_eq!(
682 SchedulerState {
683 policy: SchedulingPolicy::Fifo,
684 normal_priority: NormalPriority::default(),
685 realtime_priority: RealtimePriority { value: 2 },
686 reset_on_fork: false
687 }
688 .role_name(),
689 "fuchsia.starnix.realtime",
690 );
691 assert_eq!(
692 SchedulerState {
693 policy: SchedulingPolicy::Fifo,
694 normal_priority: NormalPriority::default(),
695 realtime_priority: RealtimePriority { value: 99 },
696 reset_on_fork: false
697 }
698 .role_name(),
699 "fuchsia.starnix.realtime",
700 );
701 }
702
703 #[fuchsia::test]
704 fn idle_role_name() {
705 assert_eq!(
706 SchedulerState {
707 policy: SchedulingPolicy::Idle,
708 normal_priority: NormalPriority { value: 1 },
709 realtime_priority: RealtimePriority::NON_REAL_TIME,
710 reset_on_fork: false,
711 }
712 .role_name(),
713 "fuchsia.starnix.fair.0"
714 );
715 assert_eq!(
716 SchedulerState {
717 policy: SchedulingPolicy::Idle,
718 normal_priority: NormalPriority::default(),
719 realtime_priority: RealtimePriority::NON_REAL_TIME,
720 reset_on_fork: false,
721 }
722 .role_name(),
723 "fuchsia.starnix.fair.0"
724 );
725 assert_eq!(
726 SchedulerState {
727 policy: SchedulingPolicy::Idle,
728 normal_priority: NormalPriority { value: 40 },
729 realtime_priority: RealtimePriority::NON_REAL_TIME,
730 reset_on_fork: false,
731 }
732 .role_name(),
733 "fuchsia.starnix.fair.0"
734 );
735 }
736
737 #[fuchsia::test]
738 fn build_policy_from_binder() {
739 assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 0), Ok(_));
740 assert_matches!(
741 SchedulerState::from_binder(SCHED_NORMAL as u8, ((-21) as i8) as u8),
742 Err(_)
743 );
744 assert_matches!(
745 SchedulerState::from_binder(SCHED_NORMAL as u8, ((-20) as i8) as u8),
746 Ok(SchedulerState {
747 policy: SchedulingPolicy::Normal,
748 normal_priority: NormalPriority { value: 40 },
749 realtime_priority: RealtimePriority::NON_REAL_TIME,
750 reset_on_fork: false,
751 })
752 );
753 assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 1), Ok(_));
754 assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 19), Ok(_));
755 assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 20), Err(_));
756 assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 0), Err(_));
757 assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 1), Ok(_));
758 assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 99), Ok(_));
759 assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 100), Err(_));
760 assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 0), Err(_));
761 assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 1), Ok(_));
762 assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 99), Ok(_));
763 assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 100), Err(_));
764 assert_matches!(SchedulerState::from_binder(SCHED_BATCH as u8, 11), Ok(_));
765 assert_eq!(SchedulerState::from_binder(SCHED_IDLE as u8, 11), error!(EINVAL));
766 assert_matches!(SchedulerState::from_binder(42, 0), Err(_));
767 assert_matches!(SchedulerState::from_binder(42, 0), Err(_));
768 }
769
770 #[fuchsia::test]
773 fn is_less_than_for_binder() {
774 let rr_50 = SchedulerState {
775 policy: SchedulingPolicy::RoundRobin,
776 normal_priority: NormalPriority { value: 1 },
777 realtime_priority: RealtimePriority { value: 50 },
778 reset_on_fork: false,
779 };
780 let rr_40 = SchedulerState {
781 policy: SchedulingPolicy::RoundRobin,
782 normal_priority: NormalPriority { value: 1 },
783 realtime_priority: RealtimePriority { value: 40 },
784 reset_on_fork: false,
785 };
786 let fifo_50 = SchedulerState {
787 policy: SchedulingPolicy::Fifo,
788 normal_priority: NormalPriority { value: 1 },
789 realtime_priority: RealtimePriority { value: 50 },
790 reset_on_fork: false,
791 };
792 let fifo_40 = SchedulerState {
793 policy: SchedulingPolicy::Fifo,
794 normal_priority: NormalPriority { value: 1 },
795 realtime_priority: RealtimePriority { value: 40 },
796 reset_on_fork: false,
797 };
798 let normal_40 = SchedulerState {
799 policy: SchedulingPolicy::Normal,
800 normal_priority: NormalPriority { value: 40 },
801 realtime_priority: RealtimePriority::NON_REAL_TIME,
802 reset_on_fork: true,
803 };
804 let normal_10 = SchedulerState {
805 policy: SchedulingPolicy::Normal,
806 normal_priority: NormalPriority { value: 10 },
807 realtime_priority: RealtimePriority::NON_REAL_TIME,
808 reset_on_fork: true,
809 };
810 let batch_40 = SchedulerState {
811 policy: SchedulingPolicy::Batch,
812 normal_priority: NormalPriority { value: 40 },
813 realtime_priority: RealtimePriority::NON_REAL_TIME,
814 reset_on_fork: true,
815 };
816 let batch_30 = SchedulerState {
817 policy: SchedulingPolicy::Batch,
818 normal_priority: NormalPriority { value: 30 },
819 realtime_priority: RealtimePriority::NON_REAL_TIME,
820 reset_on_fork: true,
821 };
822 let idle_40 = SchedulerState {
823 policy: SchedulingPolicy::Idle,
824 normal_priority: NormalPriority { value: 40 },
825 realtime_priority: RealtimePriority::NON_REAL_TIME,
826 reset_on_fork: true,
827 };
828 let idle_30 = SchedulerState {
829 policy: SchedulingPolicy::Idle,
830 normal_priority: NormalPriority { value: 30 },
831 realtime_priority: RealtimePriority::NON_REAL_TIME,
832 reset_on_fork: true,
833 };
834 assert!(!fifo_50.is_less_than_for_binder(fifo_50));
835 assert!(!rr_50.is_less_than_for_binder(rr_50));
836 assert!(!fifo_50.is_less_than_for_binder(rr_50));
837 assert!(!rr_50.is_less_than_for_binder(fifo_50));
838 assert!(!fifo_50.is_less_than_for_binder(rr_40));
839 assert!(rr_40.is_less_than_for_binder(fifo_50));
840 assert!(!rr_50.is_less_than_for_binder(fifo_40));
841 assert!(fifo_40.is_less_than_for_binder(rr_50));
842 assert!(!fifo_40.is_less_than_for_binder(normal_40));
843 assert!(normal_40.is_less_than_for_binder(fifo_40));
844 assert!(!rr_40.is_less_than_for_binder(normal_40));
845 assert!(normal_40.is_less_than_for_binder(rr_40));
846 assert!(!normal_40.is_less_than_for_binder(normal_40));
847 assert!(!normal_40.is_less_than_for_binder(normal_10));
848 assert!(normal_10.is_less_than_for_binder(normal_40));
849 assert!(!normal_10.is_less_than_for_binder(batch_40));
850 assert!(batch_40.is_less_than_for_binder(normal_10));
851 assert!(!batch_40.is_less_than_for_binder(batch_40));
852 assert!(!batch_40.is_less_than_for_binder(batch_30));
853 assert!(batch_30.is_less_than_for_binder(batch_40));
854 assert!(!batch_30.is_less_than_for_binder(idle_40));
855 assert!(idle_40.is_less_than_for_binder(batch_30));
856 assert!(!idle_40.is_less_than_for_binder(idle_40));
857 assert!(!idle_40.is_less_than_for_binder(idle_30));
858 assert!(!idle_30.is_less_than_for_binder(idle_40));
859 }
860
861 #[fuchsia::test]
862 async fn role_overrides_non_realtime() {
863 crate::testing::spawn_kernel_and_run_sync(|current_task| {
864 let mut builder = RoleOverrides::new();
865 builder.add("my_task", "my_task", None, "overridden_role");
866 let overrides = builder.build().unwrap();
867 let manager = SchedulerManager {
868 role_manager: None,
869 role_overrides: overrides,
870 profile_handle_cache: Default::default(),
871 };
872
873 current_task.thread_group().write().did_exec = true;
875 current_task.set_command_name(starnix_task_command::TaskCommand::new(b"my_task"));
876
877 let mut state = SchedulerState::default();
878 state.policy = SchedulingPolicy::Normal;
879
880 let role = manager.role_name_inner(current_task, state).expect("role_name");
881 assert_eq!(role, "overridden_role");
882 })
883 .await;
884 }
885}