mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-23 07:23:12 +02:00
Merge branch 'pm-cpufreq'
Merge Rust support for cpufreq and OPP, a new Rust-based cpufreq-dt driver, an SCMI cpufreq driver cleanup, and an ACPI cpufreq driver regression fix: - Add Rust abstractions for CPUFreq framework (Viresh Kumar). - Add Rust abstractions for OPP framework (Viresh Kumar). - Add basic Rust abstractions for Clk and Cpumask frameworks (Viresh Kumar). - Clean up the SCMI cpufreq driver somewhat (Mike Tipton). - Use KHz as the nominal_freq units in get_max_boost_ratio() in the ACPI cpufreq driver (iGautham Shenoy). * pm-cpufreq: acpi-cpufreq: Fix nominal_freq units to KHz in get_max_boost_ratio() rust: opp: Move `cfg(CONFIG_OF)` attribute to the top of doc test rust: opp: Make the doctest example depend on CONFIG_OF cpufreq: scmi: Skip SCMI devices that aren't used by the CPUs cpufreq: Add Rust-based cpufreq-dt driver rust: opp: Extend OPP abstractions with cpufreq support rust: cpufreq: Extend abstractions for driver registration rust: cpufreq: Extend abstractions for policy and driver ops rust: cpufreq: Add initial abstractions for cpufreq framework rust: opp: Add abstractions for the configuration options rust: opp: Add abstractions for the OPP table rust: opp: Add initial abstractions for OPP framework rust: cpu: Add from_cpu() rust: macros: enable use of hyphens in module names rust: clk: Add initial abstractions rust: clk: Add helpers for Rust code MAINTAINERS: Add entry for Rust cpumask API rust: cpumask: Add initial abstractions rust: cpumask: Add few more helpers
This commit is contained in:
commit
25961ae6c8
11
MAINTAINERS
11
MAINTAINERS
|
@ -5949,6 +5949,8 @@ F: include/dt-bindings/clock/
|
|||
F: include/linux/clk-pr*
|
||||
F: include/linux/clk/
|
||||
F: include/linux/of_clk.h
|
||||
F: rust/helpers/clk.c
|
||||
F: rust/kernel/clk.rs
|
||||
X: drivers/clk/clkdev.c
|
||||
|
||||
COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3)
|
||||
|
@ -6208,6 +6210,7 @@ F: drivers/cpufreq/
|
|||
F: include/linux/cpufreq.h
|
||||
F: include/linux/sched/cpufreq.h
|
||||
F: kernel/sched/cpufreq*.c
|
||||
F: rust/kernel/cpufreq.rs
|
||||
F: tools/testing/selftests/cpufreq/
|
||||
|
||||
CPU HOTPLUG
|
||||
|
@ -6221,6 +6224,7 @@ F: include/linux/cpuhotplug.h
|
|||
F: include/linux/smpboot.h
|
||||
F: kernel/cpu.c
|
||||
F: kernel/smpboot.*
|
||||
F: rust/kernel/cpu.rs
|
||||
|
||||
CPU IDLE TIME MANAGEMENT FRAMEWORK
|
||||
M: "Rafael J. Wysocki" <rafael@kernel.org>
|
||||
|
@ -6305,6 +6309,12 @@ L: linux-riscv@lists.infradead.org
|
|||
S: Maintained
|
||||
F: drivers/cpuidle/cpuidle-riscv-sbi.c
|
||||
|
||||
CPUMASK API [RUST]
|
||||
M: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
R: Yury Norov <yury.norov@gmail.com>
|
||||
S: Maintained
|
||||
F: rust/kernel/cpumask.rs
|
||||
|
||||
CRAMFS FILESYSTEM
|
||||
M: Nicolas Pitre <nico@fluxnic.net>
|
||||
S: Maintained
|
||||
|
@ -18504,6 +18514,7 @@ F: Documentation/devicetree/bindings/opp/
|
|||
F: Documentation/power/opp.rst
|
||||
F: drivers/opp/
|
||||
F: include/linux/pm_opp.h
|
||||
F: rust/kernel/opp.rs
|
||||
|
||||
OPL4 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
|
|
|
@ -217,6 +217,18 @@ config CPUFREQ_DT
|
|||
|
||||
If in doubt, say N.
|
||||
|
||||
config CPUFREQ_DT_RUST
|
||||
tristate "Rust based Generic DT based cpufreq driver"
|
||||
depends on HAVE_CLK && OF && RUST
|
||||
select CPUFREQ_DT_PLATDEV
|
||||
select PM_OPP
|
||||
help
|
||||
This adds a Rust based generic DT based cpufreq driver for frequency
|
||||
management. It supports both uniprocessor (UP) and symmetric
|
||||
multiprocessor (SMP) systems.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
config CPUFREQ_VIRT
|
||||
tristate "Virtual cpufreq driver"
|
||||
depends on GENERIC_ARCH_TOPOLOGY
|
||||
|
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
|
|||
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_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
|
||||
obj-$(CONFIG_CPUFREQ_VIRT) += virtual-cpufreq.o
|
||||
|
||||
|
|
|
@ -660,7 +660,7 @@ static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
|
|||
nominal_perf = perf_caps.nominal_perf;
|
||||
|
||||
if (nominal_freq)
|
||||
*nominal_freq = perf_caps.nominal_freq;
|
||||
*nominal_freq = perf_caps.nominal_freq * 1000;
|
||||
|
||||
if (!highest_perf || !nominal_perf) {
|
||||
pr_debug("CPU%d: highest or nominal performance missing\n", cpu);
|
||||
|
|
226
drivers/cpufreq/rcpufreq_dt.rs
Normal file
226
drivers/cpufreq/rcpufreq_dt.rs
Normal file
|
@ -0,0 +1,226 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Rust based implementation of the cpufreq-dt driver.
|
||||
|
||||
use kernel::{
|
||||
c_str,
|
||||
clk::Clk,
|
||||
cpu, cpufreq,
|
||||
cpumask::CpumaskVar,
|
||||
device::{Core, Device},
|
||||
error::code::*,
|
||||
fmt,
|
||||
macros::vtable,
|
||||
module_platform_driver, of, opp, platform,
|
||||
prelude::*,
|
||||
str::CString,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Finds exact supply name from the OF node.
|
||||
fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
|
||||
let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?;
|
||||
dev.property_present(&prop_name)
|
||||
.then(|| CString::try_from_fmt(fmt!("{name}")).ok())
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Finds supply name for the CPU from DT.
|
||||
fn find_supply_names(dev: &Device, cpu: u32) -> Option<KVec<CString>> {
|
||||
// Try "cpu0" for older DTs, fallback to "cpu".
|
||||
let name = (cpu == 0)
|
||||
.then(|| find_supply_name_exact(dev, "cpu0"))
|
||||
.flatten()
|
||||
.or_else(|| find_supply_name_exact(dev, "cpu"))?;
|
||||
|
||||
let mut list = KVec::with_capacity(1, GFP_KERNEL).ok()?;
|
||||
list.push(name, GFP_KERNEL).ok()?;
|
||||
|
||||
Some(list)
|
||||
}
|
||||
|
||||
/// Represents the cpufreq dt device.
|
||||
struct CPUFreqDTDevice {
|
||||
opp_table: opp::Table,
|
||||
freq_table: opp::FreqTable,
|
||||
_mask: CpumaskVar,
|
||||
_token: Option<opp::ConfigToken>,
|
||||
_clk: Clk,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CPUFreqDTDriver;
|
||||
|
||||
#[vtable]
|
||||
impl opp::ConfigOps for CPUFreqDTDriver {}
|
||||
|
||||
#[vtable]
|
||||
impl cpufreq::Driver for CPUFreqDTDriver {
|
||||
const NAME: &'static CStr = c_str!("cpufreq-dt");
|
||||
const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV;
|
||||
const BOOST_ENABLED: bool = true;
|
||||
|
||||
type PData = Arc<CPUFreqDTDevice>;
|
||||
|
||||
fn init(policy: &mut cpufreq::Policy) -> Result<Self::PData> {
|
||||
let cpu = policy.cpu();
|
||||
// SAFETY: The CPU device is only used during init; it won't get hot-unplugged. The cpufreq
|
||||
// core registers with CPU notifiers and the cpufreq core/driver won't use the CPU device,
|
||||
// once the CPU is hot-unplugged.
|
||||
let dev = unsafe { cpu::from_cpu(cpu)? };
|
||||
let mut mask = CpumaskVar::new_zero(GFP_KERNEL)?;
|
||||
|
||||
mask.set(cpu);
|
||||
|
||||
let token = find_supply_names(dev, cpu)
|
||||
.map(|names| {
|
||||
opp::Config::<Self>::new()
|
||||
.set_regulator_names(names)?
|
||||
.set(dev)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
// Get OPP-sharing information from "operating-points-v2" bindings.
|
||||
let fallback = match opp::Table::of_sharing_cpus(dev, &mut mask) {
|
||||
Ok(()) => false,
|
||||
Err(e) if e == ENOENT => {
|
||||
// "operating-points-v2" not supported. If the platform hasn't
|
||||
// set sharing CPUs, fallback to all CPUs share the `Policy`
|
||||
// for backward compatibility.
|
||||
opp::Table::sharing_cpus(dev, &mut mask).is_err()
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
// Initialize OPP tables for all policy cpus.
|
||||
//
|
||||
// For platforms not using "operating-points-v2" bindings, we do this
|
||||
// before updating policy cpus. Otherwise, we will end up creating
|
||||
// duplicate OPPs for the CPUs.
|
||||
//
|
||||
// OPPs might be populated at runtime, don't fail for error here unless
|
||||
// it is -EPROBE_DEFER.
|
||||
let mut opp_table = match opp::Table::from_of_cpumask(dev, &mut mask) {
|
||||
Ok(table) => table,
|
||||
Err(e) => {
|
||||
if e == EPROBE_DEFER {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// The table is added dynamically ?
|
||||
opp::Table::from_dev(dev)?
|
||||
}
|
||||
};
|
||||
|
||||
// The OPP table must be initialized, statically or dynamically, by this point.
|
||||
opp_table.opp_count()?;
|
||||
|
||||
// Set sharing cpus for fallback scenario.
|
||||
if fallback {
|
||||
mask.setall();
|
||||
opp_table.set_sharing_cpus(&mut mask)?;
|
||||
}
|
||||
|
||||
let mut transition_latency = opp_table.max_transition_latency_ns() as u32;
|
||||
if transition_latency == 0 {
|
||||
transition_latency = cpufreq::ETERNAL_LATENCY_NS;
|
||||
}
|
||||
|
||||
policy
|
||||
.set_dvfs_possible_from_any_cpu(true)
|
||||
.set_suspend_freq(opp_table.suspend_freq())
|
||||
.set_transition_latency_ns(transition_latency);
|
||||
|
||||
let freq_table = opp_table.cpufreq_table()?;
|
||||
// SAFETY: The `freq_table` is not dropped while it is getting used by the C code.
|
||||
unsafe { policy.set_freq_table(&freq_table) };
|
||||
|
||||
// SAFETY: The returned `clk` is not dropped while it is getting used by the C code.
|
||||
let clk = unsafe { policy.set_clk(dev, None)? };
|
||||
|
||||
mask.copy(policy.cpus());
|
||||
|
||||
Ok(Arc::new(
|
||||
CPUFreqDTDevice {
|
||||
opp_table,
|
||||
freq_table,
|
||||
_mask: mask,
|
||||
_token: token,
|
||||
_clk: clk,
|
||||
},
|
||||
GFP_KERNEL,
|
||||
)?)
|
||||
}
|
||||
|
||||
fn exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn online(_policy: &mut cpufreq::Policy) -> Result {
|
||||
// We did light-weight tear down earlier, nothing to do here.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn offline(_policy: &mut cpufreq::Policy) -> Result {
|
||||
// Preserve policy->data and don't free resources on light-weight
|
||||
// tear down.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn suspend(policy: &mut cpufreq::Policy) -> Result {
|
||||
policy.generic_suspend()
|
||||
}
|
||||
|
||||
fn verify(data: &mut cpufreq::PolicyData) -> Result {
|
||||
data.generic_verify()
|
||||
}
|
||||
|
||||
fn target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result {
|
||||
let Some(data) = policy.data::<Self::PData>() else {
|
||||
return Err(ENOENT);
|
||||
};
|
||||
|
||||
let freq = data.freq_table.freq(index)?;
|
||||
data.opp_table.set_rate(freq)
|
||||
}
|
||||
|
||||
fn get(policy: &mut cpufreq::Policy) -> Result<u32> {
|
||||
policy.generic_get()
|
||||
}
|
||||
|
||||
fn set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_em(policy: &mut cpufreq::Policy) {
|
||||
policy.register_em_opp()
|
||||
}
|
||||
}
|
||||
|
||||
kernel::of_device_table!(
|
||||
OF_TABLE,
|
||||
MODULE_OF_TABLE,
|
||||
<CPUFreqDTDriver as platform::Driver>::IdInfo,
|
||||
[(of::DeviceId::new(c_str!("operating-points-v2")), ())]
|
||||
);
|
||||
|
||||
impl platform::Driver for CPUFreqDTDriver {
|
||||
type IdInfo = ();
|
||||
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
|
||||
|
||||
fn probe(
|
||||
pdev: &platform::Device<Core>,
|
||||
_id_info: Option<&Self::IdInfo>,
|
||||
) -> Result<Pin<KBox<Self>>> {
|
||||
cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
|
||||
Ok(KBox::new(Self {}, GFP_KERNEL)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
module_platform_driver! {
|
||||
type: CPUFreqDTDriver,
|
||||
name: "cpufreq-dt",
|
||||
author: "Viresh Kumar <viresh.kumar@linaro.org>",
|
||||
description: "Generic CPUFreq DT driver",
|
||||
license: "GPL v2",
|
||||
}
|
|
@ -393,6 +393,40 @@ static struct cpufreq_driver scmi_cpufreq_driver = {
|
|||
.set_boost = cpufreq_boost_set_sw,
|
||||
};
|
||||
|
||||
static bool scmi_dev_used_by_cpus(struct device *scmi_dev)
|
||||
{
|
||||
struct device_node *scmi_np = dev_of_node(scmi_dev);
|
||||
struct device_node *cpu_np, *np;
|
||||
struct device *cpu_dev;
|
||||
int cpu, idx;
|
||||
|
||||
if (!scmi_np)
|
||||
return false;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
if (!cpu_dev)
|
||||
continue;
|
||||
|
||||
cpu_np = dev_of_node(cpu_dev);
|
||||
|
||||
np = of_parse_phandle(cpu_np, "clocks", 0);
|
||||
of_node_put(np);
|
||||
|
||||
if (np == scmi_np)
|
||||
return true;
|
||||
|
||||
idx = of_property_match_string(cpu_np, "power-domain-names", "perf");
|
||||
np = of_parse_phandle(cpu_np, "power-domains", idx);
|
||||
of_node_put(np);
|
||||
|
||||
if (np == scmi_np)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int scmi_cpufreq_probe(struct scmi_device *sdev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -401,7 +435,7 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
|
|||
|
||||
handle = sdev->handle;
|
||||
|
||||
if (!handle)
|
||||
if (!handle || !scmi_dev_used_by_cpus(dev))
|
||||
return -ENODEV;
|
||||
|
||||
scmi_cpufreq_driver.driver_data = sdev;
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
#include <linux/blk-mq.h>
|
||||
#include <linux/blk_types.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/configfs.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/device/faux.h>
|
||||
|
@ -35,6 +38,7 @@
|
|||
#include <linux/phy.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/refcount.h>
|
||||
|
|
66
rust/helpers/clk.c
Normal file
66
rust/helpers/clk.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
||||
/*
|
||||
* The "inline" implementation of below helpers are only available when
|
||||
* CONFIG_HAVE_CLK or CONFIG_HAVE_CLK_PREPARE aren't set.
|
||||
*/
|
||||
#ifndef CONFIG_HAVE_CLK
|
||||
struct clk *rust_helper_clk_get(struct device *dev, const char *id)
|
||||
{
|
||||
return clk_get(dev, id);
|
||||
}
|
||||
|
||||
void rust_helper_clk_put(struct clk *clk)
|
||||
{
|
||||
clk_put(clk);
|
||||
}
|
||||
|
||||
int rust_helper_clk_enable(struct clk *clk)
|
||||
{
|
||||
return clk_enable(clk);
|
||||
}
|
||||
|
||||
void rust_helper_clk_disable(struct clk *clk)
|
||||
{
|
||||
clk_disable(clk);
|
||||
}
|
||||
|
||||
unsigned long rust_helper_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
return clk_get_rate(clk);
|
||||
}
|
||||
|
||||
int rust_helper_clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
return clk_set_rate(clk, rate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_HAVE_CLK_PREPARE
|
||||
int rust_helper_clk_prepare(struct clk *clk)
|
||||
{
|
||||
return clk_prepare(clk);
|
||||
}
|
||||
|
||||
void rust_helper_clk_unprepare(struct clk *clk)
|
||||
{
|
||||
clk_unprepare(clk);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct clk *rust_helper_clk_get_optional(struct device *dev, const char *id)
|
||||
{
|
||||
return clk_get_optional(dev, id);
|
||||
}
|
||||
|
||||
int rust_helper_clk_prepare_enable(struct clk *clk)
|
||||
{
|
||||
return clk_prepare_enable(clk);
|
||||
}
|
||||
|
||||
void rust_helper_clk_disable_unprepare(struct clk *clk)
|
||||
{
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
10
rust/helpers/cpufreq.c
Normal file
10
rust/helpers/cpufreq.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/cpufreq.h>
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
void rust_helper_cpufreq_register_em_with_opp(struct cpufreq_policy *policy)
|
||||
{
|
||||
cpufreq_register_em_with_opp(policy);
|
||||
}
|
||||
#endif
|
|
@ -7,16 +7,41 @@ void rust_helper_cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
|
|||
cpumask_set_cpu(cpu, dstp);
|
||||
}
|
||||
|
||||
void rust_helper___cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
|
||||
{
|
||||
__cpumask_set_cpu(cpu, dstp);
|
||||
}
|
||||
|
||||
void rust_helper_cpumask_clear_cpu(int cpu, struct cpumask *dstp)
|
||||
{
|
||||
cpumask_clear_cpu(cpu, dstp);
|
||||
}
|
||||
|
||||
void rust_helper___cpumask_clear_cpu(int cpu, struct cpumask *dstp)
|
||||
{
|
||||
__cpumask_clear_cpu(cpu, dstp);
|
||||
}
|
||||
|
||||
bool rust_helper_cpumask_test_cpu(int cpu, struct cpumask *srcp)
|
||||
{
|
||||
return cpumask_test_cpu(cpu, srcp);
|
||||
}
|
||||
|
||||
void rust_helper_cpumask_setall(struct cpumask *dstp)
|
||||
{
|
||||
cpumask_setall(dstp);
|
||||
}
|
||||
|
||||
bool rust_helper_cpumask_empty(struct cpumask *srcp)
|
||||
{
|
||||
return cpumask_empty(srcp);
|
||||
}
|
||||
|
||||
bool rust_helper_cpumask_full(struct cpumask *srcp)
|
||||
{
|
||||
return cpumask_full(srcp);
|
||||
}
|
||||
|
||||
unsigned int rust_helper_cpumask_weight(struct cpumask *srcp)
|
||||
{
|
||||
return cpumask_weight(srcp);
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "bug.c"
|
||||
#include "build_assert.c"
|
||||
#include "build_bug.c"
|
||||
#include "clk.c"
|
||||
#include "cpufreq.c"
|
||||
#include "cpumask.c"
|
||||
#include "cred.c"
|
||||
#include "device.c"
|
||||
|
|
334
rust/kernel/clk.rs
Normal file
334
rust/kernel/clk.rs
Normal file
|
@ -0,0 +1,334 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Clock abstractions.
|
||||
//!
|
||||
//! C header: [`include/linux/clk.h`](srctree/include/linux/clk.h)
|
||||
//!
|
||||
//! Reference: <https://docs.kernel.org/driver-api/clk.html>
|
||||
|
||||
use crate::ffi::c_ulong;
|
||||
|
||||
/// The frequency unit.
|
||||
///
|
||||
/// Represents a frequency in hertz, wrapping a [`c_ulong`] value.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::clk::Hertz;
|
||||
///
|
||||
/// let hz = 1_000_000_000;
|
||||
/// let rate = Hertz(hz);
|
||||
///
|
||||
/// assert_eq!(rate.as_hz(), hz);
|
||||
/// assert_eq!(rate, Hertz(hz));
|
||||
/// assert_eq!(rate, Hertz::from_khz(hz / 1_000));
|
||||
/// assert_eq!(rate, Hertz::from_mhz(hz / 1_000_000));
|
||||
/// assert_eq!(rate, Hertz::from_ghz(hz / 1_000_000_000));
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Hertz(pub c_ulong);
|
||||
|
||||
impl Hertz {
|
||||
/// Create a new instance from kilohertz (kHz)
|
||||
pub fn from_khz(khz: c_ulong) -> Self {
|
||||
Self(khz * 1_000)
|
||||
}
|
||||
|
||||
/// Create a new instance from megahertz (MHz)
|
||||
pub fn from_mhz(mhz: c_ulong) -> Self {
|
||||
Self(mhz * 1_000_000)
|
||||
}
|
||||
|
||||
/// Create a new instance from gigahertz (GHz)
|
||||
pub fn from_ghz(ghz: c_ulong) -> Self {
|
||||
Self(ghz * 1_000_000_000)
|
||||
}
|
||||
|
||||
/// Get the frequency in hertz
|
||||
pub fn as_hz(&self) -> c_ulong {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Get the frequency in kilohertz
|
||||
pub fn as_khz(&self) -> c_ulong {
|
||||
self.0 / 1_000
|
||||
}
|
||||
|
||||
/// Get the frequency in megahertz
|
||||
pub fn as_mhz(&self) -> c_ulong {
|
||||
self.0 / 1_000_000
|
||||
}
|
||||
|
||||
/// Get the frequency in gigahertz
|
||||
pub fn as_ghz(&self) -> c_ulong {
|
||||
self.0 / 1_000_000_000
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hertz> for c_ulong {
|
||||
fn from(freq: Hertz) -> Self {
|
||||
freq.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(CONFIG_COMMON_CLK)]
|
||||
mod common_clk {
|
||||
use super::Hertz;
|
||||
use crate::{
|
||||
device::Device,
|
||||
error::{from_err_ptr, to_result, Result},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use core::{ops::Deref, ptr};
|
||||
|
||||
/// A reference-counted clock.
|
||||
///
|
||||
/// Rust abstraction for the C [`struct clk`].
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C
|
||||
/// portion of the kernel or a NULL pointer.
|
||||
///
|
||||
/// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the
|
||||
/// allocation remains valid for the lifetime of the [`Clk`].
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// The following example demonstrates how to obtain and configure a clock for a device.
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::c_str;
|
||||
/// use kernel::clk::{Clk, Hertz};
|
||||
/// use kernel::device::Device;
|
||||
/// use kernel::error::Result;
|
||||
///
|
||||
/// fn configure_clk(dev: &Device) -> Result {
|
||||
/// let clk = Clk::get(dev, Some(c_str!("apb_clk")))?;
|
||||
///
|
||||
/// clk.prepare_enable()?;
|
||||
///
|
||||
/// let expected_rate = Hertz::from_ghz(1);
|
||||
///
|
||||
/// if clk.rate() != expected_rate {
|
||||
/// clk.set_rate(expected_rate)?;
|
||||
/// }
|
||||
///
|
||||
/// clk.disable_unprepare();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
|
||||
#[repr(transparent)]
|
||||
pub struct Clk(*mut bindings::clk);
|
||||
|
||||
impl Clk {
|
||||
/// Gets [`Clk`] corresponding to a [`Device`] and a connection id.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_get`] API.
|
||||
///
|
||||
/// [`clk_get`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get
|
||||
pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
|
||||
let con_id = if let Some(name) = name {
|
||||
name.as_ptr()
|
||||
} else {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
// SAFETY: It is safe to call [`clk_get`] for a valid device pointer.
|
||||
//
|
||||
// INVARIANT: The reference-count is decremented when [`Clk`] goes out of scope.
|
||||
Ok(Self(from_err_ptr(unsafe {
|
||||
bindings::clk_get(dev.as_raw(), con_id)
|
||||
})?))
|
||||
}
|
||||
|
||||
/// Obtain the raw [`struct clk`] pointer.
|
||||
#[inline]
|
||||
pub fn as_raw(&self) -> *mut bindings::clk {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Enable the clock.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_enable`] API.
|
||||
///
|
||||
/// [`clk_enable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_enable
|
||||
#[inline]
|
||||
pub fn enable(&self) -> Result {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_enable`].
|
||||
to_result(unsafe { bindings::clk_enable(self.as_raw()) })
|
||||
}
|
||||
|
||||
/// Disable the clock.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_disable`] API.
|
||||
///
|
||||
/// [`clk_disable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_disable
|
||||
#[inline]
|
||||
pub fn disable(&self) {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_disable`].
|
||||
unsafe { bindings::clk_disable(self.as_raw()) };
|
||||
}
|
||||
|
||||
/// Prepare the clock.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_prepare`] API.
|
||||
///
|
||||
/// [`clk_prepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_prepare
|
||||
#[inline]
|
||||
pub fn prepare(&self) -> Result {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_prepare`].
|
||||
to_result(unsafe { bindings::clk_prepare(self.as_raw()) })
|
||||
}
|
||||
|
||||
/// Unprepare the clock.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_unprepare`] API.
|
||||
///
|
||||
/// [`clk_unprepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_unprepare
|
||||
#[inline]
|
||||
pub fn unprepare(&self) {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_unprepare`].
|
||||
unsafe { bindings::clk_unprepare(self.as_raw()) };
|
||||
}
|
||||
|
||||
/// Prepare and enable the clock.
|
||||
///
|
||||
/// Equivalent to calling [`Clk::prepare`] followed by [`Clk::enable`].
|
||||
#[inline]
|
||||
pub fn prepare_enable(&self) -> Result {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_prepare_enable`].
|
||||
to_result(unsafe { bindings::clk_prepare_enable(self.as_raw()) })
|
||||
}
|
||||
|
||||
/// Disable and unprepare the clock.
|
||||
///
|
||||
/// Equivalent to calling [`Clk::disable`] followed by [`Clk::unprepare`].
|
||||
#[inline]
|
||||
pub fn disable_unprepare(&self) {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_disable_unprepare`].
|
||||
unsafe { bindings::clk_disable_unprepare(self.as_raw()) };
|
||||
}
|
||||
|
||||
/// Get clock's rate.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_get_rate`] API.
|
||||
///
|
||||
/// [`clk_get_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_rate
|
||||
#[inline]
|
||||
pub fn rate(&self) -> Hertz {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_get_rate`].
|
||||
Hertz(unsafe { bindings::clk_get_rate(self.as_raw()) })
|
||||
}
|
||||
|
||||
/// Set clock's rate.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_set_rate`] API.
|
||||
///
|
||||
/// [`clk_set_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_set_rate
|
||||
#[inline]
|
||||
pub fn set_rate(&self, rate: Hertz) -> Result {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
||||
// [`clk_set_rate`].
|
||||
to_result(unsafe { bindings::clk_set_rate(self.as_raw(), rate.as_hz()) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Clk {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_put`].
|
||||
unsafe { bindings::clk_put(self.as_raw()) };
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference-counted optional clock.
|
||||
///
|
||||
/// A lightweight wrapper around an optional [`Clk`]. An [`OptionalClk`] represents a [`Clk`]
|
||||
/// that a driver can function without but may improve performance or enable additional
|
||||
/// features when available.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// An [`OptionalClk`] instance encapsulates a [`Clk`] with either a valid [`struct clk`] or
|
||||
/// `NULL` pointer.
|
||||
///
|
||||
/// Instances of this type are reference-counted. Calling [`OptionalClk::get`] ensures that the
|
||||
/// allocation remains valid for the lifetime of the [`OptionalClk`].
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// The following example demonstrates how to obtain and configure an optional clock for a
|
||||
/// device. The code functions correctly whether or not the clock is available.
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::c_str;
|
||||
/// use kernel::clk::{OptionalClk, Hertz};
|
||||
/// use kernel::device::Device;
|
||||
/// use kernel::error::Result;
|
||||
///
|
||||
/// fn configure_clk(dev: &Device) -> Result {
|
||||
/// let clk = OptionalClk::get(dev, Some(c_str!("apb_clk")))?;
|
||||
///
|
||||
/// clk.prepare_enable()?;
|
||||
///
|
||||
/// let expected_rate = Hertz::from_ghz(1);
|
||||
///
|
||||
/// if clk.rate() != expected_rate {
|
||||
/// clk.set_rate(expected_rate)?;
|
||||
/// }
|
||||
///
|
||||
/// clk.disable_unprepare();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
|
||||
pub struct OptionalClk(Clk);
|
||||
|
||||
impl OptionalClk {
|
||||
/// Gets [`OptionalClk`] corresponding to a [`Device`] and a connection id.
|
||||
///
|
||||
/// Equivalent to the kernel's [`clk_get_optional`] API.
|
||||
///
|
||||
/// [`clk_get_optional`]:
|
||||
/// https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_optional
|
||||
pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
|
||||
let con_id = if let Some(name) = name {
|
||||
name.as_ptr()
|
||||
} else {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
// SAFETY: It is safe to call [`clk_get_optional`] for a valid device pointer.
|
||||
//
|
||||
// INVARIANT: The reference-count is decremented when [`OptionalClk`] goes out of
|
||||
// scope.
|
||||
Ok(Self(Clk(from_err_ptr(unsafe {
|
||||
bindings::clk_get_optional(dev.as_raw(), con_id)
|
||||
})?)))
|
||||
}
|
||||
}
|
||||
|
||||
// Make [`OptionalClk`] behave like [`Clk`].
|
||||
impl Deref for OptionalClk {
|
||||
type Target = Clk;
|
||||
|
||||
fn deref(&self) -> &Clk {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(CONFIG_COMMON_CLK)]
|
||||
pub use common_clk::*;
|
30
rust/kernel/cpu.rs
Normal file
30
rust/kernel/cpu.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Generic CPU definitions.
|
||||
//!
|
||||
//! C header: [`include/linux/cpu.h`](srctree/include/linux/cpu.h)
|
||||
|
||||
use crate::{bindings, device::Device, error::Result, prelude::ENODEV};
|
||||
|
||||
/// Creates a new instance of CPU's device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Reference counting is not implemented for the CPU device in the C code. When a CPU is
|
||||
/// hot-unplugged, the corresponding CPU device is unregistered, but its associated memory
|
||||
/// is not freed.
|
||||
///
|
||||
/// Callers must ensure that the CPU device is not used after it has been unregistered.
|
||||
/// This can be achieved, for example, by registering a CPU hotplug notifier and removing
|
||||
/// any references to the CPU device within the notifier's callback.
|
||||
pub unsafe fn from_cpu(cpu: u32) -> Result<&'static Device> {
|
||||
// SAFETY: It is safe to call `get_cpu_device()` for any CPU.
|
||||
let ptr = unsafe { bindings::get_cpu_device(cpu) };
|
||||
if ptr.is_null() {
|
||||
return Err(ENODEV);
|
||||
}
|
||||
|
||||
// SAFETY: The pointer returned by `get_cpu_device()`, if not `NULL`, is a valid pointer to
|
||||
// a `struct device` and is never freed by the C code.
|
||||
Ok(unsafe { Device::as_ref(ptr) })
|
||||
}
|
1321
rust/kernel/cpufreq.rs
Normal file
1321
rust/kernel/cpufreq.rs
Normal file
File diff suppressed because it is too large
Load Diff
330
rust/kernel/cpumask.rs
Normal file
330
rust/kernel/cpumask.rs
Normal file
|
@ -0,0 +1,330 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! CPU Mask abstractions.
|
||||
//!
|
||||
//! C header: [`include/linux/cpumask.h`](srctree/include/linux/cpumask.h)
|
||||
|
||||
use crate::{
|
||||
alloc::{AllocError, Flags},
|
||||
prelude::*,
|
||||
types::Opaque,
|
||||
};
|
||||
|
||||
#[cfg(CONFIG_CPUMASK_OFFSTACK)]
|
||||
use core::ptr::{self, NonNull};
|
||||
|
||||
#[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
/// A CPU Mask.
|
||||
///
|
||||
/// Rust abstraction for the C `struct cpumask`.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// A [`Cpumask`] instance always corresponds to a valid C `struct cpumask`.
|
||||
///
|
||||
/// The callers must ensure that the `struct cpumask` is valid for access and
|
||||
/// remains valid for the lifetime of the returned reference.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// The following example demonstrates how to update a [`Cpumask`].
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::bindings;
|
||||
/// use kernel::cpumask::Cpumask;
|
||||
///
|
||||
/// fn set_clear_cpu(ptr: *mut bindings::cpumask, set_cpu: u32, clear_cpu: i32) {
|
||||
/// // SAFETY: The `ptr` is valid for writing and remains valid for the lifetime of the
|
||||
/// // returned reference.
|
||||
/// let mask = unsafe { Cpumask::as_mut_ref(ptr) };
|
||||
///
|
||||
/// mask.set(set_cpu);
|
||||
/// mask.clear(clear_cpu);
|
||||
/// }
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
pub struct Cpumask(Opaque<bindings::cpumask>);
|
||||
|
||||
impl Cpumask {
|
||||
/// Creates a mutable reference to an existing `struct cpumask` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime
|
||||
/// of the returned reference.
|
||||
pub unsafe fn as_mut_ref<'a>(ptr: *mut bindings::cpumask) -> &'a mut Self {
|
||||
// SAFETY: Guaranteed by the safety requirements of the function.
|
||||
//
|
||||
// INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the
|
||||
// lifetime of the returned reference.
|
||||
unsafe { &mut *ptr.cast() }
|
||||
}
|
||||
|
||||
/// Creates a reference to an existing `struct cpumask` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime
|
||||
/// of the returned reference.
|
||||
pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self {
|
||||
// SAFETY: Guaranteed by the safety requirements of the function.
|
||||
//
|
||||
// INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the
|
||||
// lifetime of the returned reference.
|
||||
unsafe { &*ptr.cast() }
|
||||
}
|
||||
|
||||
/// Obtain the raw `struct cpumask` pointer.
|
||||
pub fn as_raw(&self) -> *mut bindings::cpumask {
|
||||
let this: *const Self = self;
|
||||
this.cast_mut().cast()
|
||||
}
|
||||
|
||||
/// Set `cpu` in the cpumask.
|
||||
///
|
||||
/// ATTENTION: Contrary to C, this Rust `set()` method is non-atomic.
|
||||
/// This mismatches kernel naming convention and corresponds to the C
|
||||
/// function `__cpumask_set_cpu()`.
|
||||
#[inline]
|
||||
pub fn set(&mut self, cpu: u32) {
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `__cpumask_set_cpu`.
|
||||
unsafe { bindings::__cpumask_set_cpu(cpu, self.as_raw()) };
|
||||
}
|
||||
|
||||
/// Clear `cpu` in the cpumask.
|
||||
///
|
||||
/// ATTENTION: Contrary to C, this Rust `clear()` method is non-atomic.
|
||||
/// This mismatches kernel naming convention and corresponds to the C
|
||||
/// function `__cpumask_clear_cpu()`.
|
||||
#[inline]
|
||||
pub fn clear(&mut self, cpu: i32) {
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to
|
||||
// `__cpumask_clear_cpu`.
|
||||
unsafe { bindings::__cpumask_clear_cpu(cpu, self.as_raw()) };
|
||||
}
|
||||
|
||||
/// Test `cpu` in the cpumask.
|
||||
///
|
||||
/// Equivalent to the kernel's `cpumask_test_cpu` API.
|
||||
#[inline]
|
||||
pub fn test(&self, cpu: i32) -> bool {
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_test_cpu`.
|
||||
unsafe { bindings::cpumask_test_cpu(cpu, self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Set all CPUs in the cpumask.
|
||||
///
|
||||
/// Equivalent to the kernel's `cpumask_setall` API.
|
||||
#[inline]
|
||||
pub fn setall(&mut self) {
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_setall`.
|
||||
unsafe { bindings::cpumask_setall(self.as_raw()) };
|
||||
}
|
||||
|
||||
/// Checks if cpumask is empty.
|
||||
///
|
||||
/// Equivalent to the kernel's `cpumask_empty` API.
|
||||
#[inline]
|
||||
pub fn empty(&self) -> bool {
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_empty`.
|
||||
unsafe { bindings::cpumask_empty(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Checks if cpumask is full.
|
||||
///
|
||||
/// Equivalent to the kernel's `cpumask_full` API.
|
||||
#[inline]
|
||||
pub fn full(&self) -> bool {
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_full`.
|
||||
unsafe { bindings::cpumask_full(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Get weight of the cpumask.
|
||||
///
|
||||
/// Equivalent to the kernel's `cpumask_weight` API.
|
||||
#[inline]
|
||||
pub fn weight(&self) -> u32 {
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_weight`.
|
||||
unsafe { bindings::cpumask_weight(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Copy cpumask.
|
||||
///
|
||||
/// Equivalent to the kernel's `cpumask_copy` API.
|
||||
#[inline]
|
||||
pub fn copy(&self, dstp: &mut Self) {
|
||||
// SAFETY: By the type invariant, `Self::as_raw` is a valid argument to `cpumask_copy`.
|
||||
unsafe { bindings::cpumask_copy(dstp.as_raw(), self.as_raw()) };
|
||||
}
|
||||
}
|
||||
|
||||
/// A CPU Mask pointer.
|
||||
///
|
||||
/// Rust abstraction for the C `struct cpumask_var_t`.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// A [`CpumaskVar`] instance always corresponds to a valid C `struct cpumask_var_t`.
|
||||
///
|
||||
/// The callers must ensure that the `struct cpumask_var_t` is valid for access and remains valid
|
||||
/// for the lifetime of [`CpumaskVar`].
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// The following example demonstrates how to create and update a [`CpumaskVar`].
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::cpumask::CpumaskVar;
|
||||
///
|
||||
/// let mut mask = CpumaskVar::new_zero(GFP_KERNEL).unwrap();
|
||||
///
|
||||
/// assert!(mask.empty());
|
||||
/// mask.set(2);
|
||||
/// assert!(mask.test(2));
|
||||
/// mask.set(3);
|
||||
/// assert!(mask.test(3));
|
||||
/// assert_eq!(mask.weight(), 2);
|
||||
///
|
||||
/// let mask2 = CpumaskVar::try_clone(&mask).unwrap();
|
||||
/// assert!(mask2.test(2));
|
||||
/// assert!(mask2.test(3));
|
||||
/// assert_eq!(mask2.weight(), 2);
|
||||
/// ```
|
||||
pub struct CpumaskVar {
|
||||
#[cfg(CONFIG_CPUMASK_OFFSTACK)]
|
||||
ptr: NonNull<Cpumask>,
|
||||
#[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
|
||||
mask: Cpumask,
|
||||
}
|
||||
|
||||
impl CpumaskVar {
|
||||
/// Creates a zero-initialized instance of the [`CpumaskVar`].
|
||||
pub fn new_zero(_flags: Flags) -> Result<Self, AllocError> {
|
||||
Ok(Self {
|
||||
#[cfg(CONFIG_CPUMASK_OFFSTACK)]
|
||||
ptr: {
|
||||
let mut ptr: *mut bindings::cpumask = ptr::null_mut();
|
||||
|
||||
// SAFETY: It is safe to call this method as the reference to `ptr` is valid.
|
||||
//
|
||||
// INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of
|
||||
// scope.
|
||||
unsafe { bindings::zalloc_cpumask_var(&mut ptr, _flags.as_raw()) };
|
||||
NonNull::new(ptr.cast()).ok_or(AllocError)?
|
||||
},
|
||||
|
||||
#[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
|
||||
// SAFETY: FFI type is valid to be zero-initialized.
|
||||
//
|
||||
// INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of scope.
|
||||
mask: unsafe { core::mem::zeroed() },
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates an instance of the [`CpumaskVar`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the returned [`CpumaskVar`] is properly initialized before
|
||||
/// getting used.
|
||||
pub unsafe fn new(_flags: Flags) -> Result<Self, AllocError> {
|
||||
Ok(Self {
|
||||
#[cfg(CONFIG_CPUMASK_OFFSTACK)]
|
||||
ptr: {
|
||||
let mut ptr: *mut bindings::cpumask = ptr::null_mut();
|
||||
|
||||
// SAFETY: It is safe to call this method as the reference to `ptr` is valid.
|
||||
//
|
||||
// INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of
|
||||
// scope.
|
||||
unsafe { bindings::alloc_cpumask_var(&mut ptr, _flags.as_raw()) };
|
||||
NonNull::new(ptr.cast()).ok_or(AllocError)?
|
||||
},
|
||||
#[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
|
||||
// SAFETY: Guaranteed by the safety requirements of the function.
|
||||
//
|
||||
// INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of scope.
|
||||
mask: unsafe { MaybeUninit::uninit().assume_init() },
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a mutable reference to an existing `struct cpumask_var_t` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime
|
||||
/// of the returned reference.
|
||||
pub unsafe fn as_mut_ref<'a>(ptr: *mut bindings::cpumask_var_t) -> &'a mut Self {
|
||||
// SAFETY: Guaranteed by the safety requirements of the function.
|
||||
//
|
||||
// INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the
|
||||
// lifetime of the returned reference.
|
||||
unsafe { &mut *ptr.cast() }
|
||||
}
|
||||
|
||||
/// Creates a reference to an existing `struct cpumask_var_t` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime
|
||||
/// of the returned reference.
|
||||
pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask_var_t) -> &'a Self {
|
||||
// SAFETY: Guaranteed by the safety requirements of the function.
|
||||
//
|
||||
// INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the
|
||||
// lifetime of the returned reference.
|
||||
unsafe { &*ptr.cast() }
|
||||
}
|
||||
|
||||
/// Clones cpumask.
|
||||
pub fn try_clone(cpumask: &Cpumask) -> Result<Self> {
|
||||
// SAFETY: The returned cpumask_var is initialized right after this call.
|
||||
let mut cpumask_var = unsafe { Self::new(GFP_KERNEL) }?;
|
||||
|
||||
cpumask.copy(&mut cpumask_var);
|
||||
Ok(cpumask_var)
|
||||
}
|
||||
}
|
||||
|
||||
// Make [`CpumaskVar`] behave like a pointer to [`Cpumask`].
|
||||
impl Deref for CpumaskVar {
|
||||
type Target = Cpumask;
|
||||
|
||||
#[cfg(CONFIG_CPUMASK_OFFSTACK)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask.
|
||||
unsafe { &*self.ptr.as_ptr() }
|
||||
}
|
||||
|
||||
#[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.mask
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for CpumaskVar {
|
||||
#[cfg(CONFIG_CPUMASK_OFFSTACK)]
|
||||
fn deref_mut(&mut self) -> &mut Cpumask {
|
||||
// SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask.
|
||||
unsafe { self.ptr.as_mut() }
|
||||
}
|
||||
|
||||
#[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
|
||||
fn deref_mut(&mut self) -> &mut Cpumask {
|
||||
&mut self.mask
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CpumaskVar {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(CONFIG_CPUMASK_OFFSTACK)]
|
||||
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `free_cpumask_var`.
|
||||
unsafe {
|
||||
bindings::free_cpumask_var(self.as_raw())
|
||||
};
|
||||
}
|
||||
}
|
|
@ -44,8 +44,13 @@ pub mod auxiliary;
|
|||
pub mod block;
|
||||
#[doc(hidden)]
|
||||
pub mod build_assert;
|
||||
pub mod clk;
|
||||
#[cfg(CONFIG_CONFIGFS_FS)]
|
||||
pub mod configfs;
|
||||
pub mod cpu;
|
||||
#[cfg(CONFIG_CPU_FREQ)]
|
||||
pub mod cpufreq;
|
||||
pub mod cpumask;
|
||||
pub mod cred;
|
||||
pub mod device;
|
||||
pub mod device_id;
|
||||
|
@ -70,6 +75,8 @@ pub mod miscdevice;
|
|||
#[cfg(CONFIG_NET)]
|
||||
pub mod net;
|
||||
pub mod of;
|
||||
#[cfg(CONFIG_PM_OPP)]
|
||||
pub mod opp;
|
||||
pub mod page;
|
||||
#[cfg(CONFIG_PCI)]
|
||||
pub mod pci;
|
||||
|
|
1146
rust/kernel/opp.rs
Normal file
1146
rust/kernel/opp.rs
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -176,7 +176,9 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
|||
|
||||
let info = ModuleInfo::parse(&mut it);
|
||||
|
||||
let mut modinfo = ModInfoBuilder::new(info.name.as_ref());
|
||||
// Rust does not allow hyphens in identifiers, use underscore instead.
|
||||
let ident = info.name.replace('-', "_");
|
||||
let mut modinfo = ModInfoBuilder::new(ident.as_ref());
|
||||
if let Some(author) = info.author {
|
||||
modinfo.emit("author", &author);
|
||||
}
|
||||
|
@ -301,14 +303,15 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
|||
#[doc(hidden)]
|
||||
#[link_section = \"{initcall_section}\"]
|
||||
#[used]
|
||||
pub static __{name}_initcall: extern \"C\" fn() -> kernel::ffi::c_int = __{name}_init;
|
||||
pub static __{ident}_initcall: extern \"C\" fn() ->
|
||||
kernel::ffi::c_int = __{ident}_init;
|
||||
|
||||
#[cfg(not(MODULE))]
|
||||
#[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
|
||||
core::arch::global_asm!(
|
||||
r#\".section \"{initcall_section}\", \"a\"
|
||||
__{name}_initcall:
|
||||
.long __{name}_init - .
|
||||
__{ident}_initcall:
|
||||
.long __{ident}_init - .
|
||||
.previous
|
||||
\"#
|
||||
);
|
||||
|
@ -316,7 +319,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
|||
#[cfg(not(MODULE))]
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
pub extern \"C\" fn __{name}_init() -> kernel::ffi::c_int {{
|
||||
pub extern \"C\" fn __{ident}_init() -> kernel::ffi::c_int {{
|
||||
// SAFETY: This function is inaccessible to the outside due to the double
|
||||
// module wrapping it. It is called exactly once by the C side via its
|
||||
// placement above in the initcall section.
|
||||
|
@ -326,13 +329,13 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
|||
#[cfg(not(MODULE))]
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
pub extern \"C\" fn __{name}_exit() {{
|
||||
pub extern \"C\" fn __{ident}_exit() {{
|
||||
// SAFETY:
|
||||
// - This function is inaccessible to the outside due to the double
|
||||
// module wrapping it. It is called exactly once by the C side via its
|
||||
// unique name,
|
||||
// - furthermore it is only called after `__{name}_init` has returned `0`
|
||||
// (which delegates to `__init`).
|
||||
// - furthermore it is only called after `__{ident}_init` has
|
||||
// returned `0` (which delegates to `__init`).
|
||||
unsafe {{ __exit() }}
|
||||
}}
|
||||
|
||||
|
@ -372,6 +375,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
|||
",
|
||||
type_ = info.type_,
|
||||
name = info.name,
|
||||
ident = ident,
|
||||
modinfo = modinfo.buffer,
|
||||
initcall_section = ".initcall6.init"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user