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