Skip to content

Commit dab1e55

Browse files
committed
rust: add a Task abstraction.
This allows users of the `kernel` crate to get tasks (e.g., the current task), access their properties (e.g., pid, group_leader, etc.), and hold on to them if desired. When holding on to tasks, the reference count is automatically incremented and decremented. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent dfaa42f commit dab1e55

File tree

7 files changed

+216
-26
lines changed

7 files changed

+216
-26
lines changed

drivers/android/process.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use kernel::{
1616
prelude::*,
1717
rbtree::RBTree,
1818
sync::{Guard, Mutex, Ref},
19+
task::Task,
1920
user_ptr::{UserSlicePtr, UserSlicePtrReader},
2021
Error,
2122
};
@@ -33,10 +34,6 @@ use crate::{
3334
// TODO: Review this:
3435
// Lock order: Process::node_refs -> Process::inner -> Thread::inner
3536

36-
extern "C" {
37-
fn rust_helper_current_pid() -> c_types::c_int;
38-
}
39-
4037
pub(crate) struct AllocationInfo {
4138
/// Range within the allocation where we can find the offsets to the object descriptors.
4239
pub(crate) offsets: Range<usize>,
@@ -799,7 +796,7 @@ impl IoctlHandler for Process {
799796
cmd: u32,
800797
reader: &mut UserSlicePtrReader,
801798
) -> Result<i32> {
802-
let thread = this.get_thread(unsafe { rust_helper_current_pid() })?;
799+
let thread = this.get_thread(Task::current().pid())?;
803800
match cmd {
804801
bindings::BINDER_SET_MAX_THREADS => this.set_max_threads(reader.read()?),
805802
bindings::BINDER_SET_CONTEXT_MGR => this.set_as_manager(None, &thread)?,
@@ -813,7 +810,7 @@ impl IoctlHandler for Process {
813810
}
814811

815812
fn read_write(this: &Ref<Process>, file: &File, cmd: u32, data: UserSlicePtr) -> Result<i32> {
816-
let thread = this.get_thread(unsafe { rust_helper_current_pid() })?;
813+
let thread = this.get_thread(Task::current().pid())?;
817814
match cmd {
818815
bindings::BINDER_WRITE_READ => thread.write_read(data, file.is_blocking())?,
819816
bindings::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info(data)?,
@@ -939,7 +936,7 @@ impl FileOperations for Process {
939936
}
940937

941938
fn poll(this: &Ref<Process>, file: &File, table: &PollTable) -> Result<u32> {
942-
let thread = this.get_thread(unsafe { rust_helper_current_pid() })?;
939+
let thread = this.get_thread(Task::current().pid())?;
943940
let (from_proc, mut mask) = thread.poll(file, table);
944941
if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
945942
mask |= bindings::POLLIN;

rust/helpers.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,9 @@ void rust_helper_init_wait(struct wait_queue_entry *wq_entry)
6060
}
6161
EXPORT_SYMBOL_GPL(rust_helper_init_wait);
6262

63-
int rust_helper_current_pid(void)
63+
int rust_helper_signal_pending(struct task_struct *t)
6464
{
65-
return current->pid;
66-
}
67-
EXPORT_SYMBOL_GPL(rust_helper_current_pid);
68-
69-
int rust_helper_signal_pending(void)
70-
{
71-
return signal_pending(current);
65+
return signal_pending(t);
7266
}
7367
EXPORT_SYMBOL_GPL(rust_helper_signal_pending);
7468

@@ -171,6 +165,24 @@ void rust_helper_rb_link_node(struct rb_node *node, struct rb_node *parent,
171165
}
172166
EXPORT_SYMBOL_GPL(rust_helper_rb_link_node);
173167

168+
struct task_struct *rust_helper_current(void)
169+
{
170+
return current;
171+
}
172+
EXPORT_SYMBOL_GPL(rust_helper_current);
173+
174+
void rust_helper_get_task_struct(struct task_struct * t)
175+
{
176+
get_task_struct(t);
177+
}
178+
EXPORT_SYMBOL_GPL(rust_helper_get_task_struct);
179+
180+
void rust_helper_put_task_struct(struct task_struct * t)
181+
{
182+
put_task_struct(t);
183+
}
184+
EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
185+
174186
/* We use bindgen's --size_t-is-usize option to bind the C size_t type
175187
* as the Rust usize type, so we can use it in contexts where Rust
176188
* expects a usize like slice (array) indices. usize is defined to be

rust/kernel/file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl Drop for File {
5353
}
5454
}
5555

56-
/// A wrapper for [`File`] that doesn't automatically decrement that refcount when dropped.
56+
/// A wrapper for [`File`] that doesn't automatically decrement the refcount when dropped.
5757
///
5858
/// We need the wrapper because [`ManuallyDrop`] alone would allow callers to call
5959
/// [`ManuallyDrop::into_inner`]. This would allow an unsafe sequence to be triggered without

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub mod file_operations;
5151
pub mod miscdev;
5252
pub mod pages;
5353
pub mod str;
54+
pub mod task;
5455
pub mod traits;
5556

5657
pub mod linked_list;

rust/kernel/sync/condvar.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
//! variable.
77
88
use super::{Guard, Lock, NeedsLockClass};
9-
use crate::bindings;
10-
use crate::str::CStr;
9+
use crate::{bindings, str::CStr, task::Task};
1110
use core::{cell::UnsafeCell, marker::PhantomPinned, mem::MaybeUninit, pin::Pin};
1211

1312
extern "C" {
@@ -92,7 +91,7 @@ impl CondVar {
9291
// SAFETY: Both `wait` and `wait_list` point to valid memory.
9392
unsafe { bindings::finish_wait(self.wait_list.get(), wait.as_mut_ptr()) };
9493

95-
super::signal_pending()
94+
Task::current().signal_pending()
9695
}
9796

9897
/// Calls the kernel function to notify the appropriate number of threads with the given flags.

rust/kernel/sync/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ pub use mutex::Mutex;
3939
pub use spinlock::SpinLock;
4040

4141
extern "C" {
42-
fn rust_helper_signal_pending() -> c_types::c_int;
4342
fn rust_helper_cond_resched() -> c_types::c_int;
4443
}
4544

@@ -78,12 +77,6 @@ pub trait NeedsLockClass {
7877
unsafe fn init(self: Pin<&mut Self>, name: &'static CStr, key: *mut bindings::lock_class_key);
7978
}
8079

81-
/// Determines if a signal is pending on the current process.
82-
pub fn signal_pending() -> bool {
83-
// SAFETY: No arguments, just checks `current` for pending signals.
84-
unsafe { rust_helper_signal_pending() != 0 }
85-
}
86-
8780
/// Reschedules the caller's task if needed.
8881
pub fn cond_resched() -> bool {
8982
// SAFETY: No arguments, reschedules `current` if needed.

rust/kernel/task.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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::{cell::Cell, 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_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.n
78+
unsafe impl Send for Task {}
79+
80+
/// The type of process identifiers (PIDs).
81+
type Pid = bindings::pid_t;
82+
83+
impl Task {
84+
/// Returns a task reference for the currently executing task/thread.
85+
pub fn current<'a>() -> TaskRef<'a> {
86+
// SAFETY: Just an FFI call.
87+
let ptr = unsafe { rust_helper_current() };
88+
89+
// SAFETY: If the current thread is still running, the current task is valid. Given
90+
// that `TaskRef` is not `Send`, we know it cannot be transferred to another thread (where
91+
// it could potentially outlive the caller).
92+
unsafe { TaskRef::from_ptr(ptr) }
93+
}
94+
95+
/// Returns the group leader of the given task.
96+
pub fn group_leader(&self) -> TaskRef<'_> {
97+
// SAFETY: By the type invariant, we know that `self.ptr` is non-null and valid.
98+
let ptr = unsafe { (*self.ptr).group_leader };
99+
100+
// SAFETY: The lifetime of the returned task reference is tied to the lifetime of `self`,
101+
// and given that a task has a reference to its group leader, we know it must be valid for
102+
// the lifetime of the returned task reference.
103+
unsafe { TaskRef::from_ptr(ptr) }
104+
}
105+
106+
/// Returns the pid of the given task.
107+
pub fn pid(&self) -> Pid {
108+
// SAFETY: By the type invariant, we know that `self.ptr` is non-null and valid.
109+
unsafe { (*self.ptr).pid }
110+
}
111+
112+
/// Determines whether the given task has pending signals.
113+
pub fn signal_pending(&self) -> bool {
114+
// SAFETY: By the type invariant, we know that `self.ptr` is non-null and valid.
115+
unsafe { rust_helper_signal_pending(self.ptr) != 0 }
116+
}
117+
}
118+
119+
impl PartialEq for Task {
120+
fn eq(&self, other: &Self) -> bool {
121+
self.ptr == other.ptr
122+
}
123+
}
124+
125+
impl Eq for Task {}
126+
127+
impl Clone for Task {
128+
fn clone(&self) -> Self {
129+
// SAFETY: The type invariants guarantee that `self.ptr` has a non-zero reference count.
130+
unsafe { rust_helper_get_task_struct(self.ptr) };
131+
132+
// INVARIANT: We incremented the reference count to account for the new `Task` being
133+
// created.
134+
Self { ptr: self.ptr }
135+
}
136+
}
137+
138+
impl Drop for Task {
139+
fn drop(&mut self) {
140+
// INVARIANT: We may decrement the refcount to zero, but the `Task` is being dropped, so
141+
// this is not observable.
142+
// SAFETY: The type invariants guarantee that `Task::ptr` has a non-zero reference count.
143+
unsafe { rust_helper_put_task_struct(self.ptr) };
144+
}
145+
}
146+
147+
/// A wrapper for [`Task`] that doesn't automatically decrement the refcount when dropped.
148+
///
149+
/// We need the wrapper because [`ManuallyDrop`] alone would allow callers to call
150+
/// [`ManuallyDrop::into_inner`]. This would allow an unsafe sequence to be triggered without
151+
/// `unsafe` blocks because it would trigger an unbalanced call to `put_task_struct`.
152+
///
153+
/// We make this explicitly not [`Send`] so that we can use it to represent the current thread
154+
/// without having the increment/decrement its reference count.
155+
///
156+
/// # Invariants
157+
///
158+
/// The wrapped [`Task`] remains valid for the lifetime of the object.
159+
pub struct TaskRef<'a> {
160+
task: ManuallyDrop<Task>,
161+
_not_send: PhantomData<&'a Cell<i32>>,
162+
}
163+
164+
impl TaskRef<'_> {
165+
/// Constructs a new [`struct task_struct`] wrapper that doesn't change its reference count.
166+
///
167+
/// # Safety
168+
///
169+
/// The pointer `ptr` must be non-null and valid for the lifetime of the object.
170+
pub(crate) unsafe fn from_ptr(ptr: *mut bindings::task_struct) -> Self {
171+
Self {
172+
task: ManuallyDrop::new(Task { ptr }),
173+
_not_send: PhantomData,
174+
}
175+
}
176+
}
177+
178+
// SAFETY: It is ok to share a reference to the current thread with another thread because we know
179+
// the owner cannot go away while the shared reference exists.
180+
unsafe impl Sync for TaskRef<'_> {}
181+
182+
impl Deref for TaskRef<'_> {
183+
type Target = Task;
184+
185+
fn deref(&self) -> &Self::Target {
186+
self.task.deref()
187+
}
188+
}

0 commit comments

Comments
 (0)