-
Notifications
You must be signed in to change notification settings - Fork 25
feat: Add CPU family/model profile detection for intel-lpmd support #233
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| [intel-lpmd] | ||
| desc = 'Intel Low Power Mode Daemon for supported hybrid Intel CPUs' | ||
| class_ids = "0600" | ||
| vendor_ids = "8086" | ||
| device_ids = "*" | ||
| cpu_family = "6" | ||
| cpu_models = "151 154 183 186 191 170 172 189 204" | ||
| priority = 5 | ||
| packages = 'intel-lpmd' | ||
| post_install = """ | ||
| systemctl enable --now intel_lpmd.service | ||
| """ | ||
| post_remove = """ | ||
| systemctl disable intel_lpmd.service | ||
| """ |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -46,20 +46,40 @@ pub struct Profile { | |||||||||||||
| pub device_name_pattern: Option<String>, | ||||||||||||||
| pub hwd_product_name_pattern: Option<String>, | ||||||||||||||
| pub gc_versions: Option<Vec<String>>, | ||||||||||||||
| pub cpu_family: Option<String>, | ||||||||||||||
| pub cpu_models: Option<Vec<String>>, | ||||||||||||||
|
|
||||||||||||||
| pub hwd_ids: Vec<HardwareID>, | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| impl Default for Profile { | ||||||||||||||
| fn default() -> Self { | ||||||||||||||
| Self::new() | ||||||||||||||
| Self { | ||||||||||||||
| is_ai_sdk: false, | ||||||||||||||
| prof_path: String::new(), | ||||||||||||||
| name: String::new(), | ||||||||||||||
| desc: String::new(), | ||||||||||||||
| priority: 0, | ||||||||||||||
| packages: String::new(), | ||||||||||||||
| post_install: String::new(), | ||||||||||||||
| post_remove: String::new(), | ||||||||||||||
| pre_install: String::new(), | ||||||||||||||
| pre_remove: String::new(), | ||||||||||||||
| conditional_packages: String::new(), | ||||||||||||||
| device_name_pattern: None, | ||||||||||||||
| hwd_product_name_pattern: None, | ||||||||||||||
| gc_versions: None, | ||||||||||||||
| cpu_family: None, | ||||||||||||||
| cpu_models: None, | ||||||||||||||
| hwd_ids: vec![Default::default()], | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| impl Profile { | ||||||||||||||
| #[must_use] | ||||||||||||||
| pub fn new() -> Self { | ||||||||||||||
| Self { hwd_ids: vec![Default::default()], ..Default::default() } | ||||||||||||||
| Self::default() | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -224,13 +244,18 @@ fn parse_profile(node: &toml::Table, profile_name: &str) -> Result<Profile> { | |||||||||||||
| hwd_product_name_pattern: node | ||||||||||||||
| .get("hwd_product_name_pattern") | ||||||||||||||
| .and_then(|x| x.as_str().map(str::to_string)), | ||||||||||||||
| gc_versions: node.get("gc_versions").and_then(|x| { | ||||||||||||||
| x.as_str() | ||||||||||||||
| .map(str::split_ascii_whitespace) | ||||||||||||||
| .map(|x| x.map(str::to_string).collect::<Vec<_>>()) | ||||||||||||||
| }), | ||||||||||||||
| gc_versions: parse_whitespace_list(node, "gc_versions"), | ||||||||||||||
| cpu_family: node.get("cpu_family").and_then(|x| x.as_str().map(str::to_string)), | ||||||||||||||
| cpu_models: parse_whitespace_list(node, "cpu_models"), | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| if profile.cpu_models.is_some() && profile.cpu_family.is_none() { | ||||||||||||||
| let msg = | ||||||||||||||
| format!("profile '{profile_name}' specifies cpu_models without cpu_family"); | ||||||||||||||
| eprintln!("Warning: skipping profile '{profile_name}': {msg}"); | ||||||||||||||
| anyhow::bail!(msg); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| let conf_devids = node.get("device_ids").and_then(|x| x.as_str()).unwrap_or(""); | ||||||||||||||
| let conf_vendorids = node.get("vendor_ids").and_then(|x| x.as_str()).unwrap_or(""); | ||||||||||||||
| let conf_classids = node.get("class_ids").and_then(|x| x.as_str()).unwrap_or(""); | ||||||||||||||
|
|
@@ -302,6 +327,10 @@ fn parse_ids_file(file_path: &str) -> Result<String> { | |||||||||||||
| Ok(parsed_ids.split_ascii_whitespace().collect::<Vec<_>>().join(" ")) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| fn parse_whitespace_list(node: &toml::Table, key: &str) -> Option<Vec<String>> { | ||||||||||||||
| node.get(key)?.as_str().map(|s| s.split_ascii_whitespace().map(str::to_string).collect()) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| fn merge_table_left(lhs: &mut toml::Table, rhs: &toml::Table) { | ||||||||||||||
| for (rhs_key, rhs_val) in rhs { | ||||||||||||||
| // rhs key not found in lhs - direct move | ||||||||||||||
|
|
@@ -414,7 +443,13 @@ fn profile_into_toml(profile: &Profile) -> toml::Table { | |||||||||||||
| table.insert("hwd_product_name_pattern".to_owned(), product_name_pattern.clone().into()); | ||||||||||||||
| } | ||||||||||||||
| if let Some(gc_versions) = &profile.gc_versions { | ||||||||||||||
| table.insert("gc_versions".to_owned(), gc_versions.clone().into()); | ||||||||||||||
| table.insert("gc_versions".to_owned(), gc_versions.join(" ").into()); | ||||||||||||||
| } | ||||||||||||||
| if let Some(cpu_family) = &profile.cpu_family { | ||||||||||||||
| table.insert("cpu_family".to_owned(), cpu_family.clone().into()); | ||||||||||||||
| } | ||||||||||||||
| if let Some(cpu_models) = &profile.cpu_models { | ||||||||||||||
| table.insert("cpu_models".to_owned(), cpu_models.join(" ").into()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| let last_hwd_id = profile.hwd_ids.last().unwrap(); | ||||||||||||||
|
|
@@ -686,4 +721,46 @@ mod tests { | |||||||||||||
|
|
||||||||||||||
| assert_eq!(orig_content, expected_output); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| #[test] | ||||||||||||||
| fn cpu_profile_parse_test() { | ||||||||||||||
| let prof_path = "tests/profiles/cpu-profile-test.toml"; | ||||||||||||||
| let parsed_profiles = parse_profiles(prof_path); | ||||||||||||||
| assert!(parsed_profiles.is_ok()); | ||||||||||||||
| let parsed_profiles = parsed_profiles.unwrap(); | ||||||||||||||
|
|
||||||||||||||
| assert_eq!(parsed_profiles.len(), 1); | ||||||||||||||
| assert_eq!(parsed_profiles[0].name, "intel-lpmd"); | ||||||||||||||
| assert_eq!( | ||||||||||||||
| parsed_profiles[0].desc, | ||||||||||||||
| "Intel Low Power Mode Daemon for supported hybrid Intel CPUs" | ||||||||||||||
| ); | ||||||||||||||
| assert_eq!(parsed_profiles[0].priority, 5); | ||||||||||||||
| assert_eq!(parsed_profiles[0].packages, "intel-lpmd"); | ||||||||||||||
| assert_eq!(parsed_profiles[0].cpu_family, Some("6".to_owned())); | ||||||||||||||
| assert_eq!( | ||||||||||||||
| parsed_profiles[0].cpu_models, | ||||||||||||||
| Some(vec![ | ||||||||||||||
| "151".to_owned(), | ||||||||||||||
| "154".to_owned(), | ||||||||||||||
| "183".to_owned(), | ||||||||||||||
| "186".to_owned(), | ||||||||||||||
| "191".to_owned(), | ||||||||||||||
| "170".to_owned(), | ||||||||||||||
| "172".to_owned(), | ||||||||||||||
| "189".to_owned(), | ||||||||||||||
| "204".to_owned(), | ||||||||||||||
| ]) | ||||||||||||||
| ); | ||||||||||||||
| assert!(!parsed_profiles[0].post_install.is_empty()); | ||||||||||||||
| assert!(!parsed_profiles[0].post_remove.is_empty()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| #[test] | ||||||||||||||
| fn cpu_models_without_family_is_rejected() { | ||||||||||||||
| let prof_path = "tests/profiles/cpu-models-no-family-test.toml"; | ||||||||||||||
| let parsed_profiles = parse_profiles(prof_path); | ||||||||||||||
| // Should fail because cpu_models is set without cpu_family | ||||||||||||||
| assert!(parsed_profiles.is_err() || parsed_profiles.unwrap().is_empty()); | ||||||||||||||
|
Comment on lines
+763
to
+764
|
||||||||||||||
| // Should fail because cpu_models is set without cpu_family | |
| assert!(parsed_profiles.is_err() || parsed_profiles.unwrap().is_empty()); | |
| // Should be rejected: no valid profiles must be returned | |
| assert!(parsed_profiles.is_ok()); | |
| let parsed_profiles = parsed_profiles.unwrap(); | |
| assert!(parsed_profiles.is_empty()); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| [bad-profile] | ||
| desc = 'Invalid profile with cpu_models but no cpu_family' | ||
| class_ids = "0600" | ||
| vendor_ids = "8086" | ||
| device_ids = "*" | ||
| cpu_models = "151 154 183" | ||
| priority = 5 | ||
| packages = 'test-pkg' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| [intel-lpmd] | ||
| desc = 'Intel Low Power Mode Daemon for supported hybrid Intel CPUs' | ||
| class_ids = "0600" | ||
| vendor_ids = "8086" | ||
| device_ids = "*" | ||
| cpu_family = "6" | ||
| cpu_models = "151 154 183 186 191 170 172 189 204" | ||
| priority = 5 | ||
| packages = 'intel-lpmd' | ||
| post_install = """ | ||
| systemctl enable --now intel_lpmd.service | ||
| """ | ||
| post_remove = """ | ||
| systemctl disable intel_lpmd.service | ||
| """ |
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.
cpu_family/cpu_modelsare parsed only from TOML strings (as_str). If a user writescpu_models = [151, 154]orcpu_family = 6(valid TOML), the filter will be silently ignored or downgraded (e.g., model restriction dropped), which can cause unintended profile matches. Consider either supporting integer/array forms here or explicitly erroring when the key exists but is not a string, so misconfigurations fail fast.