Commit | Line | Data |
---|---|---|
19096bce WAF |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | //! A condition variable. | |
4 | //! | |
5 | //! This module allows Rust code to use the kernel's [`struct wait_queue_head`] as a condition | |
6 | //! variable. | |
7 | ||
8 | use super::{lock::Backend, lock::Guard, LockClassKey}; | |
e7b9b1ff | 9 | use crate::{ |
f090f0d0 AR |
10 | init::PinInit, |
11 | pin_init, | |
12 | str::CStr, | |
13 | task::{MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE}, | |
14 | time::Jiffies, | |
e7b9b1ff AR |
15 | types::Opaque, |
16 | }; | |
f090f0d0 | 17 | use core::ffi::{c_int, c_long}; |
19096bce | 18 | use core::marker::PhantomPinned; |
f090f0d0 | 19 | use core::ptr; |
19096bce WAF |
20 | use macros::pin_data; |
21 | ||
22 | /// Creates a [`CondVar`] initialiser with the given name and a newly-created lock class. | |
23 | #[macro_export] | |
24 | macro_rules! new_condvar { | |
25 | ($($name:literal)?) => { | |
26 | $crate::sync::CondVar::new($crate::optional_name!($($name)?), $crate::static_lock_class!()) | |
27 | }; | |
28 | } | |
e283ee23 | 29 | pub use new_condvar; |
19096bce WAF |
30 | |
31 | /// A conditional variable. | |
32 | /// | |
33 | /// Exposes the kernel's [`struct wait_queue_head`] as a condition variable. It allows the caller to | |
34 | /// atomically release the given lock and go to sleep. It reacquires the lock when it wakes up. And | |
35 | /// it wakes up when notified by another thread (via [`CondVar::notify_one`] or | |
36 | /// [`CondVar::notify_all`]) or because the thread received a signal. It may also wake up | |
37 | /// spuriously. | |
38 | /// | |
39 | /// Instances of [`CondVar`] need a lock class and to be pinned. The recommended way to create such | |
40 | /// instances is with the [`pin_init`](crate::pin_init) and [`new_condvar`] macros. | |
41 | /// | |
42 | /// # Examples | |
43 | /// | |
44 | /// The following is an example of using a condvar with a mutex: | |
45 | /// | |
46 | /// ``` | |
e283ee23 | 47 | /// use kernel::sync::{new_condvar, new_mutex, CondVar, Mutex}; |
19096bce WAF |
48 | /// |
49 | /// #[pin_data] | |
50 | /// pub struct Example { | |
51 | /// #[pin] | |
52 | /// value: Mutex<u32>, | |
53 | /// | |
54 | /// #[pin] | |
55 | /// value_changed: CondVar, | |
56 | /// } | |
57 | /// | |
58 | /// /// Waits for `e.value` to become `v`. | |
59 | /// fn wait_for_value(e: &Example, v: u32) { | |
60 | /// let mut guard = e.value.lock(); | |
61 | /// while *guard != v { | |
0a7f5ba7 | 62 | /// e.value_changed.wait(&mut guard); |
19096bce WAF |
63 | /// } |
64 | /// } | |
65 | /// | |
66 | /// /// Increments `e.value` and notifies all potential waiters. | |
67 | /// fn increment(e: &Example) { | |
68 | /// *e.value.lock() += 1; | |
69 | /// e.value_changed.notify_all(); | |
70 | /// } | |
71 | /// | |
72 | /// /// Allocates a new boxed `Example`. | |
73 | /// fn new_example() -> Result<Pin<Box<Example>>> { | |
74 | /// Box::pin_init(pin_init!(Example { | |
75 | /// value <- new_mutex!(0), | |
76 | /// value_changed <- new_condvar!(), | |
c34aa00d | 77 | /// }), GFP_KERNEL) |
19096bce WAF |
78 | /// } |
79 | /// ``` | |
80 | /// | |
bc2e7d5c | 81 | /// [`struct wait_queue_head`]: srctree/include/linux/wait.h |
19096bce WAF |
82 | #[pin_data] |
83 | pub struct CondVar { | |
84 | #[pin] | |
6b1b2326 | 85 | pub(crate) wait_queue_head: Opaque<bindings::wait_queue_head>, |
19096bce WAF |
86 | |
87 | /// A condvar needs to be pinned because it contains a [`struct list_head`] that is | |
88 | /// self-referential, so it cannot be safely moved once it is initialised. | |
ed859653 VO |
89 | /// |
90 | /// [`struct list_head`]: srctree/include/linux/types.h | |
19096bce WAF |
91 | #[pin] |
92 | _pin: PhantomPinned, | |
93 | } | |
94 | ||
95 | // SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on any thread. | |
96 | #[allow(clippy::non_send_fields_in_send_ty)] | |
97 | unsafe impl Send for CondVar {} | |
98 | ||
99 | // SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on multiple threads | |
100 | // concurrently. | |
101 | unsafe impl Sync for CondVar {} | |
102 | ||
103 | impl CondVar { | |
104 | /// Constructs a new condvar initialiser. | |
19096bce WAF |
105 | pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> { |
106 | pin_init!(Self { | |
107 | _pin: PhantomPinned, | |
108 | // SAFETY: `slot` is valid while the closure is called and both `name` and `key` have | |
109 | // static lifetimes so they live indefinitely. | |
6b1b2326 | 110 | wait_queue_head <- Opaque::ffi_init(|slot| unsafe { |
19096bce WAF |
111 | bindings::__init_waitqueue_head(slot, name.as_char_ptr(), key.as_ptr()) |
112 | }), | |
113 | }) | |
114 | } | |
115 | ||
e7b9b1ff AR |
116 | fn wait_internal<T: ?Sized, B: Backend>( |
117 | &self, | |
f090f0d0 | 118 | wait_state: c_int, |
e7b9b1ff AR |
119 | guard: &mut Guard<'_, T, B>, |
120 | timeout_in_jiffies: c_long, | |
121 | ) -> c_long { | |
19096bce WAF |
122 | let wait = Opaque::<bindings::wait_queue_entry>::uninit(); |
123 | ||
124 | // SAFETY: `wait` points to valid memory. | |
125 | unsafe { bindings::init_wait(wait.get()) }; | |
126 | ||
6b1b2326 | 127 | // SAFETY: Both `wait` and `wait_queue_head` point to valid memory. |
19096bce | 128 | unsafe { |
f090f0d0 | 129 | bindings::prepare_to_wait_exclusive(self.wait_queue_head.get(), wait.get(), wait_state) |
19096bce WAF |
130 | }; |
131 | ||
e7b9b1ff AR |
132 | // SAFETY: Switches to another thread. The timeout can be any number. |
133 | let ret = guard.do_unlocked(|| unsafe { bindings::schedule_timeout(timeout_in_jiffies) }); | |
19096bce | 134 | |
6b1b2326 CM |
135 | // SAFETY: Both `wait` and `wait_queue_head` point to valid memory. |
136 | unsafe { bindings::finish_wait(self.wait_queue_head.get(), wait.get()) }; | |
e7b9b1ff AR |
137 | |
138 | ret | |
19096bce WAF |
139 | } |
140 | ||
0a7f5ba7 | 141 | /// Releases the lock and waits for a notification in uninterruptible mode. |
19096bce WAF |
142 | /// |
143 | /// Atomically releases the given lock (whose ownership is proven by the guard) and puts the | |
144 | /// thread to sleep, reacquiring the lock on wake up. It wakes up when notified by | |
0a7f5ba7 BF |
145 | /// [`CondVar::notify_one`] or [`CondVar::notify_all`]. Note that it may also wake up |
146 | /// spuriously. | |
147 | pub fn wait<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) { | |
f090f0d0 | 148 | self.wait_internal(TASK_UNINTERRUPTIBLE, guard, MAX_SCHEDULE_TIMEOUT); |
0a7f5ba7 BF |
149 | } |
150 | ||
151 | /// Releases the lock and waits for a notification in interruptible mode. | |
152 | /// | |
153 | /// Similar to [`CondVar::wait`], except that the wait is interruptible. That is, the thread may | |
154 | /// wake up due to signals. It may also wake up spuriously. | |
19096bce WAF |
155 | /// |
156 | /// Returns whether there is a signal pending. | |
0a7f5ba7 BF |
157 | #[must_use = "wait_interruptible returns if a signal is pending, so the caller must check the return value"] |
158 | pub fn wait_interruptible<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) -> bool { | |
f090f0d0 | 159 | self.wait_internal(TASK_INTERRUPTIBLE, guard, MAX_SCHEDULE_TIMEOUT); |
19096bce WAF |
160 | crate::current!().signal_pending() |
161 | } | |
162 | ||
e7b9b1ff AR |
163 | /// Releases the lock and waits for a notification in interruptible mode. |
164 | /// | |
165 | /// Atomically releases the given lock (whose ownership is proven by the guard) and puts the | |
166 | /// thread to sleep. It wakes up when notified by [`CondVar::notify_one`] or | |
167 | /// [`CondVar::notify_all`], or when a timeout occurs, or when the thread receives a signal. | |
168 | #[must_use = "wait_interruptible_timeout returns if a signal is pending, so the caller must check the return value"] | |
169 | pub fn wait_interruptible_timeout<T: ?Sized, B: Backend>( | |
170 | &self, | |
171 | guard: &mut Guard<'_, T, B>, | |
172 | jiffies: Jiffies, | |
173 | ) -> CondVarTimeoutResult { | |
174 | let jiffies = jiffies.try_into().unwrap_or(MAX_SCHEDULE_TIMEOUT); | |
f090f0d0 | 175 | let res = self.wait_internal(TASK_INTERRUPTIBLE, guard, jiffies); |
e7b9b1ff AR |
176 | |
177 | match (res as Jiffies, crate::current!().signal_pending()) { | |
178 | (jiffies, true) => CondVarTimeoutResult::Signal { jiffies }, | |
179 | (0, false) => CondVarTimeoutResult::Timeout, | |
180 | (jiffies, false) => CondVarTimeoutResult::Woken { jiffies }, | |
181 | } | |
182 | } | |
183 | ||
f090f0d0 AR |
184 | /// Calls the kernel function to notify the appropriate number of threads. |
185 | fn notify(&self, count: c_int) { | |
6b1b2326 | 186 | // SAFETY: `wait_queue_head` points to valid memory. |
19096bce WAF |
187 | unsafe { |
188 | bindings::__wake_up( | |
6b1b2326 | 189 | self.wait_queue_head.get(), |
f090f0d0 | 190 | TASK_NORMAL, |
19096bce | 191 | count, |
f090f0d0 | 192 | ptr::null_mut(), |
19096bce WAF |
193 | ) |
194 | }; | |
195 | } | |
196 | ||
3e645417 AR |
197 | /// Calls the kernel function to notify one thread synchronously. |
198 | /// | |
199 | /// This method behaves like `notify_one`, except that it hints to the scheduler that the | |
200 | /// current thread is about to go to sleep, so it should schedule the target thread on the same | |
201 | /// CPU. | |
202 | pub fn notify_sync(&self) { | |
203 | // SAFETY: `wait_queue_head` points to valid memory. | |
f090f0d0 | 204 | unsafe { bindings::__wake_up_sync(self.wait_queue_head.get(), TASK_NORMAL) }; |
3e645417 AR |
205 | } |
206 | ||
19096bce WAF |
207 | /// Wakes a single waiter up, if any. |
208 | /// | |
209 | /// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost | |
210 | /// completely (as opposed to automatically waking up the next waiter). | |
211 | pub fn notify_one(&self) { | |
f090f0d0 | 212 | self.notify(1); |
19096bce WAF |
213 | } |
214 | ||
215 | /// Wakes all waiters up, if any. | |
216 | /// | |
217 | /// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost | |
218 | /// completely (as opposed to automatically waking up the next waiter). | |
219 | pub fn notify_all(&self) { | |
f090f0d0 | 220 | self.notify(0); |
19096bce WAF |
221 | } |
222 | } | |
e7b9b1ff AR |
223 | |
224 | /// The return type of `wait_timeout`. | |
225 | pub enum CondVarTimeoutResult { | |
226 | /// The timeout was reached. | |
227 | Timeout, | |
228 | /// Somebody woke us up. | |
229 | Woken { | |
230 | /// Remaining sleep duration. | |
231 | jiffies: Jiffies, | |
232 | }, | |
233 | /// A signal occurred. | |
234 | Signal { | |
235 | /// Remaining sleep duration. | |
236 | jiffies: Jiffies, | |
237 | }, | |
238 | } |