Skip to content

Commit 8972853

Browse files
Darksonnfbq
authored andcommitted
rust: sync: add CondVar::wait_timeout
Sleep on a condition variable with a timeout. This is used by Rust Binder for process freezing. There, we want to sleep until the freeze operation completes, but we want to be able to abort the process freezing if it doesn't complete within some timeout. Reviewed-by: Martin Rodriguez Reboredo <[email protected]> Reviewed-by: Tiago Lam <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected] [boqun: Resolve conflicts in task.rs] [boqun: Add missing use core::ffi::c_long]
1 parent 0f98e6b commit 8972853

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

rust/kernel/sync/condvar.rs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
//! variable.
77
88
use super::{lock::Backend, lock::Guard, LockClassKey};
9-
use crate::{bindings, init::PinInit, pin_init, str::CStr, types::Opaque};
9+
use crate::{
10+
bindings, init::PinInit, pin_init, str::CStr, task::MAX_SCHEDULE_TIMEOUT, time::Jiffies,
11+
types::Opaque,
12+
};
13+
use core::ffi::c_long;
1014
use core::marker::PhantomPinned;
1115
use macros::pin_data;
1216

@@ -102,7 +106,12 @@ impl CondVar {
102106
})
103107
}
104108

105-
fn wait_internal<T: ?Sized, B: Backend>(&self, wait_state: u32, guard: &mut Guard<'_, T, B>) {
109+
fn wait_internal<T: ?Sized, B: Backend>(
110+
&self,
111+
wait_state: u32,
112+
guard: &mut Guard<'_, T, B>,
113+
timeout_in_jiffies: c_long,
114+
) -> c_long {
106115
let wait = Opaque::<bindings::wait_queue_entry>::uninit();
107116

108117
// SAFETY: `wait` points to valid memory.
@@ -113,11 +122,13 @@ impl CondVar {
113122
bindings::prepare_to_wait_exclusive(self.wait_list.get(), wait.get(), wait_state as _)
114123
};
115124

116-
// SAFETY: No arguments, switches to another thread.
117-
guard.do_unlocked(|| unsafe { bindings::schedule() });
125+
// SAFETY: Switches to another thread. The timeout can be any number.
126+
let ret = guard.do_unlocked(|| unsafe { bindings::schedule_timeout(timeout_in_jiffies) });
118127

119128
// SAFETY: Both `wait` and `wait_list` point to valid memory.
120129
unsafe { bindings::finish_wait(self.wait_list.get(), wait.get()) };
130+
131+
ret
121132
}
122133

123134
/// Releases the lock and waits for a notification in uninterruptible mode.
@@ -127,7 +138,7 @@ impl CondVar {
127138
/// [`CondVar::notify_one`] or [`CondVar::notify_all`]. Note that it may also wake up
128139
/// spuriously.
129140
pub fn wait<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) {
130-
self.wait_internal(bindings::TASK_UNINTERRUPTIBLE, guard);
141+
self.wait_internal(bindings::TASK_UNINTERRUPTIBLE, guard, MAX_SCHEDULE_TIMEOUT);
131142
}
132143

133144
/// Releases the lock and waits for a notification in interruptible mode.
@@ -138,10 +149,31 @@ impl CondVar {
138149
/// Returns whether there is a signal pending.
139150
#[must_use = "wait_interruptible returns if a signal is pending, so the caller must check the return value"]
140151
pub fn wait_interruptible<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) -> bool {
141-
self.wait_internal(bindings::TASK_INTERRUPTIBLE, guard);
152+
self.wait_internal(bindings::TASK_INTERRUPTIBLE, guard, MAX_SCHEDULE_TIMEOUT);
142153
crate::current!().signal_pending()
143154
}
144155

156+
/// Releases the lock and waits for a notification in interruptible mode.
157+
///
158+
/// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
159+
/// thread to sleep. It wakes up when notified by [`CondVar::notify_one`] or
160+
/// [`CondVar::notify_all`], or when a timeout occurs, or when the thread receives a signal.
161+
#[must_use = "wait_interruptible_timeout returns if a signal is pending, so the caller must check the return value"]
162+
pub fn wait_interruptible_timeout<T: ?Sized, B: Backend>(
163+
&self,
164+
guard: &mut Guard<'_, T, B>,
165+
jiffies: Jiffies,
166+
) -> CondVarTimeoutResult {
167+
let jiffies = jiffies.try_into().unwrap_or(MAX_SCHEDULE_TIMEOUT);
168+
let res = self.wait_internal(bindings::TASK_INTERRUPTIBLE, guard, jiffies);
169+
170+
match (res as Jiffies, crate::current!().signal_pending()) {
171+
(jiffies, true) => CondVarTimeoutResult::Signal { jiffies },
172+
(0, false) => CondVarTimeoutResult::Timeout,
173+
(jiffies, false) => CondVarTimeoutResult::Woken { jiffies },
174+
}
175+
}
176+
145177
/// Calls the kernel function to notify the appropriate number of threads with the given flags.
146178
fn notify(&self, count: i32, flags: u32) {
147179
// SAFETY: `wait_list` points to valid memory.
@@ -177,3 +209,19 @@ impl CondVar {
177209
self.notify(0, 0);
178210
}
179211
}
212+
213+
/// The return type of `wait_timeout`.
214+
pub enum CondVarTimeoutResult {
215+
/// The timeout was reached.
216+
Timeout,
217+
/// Somebody woke us up.
218+
Woken {
219+
/// Remaining sleep duration.
220+
jiffies: Jiffies,
221+
},
222+
/// A signal occurred.
223+
Signal {
224+
/// Remaining sleep duration.
225+
jiffies: Jiffies,
226+
},
227+
}

rust/kernel/sync/lock.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,15 @@ pub struct Guard<'a, T: ?Sized, B: Backend> {
139139
unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}
140140

141141
impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
142-
pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
142+
pub(crate) fn do_unlocked<U>(&mut self, cb: impl FnOnce() -> U) -> U {
143143
// SAFETY: The caller owns the lock, so it is safe to unlock it.
144144
unsafe { B::unlock(self.lock.state.get(), &self.state) };
145145

146146
// SAFETY: The lock was just unlocked above and is being relocked now.
147147
let _relock =
148148
ScopeGuard::new(|| unsafe { B::relock(self.lock.state.get(), &mut self.state) });
149149

150-
cb();
150+
cb()
151151
}
152152
}
153153

rust/kernel/task.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ use core::{
1212
ptr,
1313
};
1414

15+
use core::ffi::c_long;
16+
17+
/// A sentinal value used for infinite timeouts.
18+
pub const MAX_SCHEDULE_TIMEOUT: c_long = c_long::MAX;
19+
1520
/// Returns the currently running task.
1621
#[macro_export]
1722
macro_rules! current {

0 commit comments

Comments
 (0)