Skip to content

Commit 5f9ad6c

Browse files
authored
Merge pull request #410 from wedsonaf/task
rust: add a `Task` abstraction.
2 parents c85dd9a + 6556b37 commit 5f9ad6c

File tree

7 files changed

+221
-26
lines changed

7 files changed

+221
-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>,
@@ -798,7 +795,7 @@ impl IoctlHandler for Process {
798795
cmd: u32,
799796
reader: &mut UserSlicePtrReader,
800797
) -> Result<i32> {
801-
let thread = this.get_thread(unsafe { rust_helper_current_pid() })?;
798+
let thread = this.get_thread(Task::current().pid())?;
802799
match cmd {
803800
bindings::BINDER_SET_MAX_THREADS => this.set_max_threads(reader.read()?),
804801
bindings::BINDER_SET_CONTEXT_MGR => this.set_as_manager(None, &thread)?,
@@ -812,7 +809,7 @@ impl IoctlHandler for Process {
812809
}
813810

814811
fn read_write(this: &Ref<Process>, file: &File, cmd: u32, data: UserSlicePtr) -> Result<i32> {
815-
let thread = this.get_thread(unsafe { rust_helper_current_pid() })?;
812+
let thread = this.get_thread(Task::current().pid())?;
816813
match cmd {
817814
bindings::BINDER_WRITE_READ => thread.write_read(data, file.is_blocking())?,
818815
bindings::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info(data)?,
@@ -938,7 +935,7 @@ impl FileOperations for Process {
938935
}
939936

940937
fn poll(this: &Ref<Process>, file: &File, table: &PollTable) -> Result<u32> {
941-
let thread = this.get_thread(unsafe { rust_helper_current_pid() })?;
938+
let thread = this.get_thread(Task::current().pid())?;
942939
let (from_proc, mut mask) = thread.poll(file, table);
943940
if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
944941
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_get_current(void)
169+
{
170+
return current;
171+
}
172+
EXPORT_SYMBOL_GPL(rust_helper_get_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: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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

Comments
 (0)