-
Notifications
You must be signed in to change notification settings - Fork 235
WIP: Kernel accounting of task CPU usage #2592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| //! Precise time | ||
| //! | ||
| //! Currently only used for profiling | ||
|
|
||
| use core::sync::atomic::{AtomicPtr, Ordering}; | ||
|
|
||
| #[derive(Debug, Clone, Copy)] | ||
| pub struct Instant(pub u64); | ||
| #[derive(Debug, Clone, Copy)] | ||
| pub struct Duration(pub u64); | ||
|
|
||
| impl Duration { | ||
| pub const ZERO: Self = Self(0); | ||
| } | ||
|
|
||
| pub type NowFunc = fn() -> Instant; | ||
| // TODO: do we want this to return an Instant? It's probably a bit cheaper | ||
| // than calling timekeep() -> now(), but I'm not sure if we *need* it for | ||
| // anything, as systick doesn't do anything with it. | ||
| pub type TimeKeepFunc = fn(); | ||
| pub type TickRateFunc = fn() -> u32; | ||
|
|
||
| pub struct PTimeVTable { | ||
| pub now: NowFunc, | ||
| pub timekeep: TimeKeepFunc, | ||
| pub tickrate: TickRateFunc, | ||
| } | ||
|
|
||
| // hate this | ||
| #[unsafe(no_mangle)] | ||
| pub(crate) static mut PTIME_LAST_SWITCH: Instant = Instant(0); | ||
|
|
||
| static PTIME_VTABLE: AtomicPtr<PTimeVTable> = | ||
| AtomicPtr::new(core::ptr::null_mut()); | ||
|
|
||
| pub fn set_ptime_vtable(vtable: &'static PTimeVTable) { | ||
| let vtable: *const PTimeVTable = vtable; | ||
| let vtable: *mut PTimeVTable = vtable.cast_mut(); | ||
| PTIME_VTABLE.store(vtable, Ordering::Release) | ||
| } | ||
|
|
||
| pub fn ptimer() -> Option<&'static PTimeVTable> { | ||
| let vtable: *mut PTimeVTable = PTIME_VTABLE.load(Ordering::Acquire); | ||
| let vtable: *const PTimeVTable = vtable.cast_const(); | ||
| unsafe { vtable.as_ref() } | ||
|
Comment on lines
+47
to
+49
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Had to go re-read the Rust doc to refresh myself on this but it looks great :) Also could use // SAFETY comment |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,8 +51,9 @@ pub unsafe fn start_kernel(tick_divisor: u32) -> ! { | |
| let task_descs = &HUBRIS_TASK_DESCS; | ||
| // Safety: this reference will remain unique so long as the "only called | ||
| // once per boot" contract on this function is upheld. | ||
| let task_table = | ||
| unsafe { &mut *core::ptr::addr_of_mut!(HUBRIS_TASK_TABLE_SPACE) }; | ||
| let task_table = unsafe { | ||
| core::ptr::addr_of_mut!(HUBRIS_TASK_TABLE_SPACE).as_mut_unchecked() | ||
| }; | ||
|
|
||
| // Initialize our RAM data structures. | ||
|
|
||
|
|
@@ -64,21 +65,22 @@ pub unsafe fn start_kernel(tick_divisor: u32) -> ! { | |
| // states. | ||
|
|
||
| // Now, generate the task table. | ||
| // Safety: MaybeUninit<[T]> -> [MaybeUninit<T>] is defined as safe. | ||
| let task_table: &mut [MaybeUninit<Task>; HUBRIS_TASK_COUNT] = | ||
| unsafe { &mut *(task_table as *mut _ as *mut _) }; | ||
| for (i, task) in task_table.iter_mut().enumerate() { | ||
| task.write(Task::from_descriptor(&task_descs[i])); | ||
| } | ||
| task_table.as_mut(); | ||
|
|
||
| task_table | ||
| .iter_mut() | ||
| .zip(task_descs.iter()) | ||
| .for_each(|(t, d)| { | ||
| let task = t.write(Task::from_descriptor(d)); | ||
|
|
||
| // With init done, set up initial register state etc. | ||
| crate::arch::reinitialize(task); | ||
|
Comment on lines
+77
to
+78
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The was originally written to be very very explicit about when were were dealing with init vs uninit code. I think that deserves a bigger comment/explanation |
||
| }); | ||
|
|
||
| // Safety: we have fully initialized this and can shed the uninit part. | ||
| let task_table: &mut [Task; HUBRIS_TASK_COUNT] = | ||
| unsafe { &mut *(task_table as *mut _ as *mut _) }; | ||
|
|
||
| // With that done, set up initial register state etc. | ||
| for task in task_table.iter_mut() { | ||
| crate::arch::reinitialize(task); | ||
| } | ||
| unsafe { core::mem::transmute(task_table) }; | ||
|
|
||
| // Great! Pick our first task. We'll act like we're scheduling after the | ||
| // last task, which will cause a scan from 0 on. | ||
|
|
@@ -99,22 +101,14 @@ pub(crate) fn with_task_table<R>(body: impl FnOnce(&mut [Task]) -> R) -> R { | |
| } | ||
| let task_table: *mut MaybeUninit<[Task; HUBRIS_TASK_COUNT]> = | ||
| core::ptr::addr_of_mut!(HUBRIS_TASK_TABLE_SPACE); | ||
| // Pointer cast valid as MaybeUninit<[T; N]> and [MaybeUninit<T>; N] have | ||
| // same in-memory representation. At the time of this writing | ||
| // MaybeUninit::transpose is not yet stable. | ||
| let task_table: *mut [MaybeUninit<Task>; HUBRIS_TASK_COUNT] = | ||
| task_table as _; | ||
| // This pointer cast is doing the equivalent of | ||
| // MaybeUninit::array_assume_init, which at the time of this writing is not | ||
| // stable. | ||
| let task_table: *mut [Task; HUBRIS_TASK_COUNT] = task_table as _; | ||
|
|
||
| // Safety: we have observed `TASK_TABLE_IN_USE` being false, which means the | ||
| // task table is initialized (note that at reset it starts out true) and | ||
| // that we're not already within a call to with_task_table. Thus, we can | ||
| // produce a reference to the task table without aliasing, and we can be | ||
| // confident that the memory it's pointing to is initialized and shed the | ||
| // MaybeUninit. | ||
| let task_table = unsafe { &mut *task_table }; | ||
| let task_table = unsafe { task_table.as_mut_unchecked().assume_init_mut() }; | ||
|
|
||
| let r = body(task_table); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unfortunate to introduce a dep on
kernfor something indrvsince we've been able to have that dep be limited to the core app so far.