|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Tasks (threads and processes). |
| 4 | +//! |
| 5 | +//! C header: [`include/linux/sched.h`](../../../../include/linux/sched.h). |
| 6 | +
|
| 7 | +use crate::{bindings, c_types}; |
| 8 | +use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref}; |
| 9 | + |
| 10 | +extern "C" { |
| 11 | + #[allow(improper_ctypes)] |
| 12 | + fn rust_helper_signal_pending(t: *const bindings::task_struct) -> c_types::c_int; |
| 13 | + #[allow(improper_ctypes)] |
| 14 | + fn rust_helper_get_current() -> *mut bindings::task_struct; |
| 15 | + #[allow(improper_ctypes)] |
| 16 | + fn rust_helper_get_task_struct(t: *mut bindings::task_struct); |
| 17 | + #[allow(improper_ctypes)] |
| 18 | + fn rust_helper_put_task_struct(t: *mut bindings::task_struct); |
| 19 | +} |
| 20 | + |
| 21 | +/// Wraps the kernel's `struct task_struct`. |
| 22 | +/// |
| 23 | +/// # Invariants |
| 24 | +/// |
| 25 | +/// The pointer [`Task::ptr`] is non-null and valid. Its reference count is also non-zero. |
| 26 | +/// |
| 27 | +/// # Examples |
| 28 | +/// |
| 29 | +/// The following is an example of getting the PID of the current thread with zero additional cost |
| 30 | +/// when compared to the C version: |
| 31 | +/// |
| 32 | +/// ``` |
| 33 | +/// # use kernel::prelude::*; |
| 34 | +/// use kernel::task::Task; |
| 35 | +/// |
| 36 | +/// # fn test() { |
| 37 | +/// Task::current().pid(); |
| 38 | +/// # } |
| 39 | +/// ``` |
| 40 | +/// |
| 41 | +/// Getting the PID of the current process, also zero additional cost: |
| 42 | +/// |
| 43 | +/// ``` |
| 44 | +/// # use kernel::prelude::*; |
| 45 | +/// use kernel::task::Task; |
| 46 | +/// |
| 47 | +/// # fn test() { |
| 48 | +/// Task::current().group_leader().pid(); |
| 49 | +/// # } |
| 50 | +/// ``` |
| 51 | +/// |
| 52 | +/// Getting the current task and storing it in some struct. The reference count is automatically |
| 53 | +/// incremented when creating `State` and decremented when it is dropped: |
| 54 | +/// |
| 55 | +/// ``` |
| 56 | +/// # use kernel::prelude::*; |
| 57 | +/// use kernel::task::Task; |
| 58 | +/// |
| 59 | +/// struct State { |
| 60 | +/// creator: Task, |
| 61 | +/// index: u32, |
| 62 | +/// } |
| 63 | +/// |
| 64 | +/// impl State { |
| 65 | +/// fn new() -> Self { |
| 66 | +/// Self { |
| 67 | +/// creator: Task::current().clone(), |
| 68 | +/// index: 0, |
| 69 | +/// } |
| 70 | +/// } |
| 71 | +/// } |
| 72 | +/// ``` |
| 73 | +pub struct Task { |
| 74 | + pub(crate) ptr: *mut bindings::task_struct, |
| 75 | +} |
| 76 | + |
| 77 | +// SAFETY: Given that the task is referenced, it is OK to send it to another thread. |
| 78 | +unsafe impl Send for Task {} |
| 79 | + |
| 80 | +// SAFETY: It's OK to access `Task` through references from other threads because we're either |
| 81 | +// accessing properties that don't change (e.g., `pid`, `group_leader`) or that are properly |
| 82 | +// synchronised by C code (e.g., `signal_pending`). |
| 83 | +unsafe impl Sync for Task {} |
| 84 | + |
| 85 | +/// The type of process identifiers (PIDs). |
| 86 | +type Pid = bindings::pid_t; |
| 87 | + |
| 88 | +impl Task { |
| 89 | + /// Returns a task reference for the currently executing task/thread. |
| 90 | + pub fn current<'a>() -> TaskRef<'a> { |
| 91 | + // SAFETY: Just an FFI call. |
| 92 | + let ptr = unsafe { rust_helper_get_current() }; |
| 93 | + |
| 94 | + // SAFETY: If the current thread is still running, the current task is valid. Given |
| 95 | + // that `TaskRef` is not `Send`, we know it cannot be transferred to another thread (where |
| 96 | + // it could potentially outlive the caller). |
| 97 | + unsafe { TaskRef::from_ptr(ptr) } |
| 98 | + } |
| 99 | + |
| 100 | + /// Returns the group leader of the given task. |
| 101 | + pub fn group_leader(&self) -> TaskRef<'_> { |
| 102 | + // SAFETY: By the type invariant, we know that `self.ptr` is non-null and valid. |
| 103 | + let ptr = unsafe { (*self.ptr).group_leader }; |
| 104 | + |
| 105 | + // SAFETY: The lifetime of the returned task reference is tied to the lifetime of `self`, |
| 106 | + // and given that a task has a reference to its group leader, we know it must be valid for |
| 107 | + // the lifetime of the returned task reference. |
| 108 | + unsafe { TaskRef::from_ptr(ptr) } |
| 109 | + } |
| 110 | + |
| 111 | + /// Returns the PID of the given task. |
| 112 | + pub fn pid(&self) -> Pid { |
| 113 | + // SAFETY: By the type invariant, we know that `self.ptr` is non-null and valid. |
| 114 | + unsafe { (*self.ptr).pid } |
| 115 | + } |
| 116 | + |
| 117 | + /// Determines whether the given task has pending signals. |
| 118 | + pub fn signal_pending(&self) -> bool { |
| 119 | + // SAFETY: By the type invariant, we know that `self.ptr` is non-null and valid. |
| 120 | + unsafe { rust_helper_signal_pending(self.ptr) != 0 } |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +impl PartialEq for Task { |
| 125 | + fn eq(&self, other: &Self) -> bool { |
| 126 | + self.ptr == other.ptr |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +impl Eq for Task {} |
| 131 | + |
| 132 | +impl Clone for Task { |
| 133 | + fn clone(&self) -> Self { |
| 134 | + // SAFETY: The type invariants guarantee that `self.ptr` has a non-zero reference count. |
| 135 | + unsafe { rust_helper_get_task_struct(self.ptr) }; |
| 136 | + |
| 137 | + // INVARIANT: We incremented the reference count to account for the new `Task` being |
| 138 | + // created. |
| 139 | + Self { ptr: self.ptr } |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +impl Drop for Task { |
| 144 | + fn drop(&mut self) { |
| 145 | + // INVARIANT: We may decrement the refcount to zero, but the `Task` is being dropped, so |
| 146 | + // this is not observable. |
| 147 | + // SAFETY: The type invariants guarantee that `Task::ptr` has a non-zero reference count. |
| 148 | + unsafe { rust_helper_put_task_struct(self.ptr) }; |
| 149 | + } |
| 150 | +} |
| 151 | + |
| 152 | +/// A wrapper for [`Task`] that doesn't automatically decrement the refcount when dropped. |
| 153 | +/// |
| 154 | +/// We need the wrapper because [`ManuallyDrop`] alone would allow callers to call |
| 155 | +/// [`ManuallyDrop::into_inner`]. This would allow an unsafe sequence to be triggered without |
| 156 | +/// `unsafe` blocks because it would trigger an unbalanced call to `put_task_struct`. |
| 157 | +/// |
| 158 | +/// We make this explicitly not [`Send`] so that we can use it to represent the current thread |
| 159 | +/// without having to increment/decrement its reference count. |
| 160 | +/// |
| 161 | +/// # Invariants |
| 162 | +/// |
| 163 | +/// The wrapped [`Task`] remains valid for the lifetime of the object. |
| 164 | +pub struct TaskRef<'a> { |
| 165 | + task: ManuallyDrop<Task>, |
| 166 | + _not_send: PhantomData<(&'a (), *mut ())>, |
| 167 | +} |
| 168 | + |
| 169 | +impl TaskRef<'_> { |
| 170 | + /// Constructs a new `struct task_struct` wrapper that doesn't change its reference count. |
| 171 | + /// |
| 172 | + /// # Safety |
| 173 | + /// |
| 174 | + /// The pointer `ptr` must be non-null and valid for the lifetime of the object. |
| 175 | + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::task_struct) -> Self { |
| 176 | + Self { |
| 177 | + task: ManuallyDrop::new(Task { ptr }), |
| 178 | + _not_send: PhantomData, |
| 179 | + } |
| 180 | + } |
| 181 | +} |
| 182 | + |
| 183 | +// SAFETY: It is OK to share a reference to the current thread with another thread because we know |
| 184 | +// the owner cannot go away while the shared reference exists (and `Task` itself is `Sync`). |
| 185 | +unsafe impl Sync for TaskRef<'_> {} |
| 186 | + |
| 187 | +impl Deref for TaskRef<'_> { |
| 188 | + type Target = Task; |
| 189 | + |
| 190 | + fn deref(&self) -> &Self::Target { |
| 191 | + self.task.deref() |
| 192 | + } |
| 193 | +} |
0 commit comments