* [RFC PATCH 1/6] ACPI: CPPC: Move struct cppc_cpudata to cppc_cpufreq driver
2024-08-15 8:29 [RFC PATCH 0/6] rust: cpufreq: Add cppc_cpufreq driver implementation Pierre Gondois
@ 2024-08-15 8:29 ` Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 2/6] cpufreq: cppc: Remove perf_fb_ctrs field from struct cppc_cpudata Pierre Gondois
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Pierre Gondois @ 2024-08-15 8:29 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Gondois, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
FUJITA Tomonori, Mika Westerberg, Manos Pitsidianakis,
Thomas Bertschinger, Danilo Krummrich, linux-acpi, linux-pm,
acpica-devel, rust-for-linux
The `struct cppc_cpudata` is populated by the cppc_cpufreq driver.
Outside of the driver's code, it is only used acpi_get_psd_map().
To facilitate a re-implementation of the cppc_cpufreq driver
and its internal data structure in rust:
- Move the structure definition to the driver's code
- Udate acpi_get_psd_map() to take individual parameters instead
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
drivers/acpi/cppc_acpi.c | 26 ++++++++++++++++----------
drivers/cpufreq/cppc_cpufreq.c | 13 ++++++++++++-
include/acpi/cppc_acpi.h | 13 ++-----------
3 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 1d857978f5f4..b0fd141acb9d 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -468,17 +468,23 @@ EXPORT_SYMBOL_GPL(cppc_allow_fast_switch);
/**
* acpi_get_psd_map - Map the CPUs in the freq domain of a given cpu
* @cpu: Find all CPUs that share a domain with cpu.
- * @cpu_data: Pointer to CPU specific CPPC data including PSD info.
+ * @shared_cpu_map: cpumask to populate with CPUs belonging to the same _PSD
+ * domain.
+ * @shared_type: P-state coordination type for CPUs in the same _PSD as @cpu.
*
* Return: 0 for success or negative value for err.
*/
-int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data)
+int acpi_get_psd_map(unsigned int cpu, cpumask_var_t shared_cpu_map,
+ unsigned int *shared_type)
{
struct cpc_desc *cpc_ptr, *match_cpc_ptr;
struct acpi_psd_package *match_pdomain;
struct acpi_psd_package *pdomain;
int count_target, i;
+ if (!shared_cpu_map || !shared_type)
+ return -EINVAL;
+
/*
* Now that we have _PSD data from all CPUs, let's setup P-state
* domain info.
@@ -488,18 +494,18 @@ int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data)
return -EFAULT;
pdomain = &(cpc_ptr->domain_info);
- cpumask_set_cpu(cpu, cpu_data->shared_cpu_map);
+ cpumask_set_cpu(cpu, shared_cpu_map);
if (pdomain->num_processors <= 1)
return 0;
/* Validate the Domain info */
count_target = pdomain->num_processors;
if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
- cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+ *shared_type = CPUFREQ_SHARED_TYPE_ALL;
else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
- cpu_data->shared_type = CPUFREQ_SHARED_TYPE_HW;
+ *shared_type = CPUFREQ_SHARED_TYPE_HW;
else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
- cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+ *shared_type = CPUFREQ_SHARED_TYPE_ANY;
for_each_possible_cpu(i) {
if (i == cpu)
@@ -520,16 +526,16 @@ int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data)
if (pdomain->coord_type != match_pdomain->coord_type)
goto err_fault;
- cpumask_set_cpu(i, cpu_data->shared_cpu_map);
+ cpumask_set_cpu(i, shared_cpu_map);
}
return 0;
err_fault:
/* Assume no coordination on any error parsing domain info */
- cpumask_clear(cpu_data->shared_cpu_map);
- cpumask_set_cpu(cpu, cpu_data->shared_cpu_map);
- cpu_data->shared_type = CPUFREQ_SHARED_TYPE_NONE;
+ cpumask_clear(shared_cpu_map);
+ cpumask_set_cpu(cpu, shared_cpu_map);
+ *shared_type = CPUFREQ_SHARED_TYPE_NONE;
return -EFAULT;
}
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 32780bb4e911..208d7b176fd0 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -26,6 +26,16 @@
#include <acpi/cppc_acpi.h>
+/* Per CPU container for runtime CPPC management. */
+struct cppc_cpudata {
+ struct list_head node;
+ struct cppc_perf_caps perf_caps;
+ struct cppc_perf_ctrls perf_ctrls;
+ struct cppc_perf_fb_ctrs perf_fb_ctrs;
+ unsigned int shared_type;
+ cpumask_var_t shared_cpu_map;
+};
+
/*
* This list contains information parsed from per CPU ACPI _CPC and _PSD
* structures: e.g. the highest and lowest supported performance, capabilities,
@@ -572,7 +582,8 @@ static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
if (!zalloc_cpumask_var(&cpu_data->shared_cpu_map, GFP_KERNEL))
goto free_cpu;
- ret = acpi_get_psd_map(cpu, cpu_data);
+ ret = acpi_get_psd_map(cpu, cpu_data->shared_cpu_map,
+ &cpu_data->shared_type);
if (ret) {
pr_debug("Err parsing CPU%d PSD data: ret:%d\n", cpu, ret);
goto free_mask;
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 930b6afba6f4..5164aaca7eb0 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -126,16 +126,6 @@ struct cppc_perf_fb_ctrs {
u64 wraparound_time;
};
-/* Per CPU container for runtime CPPC management. */
-struct cppc_cpudata {
- struct list_head node;
- struct cppc_perf_caps perf_caps;
- struct cppc_perf_ctrls perf_ctrls;
- struct cppc_perf_fb_ctrs perf_fb_ctrs;
- unsigned int shared_type;
- cpumask_var_t shared_cpu_map;
-};
-
#ifdef CONFIG_ACPI_CPPC_LIB
extern int cppc_get_desired_perf(int cpunum, u64 *desired_perf);
extern int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf);
@@ -149,7 +139,8 @@ extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int p
extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq);
extern bool acpi_cpc_valid(void);
extern bool cppc_allow_fast_switch(void);
-extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data);
+extern int acpi_get_psd_map(unsigned int cpu, cpumask_var_t shared_cpu_map,
+ unsigned int *shared_type);
extern unsigned int cppc_get_transition_latency(int cpu);
extern bool cpc_ffh_supported(void);
extern bool cpc_supported_by_cpu(void);
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 2/6] cpufreq: cppc: Remove perf_fb_ctrs field from struct cppc_cpudata
2024-08-15 8:29 [RFC PATCH 0/6] rust: cpufreq: Add cppc_cpufreq driver implementation Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 1/6] ACPI: CPPC: Move struct cppc_cpudata to cppc_cpufreq driver Pierre Gondois
@ 2024-08-15 8:29 ` Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 3/6] rust: module: Allow modules to specify initcall section Pierre Gondois
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Pierre Gondois @ 2024-08-15 8:29 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Gondois, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
FUJITA Tomonori, Asahi Lina, Mika Westerberg, Manos Pitsidianakis,
Danilo Krummrich, Thomas Bertschinger, linux-acpi, linux-pm,
acpica-devel, rust-for-linux
Remove the perf_fb_ctrs field from `struct cppc_cpudata` as it
was never used.
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
drivers/cpufreq/cppc_cpufreq.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 208d7b176fd0..632f2caebbb5 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -31,7 +31,6 @@ struct cppc_cpudata {
struct list_head node;
struct cppc_perf_caps perf_caps;
struct cppc_perf_ctrls perf_ctrls;
- struct cppc_perf_fb_ctrs perf_fb_ctrs;
unsigned int shared_type;
cpumask_var_t shared_cpu_map;
};
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 3/6] rust: module: Allow modules to specify initcall section
2024-08-15 8:29 [RFC PATCH 0/6] rust: cpufreq: Add cppc_cpufreq driver implementation Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 1/6] ACPI: CPPC: Move struct cppc_cpudata to cppc_cpufreq driver Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 2/6] cpufreq: cppc: Remove perf_fb_ctrs field from struct cppc_cpudata Pierre Gondois
@ 2024-08-15 8:29 ` Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 4/6] rust: cpufreq: Add methods to struct Cpufreq Pierre Gondois
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Pierre Gondois @ 2024-08-15 8:29 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Gondois, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
Asahi Lina, Danilo Krummrich, Rob Herring (Arm), FUJITA Tomonori,
Mika Westerberg, Manos Pitsidianakis, Thomas Bertschinger,
linux-acpi, linux-pm, acpica-devel, rust-for-linux
To give more flexibility to modules regarding their initialization,
add an `initcall` field allowing to specify the initicall section
their initialization function belongs to.
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
rust/macros/module.rs | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index be03b2cf77a1..8724738f2a52 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -97,14 +97,22 @@ struct ModuleInfo {
author: Option<String>,
description: Option<String>,
alias: Option<Vec<String>>,
+ initcall: Option<String>,
}
impl ModuleInfo {
fn parse(it: &mut token_stream::IntoIter) -> Self {
let mut info = ModuleInfo::default();
- const EXPECTED_KEYS: &[&str] =
- &["type", "name", "author", "description", "license", "alias"];
+ const EXPECTED_KEYS: &[&str] = &[
+ "type",
+ "name",
+ "author",
+ "description",
+ "license",
+ "alias",
+ "initcall",
+ ];
const REQUIRED_KEYS: &[&str] = &["type", "name", "license"];
let mut seen_keys = Vec::new();
@@ -131,6 +139,7 @@ fn parse(it: &mut token_stream::IntoIter) -> Self {
"description" => info.description = Some(expect_string(it)),
"license" => info.license = expect_string_ascii(it),
"alias" => info.alias = Some(expect_string_array(it)),
+ "initcall" => info.initcall = Some(expect_string(it)),
_ => panic!(
"Unknown key \"{}\". Valid keys are: {:?}.",
key, EXPECTED_KEYS
@@ -187,6 +196,12 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
}
}
+ let initcall_section = if let Some(section) = info.initcall {
+ section
+ } else {
+ String::from(".initcall6.init")
+ };
+
// Built-in modules also export the `file` modinfo string.
let file =
std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable");
@@ -335,7 +350,7 @@ unsafe fn __exit() {{
type_ = info.type_,
name = info.name,
modinfo = modinfo.buffer,
- initcall_section = ".initcall6.init"
+ initcall_section = initcall_section,
)
.parse()
.expect("Error parsing formatted string into token stream.")
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 4/6] rust: cpufreq: Add methods to struct Cpufreq
2024-08-15 8:29 [RFC PATCH 0/6] rust: cpufreq: Add cppc_cpufreq driver implementation Pierre Gondois
` (2 preceding siblings ...)
2024-08-15 8:29 ` [RFC PATCH 3/6] rust: module: Allow modules to specify initcall section Pierre Gondois
@ 2024-08-15 8:29 ` Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 5/6] rust: bindings: Add bindings for rcppc_cpufreq driver Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver Pierre Gondois
5 siblings, 0 replies; 11+ messages in thread
From: Pierre Gondois @ 2024-08-15 8:29 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Gondois, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
Manos Pitsidianakis, Danilo Krummrich, Rob Herring (Arm),
FUJITA Tomonori, Mika Westerberg, Thomas Bertschinger, linux-acpi,
linux-pm, acpica-devel, rust-for-linux
The following methods are added and seamlessly allow to get/set
the matching fields in the C definition of the `struct cpufreq`:
- set_min()
- set_max()
- set_cur()
- fast_switch_possible()
- set_fast_switch_possible()
- set_cpuinfo_min_freq()
- set_cpuinfo_max_freq()
- set_transition_delay_us()
- set_shared_type()
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
rust/kernel/cpufreq.rs | 53 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index b395694de6c4..588080724fbf 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -318,16 +318,34 @@ pub fn min(&self) -> u32 {
self.as_ref().min
}
+ /// Set the minimum frequency for a cpufreq policy.
+ pub fn set_min(&mut self, min: u32) -> &mut Self {
+ self.as_mut_ref().min = min;
+ self
+ }
+
/// Returns the maximum frequency for a cpufreq policy.
pub fn max(&self) -> u32 {
self.as_ref().max
}
+ /// Set the maximum frequency for a cpufreq policy.
+ pub fn set_max(&mut self, max: u32) -> &mut Self {
+ self.as_mut_ref().max = max;
+ self
+ }
+
/// Returns the current frequency for a cpufreq policy.
pub fn cur(&self) -> u32 {
self.as_ref().cur
}
+ /// Set the current frequency for a cpufreq policy.
+ pub fn set_cur(&mut self, cur: u32) -> &mut Self {
+ self.as_mut_ref().cur = cur;
+ self
+ }
+
/// Sets the suspend frequency for a cpufreq policy.
pub fn set_suspend_freq(&mut self, freq: u32) -> &mut Self {
self.as_mut_ref().suspend_freq = freq;
@@ -378,12 +396,47 @@ pub fn set_dvfs_possible_from_any_cpu(&mut self) -> &mut Self {
self
}
+ /// Get fast_switch_possible value.
+ pub fn fast_switch_possible(&self) -> bool {
+ self.as_ref().fast_switch_possible
+ }
+
+ /// Enable/disable fast frequency switching.
+ pub fn set_fast_switch_possible(&mut self, val: bool) -> &mut Self {
+ self.as_mut_ref().fast_switch_possible = val;
+ self
+ }
+
/// Sets transition latency for a cpufreq policy.
pub fn set_transition_latency(&mut self, latency: u32) -> &mut Self {
self.as_mut_ref().cpuinfo.transition_latency = latency;
self
}
+ /// Set cpuinfo.min_freq.
+ pub fn set_cpuinfo_min_freq(&mut self, min_freq: u32) -> &mut Self {
+ self.as_mut_ref().cpuinfo.min_freq = min_freq;
+ self
+ }
+
+ /// Set cpuinfo.max_freq.
+ pub fn set_cpuinfo_max_freq(&mut self, max_freq: u32) -> &mut Self {
+ self.as_mut_ref().cpuinfo.max_freq = max_freq;
+ self
+ }
+
+ /// Set transition_delay_us, i.e. time between successive freq. change requests.
+ pub fn set_transition_delay_us(&mut self, transition_delay_us: u32) -> &mut Self {
+ self.as_mut_ref().transition_delay_us = transition_delay_us;
+ self
+ }
+
+ /// Set shared_type, how CPUs coordinate freq. requests (ACPI only).
+ pub fn set_shared_type(&mut self, shared_type: u32) -> &mut Self {
+ self.as_mut_ref().shared_type = shared_type;
+ self
+ }
+
/// Returns the cpufreq table for a cpufreq policy. The cpufreq table is recreated in a
/// light-weight manner from the raw pointer. The table in C code is not freed once this table
/// is dropped.
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 5/6] rust: bindings: Add bindings for rcppc_cpufreq driver
2024-08-15 8:29 [RFC PATCH 0/6] rust: cpufreq: Add cppc_cpufreq driver implementation Pierre Gondois
` (3 preceding siblings ...)
2024-08-15 8:29 ` [RFC PATCH 4/6] rust: cpufreq: Add methods to struct Cpufreq Pierre Gondois
@ 2024-08-15 8:29 ` Pierre Gondois
2024-08-15 8:29 ` [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver Pierre Gondois
5 siblings, 0 replies; 11+ messages in thread
From: Pierre Gondois @ 2024-08-15 8:29 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Gondois, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
Rob Herring (Arm), FUJITA Tomonori, Mika Westerberg,
Manos Pitsidianakis, Danilo Krummrich, Thomas Bertschinger,
linux-acpi, linux-pm, acpica-devel, rust-for-linux
Add bindings to prepare the enablement of the rcppc_cpufreq
driver.
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
rust/bindings/bindings_helper.h | 1 +
rust/helpers.c | 6 ++++++
2 files changed, 7 insertions(+)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index bee2b6013690..a7ba64b5614b 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <acpi/cppc_acpi.h>
/* `bindgen` gets confused at certain things. */
const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
diff --git a/rust/helpers.c b/rust/helpers.c
index 3b2850a11859..624b5c94dad6 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -370,6 +370,12 @@ void rust_helper_cpufreq_register_em_with_opp(struct cpufreq_policy *policy)
cpufreq_register_em_with_opp(policy);
}
EXPORT_SYMBOL_GPL(rust_helper_cpufreq_register_em_with_opp);
+
+void rust_helper_cpufreq_verify_within_cpu_limits(struct cpufreq_policy_data *policy)
+{
+ cpufreq_verify_within_cpu_limits(policy);
+}
+EXPORT_SYMBOL_GPL(rust_helper_cpufreq_verify_within_cpu_limits);
#endif
#ifndef CONFIG_OF_DYNAMIC
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver
2024-08-15 8:29 [RFC PATCH 0/6] rust: cpufreq: Add cppc_cpufreq driver implementation Pierre Gondois
` (4 preceding siblings ...)
2024-08-15 8:29 ` [RFC PATCH 5/6] rust: bindings: Add bindings for rcppc_cpufreq driver Pierre Gondois
@ 2024-08-15 8:29 ` Pierre Gondois
2024-08-15 9:24 ` Greg KH
2024-08-15 13:04 ` Miguel Ojeda
5 siblings, 2 replies; 11+ messages in thread
From: Pierre Gondois @ 2024-08-15 8:29 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Gondois, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
Manos Pitsidianakis, Mika Westerberg, FUJITA Tomonori,
Thomas Bertschinger, Danilo Krummrich, linux-acpi, linux-pm,
acpica-devel, rust-for-linux
In an effort to add test/support the cpufreq framework in rust,
add a rust implementation of the cppc_cpufreq driver named:
`rcppc_cpufreq`.
This implementation doesn't support/implement:
- vendor specific workarounds
- Frequency Invariance Engine (FIE)
- artificial Energy Model (EM)
- (struct cpufreq_driver).attr field
- QoS requests
Basic support is provided to get/set the frequency on a platform
implementing the CPPC section of the ACPI spec.
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
drivers/cpufreq/Kconfig | 16 ++
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/rcppc_cpufreq.rs | 333 +++++++++++++++++++++++++++++++
3 files changed, 350 insertions(+)
create mode 100644 drivers/cpufreq/rcppc_cpufreq.rs
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index eb9359bd3c5c..57130d0789b0 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -343,4 +343,20 @@ config ACPI_CPPC_CPUFREQ_FIE
If in doubt, say N.
+config ACPI_CPPC_CPUFREQ_RUST
+ tristate "Rust based CPUFreq driver based on the ACPI CPPC spec"
+ depends on ACPI_PROCESSOR
+ depends on ARM || ARM64 || RISCV
+ select ACPI_CPPC_LIB
+ help
+ This adds a Rust based CPUFreq driver based on the ACPI CPPC spec.
+ Basic support is only available for now, i.e. the following are
+ not supported:
+ - vendor specific workarounds
+ - Frequency Invariance Engine (FIE)
+ - artificial Energy Model (EM)
+ - QoS requests
+
+ If in doubt, say N.
+
endmenu
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 4981d908b803..5e17db481a50 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o
obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
obj-$(CONFIG_CPUFREQ_DT_RUST) += rcpufreq_dt.o
+obj-$(CONFIG_ACPI_CPPC_CPUFREQ_RUST) += rcppc_cpufreq.o
obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
# Traces
diff --git a/drivers/cpufreq/rcppc_cpufreq.rs b/drivers/cpufreq/rcppc_cpufreq.rs
new file mode 100644
index 000000000000..198857a5b966
--- /dev/null
+++ b/drivers/cpufreq/rcppc_cpufreq.rs
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust based implementation of the cppc_cpufreq driver.
+
+use core::format_args;
+
+use kernel::prelude::*;
+
+use kernel::{
+ bindings, c_str, cpufreq,
+ cpumask::Cpumask,
+ error::{to_result, Result},
+ sync::Arc,
+};
+
+/// 2 usec delay between sampling
+const COUNTERS_SAMPLING_DELAY_US: u64 = 2;
+
+// Whether boost is supported
+static mut BOOST_SUPPORTED: bool = false;
+
+struct CPUFreqCppcDriver {
+ _drv: cpufreq::Registration<Self>,
+}
+
+struct CPUFreqCppcData {
+ perf_caps: bindings::cppc_perf_caps,
+ perf_ctrls: bindings::cppc_perf_ctrls,
+ shared_type: u32,
+ shared_cpu_map: Cpumask,
+}
+
+impl CPUFreqCppcData {
+ fn new(cpu: u32) -> Result<Self> {
+ let mut shared_type = bindings::CPUFREQ_SHARED_TYPE_NONE;
+ let mut perf_caps = bindings::cppc_perf_caps::default();
+ let mut shared_cpu_map = Cpumask::new()?;
+
+ match acpi_get_psd_map(cpu, &mut shared_cpu_map, &mut shared_type) {
+ Err(e) => {
+ pr_debug!("Err parsing CPU{cpu} PSD data: err:{e:?}\n");
+ return Err(e);
+ }
+ _ => {}
+ }
+
+ match cppc_get_perf_caps(cpu as i32, &mut perf_caps) {
+ Err(e) => {
+ pr_debug!("Err reading CPU{cpu} perf caps: e:{e:?}\n");
+ return Err(e);
+ }
+ _ => {}
+ }
+
+ Ok(Self {
+ perf_caps: perf_caps,
+ perf_ctrls: bindings::cppc_perf_ctrls::default(),
+ shared_type: shared_type,
+ shared_cpu_map: shared_cpu_map,
+ })
+ }
+}
+
+fn acpi_get_psd_map(cpu: u32, shared_cpu_map: &mut Cpumask, shared_type: &mut u32) -> Result<()> {
+ unsafe {
+ to_result(bindings::acpi_get_psd_map(
+ cpu,
+ shared_cpu_map.as_mut_ptr(),
+ shared_type,
+ ))
+ }
+}
+
+fn cppc_get_perf_caps(cpu: i32, perf_caps: &mut bindings::cppc_perf_caps) -> Result<()> {
+ unsafe { to_result(bindings::cppc_get_perf_caps(cpu, perf_caps)) }
+}
+
+fn cppc_perf_to_khz(caps: &mut bindings::cppc_perf_caps, perf: u32) -> u32 {
+ unsafe { bindings::cppc_perf_to_khz(caps, perf) }
+}
+
+fn cppc_khz_to_perf(caps: &mut bindings::cppc_perf_caps, freq: u32) -> u32 {
+ unsafe { bindings::cppc_khz_to_perf(caps, freq) }
+}
+
+fn cppc_set_perf(cpu: u32, perf_ctrls: &mut bindings::cppc_perf_ctrls) -> Result<()> {
+ unsafe { to_result(bindings::cppc_set_perf(cpu as i32, perf_ctrls)) }
+}
+
+fn cpufreq_freq_transition_begin(
+ policy: &mut cpufreq::Policy,
+ freqs: &mut bindings::cpufreq_freqs,
+) {
+ unsafe { bindings::cpufreq_freq_transition_begin(policy.as_raw(), freqs) }
+}
+
+fn cpufreq_freq_transition_end(
+ policy: &mut cpufreq::Policy,
+ freqs: &mut bindings::cpufreq_freqs,
+ transition_failed: bool,
+) {
+ unsafe {
+ bindings::cpufreq_freq_transition_end(policy.as_raw(), freqs, transition_failed as i32)
+ }
+}
+
+fn cppc_get_perf_ctrs(cpu: u32, fb_ctrs: &mut bindings::cppc_perf_fb_ctrs) -> Result<()> {
+ unsafe { to_result(bindings::cppc_get_perf_ctrs(cpu as i32, fb_ctrs)) }
+}
+
+fn cppc_perf_from_fbctrs(
+ desired_perf: u32,
+ fb_ctrs_t0: bindings::cppc_perf_fb_ctrs,
+ fb_ctrs_t1: bindings::cppc_perf_fb_ctrs,
+) -> u32 {
+ let reference_perf = fb_ctrs_t0.reference_perf;
+ let delta_reference = fb_ctrs_t1.reference.wrapping_sub(fb_ctrs_t0.reference);
+ let delta_delivered = fb_ctrs_t1.delivered.wrapping_sub(fb_ctrs_t0.delivered);
+
+ if delta_reference == 0 || delta_delivered == 0 {
+ return desired_perf;
+ }
+
+ return ((reference_perf - delta_delivered) / delta_reference) as u32;
+}
+
+#[vtable]
+impl cpufreq::Driver for CPUFreqCppcDriver {
+ type Data = ();
+ type PData = Arc<CPUFreqCppcData>;
+
+ fn init(policy: &mut cpufreq::Policy) -> Result<Self::PData> {
+ let cpu = policy.cpu();
+ let mut data = CPUFreqCppcData::new(cpu)?;
+
+ // Set min to lowest nonlinear perf to avoid any efficiency penalty
+ // (see Section 8.4.7.1.1.5 of ACPI 6.1 spec)
+ policy
+ .set_min(data.perf_caps.lowest_nonlinear_perf)
+ .set_max(data.perf_caps.nominal_perf);
+
+ let lowest_nonlinear_perf = data.perf_caps.lowest_nonlinear_perf;
+ let nominal_perf = data.perf_caps.nominal_perf;
+ let highest_perf = data.perf_caps.highest_perf;
+
+ // Set cpuinfo.min_freq to Lowest to make the full range of performance
+ // available if userspace wants to use any perf between lowest & lowest
+ // nonlinear perf
+ policy
+ .set_cpuinfo_min_freq(cppc_perf_to_khz(&mut data.perf_caps, lowest_nonlinear_perf))
+ .set_cpuinfo_max_freq(cppc_perf_to_khz(&mut data.perf_caps, nominal_perf));
+
+ policy
+ .set_transition_delay_us(unsafe { bindings::cppc_get_transition_latency(cpu as i32) })
+ .set_shared_type(data.shared_type);
+
+ match data.shared_type {
+ bindings::CPUFREQ_SHARED_TYPE_HW => {}
+ bindings::CPUFREQ_SHARED_TYPE_NONE => {}
+ bindings::CPUFREQ_SHARED_TYPE_ANY => {
+ // All CPUs in the domain will share a policy and all cpufreq
+ // operations will use the same CPUFreqCppcData struct.
+ let cpus = policy.cpus();
+ data.shared_cpu_map.copy(cpus);
+ }
+ default => {
+ pr_err!("Unsupported CPU co-ord type: {default}\n");
+ return Err(EFAULT);
+ }
+ }
+
+ if unsafe { bindings::cppc_allow_fast_switch() } {
+ policy.set_fast_switch_possible(true);
+ }
+
+ // If 'highest_perf' is greater than 'nominal_perf', we assume CPU Boost
+ // is supported.
+ if highest_perf > nominal_perf {
+ unsafe { BOOST_SUPPORTED = true };
+ }
+
+ // Set policy->cur to max now. The governors will adjust later.
+ policy
+ .set_dvfs_possible_from_any_cpu()
+ .set_cur(cppc_perf_to_khz(&mut data.perf_caps, highest_perf));
+
+ cppc_set_perf(cpu, &mut data.perf_ctrls)?;
+
+ Ok(Arc::new(data, GFP_KERNEL)?)
+ }
+
+ fn exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result<()> {
+ Ok(())
+ }
+
+ fn verify(policy_data: &mut cpufreq::PolicyData) -> Result<()> {
+ unsafe { bindings::cpufreq_verify_within_cpu_limits(policy_data.as_raw()) };
+ Ok(())
+ }
+
+ fn target(
+ policy: &mut cpufreq::Policy,
+ target_freq: u32,
+ _relation: cpufreq::Relation,
+ ) -> Result<()> {
+ let data = match policy.data::<Self::PData>() {
+ Some(data) => data,
+ None => return Err(ENOENT),
+ };
+
+ let mut perf_ctrls: bindings::cppc_perf_ctrls = data.perf_ctrls;
+ let mut perf_caps: bindings::cppc_perf_caps = data.perf_caps;
+ let desired_perf: u32 = cppc_khz_to_perf(&mut perf_caps, target_freq);
+ let cpu = policy.cpu();
+
+ // Return if it is exactly the same perf.
+ if desired_perf == perf_ctrls.desired_perf {
+ return Ok(());
+ }
+
+ perf_ctrls.desired_perf = desired_perf;
+
+ let mut freqs = bindings::cpufreq_freqs::default();
+ freqs.old = policy.cur();
+ freqs.new = target_freq;
+
+ cpufreq_freq_transition_begin(policy, &mut freqs);
+ let ret = cppc_set_perf(cpu, &mut perf_ctrls);
+ let transition_failed = match ret {
+ Ok(_) => false,
+ Err(e) => {
+ pr_debug!("Failed to set target on CPU:{cpu}. err:{e:?}\n");
+ true
+ }
+ };
+ cpufreq_freq_transition_end(policy, &mut freqs, transition_failed);
+
+ ret
+ }
+
+ fn fast_switch(policy: &mut cpufreq::Policy, target_freq: u32) -> u32 {
+ let data = match policy.data::<Self::PData>() {
+ Some(data) => data,
+ None => return 0,
+ };
+
+ let mut perf_caps = data.perf_caps;
+ let mut perf_ctrls = data.perf_ctrls;
+ let desired_perf = cppc_khz_to_perf(&mut perf_caps, target_freq);
+ let cpu = policy.cpu();
+
+ perf_ctrls.desired_perf = desired_perf;
+
+ if let Err(ret) = cppc_set_perf(cpu, &mut perf_ctrls) {
+ pr_debug!("Failed to set target on CPU:{cpu}. ret:{ret:?}\n");
+ return 0;
+ }
+
+ return target_freq;
+ }
+
+ fn get(policy: &mut cpufreq::Policy) -> Result<u32> {
+ let mut fb_ctrs_t0 = bindings::cppc_perf_fb_ctrs::default();
+ let mut fb_ctrs_t1 = bindings::cppc_perf_fb_ctrs::default();
+ let cpu = policy.cpu();
+
+ let data = match policy.data::<Self::PData>() {
+ Some(data) => data,
+ None => return Err(ENOENT),
+ };
+
+ let mut perf_caps = data.perf_caps;
+ let desired_perf = data.perf_ctrls.desired_perf;
+
+ cppc_get_perf_ctrs(cpu, &mut fb_ctrs_t0)?;
+ unsafe { bindings::__udelay(COUNTERS_SAMPLING_DELAY_US) };
+ cppc_get_perf_ctrs(cpu, &mut fb_ctrs_t1)?;
+
+ let delivered_perf = cppc_perf_from_fbctrs(desired_perf, fb_ctrs_t0, fb_ctrs_t1);
+ let freq = cppc_perf_to_khz(&mut perf_caps, delivered_perf);
+
+ Ok(freq)
+ }
+
+ fn set_boost(policy: &mut cpufreq::Policy, state: i32) -> Result<()> {
+ if unsafe { !BOOST_SUPPORTED } {
+ pr_err!("BOOST not supported by CPU or firmware\n");
+ return Err(EINVAL);
+ }
+
+ let data = match policy.data::<Self::PData>() {
+ Some(data) => data,
+ None => return Err(ENOENT),
+ };
+ let mut caps = data.perf_caps;
+ let highest_perf = caps.highest_perf;
+ let nominal_perf = caps.nominal_perf;
+
+ let max_freq = if state != 0 {
+ cppc_perf_to_khz(&mut caps, highest_perf)
+ } else {
+ cppc_perf_to_khz(&mut caps, nominal_perf)
+ };
+
+ policy
+ .set_max(max_freq)
+ .set_cpuinfo_max_freq(max_freq);
+
+ Ok(())
+ }
+}
+
+module! {
+ type: CPUFreqCppcDriver,
+ name: "cppc_cpufreq",
+ author: "Pierre Gondois",
+ description: "CPPC cpufreq driver",
+ license: "GPL v2",
+ initcall: ".initcall7.init",
+}
+
+impl kernel::Module for CPUFreqCppcDriver {
+ fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
+ let drv = cpufreq::Registration::<CPUFreqCppcDriver>::register(
+ c_str!("rcppc-cpufreq"),
+ (),
+ cpufreq::flags::CONST_LOOPS,
+ false,
+ )?;
+
+ Ok(CPUFreqCppcDriver { _drv: drv })
+ }
+}
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver
2024-08-15 8:29 ` [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver Pierre Gondois
@ 2024-08-15 9:24 ` Greg KH
2024-08-15 13:04 ` Miguel Ojeda
1 sibling, 0 replies; 11+ messages in thread
From: Greg KH @ 2024-08-15 9:24 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
Manos Pitsidianakis, Mika Westerberg, FUJITA Tomonori,
Thomas Bertschinger, Danilo Krummrich, linux-acpi, linux-pm,
acpica-devel, rust-for-linux
On Thu, Aug 15, 2024 at 10:29:10AM +0200, Pierre Gondois wrote:
> In an effort to add test/support the cpufreq framework in rust,
> add a rust implementation of the cppc_cpufreq driver named:
> `rcppc_cpufreq`.
>
> This implementation doesn't support/implement:
> - vendor specific workarounds
> - Frequency Invariance Engine (FIE)
> - artificial Energy Model (EM)
> - (struct cpufreq_driver).attr field
> - QoS requests
>
> Basic support is provided to get/set the frequency on a platform
> implementing the CPPC section of the ACPI spec.
>
> Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
> ---
> drivers/cpufreq/Kconfig | 16 ++
> drivers/cpufreq/Makefile | 1 +
> drivers/cpufreq/rcppc_cpufreq.rs | 333 +++++++++++++++++++++++++++++++
> 3 files changed, 350 insertions(+)
> create mode 100644 drivers/cpufreq/rcppc_cpufreq.rs
I'm missing why you want to re-implement an existing driver here. Why
are you going to have 2 drivers for the same functionality/hardware?
How is the system going to handle switching between the two drivers?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver
2024-08-15 8:29 ` [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver Pierre Gondois
2024-08-15 9:24 ` Greg KH
@ 2024-08-15 13:04 ` Miguel Ojeda
2024-08-16 6:59 ` Pierre Gondois
1 sibling, 1 reply; 11+ messages in thread
From: Miguel Ojeda @ 2024-08-15 13:04 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
Manos Pitsidianakis, Mika Westerberg, FUJITA Tomonori,
Thomas Bertschinger, Danilo Krummrich, linux-acpi, linux-pm,
acpica-devel, rust-for-linux
On Thu, Aug 15, 2024 at 10:31 AM Pierre Gondois <pierre.gondois@arm.com> wrote:
>
> In an effort to add test/support the cpufreq framework in rust,
> add a rust implementation of the cppc_cpufreq driver named:
> `rcppc_cpufreq`.
Similar to what Greg said -- is this intended to be something like a
"Rust reference driver" [1] for the subsystem?
[1] https://rust-for-linux.com/rust-reference-drivers
> + depends on ACPI_PROCESSOR
> + depends on ARM || ARM64 || RISCV
`depends on RUST`?
Also, I imagine you skipped all safety comments etc. since it is an
RFC, but I thought I would mention it nevertheless.
Thanks for experimenting with Rust!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver
2024-08-15 13:04 ` Miguel Ojeda
@ 2024-08-16 6:59 ` Pierre Gondois
2024-08-16 7:06 ` Greg Kroah-Hartman
0 siblings, 1 reply; 11+ messages in thread
From: Pierre Gondois @ 2024-08-16 6:59 UTC (permalink / raw)
To: Miguel Ojeda, Greg Kroah-Hartman
Cc: linux-kernel, Rafael J. Wysocki, Len Brown, Viresh Kumar,
Robert Moore, Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Martin Rodriguez Reboredo,
Manos Pitsidianakis, Mika Westerberg, FUJITA Tomonori,
Thomas Bertschinger, Danilo Krummrich, linux-acpi, linux-pm,
acpica-devel, rust-for-linux, Metin Kaya
Hello Greg, Miguel,
On 8/15/24 15:04, Miguel Ojeda wrote:
> On Thu, Aug 15, 2024 at 10:31 AM Pierre Gondois <pierre.gondois@arm.com> wrote:
>>
>> In an effort to add test/support the cpufreq framework in rust,
>> add a rust implementation of the cppc_cpufreq driver named:
>> `rcppc_cpufreq`.
>
> Similar to what Greg said -- is this intended to be something like a
> "Rust reference driver" [1] for the subsystem?
The initial intent was to review/test Viresh's patchset [1]. I then
thought it would be a good idea to implement another cpufreq driver
to see if the provided interface would work.
As the cpufreq-dt driver is re-implemented in Viresh's patchset,
I thought it was also ok to have this driver.
>
> [1] https://rust-for-linux.com/rust-reference-drivers
>
>> + depends on ACPI_PROCESSOR
>> + depends on ARM || ARM64 || RISCV
>
Yes right
> `depends on RUST`?
>
> Also, I imagine you skipped all safety comments etc. since it is an
> RFC, but I thought I would mention it nevertheless.
Ok, if it is decided not to drop this patchset, I ll add some comments,
>
> Thanks for experimenting with Rust!
>
> Cheers,
> Miguel
Regards,
Pierre
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 6/6] rust: cpufreq: Add rust implementation of cppc_cpufreq driver
2024-08-16 6:59 ` Pierre Gondois
@ 2024-08-16 7:06 ` Greg Kroah-Hartman
0 siblings, 0 replies; 11+ messages in thread
From: Greg Kroah-Hartman @ 2024-08-16 7:06 UTC (permalink / raw)
To: Pierre Gondois
Cc: Miguel Ojeda, linux-kernel, Rafael J. Wysocki, Len Brown,
Viresh Kumar, Robert Moore, Miguel Ojeda, Alex Gaynor,
Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl,
Martin Rodriguez Reboredo, Manos Pitsidianakis, Mika Westerberg,
FUJITA Tomonori, Thomas Bertschinger, Danilo Krummrich,
linux-acpi, linux-pm, acpica-devel, rust-for-linux, Metin Kaya
On Fri, Aug 16, 2024 at 08:59:22AM +0200, Pierre Gondois wrote:
> Hello Greg, Miguel,
>
> On 8/15/24 15:04, Miguel Ojeda wrote:
> > On Thu, Aug 15, 2024 at 10:31 AM Pierre Gondois <pierre.gondois@arm.com> wrote:
> > >
> > > In an effort to add test/support the cpufreq framework in rust,
> > > add a rust implementation of the cppc_cpufreq driver named:
> > > `rcppc_cpufreq`.
> >
> > Similar to what Greg said -- is this intended to be something like a
> > "Rust reference driver" [1] for the subsystem?
>
> The initial intent was to review/test Viresh's patchset [1]. I then
> thought it would be a good idea to implement another cpufreq driver
> to see if the provided interface would work.
> As the cpufreq-dt driver is re-implemented in Viresh's patchset,
> I thought it was also ok to have this driver.
Duplicate drivers for the same hardware are never a good idea, we need
to learn from our past mistakes when we have done this before (hint, it
did not work out and we ended up dropping the duplicates.)
However, if the subsystem maintainer agrees, they are free to have
duplicate drivers, as long as the maintainer of the "new" one will be
there to help out with all of the confusion and problems that users and
distros will have :)
good luck!
greg k-h
^ permalink raw reply [flat|nested] 11+ messages in thread