Skip to content

Commit 6bc6732

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 6bc6732

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

rust/kernel/cpumask.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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+
#[cfg(CONFIG_HOTPLUG_CPU)]
71+
if self.index == -1 {
72+
// The [`bindings::__cpu_online_mask`] and [`bindings::nr_cpu_ids`] may chanage if
73+
// `CONFIG_HOTPLUG_CPU` is enabled. In case of race condition, a lock is needed
74+
// here. If `CONFIG_HOTPLUG_CPU` is disabled, this function will not have any cost.
75+
//
76+
// SAFETY: FFI call, this is called once during iteration in case of dead lock.
77+
unsafe { bindings::cpus_read_lock() };
78+
}
79+
let next_cpu_id =
80+
// SAFETY: The [`bindings::cpus_read_lock`] prevents the data race. When the last
81+
// valid CPU index is found, this iterator will return `None`. Therefore, the
82+
// index parameter is always valid.
83+
unsafe { bindings::cpumask_next(self.index, &bindings::__cpu_online_mask) };
84+
// When [`bindings::cpumask_next`] can not find further CPUs set in the
85+
// [`bindings::__cpu_online_mask`], it returns a value >= [`bindings::nr_cpu_ids`].
86+
//
87+
// SAFETY: The [`bindings::nr_cpu_ids`] is fixed at the boot time.
88+
if next_cpu_id >= unsafe { bindings::nr_cpu_ids } {
89+
// Unlock after finishing iteration.
90+
//
91+
// SAFETY: FFI call.
92+
#[cfg(CONFIG_HOTPLUG_CPU)]
93+
unsafe {
94+
bindings::cpus_read_unlock()
95+
};
96+
return None;
97+
}
98+
self.index = next_cpu_id as i32;
99+
Some(ValidCpuIndex(next_cpu_id))
100+
}
101+
}
102+
103+
impl Iterator for PresentCpusIndexIter {
104+
type Item = ValidCpuIndex;
105+
106+
fn next(&mut self) -> Option<ValidCpuIndex> {
107+
#[cfg(CONFIG_HOTPLUG_CPU)]
108+
if self.index == -1 {
109+
// The [`bindings::__cpu_present_mask`] and [`bindings::nr_cpu_ids`] may chanage
110+
// if `CONFIG_HOTPLUG_CPU` is enabled. In case of race condition, a lock is needed
111+
// here. If `CONFIG_HOTPLUG_CPU` is disabled, this function will not have any cost.
112+
//
113+
// SAFETY: FFI call, this is called once during iteration in case of dead lock.
114+
unsafe { bindings::cpus_read_lock() };
115+
}
116+
let next_cpu_id =
117+
// SAFETY: The [`bindings::cpus_read_lock`] prevents the data race. When the last
118+
// valid CPU index is found, this iterator will return `None`. Therefore, the
119+
// index parameter is always valid.
120+
unsafe { bindings::cpumask_next(self.index, &bindings::__cpu_present_mask) };
121+
// When [`bindings::cpumask_next`] can not find further CPUs set in the
122+
// [`bindings::__cpu_present_mask`], it returns a value >= [`bindings::nr_cpu_ids`].
123+
//
124+
// SAFETY: The [`bindings::nr_cpu_ids`] is fixed at the boot time.
125+
if next_cpu_id >= unsafe { bindings::nr_cpu_ids } {
126+
// Unlock after finishing iteration.
127+
//
128+
// SAFETY: FFI call.
129+
#[cfg(CONFIG_HOTPLUG_CPU)]
130+
unsafe {
131+
bindings::cpus_read_unlock()
132+
};
133+
return None;
134+
}
135+
self.index = next_cpu_id as i32;
136+
Some(ValidCpuIndex(next_cpu_id))
137+
}
138+
}
139+
140+
/// Returns a [`PossibleCpusIndexIter`] that gives the possible CPU indexes.
141+
///
142+
/// # Examples
143+
///
144+
/// ```
145+
/// # use kernel::prelude::*;
146+
/// # use kernel::cpumask::possible_cpus;
147+
///
148+
/// fn example() {
149+
/// // This prints all the possible cpu indexes.
150+
/// for cpu in possible_cpus(){
151+
/// pr_info!("{}\n", cpu.get());
152+
/// }
153+
/// }
154+
/// ```
155+
pub fn possible_cpus() -> PossibleCpusIndexIter {
156+
// Initial index is set to -1. Since [`bindings::cpumask_next`] return the next set bit in a
157+
// [`bindings::__cpu_possible_mask`], the CPU index should begins from 0.
158+
PossibleCpusIndexIter { index: -1 }
159+
}
160+
161+
/// Returns a [`OnlineCpusIndexIter`] that gives the online CPU indexes.
162+
///
163+
/// # Examples
164+
///
165+
/// ```
166+
/// # use kernel::prelude::*;
167+
/// # use kernel::cpumask::online_cpus;
168+
///
169+
/// fn example() {
170+
/// // This prints all the online cpu indexes.
171+
/// for cpu in online_cpus(){
172+
/// pr_info!("{}\n", cpu.get());
173+
/// }
174+
/// }
175+
/// ```
176+
pub fn online_cpus() -> OnlineCpusIndexIter {
177+
// Initial index is set to -1. Since [`bindings::cpumask_next`] return the next set bit in a
178+
// [`bindings::__cpu_online_mask`], the CPU index should begins from 0.
179+
OnlineCpusIndexIter { index: -1 }
180+
}
181+
182+
/// Returns a [`PresentCpusIndexIter`] that gives the present CPU indexes.
183+
///
184+
/// # Examples
185+
///
186+
/// ```
187+
/// # use kernel::prelude::*;
188+
/// # use kernel::cpumask::present_cpus;
189+
///
190+
/// fn example() {
191+
/// // This prints all the present cpu indexes.
192+
/// for cpu in present_cpus(){
193+
/// pr_info!("{}\n", cpu.get());
194+
/// }
195+
/// }
196+
/// ```
197+
pub fn present_cpus() -> PresentCpusIndexIter {
198+
// Initial index is set to -1. Since [`bindings::cpumask_next`] return the next set bit in a
199+
// [`bindings::__cpu_present_mask`], the CPU index should begins from 0.
200+
PresentCpusIndexIter { index: -1 }
201+
}

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)