From c6cc59e8b42b2331c2d25d4ae33a9c54d973f29f Mon Sep 17 00:00:00 2001 From: David Vernet Date: Wed, 15 May 2024 13:31:07 -0500 Subject: [PATCH 1/4] uei: Expose exit code enums from user_exit_info.rs Schedulers and the kernel can include an exit code when exiting a scheduler. There are some built-in codes that can be specified: SCX_ECODE_RSN_HOTPLUG, and SCX_ECODE_ACT_RESTART. Some schedulers may want to check the exit code against these values, so let's export them from user_exit_info.rs. We use lazy_static so that we can read the values for the enum for the currently-running kernel. Signed-off-by: David Vernet --- rust/scx_utils/src/lib.rs | 2 ++ rust/scx_utils/src/user_exit_info.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/rust/scx_utils/src/lib.rs b/rust/scx_utils/src/lib.rs index 239483b..cce0ffa 100644 --- a/rust/scx_utils/src/lib.rs +++ b/rust/scx_utils/src/lib.rs @@ -44,6 +44,8 @@ pub use builder::Builder; mod user_exit_info; pub use user_exit_info::ScxExitKind; pub use user_exit_info::ScxConsts; +pub use user_exit_info::SCX_ECODE_RSN_HOTPLUG; +pub use user_exit_info::SCX_ECODE_ACT_RESTART; pub use user_exit_info::UeiDumpPtr; pub use user_exit_info::UserExitInfo; pub use user_exit_info::UEI_DUMP_PTR_MUTEX; diff --git a/rust/scx_utils/src/user_exit_info.rs b/rust/scx_utils/src/user_exit_info.rs index ee36e98..8faf63f 100644 --- a/rust/scx_utils/src/user_exit_info.rs +++ b/rust/scx_utils/src/user_exit_info.rs @@ -3,6 +3,7 @@ // This software may be used and distributed according to the terms of the // GNU General Public License version 2. use crate::bindings; +use crate::compat; use anyhow::bail; use anyhow::Result; use std::ffi::CStr; @@ -18,6 +19,16 @@ pub static UEI_DUMP_PTR_MUTEX: Mutex = Mutex::new(UeiDumpPtr { ptr: std::ptr::null(), }); +lazy_static::lazy_static! { + pub static ref SCX_ECODE_RSN_HOTPLUG: u64 = + compat::read_enum("scx_exit_code", "SCX_ECODE_RSN_HOTPLUG").unwrap_or(0); +} + +lazy_static::lazy_static! { + pub static ref SCX_ECODE_ACT_RESTART: u64 = + compat::read_enum("scx_exit_code", "SCX_ECODE_ACT_RESTART").unwrap_or(0); +} + pub enum ScxExitKind { None = bindings::scx_exit_kind_SCX_EXIT_NONE as isize, Done = bindings::scx_exit_kind_SCX_EXIT_DONE as isize, From 34818de54d6721f466b8eb2aad40af7d7d5bc5d1 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Wed, 15 May 2024 13:33:53 -0500 Subject: [PATCH 2/4] rusty: Use built-in exit code for restarting Now that the kernel exports the SCX_ECODE_ACT_RESTART exit code, we can remove the custom hotplug logic from scx_rusty, and instead rely on the built-in logic from the kernel. There's still a corner case that we're not honoring: when a hotplug event happens on the init path. A future change will address this as well. Signed-off-by: David Vernet --- rust/scx_utils/src/user_exit_info.rs | 5 +++-- scheds/rust/scx_rusty/src/bpf/intf.h | 4 ---- scheds/rust/scx_rusty/src/bpf/main.bpf.c | 12 ------------ scheds/rust/scx_rusty/src/main.rs | 3 ++- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/rust/scx_utils/src/user_exit_info.rs b/rust/scx_utils/src/user_exit_info.rs index 8faf63f..740d7bb 100644 --- a/rust/scx_utils/src/user_exit_info.rs +++ b/rust/scx_utils/src/user_exit_info.rs @@ -34,6 +34,7 @@ pub enum ScxExitKind { Done = bindings::scx_exit_kind_SCX_EXIT_DONE as isize, Unreg = bindings::scx_exit_kind_SCX_EXIT_UNREG as isize, UnregBPF = bindings::scx_exit_kind_SCX_EXIT_UNREG_BPF as isize, + UnregKern = bindings::scx_exit_kind_SCX_EXIT_UNREG_KERN as isize, SysRq = bindings::scx_exit_kind_SCX_EXIT_SYSRQ as isize, Error = bindings::scx_exit_kind_SCX_EXIT_ERROR as isize, ErrorBPF = bindings::scx_exit_kind_SCX_EXIT_ERROR_BPF as isize, @@ -205,7 +206,7 @@ impl UserExitInfo { _ => "".into(), }; - if self.kind <= ScxExitKind::UnregBPF as i32 { + if self.kind <= ScxExitKind::UnregKern as i32 { eprintln!("{}", why); Ok(()) } else { @@ -217,7 +218,7 @@ impl UserExitInfo { /// only applies when the BPF scheduler exits with scx_bpf_exit(), i.e. kind /// ScxExitKind::UnregBPF. pub fn exit_code(&self) -> Option { - if self.kind == ScxExitKind::UnregBPF as i32 { + if self.kind == ScxExitKind::UnregBPF as i32 || self.kind == ScxExitKind::UnregKern as i32 { Some(self.exit_code) } else { None diff --git a/scheds/rust/scx_rusty/src/bpf/intf.h b/scheds/rust/scx_rusty/src/bpf/intf.h index 01bb052..5f5ae23 100644 --- a/scheds/rust/scx_rusty/src/bpf/intf.h +++ b/scheds/rust/scx_rusty/src/bpf/intf.h @@ -61,10 +61,6 @@ enum consts { MAX_DOM_ACTIVE_PIDS = 1024, }; -enum rusty_exit_codes { - RUSTY_EXIT_HOTPLUG, -}; - /* Statistics */ enum stat_idx { /* The following fields add up to all dispatched tasks */ diff --git a/scheds/rust/scx_rusty/src/bpf/main.bpf.c b/scheds/rust/scx_rusty/src/bpf/main.bpf.c index 1a72e88..27c1a91 100644 --- a/scheds/rust/scx_rusty/src/bpf/main.bpf.c +++ b/scheds/rust/scx_rusty/src/bpf/main.bpf.c @@ -1743,16 +1743,6 @@ static s32 initialize_cpu(s32 cpu) return 0; } -void BPF_STRUCT_OPS(rusty_cpu_online, s32 cpu) -{ - __COMPAT_scx_bpf_exit(RUSTY_EXIT_HOTPLUG, "CPU %d went online", cpu); -} - -void BPF_STRUCT_OPS(rusty_cpu_offline, s32 cpu) -{ - __COMPAT_scx_bpf_exit(RUSTY_EXIT_HOTPLUG, "CPU %d went offline", cpu); -} - s32 BPF_STRUCT_OPS_SLEEPABLE(rusty_init) { s32 i, ret; @@ -1812,8 +1802,6 @@ SCX_OPS_DEFINE(rusty, .set_cpumask = (void *)rusty_set_cpumask, .init_task = (void *)rusty_init_task, .exit_task = (void *)rusty_exit_task, - .cpu_online = (void *)rusty_cpu_online, - .cpu_offline = (void *)rusty_cpu_offline, .init = (void *)rusty_init, .exit = (void *)rusty_exit, .timeout_ms = 10000, diff --git a/scheds/rust/scx_rusty/src/main.rs b/scheds/rust/scx_rusty/src/main.rs index 6ef17b2..1bf0c4c 100644 --- a/scheds/rust/scx_rusty/src/main.rs +++ b/scheds/rust/scx_rusty/src/main.rs @@ -40,6 +40,7 @@ use scx_utils::scx_ops_attach; use scx_utils::scx_ops_load; use scx_utils::uei_exited; use scx_utils::uei_read; +use scx_utils::SCX_ECODE_ACT_RESTART; use scx_utils::Cpumask; use scx_utils::Topology; use scx_utils::UserExitInfo; @@ -620,7 +621,7 @@ fn main() -> Result<()> { let uei = sched.run(shutdown.clone())?; if let Some(exit_code) = uei.exit_code() { - if exit_code == bpf_intf::rusty_exit_codes_RUSTY_EXIT_HOTPLUG as i64 { + if (exit_code & *SCX_ECODE_ACT_RESTART as i64) != 0 { continue; } } From 4edd8d75e61eb9321ed0540c15eb2bea2fa2233b Mon Sep 17 00:00:00 2001 From: David Vernet Date: Wed, 15 May 2024 15:18:03 -0500 Subject: [PATCH 3/4] compat: Add scx_ops_open!() macro In order to make it easy for schedulers to use the hotplug_seq feature that's available in recent kernels, we'll need to provide a macro wrapper so that we can support the feature with backwards compatibility. This adds scx_ops_open!() to abstract that. Any scheduler that uses scx_ops_open()! will be exited if a hotplug event happens between opening the skeleton, and loading it. Signed-off-by: David Vernet --- rust/scx_utils/src/compat.rs | 44 +++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/rust/scx_utils/src/compat.rs b/rust/scx_utils/src/compat.rs index 8627579..2ca2a8f 100644 --- a/rust/scx_utils/src/compat.rs +++ b/rust/scx_utils/src/compat.rs @@ -151,9 +151,47 @@ pub fn is_sched_ext_enabled() -> io::Result { } } -/// struct sched_ext_ops can change over time. If -/// compat.bpf.h::SCX_OPS_DEFINE() is used to define ops and scx_ops_load!() -/// and scx_ops_attach!() are used to load and attach it, backward +/// struct sched_ext_ops can change over time. If compat.bpf.h::SCX_OPS_DEFINE() +/// is used to define ops, and scx_ops_open!(), scx_ops_load!(), and +/// scx_ops_attach!() are used to open, load and attach it, backward +/// compatibility is automatically maintained where reasonable. +/// +/// - sched_ext_ops.hotplug_seq was added later. On kernels which support it, +/// set the value to a nonzero value to trigger an exit in the scheduler when +/// a hotplug event occurs between opening and attaching the scheduler. +#[macro_export] +macro_rules! scx_ops_open { + ($builder: expr, $ops: ident) => {{ + scx_utils::paste! { + let mut skel = $builder.open().context("Failed to open BPF program")?; + let ops = skel.struct_ops.[<$ops _mut>](); + let has_field = scx_utils::compat::struct_has_field("sched_ext_ops", "hotplug_seq")?; + if has_field { + let path = std::path::Path::new("/sys/kernel/sched_ext/hotplug_seq"); + let val = match std::fs::read_to_string(&path) { + Ok(val) => val, + Err(_) => { + return Err(anyhow::anyhow!("Failed to open or read file {:?}", path)); + } + }; + + ops.hotplug_seq = match val.trim().parse::() { + Ok(parsed) => parsed, + Err(_) => { + return Err(anyhow::anyhow!("Failed to parse hotplug seq {}", val)); + } + }; + } + + let result : Result, anyhow::Error> = Ok(skel); + result + } + }}; +} + +/// struct sched_ext_ops can change over time. If compat.bpf.h::SCX_OPS_DEFINE() +/// is used to define ops, and scx_ops_open!(), scx_ops_load!(), and +/// scx_ops_attach!() are used to open, load and attach it, backward /// compatibility is automatically maintained where reasonable. /// /// - sched_ext_ops.exit_dump_len was added later. On kernels which don't From 27d2490b1e5a8f3386cc9cb0730454503a0ccc23 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Wed, 15 May 2024 16:07:14 -0500 Subject: [PATCH 4/4] rusty: Use scx_ops_open!() in scx_rusty Now that the scx_ops_open!() macro is available, let's use it in scx_rusty to cover all cases of when hotplug can happen. Signed-off-by: David Vernet --- scheds/rust/scx_rusty/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scheds/rust/scx_rusty/src/main.rs b/scheds/rust/scx_rusty/src/main.rs index 1bf0c4c..7a88dc4 100644 --- a/scheds/rust/scx_rusty/src/main.rs +++ b/scheds/rust/scx_rusty/src/main.rs @@ -38,6 +38,7 @@ use scx_utils::compat; use scx_utils::init_libbpf_logging; use scx_utils::scx_ops_attach; use scx_utils::scx_ops_load; +use scx_utils::scx_ops_open; use scx_utils::uei_exited; use scx_utils::uei_read; use scx_utils::SCX_ECODE_ACT_RESTART; @@ -235,7 +236,7 @@ impl<'a> Scheduler<'a> { let mut skel_builder = BpfSkelBuilder::default(); skel_builder.obj_builder.debug(opts.verbose > 0); init_libbpf_logging(None); - let mut skel = skel_builder.open().context("Failed to open BPF program")?; + let mut skel = scx_ops_open!(skel_builder, rusty).unwrap(); // Initialize skel according to @opts. let top = Arc::new(Topology::new()?);