Skip to content

Commit af3c12d

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. Note that it is not enough to avoid jiffies by introducing a variant of `CondVar::wait_timeout` that takes the timeout in msecs because we need to be able to restart the sleep with the remaining sleep duration if it is interrupted, and if the API takes msecs rather than jiffies, then that would require a conversion roundtrip jiffies->msecs->jiffies that is best avoided. Reviewed-by: Martin Rodriguez Reboredo <[email protected]> Reviewed-by: Tiago Lam <[email protected]> Reviewed-by: Boqun Feng <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent b13bfeb commit af3c12d

File tree

3 files changed

+60
-9
lines changed

3 files changed

+60
-9
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.
@@ -181,3 +213,19 @@ impl CondVar {
181213
self.notify(0, 0);
182214
}
183215
}
216+
217+
/// The return type of `wait_timeout`.
218+
pub enum CondVarTimeoutResult {
219+
/// The timeout was reached.
220+
Timeout,
221+
/// Somebody woke us up.
222+
Woken {
223+
/// Remaining sleep duration.
224+
jiffies: Jiffies,
225+
},
226+
/// A signal occurred.
227+
Signal {
228+
/// Remaining sleep duration.
229+
jiffies: Jiffies,
230+
},
231+
}

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: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
//! C header: [`include/linux/sched.h`](srctree/include/linux/sched.h).
66
77
use crate::{bindings, types::Opaque};
8-
use core::{marker::PhantomData, ops::Deref, ptr};
8+
use core::{ffi::c_long, marker::PhantomData, ops::Deref, ptr};
9+
10+
/// A sentinal value used for infinite timeouts.
11+
pub const MAX_SCHEDULE_TIMEOUT: c_long = c_long::MAX;
912

1013
/// Returns the currently running task.
1114
#[macro_export]

0 commit comments

Comments
 (0)