starnix_core/task/scheduler/
manager.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::task::{RoleOverrides, Task};
6use fidl::HandleBased;
7use fidl_fuchsia_scheduler::{
8    RoleManagerMarker, RoleManagerSetRoleRequest, RoleManagerSynchronousProxy, RoleName, RoleTarget,
9};
10use fuchsia_component::client::connect_to_protocol_sync;
11use starnix_logging::{impossible_error, log_debug, log_warn, track_stub};
12use starnix_uapi::errors::Errno;
13use starnix_uapi::{
14    SCHED_BATCH, SCHED_DEADLINE, SCHED_FIFO, SCHED_IDLE, SCHED_NORMAL, SCHED_RESET_ON_FORK,
15    SCHED_RR, errno, error, sched_param,
16};
17
18pub struct SchedulerManager {
19    role_manager: Option<RoleManagerSynchronousProxy>,
20    role_overrides: RoleOverrides,
21}
22
23impl SchedulerManager {
24    /// Create a new SchedulerManager which will apply any provided `role_overrides` before
25    /// computing a role name based on a Task's scheduler state.
26    pub fn new(role_overrides: RoleOverrides) -> SchedulerManager {
27        let role_manager = fuchsia_runtime::with_thread_self(|thread| {
28            let role_manager = connect_to_protocol_sync::<RoleManagerMarker>().unwrap();
29            if let Err(e) = Self::set_thread_role_inner(
30                &role_manager,
31                thread,
32                SchedulerState::default().role_name(),
33            ) {
34                log_debug!("Setting thread role failed ({e:?}), will not set thread priority.");
35                None
36            } else {
37                log_debug!("Thread role set successfully, scheduler manager initialized.");
38                Some(role_manager)
39            }
40        });
41
42        SchedulerManager { role_manager, role_overrides }
43    }
44
45    /// Create a new empty SchedulerManager for testing.
46    pub fn empty_for_tests() -> Self {
47        Self { role_manager: None, role_overrides: RoleOverrides::new().build().unwrap() }
48    }
49
50    /// Return the currently active role name for this task. Requires read access to `task`'s state,
51    /// should only be called by code which is not already modifying the provided `task`.
52    pub fn role_name(&self, task: &Task) -> Result<&str, Errno> {
53        let scheduler_state = task.read().scheduler_state;
54        self.role_name_inner(task, scheduler_state)
55    }
56
57    fn role_name_inner(&self, task: &Task, scheduler_state: SchedulerState) -> Result<&str, Errno> {
58        Ok(if scheduler_state.is_realtime() {
59            let process_name = task
60                .thread_group()
61                .read()
62                .get_task(task.thread_group().leader)
63                .ok_or_else(|| errno!(EINVAL))?
64                .command();
65            let thread_name = task.command();
66            if let Some(name) = self.role_overrides.get_role_name(&process_name, &thread_name) {
67                name
68            } else {
69                scheduler_state.role_name()
70            }
71        } else {
72            scheduler_state.role_name()
73        })
74    }
75
76    /// Give the provided `task`'s Zircon thread a role.
77    ///
78    /// Requires passing the current `SchedulerState` so that this can be
79    /// performed without touching `task`'s state lock.
80    pub fn set_thread_role(
81        &self,
82        task: &Task,
83        scheduler_state: SchedulerState,
84    ) -> Result<(), Errno> {
85        let Some(role_manager) = self.role_manager.as_ref() else {
86            log_debug!("no role manager for setting role");
87            return Ok(());
88        };
89
90        let role_name = self.role_name_inner(task, scheduler_state)?;
91        let thread = task.thread.read();
92        let Some(thread) = thread.as_ref() else {
93            log_debug!("thread role update requested for task without thread, skipping");
94            return Ok(());
95        };
96        Self::set_thread_role_inner(role_manager, thread, role_name)
97    }
98
99    fn set_thread_role_inner(
100        role_manager: &RoleManagerSynchronousProxy,
101        thread: &zx::Thread,
102        role_name: &str,
103    ) -> Result<(), Errno> {
104        log_debug!(role_name; "setting thread role");
105
106        let thread = thread.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?;
107        let request = RoleManagerSetRoleRequest {
108            target: Some(RoleTarget::Thread(thread)),
109            role: Some(RoleName { role: role_name.to_string() }),
110            ..Default::default()
111        };
112        let _ = role_manager.set_role(request, zx::MonotonicInstant::INFINITE).map_err(|err| {
113            log_warn!(err:%; "Unable to set thread role.");
114            errno!(EINVAL)
115        })?;
116        Ok(())
117    }
118}
119
120/// The task normal priority, used for favoring or disfavoring a task running
121/// with some non-real-time scheduling policies. Ranges from -20 to +19 in
122/// "user-space" representation and +1 to +40 in "kernel-internal"
123/// representation. See "The nice value" at sched(7) for full specification.
124#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
125pub struct NormalPriority {
126    /// 1 (weakest) to 40 (strongest) (in "kernel-internal" representation),
127    /// from setpriority()
128    value: u8,
129}
130
131impl NormalPriority {
132    const MIN_VALUE: u8 = 1;
133    const DEFAULT_VALUE: u8 = 20;
134    const MAX_VALUE: u8 = 40;
135
136    /// Creates a normal priority from a value to be interpreted according
137    /// to the "user-space nice" (-20..=19) scale, clamping values outside
138    /// that scale.
139    ///
140    /// It would be strange for this to be called from anywhere outside of
141    /// the setpriority system call.
142    pub(crate) fn from_setpriority_syscall(user_nice: i32) -> Self {
143        Self {
144            value: (Self::DEFAULT_VALUE as i32)
145                .saturating_sub(user_nice)
146                .clamp(Self::MIN_VALUE as i32, Self::MAX_VALUE as i32) as u8,
147        }
148    }
149
150    /// Creates a normal priority from a value to be interpreted according
151    /// to the "user-space nice" (-20..=19) scale, rejecting values outside
152    /// that scale.
153    ///
154    /// It would be strange for this to be called from anywhere outside of
155    /// our Binder implementation.
156    pub fn from_binder(user_nice: i8) -> Result<Self, Errno> {
157        let value = (Self::DEFAULT_VALUE as i8).saturating_sub(user_nice);
158        if value < (Self::MIN_VALUE as i8) || value > (Self::MAX_VALUE as i8) {
159            return error!(EINVAL);
160        }
161        Ok(Self { value: u8::try_from(value).expect("normal priority should fit in a u8") })
162    }
163
164    /// Returns this normal priority's integer representation according
165    /// to the "user-space nice" (-20..=19) scale.
166    pub fn as_nice(&self) -> i8 {
167        (Self::DEFAULT_VALUE as i8) - (self.value as i8)
168    }
169
170    /// Returns this normal priority's integer representation according
171    /// to the "kernel space nice" (1..=40) scale.
172    pub(crate) fn raw_priority(&self) -> u8 {
173        self.value
174    }
175
176    /// Returns whether this normal priority exceeds the given limit.
177    pub(crate) fn exceeds(&self, limit: u64) -> bool {
178        limit < (self.value as u64)
179    }
180}
181
182impl std::default::Default for NormalPriority {
183    fn default() -> Self {
184        Self { value: Self::DEFAULT_VALUE }
185    }
186}
187
188/// The task real-time priority, used for favoring or disfavoring a task
189/// running with real-time scheduling policies. See "Scheduling policies"
190/// at sched(7) for full specification.
191#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
192pub(crate) struct RealtimePriority {
193    /// 1 (weakest) to 99 (strongest), from sched_setscheduler() and
194    /// sched_setparam(). Only meaningfully used for Fifo and
195    /// Round-Robin; set to 0 for other policies.
196    value: u8,
197}
198
199impl RealtimePriority {
200    const NON_REAL_TIME_VALUE: u8 = 0;
201    const MIN_VALUE: u8 = 1;
202    const MAX_VALUE: u8 = 99;
203
204    const NON_REAL_TIME: RealtimePriority = RealtimePriority { value: Self::NON_REAL_TIME_VALUE };
205
206    pub(crate) fn exceeds(&self, limit: u64) -> bool {
207        limit < (self.value as u64)
208    }
209}
210
211/// The scheduling policies described in "Scheduling policies" at sched(7).
212#[derive(Clone, Copy, Debug, Eq, PartialEq)]
213pub(crate) enum SchedulingPolicy {
214    Normal,
215    Batch,
216    Idle,
217    Fifo,
218    RoundRobin,
219}
220
221impl SchedulingPolicy {
222    fn realtime_priority_min(&self) -> u8 {
223        match self {
224            Self::Normal | Self::Batch | Self::Idle => RealtimePriority::NON_REAL_TIME_VALUE,
225            Self::Fifo | Self::RoundRobin => RealtimePriority::MIN_VALUE,
226        }
227    }
228
229    fn realtime_priority_max(&self) -> u8 {
230        match self {
231            Self::Normal | Self::Batch | Self::Idle => RealtimePriority::NON_REAL_TIME_VALUE,
232            Self::Fifo | Self::RoundRobin => RealtimePriority::MAX_VALUE,
233        }
234    }
235
236    pub(crate) fn realtime_priority_from(&self, priority: i32) -> Result<RealtimePriority, Errno> {
237        let priority = u8::try_from(priority).map_err(|_| errno!(EINVAL))?;
238        if priority < self.realtime_priority_min() || priority > self.realtime_priority_max() {
239            return error!(EINVAL);
240        }
241        Ok(RealtimePriority { value: priority })
242    }
243}
244
245impl TryFrom<u32> for SchedulingPolicy {
246    type Error = Errno;
247
248    fn try_from(value: u32) -> Result<Self, Errno> {
249        Ok(match value {
250            SCHED_NORMAL => Self::Normal,
251            SCHED_BATCH => Self::Batch,
252            SCHED_IDLE => Self::Idle,
253            SCHED_FIFO => Self::Fifo,
254            SCHED_RR => Self::RoundRobin,
255            _ => {
256                return error!(EINVAL);
257            }
258        })
259    }
260}
261
262#[derive(Clone, Copy, Debug, Eq, PartialEq)]
263pub struct SchedulerState {
264    pub(crate) policy: SchedulingPolicy,
265    /// Although nice is only used for Normal and Batch, normal priority
266    /// ("nice") is still maintained, observable, and alterable when a
267    /// task is using Idle, Fifo, and RoundRobin.
268    pub(crate) normal_priority: NormalPriority,
269    /// 1 (weakest) to 99 (strongest), from sched_setscheduler() and
270    /// sched_setparam(). Only used for Fifo and Round-Robin.
271    pub(crate) realtime_priority: RealtimePriority,
272    pub(crate) reset_on_fork: bool,
273}
274
275impl SchedulerState {
276    pub fn is_default(&self) -> bool {
277        self == &Self::default()
278    }
279
280    /// Create a policy according to the "sched_policy" and "priority" bits of
281    /// a flat_binder_object_flags bitmask (see uapi/linux/android/binder.h).
282    ///
283    /// It would be very strange for this to need to be called anywhere outside
284    /// of our Binder implementation.
285    pub fn from_binder(policy: u8, priority_or_nice: u8) -> Result<Self, Errno> {
286        let (policy, normal_priority, realtime_priority) = match policy as u32 {
287            SCHED_NORMAL => (
288                SchedulingPolicy::Normal,
289                NormalPriority::from_binder(priority_or_nice as i8)?,
290                RealtimePriority::NON_REAL_TIME,
291            ),
292            SCHED_BATCH => (
293                SchedulingPolicy::Batch,
294                NormalPriority::from_binder(priority_or_nice as i8)?,
295                RealtimePriority::NON_REAL_TIME,
296            ),
297            SCHED_FIFO => (
298                SchedulingPolicy::Fifo,
299                NormalPriority::default(),
300                SchedulingPolicy::Fifo.realtime_priority_from(priority_or_nice as i32)?,
301            ),
302            SCHED_RR => (
303                SchedulingPolicy::RoundRobin,
304                NormalPriority::default(),
305                SchedulingPolicy::RoundRobin.realtime_priority_from(priority_or_nice as i32)?,
306            ),
307            _ => return error!(EINVAL),
308        };
309        Ok(Self { policy, normal_priority, realtime_priority, reset_on_fork: false })
310    }
311
312    pub fn fork(self) -> Self {
313        if self.reset_on_fork {
314            let (policy, normal_priority, realtime_priority) = if self.is_realtime() {
315                // If the calling task has a real-time scheduling policy, the
316                // policy given to child processes is SCHED_OTHER and the nice is
317                // NormalPriority::default() (in all such cases and without caring
318                // about whether the caller's nice had been stronger or weaker than
319                // NormalPriority::default()).
320                (
321                    SchedulingPolicy::Normal,
322                    NormalPriority::default(),
323                    RealtimePriority::NON_REAL_TIME,
324                )
325            } else {
326                // If the calling task has a non-real-time scheduling policy, the
327                // state given to child processes is the same as that of the
328                // caller except with the caller's nice clamped to
329                // NormalPriority::default() at the strongest.
330                (
331                    self.policy,
332                    std::cmp::min(self.normal_priority, NormalPriority::default()),
333                    RealtimePriority::NON_REAL_TIME,
334                )
335            };
336            Self {
337                policy,
338                normal_priority,
339                realtime_priority,
340                // This flag is disabled in child processes created by fork(2).
341                reset_on_fork: false,
342            }
343        } else {
344            self
345        }
346    }
347
348    /// Return the policy as an integer (SCHED_NORMAL, SCHED_BATCH, &c) bitwise-ored
349    /// with the current reset-on-fork status (SCHED_RESET_ON_FORK or 0, depending).
350    ///
351    /// It would be strange for this to need to be called anywhere outside the
352    /// implementation of the sched_getscheduler system call.
353    pub fn policy_for_sched_getscheduler(&self) -> u32 {
354        let mut base = match self.policy {
355            SchedulingPolicy::Normal => SCHED_NORMAL,
356            SchedulingPolicy::Batch => SCHED_BATCH,
357            SchedulingPolicy::Idle => SCHED_IDLE,
358            SchedulingPolicy::Fifo => SCHED_FIFO,
359            SchedulingPolicy::RoundRobin => SCHED_RR,
360        };
361        if self.reset_on_fork {
362            base |= SCHED_RESET_ON_FORK;
363        }
364        base
365    }
366
367    /// Return the priority as a field in a sched_param struct.
368    ///
369    /// It would be strange for this to need to be called anywhere outside the
370    /// implementation of the sched_getparam system call.
371    pub fn get_sched_param(&self) -> sched_param {
372        sched_param {
373            sched_priority: (if self.is_realtime() {
374                self.realtime_priority.value
375            } else {
376                RealtimePriority::NON_REAL_TIME_VALUE
377            }) as i32,
378        }
379    }
380
381    pub fn normal_priority(&self) -> NormalPriority {
382        self.normal_priority
383    }
384
385    pub fn is_realtime(&self) -> bool {
386        match self.policy {
387            SchedulingPolicy::Normal | SchedulingPolicy::Batch | SchedulingPolicy::Idle => false,
388            SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => true,
389        }
390    }
391
392    /// Returns a number 0-31 (inclusive) mapping Linux scheduler priority to a Zircon priority
393    /// level for the fair scheduler.
394    ///
395    /// The range of 32 Zircon priorities is divided into a region for each flavor of Linux
396    /// scheduling:
397    ///
398    /// 1. 0 is used for SCHED_IDLE, the lowest priority Linux tasks.
399    /// 2. 6-15 (inclusive) is used for lower-than-default-priority SCHED_OTHER/SCHED_BATCH tasks.
400    /// 3. 16 is used for the default priority SCHED_OTHER/SCHED_BATCH, the same as Zircon's
401    ///    default for Fuchsia processes.
402    /// 4. 17-26 (inclusive) is used for higher-than-default-priority SCHED_OTHER/SCHED_BATCH tasks.
403    /// 5. Realtime tasks receive their own profile name.
404    fn role_name(&self) -> &'static str {
405        match self.policy {
406            // Mapped to 0; see "the [...] nice value has no influence for [the SCHED_IDLE] policy"
407            // at sched(7).
408            SchedulingPolicy::Idle => FAIR_PRIORITY_ROLE_NAMES[0],
409
410            // Configured with nice 0-40 and mapped to 6-26. 20 is the default nice which we want to
411            // map to 16.
412            SchedulingPolicy::Normal => {
413                FAIR_PRIORITY_ROLE_NAMES[(self.normal_priority.value as usize / 2) + 6]
414            }
415            SchedulingPolicy::Batch => {
416                track_stub!(TODO("https://fxbug.dev/308055542"), "SCHED_BATCH hinting");
417                FAIR_PRIORITY_ROLE_NAMES[(self.normal_priority.value as usize / 2) + 6]
418            }
419
420            // Configured with priority 1-99, mapped to a constant bandwidth profile. Priority
421            // between realtime tasks is ignored because we don't currently have a way to tell the
422            // scheduler that a given realtime task is more important than another without
423            // specifying an earlier deadline for the higher priority task. We can't specify
424            // deadlines at runtime, so we'll treat their priorities all the same.
425            SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => REALTIME_ROLE_NAME,
426        }
427    }
428
429    // TODO: https://fxbug.dev/425726327 - better understand what are Binder's requirements when
430    // comparing one scheduling with another.
431    pub fn is_less_than_for_binder(&self, other: Self) -> bool {
432        match self.policy {
433            SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => match other.policy {
434                SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => {
435                    self.realtime_priority < other.realtime_priority
436                }
437                SchedulingPolicy::Normal | SchedulingPolicy::Batch | SchedulingPolicy::Idle => {
438                    false
439                }
440            },
441            SchedulingPolicy::Normal => match other.policy {
442                SchedulingPolicy::Fifo | SchedulingPolicy::RoundRobin => true,
443                SchedulingPolicy::Normal => {
444                    self.normal_priority.value < other.normal_priority.value
445                }
446                SchedulingPolicy::Batch | SchedulingPolicy::Idle => false,
447            },
448            SchedulingPolicy::Batch => match other.policy {
449                SchedulingPolicy::Fifo
450                | SchedulingPolicy::RoundRobin
451                | SchedulingPolicy::Normal => true,
452                SchedulingPolicy::Batch => self.normal_priority.value < other.normal_priority.value,
453                SchedulingPolicy::Idle => false,
454            },
455            // see "the [...] nice value has no influence for [the SCHED_IDLE] policy" at sched(7).
456            SchedulingPolicy::Idle => match other.policy {
457                SchedulingPolicy::Fifo
458                | SchedulingPolicy::RoundRobin
459                | SchedulingPolicy::Normal
460                | SchedulingPolicy::Batch => true,
461                SchedulingPolicy::Idle => false,
462            },
463        }
464    }
465}
466
467impl std::default::Default for SchedulerState {
468    fn default() -> Self {
469        Self {
470            policy: SchedulingPolicy::Normal,
471            normal_priority: NormalPriority::default(),
472            realtime_priority: RealtimePriority::NON_REAL_TIME,
473            reset_on_fork: false,
474        }
475    }
476}
477
478pub fn min_priority_for_sched_policy(policy: u32) -> Result<u8, Errno> {
479    Ok(match policy {
480        SCHED_DEADLINE => RealtimePriority::NON_REAL_TIME_VALUE,
481        _ => SchedulingPolicy::try_from(policy)?.realtime_priority_min(),
482    })
483}
484
485pub fn max_priority_for_sched_policy(policy: u32) -> Result<u8, Errno> {
486    Ok(match policy {
487        SCHED_DEADLINE => RealtimePriority::NON_REAL_TIME_VALUE,
488        _ => SchedulingPolicy::try_from(policy)?.realtime_priority_max(),
489    })
490}
491
492/// Names of RoleManager roles for each static Zircon priority in the fair scheduler.
493/// The index in the array is equal to the static priority.
494// LINT.IfChange
495const FAIR_PRIORITY_ROLE_NAMES: [&str; 32] = [
496    "fuchsia.starnix.fair.0",
497    "fuchsia.starnix.fair.1",
498    "fuchsia.starnix.fair.2",
499    "fuchsia.starnix.fair.3",
500    "fuchsia.starnix.fair.4",
501    "fuchsia.starnix.fair.5",
502    "fuchsia.starnix.fair.6",
503    "fuchsia.starnix.fair.7",
504    "fuchsia.starnix.fair.8",
505    "fuchsia.starnix.fair.9",
506    "fuchsia.starnix.fair.10",
507    "fuchsia.starnix.fair.11",
508    "fuchsia.starnix.fair.12",
509    "fuchsia.starnix.fair.13",
510    "fuchsia.starnix.fair.14",
511    "fuchsia.starnix.fair.15",
512    "fuchsia.starnix.fair.16",
513    "fuchsia.starnix.fair.17",
514    "fuchsia.starnix.fair.18",
515    "fuchsia.starnix.fair.19",
516    "fuchsia.starnix.fair.20",
517    "fuchsia.starnix.fair.21",
518    "fuchsia.starnix.fair.22",
519    "fuchsia.starnix.fair.23",
520    "fuchsia.starnix.fair.24",
521    "fuchsia.starnix.fair.25",
522    "fuchsia.starnix.fair.26",
523    "fuchsia.starnix.fair.27",
524    "fuchsia.starnix.fair.28",
525    "fuchsia.starnix.fair.29",
526    "fuchsia.starnix.fair.30",
527    "fuchsia.starnix.fair.31",
528];
529const REALTIME_ROLE_NAME: &str = "fuchsia.starnix.realtime";
530// LINT.ThenChange(src/starnix/config/starnix.profiles)
531
532#[cfg(test)]
533mod tests {
534    use super::*;
535    use assert_matches::assert_matches;
536
537    #[fuchsia::test]
538    fn default_role_name() {
539        assert_eq!(SchedulerState::default().role_name(), "fuchsia.starnix.fair.16");
540    }
541
542    #[fuchsia::test]
543    fn normal_with_non_default_nice_role_name() {
544        assert_eq!(
545            SchedulerState {
546                policy: SchedulingPolicy::Normal,
547                normal_priority: NormalPriority { value: 10 },
548                realtime_priority: RealtimePriority::NON_REAL_TIME,
549                reset_on_fork: false
550            }
551            .role_name(),
552            "fuchsia.starnix.fair.11"
553        );
554        assert_eq!(
555            SchedulerState {
556                policy: SchedulingPolicy::Normal,
557                normal_priority: NormalPriority { value: 27 },
558                realtime_priority: RealtimePriority::NON_REAL_TIME,
559                reset_on_fork: false
560            }
561            .role_name(),
562            "fuchsia.starnix.fair.19"
563        );
564    }
565
566    #[fuchsia::test]
567    fn fifo_role_name() {
568        assert_eq!(
569            SchedulerState {
570                policy: SchedulingPolicy::Fifo,
571                normal_priority: NormalPriority::default(),
572                realtime_priority: RealtimePriority { value: 1 },
573                reset_on_fork: false
574            }
575            .role_name(),
576            "fuchsia.starnix.realtime",
577        );
578        assert_eq!(
579            SchedulerState {
580                policy: SchedulingPolicy::Fifo,
581                normal_priority: NormalPriority::default(),
582                realtime_priority: RealtimePriority { value: 2 },
583                reset_on_fork: false
584            }
585            .role_name(),
586            "fuchsia.starnix.realtime",
587        );
588        assert_eq!(
589            SchedulerState {
590                policy: SchedulingPolicy::Fifo,
591                normal_priority: NormalPriority::default(),
592                realtime_priority: RealtimePriority { value: 99 },
593                reset_on_fork: false
594            }
595            .role_name(),
596            "fuchsia.starnix.realtime",
597        );
598    }
599
600    #[fuchsia::test]
601    fn idle_role_name() {
602        assert_eq!(
603            SchedulerState {
604                policy: SchedulingPolicy::Idle,
605                normal_priority: NormalPriority { value: 1 },
606                realtime_priority: RealtimePriority::NON_REAL_TIME,
607                reset_on_fork: false,
608            }
609            .role_name(),
610            "fuchsia.starnix.fair.0"
611        );
612        assert_eq!(
613            SchedulerState {
614                policy: SchedulingPolicy::Idle,
615                normal_priority: NormalPriority::default(),
616                realtime_priority: RealtimePriority::NON_REAL_TIME,
617                reset_on_fork: false,
618            }
619            .role_name(),
620            "fuchsia.starnix.fair.0"
621        );
622        assert_eq!(
623            SchedulerState {
624                policy: SchedulingPolicy::Idle,
625                normal_priority: NormalPriority { value: 40 },
626                realtime_priority: RealtimePriority::NON_REAL_TIME,
627                reset_on_fork: false,
628            }
629            .role_name(),
630            "fuchsia.starnix.fair.0"
631        );
632    }
633
634    #[fuchsia::test]
635    fn build_policy_from_binder() {
636        assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 0), Ok(_));
637        assert_matches!(
638            SchedulerState::from_binder(SCHED_NORMAL as u8, ((-21) as i8) as u8),
639            Err(_)
640        );
641        assert_matches!(
642            SchedulerState::from_binder(SCHED_NORMAL as u8, ((-20) as i8) as u8),
643            Ok(SchedulerState {
644                policy: SchedulingPolicy::Normal,
645                normal_priority: NormalPriority { value: 40 },
646                realtime_priority: RealtimePriority::NON_REAL_TIME,
647                reset_on_fork: false,
648            })
649        );
650        assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 1), Ok(_));
651        assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 19), Ok(_));
652        assert_matches!(SchedulerState::from_binder(SCHED_NORMAL as u8, 20), Err(_));
653        assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 0), Err(_));
654        assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 1), Ok(_));
655        assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 99), Ok(_));
656        assert_matches!(SchedulerState::from_binder(SCHED_FIFO as u8, 100), Err(_));
657        assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 0), Err(_));
658        assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 1), Ok(_));
659        assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 99), Ok(_));
660        assert_matches!(SchedulerState::from_binder(SCHED_RR as u8, 100), Err(_));
661        assert_matches!(SchedulerState::from_binder(SCHED_BATCH as u8, 11), Ok(_));
662        assert_eq!(SchedulerState::from_binder(SCHED_IDLE as u8, 11), error!(EINVAL));
663        assert_matches!(SchedulerState::from_binder(42, 0), Err(_));
664        assert_matches!(SchedulerState::from_binder(42, 0), Err(_));
665    }
666
667    // NOTE(https://fxbug.dev/425726327): some or all of this test may need to change based
668    // on what is learned in https://fxbug.dev/425726327.
669    #[fuchsia::test]
670    fn is_less_than_for_binder() {
671        let rr_50 = SchedulerState {
672            policy: SchedulingPolicy::RoundRobin,
673            normal_priority: NormalPriority { value: 1 },
674            realtime_priority: RealtimePriority { value: 50 },
675            reset_on_fork: false,
676        };
677        let rr_40 = SchedulerState {
678            policy: SchedulingPolicy::RoundRobin,
679            normal_priority: NormalPriority { value: 1 },
680            realtime_priority: RealtimePriority { value: 40 },
681            reset_on_fork: false,
682        };
683        let fifo_50 = SchedulerState {
684            policy: SchedulingPolicy::Fifo,
685            normal_priority: NormalPriority { value: 1 },
686            realtime_priority: RealtimePriority { value: 50 },
687            reset_on_fork: false,
688        };
689        let fifo_40 = SchedulerState {
690            policy: SchedulingPolicy::Fifo,
691            normal_priority: NormalPriority { value: 1 },
692            realtime_priority: RealtimePriority { value: 40 },
693            reset_on_fork: false,
694        };
695        let normal_40 = SchedulerState {
696            policy: SchedulingPolicy::Normal,
697            normal_priority: NormalPriority { value: 40 },
698            realtime_priority: RealtimePriority::NON_REAL_TIME,
699            reset_on_fork: true,
700        };
701        let normal_10 = SchedulerState {
702            policy: SchedulingPolicy::Normal,
703            normal_priority: NormalPriority { value: 10 },
704            realtime_priority: RealtimePriority::NON_REAL_TIME,
705            reset_on_fork: true,
706        };
707        let batch_40 = SchedulerState {
708            policy: SchedulingPolicy::Batch,
709            normal_priority: NormalPriority { value: 40 },
710            realtime_priority: RealtimePriority::NON_REAL_TIME,
711            reset_on_fork: true,
712        };
713        let batch_30 = SchedulerState {
714            policy: SchedulingPolicy::Batch,
715            normal_priority: NormalPriority { value: 30 },
716            realtime_priority: RealtimePriority::NON_REAL_TIME,
717            reset_on_fork: true,
718        };
719        let idle_40 = SchedulerState {
720            policy: SchedulingPolicy::Idle,
721            normal_priority: NormalPriority { value: 40 },
722            realtime_priority: RealtimePriority::NON_REAL_TIME,
723            reset_on_fork: true,
724        };
725        let idle_30 = SchedulerState {
726            policy: SchedulingPolicy::Idle,
727            normal_priority: NormalPriority { value: 30 },
728            realtime_priority: RealtimePriority::NON_REAL_TIME,
729            reset_on_fork: true,
730        };
731        assert!(!fifo_50.is_less_than_for_binder(fifo_50));
732        assert!(!rr_50.is_less_than_for_binder(rr_50));
733        assert!(!fifo_50.is_less_than_for_binder(rr_50));
734        assert!(!rr_50.is_less_than_for_binder(fifo_50));
735        assert!(!fifo_50.is_less_than_for_binder(rr_40));
736        assert!(rr_40.is_less_than_for_binder(fifo_50));
737        assert!(!rr_50.is_less_than_for_binder(fifo_40));
738        assert!(fifo_40.is_less_than_for_binder(rr_50));
739        assert!(!fifo_40.is_less_than_for_binder(normal_40));
740        assert!(normal_40.is_less_than_for_binder(fifo_40));
741        assert!(!rr_40.is_less_than_for_binder(normal_40));
742        assert!(normal_40.is_less_than_for_binder(rr_40));
743        assert!(!normal_40.is_less_than_for_binder(normal_40));
744        assert!(!normal_40.is_less_than_for_binder(normal_10));
745        assert!(normal_10.is_less_than_for_binder(normal_40));
746        assert!(!normal_10.is_less_than_for_binder(batch_40));
747        assert!(batch_40.is_less_than_for_binder(normal_10));
748        assert!(!batch_40.is_less_than_for_binder(batch_40));
749        assert!(!batch_40.is_less_than_for_binder(batch_30));
750        assert!(batch_30.is_less_than_for_binder(batch_40));
751        assert!(!batch_30.is_less_than_for_binder(idle_40));
752        assert!(idle_40.is_less_than_for_binder(batch_30));
753        assert!(!idle_40.is_less_than_for_binder(idle_40));
754        assert!(!idle_40.is_less_than_for_binder(idle_30));
755        assert!(!idle_30.is_less_than_for_binder(idle_40));
756    }
757}