Skip to content

Commit f232e20

Browse files
committed
cpumask: add functions similar to for_each_*_cpu
This commit adds three iterators which returns the CPU index. They are PossibleCpusIndexIter, OnlineCpusIndexIter, and PresentCpusIndexIter corresponding to C macros for_each_possible_cpu, for_each_online_cpu, and for_each_present_cpu exclusively. Signed-off-by: Li Hongyu <[email protected]>
1 parent a0c954b commit f232e20

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

rust/kernel/cpumask.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Cpumask variables and related functions.
4+
//!
5+
//! C header: [`include/linux/cpumask.h`](../../../../include/linux/cpumask.h).
6+
7+
use crate::bindings;
8+
use core::iter::Iterator;
9+
10+
/// A valid CPU index.
11+
///
12+
/// # Safety
13+
///
14+
/// - The 'ValidCpuIndex' should be used during the iteration of a CPU index iterator.
15+
pub struct ValidCpuIndex(u32);
16+
17+
impl ValidCpuIndex {
18+
/// Get the valid CPU index in u32.
19+
pub fn get(&self) -> u32 {
20+
self.0
21+
}
22+
}
23+
24+
/// An possible CPU index iterator.
25+
///
26+
/// This iterator has a similar abilitiy to the kernel's macro `for_each_possible_cpu`.
27+
pub struct PossibleCpusIndexIter {
28+
index: i32,
29+
}
30+
31+
/// An online CPU index iterator.
32+
///
33+
/// This iterator has a similar abilitiy to the kernel's macro `for_each_online_cpu`.
34+
pub struct OnlineCpusIndexIter {
35+
index: i32,
36+
}
37+
38+
/// An present CPU index iterator.
39+
///
40+
/// This iterator has a similar abilitiy to the kernel's macro `for_each_present_cpu`.
41+
pub struct PresentCpusIndexIter {
42+
index: i32,
43+
}
44+
45+
impl Iterator for PossibleCpusIndexIter {
46+
type Item = ValidCpuIndex;
47+
48+
fn next(&mut self) -> Option<ValidCpuIndex> {
49+
let next_cpu_id =
50+
// SAFETY: Since [`bindings::__cpu_possible_mask`] will not change, there will not
51+
// be data race in this part. When the last valid CPU index is found, this iterator
52+
// will return `None`. Therefore, the index parameter is always valid.
53+
unsafe { bindings::cpumask_next(self.index, &bindings::__cpu_possible_mask) };
54+
// When [`bindings::cpumask_next`] can not find further CPUs set in the
55+
// [`bindings::__cpu_possible_mask`], it returns a value >= [`bindings::nr_cpu_ids`].
56+
//
57+
// SAFETY: The [`bindings::nr_cpu_ids`] is fixed at the boot time.
58+
if next_cpu_id >= unsafe { bindings::nr_cpu_ids } {
59+
return None;
60+
}
61+
self.index = next_cpu_id as i32;
62+
Some(ValidCpuIndex(next_cpu_id))
63+
}
64+
}
65+
66+
impl Iterator for OnlineCpusIndexIter {
67+
type Item = ValidCpuIndex;
68+
69+
fn next(&mut self) -> Option<ValidCpuIndex> {
70+
if self.index == -1 {
71+
// The [`bindings::__cpu_online_mask`] and [`bindings::nr_cpu_ids`] may chanage if
72+
// `CONFIG_HOTPLUG_CPU` is enabled. In case of race condition, a lock is needed
73+
// here. If `CONFIG_HOTPLUG_CPU` is disabled, this function will not have any cost.
74+
//
75+
// SAFETY: FFI call, this is called once during iteration in case of dead lock.
76+
unsafe { bindings::cpus_read_lock() };
77+
}
78+
let next_cpu_id =
79+
// SAFETY: The [`bindings::cpus_read_lock`] prevents the data race. When the last
80+
// valid CPU index is found, this iterator will return `None`. Therefore, the
81+
// index parameter is always valid.
82+
unsafe { bindings::cpumask_next(self.index, &bindings::__cpu_online_mask) };
83+
// When [`bindings::cpumask_next`] can not find further CPUs set in the
84+
// [`bindings::__cpu_online_mask`], it returns a value >= [`bindings::nr_cpu_ids`].
85+
//
86+
// SAFETY: The [`bindings::nr_cpu_ids`] is fixed at the boot time.
87+
if next_cpu_id >= unsafe { bindings::nr_cpu_ids } {
88+
// Unlock after finishing iteration.
89+
//
90+
// SAFETY: FFI call.
91+
unsafe { bindings::cpus_read_unlock() };
92+
return None;
93+
}
94+
self.index = next_cpu_id as i32;
95+
Some(ValidCpuIndex(next_cpu_id))
96+
}
97+
}
98+
99+
impl Iterator for PresentCpusIndexIter {
100+
type Item = ValidCpuIndex;
101+
102+
fn next(&mut self) -> Option<ValidCpuIndex> {
103+
if self.index == -1 {
104+
// The [`bindings::__cpu_present_mask`] and [`bindings::nr_cpu_ids`] may chanage
105+
// if `CONFIG_HOTPLUG_CPU` is enabled. In case of race condition, a lock is needed
106+
// here. If `CONFIG_HOTPLUG_CPU` is disabled, this function will not have any cost.
107+
//
108+
// SAFETY: FFI call, this is called once during iteration in case of dead lock.
109+
unsafe { bindings::cpus_read_lock() };
110+
}
111+
let next_cpu_id =
112+
// SAFETY: The [`bindings::cpus_read_lock`] prevents the data race. When the last
113+
// valid CPU index is found, this iterator will return `None`. Therefore, the
114+
// index parameter is always valid.
115+
unsafe { bindings::cpumask_next(self.index, &bindings::__cpu_present_mask) };
116+
// When [`bindings::cpumask_next`] can not find further CPUs set in the
117+
// [`bindings::__cpu_present_mask`], it returns a value >= [`bindings::nr_cpu_ids`].
118+
//
119+
// SAFETY: The [`bindings::nr_cpu_ids`] is fixed at the boot time.
120+
if next_cpu_id >= unsafe { bindings::nr_cpu_ids } {
121+
// Unlock after finishing iteration.
122+
//
123+
// SAFETY: FFI call.
124+
unsafe { bindings::cpus_read_unlock() };
125+
return None;
126+
}
127+
self.index = next_cpu_id as i32;
128+
Some(ValidCpuIndex(next_cpu_id))
129+
}
130+
}
131+
132+
/// Returns a [`PossibleCpusIndexIter`] that gives the possible CPU indexes.
133+
///
134+
/// # Examples
135+
///
136+
/// ```
137+
/// # use kernel::prelude::*;
138+
/// # use kernel::cpumask::possible_cpus;
139+
///
140+
/// fn example() {
141+
/// // This prints all the possible cpu indexes.
142+
/// for cpu in possible_cpus(){
143+
/// pr_info!("{}\n", cpu.get());
144+
/// }
145+
/// }
146+
/// ```
147+
pub fn possible_cpus() -> PossibleCpusIndexIter {
148+
// Initial index is set to -1. Since [`bindings::cpumask_next`] return the next set bit in a
149+
// [`bindings::__cpu_possible_mask`], the CPU index should begins from 0.
150+
PossibleCpusIndexIter { index: -1 }
151+
}
152+
153+
/// Returns a [`OnlineCpusIndexIter`] that gives the online CPU indexes.
154+
///
155+
/// # Examples
156+
///
157+
/// ```
158+
/// # use kernel::prelude::*;
159+
/// # use kernel::cpumask::online_cpus;
160+
///
161+
/// fn example() {
162+
/// // This prints all the online cpu indexes.
163+
/// for cpu in online_cpus(){
164+
/// pr_info!("{}\n", cpu.get());
165+
/// }
166+
/// }
167+
/// ```
168+
pub fn online_cpus() -> OnlineCpusIndexIter {
169+
// Initial index is set to -1. Since [`bindings::cpumask_next`] return the next set bit in a
170+
// [`bindings::__cpu_online_mask`], the CPU index should begins from 0.
171+
OnlineCpusIndexIter { index: -1 }
172+
}
173+
174+
/// Returns a [`PresentCpusIndexIter`] that gives the present CPU indexes.
175+
///
176+
/// # Examples
177+
///
178+
/// ```
179+
/// # use kernel::prelude::*;
180+
/// # use kernel::cpumask::present_cpus;
181+
///
182+
/// fn example() {
183+
/// // This prints all the present cpu indexes.
184+
/// for cpu in present_cpus(){
185+
/// pr_info!("{}\n", cpu.get());
186+
/// }
187+
/// }
188+
/// ```
189+
pub fn present_cpus() -> PresentCpusIndexIter {
190+
// Initial index is set to -1. Since [`bindings::cpumask_next`] return the next set bit in a
191+
// [`bindings::__cpu_present_mask`], the CPU index should begins from 0.
192+
PresentCpusIndexIter { index: -1 }
193+
}

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub mod platform;
9393
mod types;
9494
pub mod user_ptr;
9595

96+
pub mod cpumask;
9697
#[doc(hidden)]
9798
pub use build_error::build_error;
9899

0 commit comments

Comments
 (0)