Skip to content

Commit 1f8f11d

Browse files
committed
rust: add basic Task
It is an abstraction for C's `struct task_struct`. It implements `AlwaysRefCounted`, so the refcount of the wrapped object is managed safely on the Rust side. Cc: Ingo Molnar <[email protected]> Cc: Peter Zijlstra <[email protected]> Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 2f37c0d commit 1f8f11d

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

rust/bindings/bindings_helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <linux/slab.h>
1010
#include <linux/refcount.h>
11+
#include <linux/sched.h>
1112

1213
/* `bindgen` gets confused at certain things. */
1314
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;

rust/helpers.c

+19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/refcount.h>
2424
#include <linux/mutex.h>
2525
#include <linux/spinlock.h>
26+
#include <linux/sched/signal.h>
2627

2728
__noreturn void rust_helper_BUG(void)
2829
{
@@ -75,6 +76,12 @@ void rust_helper_spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
7576
}
7677
EXPORT_SYMBOL_GPL(rust_helper_spin_unlock_irqrestore);
7778

79+
int rust_helper_signal_pending(struct task_struct *t)
80+
{
81+
return signal_pending(t);
82+
}
83+
EXPORT_SYMBOL_GPL(rust_helper_signal_pending);
84+
7885
refcount_t rust_helper_REFCOUNT_INIT(int n)
7986
{
8087
return (refcount_t)REFCOUNT_INIT(n);
@@ -93,6 +100,18 @@ bool rust_helper_refcount_dec_and_test(refcount_t *r)
93100
}
94101
EXPORT_SYMBOL_GPL(rust_helper_refcount_dec_and_test);
95102

103+
void rust_helper_get_task_struct(struct task_struct *t)
104+
{
105+
get_task_struct(t);
106+
}
107+
EXPORT_SYMBOL_GPL(rust_helper_get_task_struct);
108+
109+
void rust_helper_put_task_struct(struct task_struct *t)
110+
{
111+
put_task_struct(t);
112+
}
113+
EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
114+
96115
/*
97116
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
98117
* as the Rust `usize` type, so we can use it in contexts where Rust

rust/kernel/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ mod static_assert;
4343
pub mod std_vendor;
4444
pub mod str;
4545
pub mod sync;
46+
pub mod task;
4647
pub mod types;
4748

4849
#[doc(hidden)]

rust/kernel/task.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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;
8+
use core::{cell::UnsafeCell, ptr};
9+
10+
/// Wraps the kernel's `struct task_struct`.
11+
///
12+
/// # Invariants
13+
///
14+
/// Instances of this type are always ref-counted, that is, a call to `get_task_struct` ensures
15+
/// that the allocation remains valid at least until the matching call to `put_task_struct`.
16+
#[repr(transparent)]
17+
pub struct Task(pub(crate) UnsafeCell<bindings::task_struct>);
18+
19+
// SAFETY: It's OK to access `Task` through references from other threads because we're either
20+
// accessing properties that don't change (e.g., `pid`, `group_leader`) or that are properly
21+
// synchronised by C code (e.g., `signal_pending`).
22+
unsafe impl Sync for Task {}
23+
24+
/// The type of process identifiers (PIDs).
25+
type Pid = bindings::pid_t;
26+
27+
impl Task {
28+
/// Returns the group leader of the given task.
29+
pub fn group_leader(&self) -> &Task {
30+
// SAFETY: By the type invariant, we know that `self.0` is valid.
31+
let ptr = unsafe { *ptr::addr_of!((*self.0.get()).group_leader) };
32+
33+
// SAFETY: The lifetime of the returned task reference is tied to the lifetime of `self`,
34+
// and given that a task has a reference to its group leader, we know it must be valid for
35+
// the lifetime of the returned task reference.
36+
unsafe { &*ptr.cast() }
37+
}
38+
39+
/// Returns the PID of the given task.
40+
pub fn pid(&self) -> Pid {
41+
// SAFETY: By the type invariant, we know that `self.0` is valid.
42+
unsafe { *ptr::addr_of!((*self.0.get()).pid) }
43+
}
44+
45+
/// Determines whether the given task has pending signals.
46+
pub fn signal_pending(&self) -> bool {
47+
// SAFETY: By the type invariant, we know that `self.0` is valid.
48+
unsafe { bindings::signal_pending(self.0.get()) != 0 }
49+
}
50+
51+
/// Wakes up the task.
52+
pub fn wake_up(&self) {
53+
// SAFETY: By the type invariant, we know that `self.0.get()` is non-null and valid.
54+
// And `wake_up_process` is safe to be called for any valid task, even if the task is
55+
// running.
56+
unsafe { bindings::wake_up_process(self.0.get()) };
57+
}
58+
}
59+
60+
// SAFETY: The type invariants guarantee that `Task` is always ref-counted.
61+
unsafe impl crate::types::AlwaysRefCounted for Task {
62+
fn inc_ref(&self) {
63+
// SAFETY: The existence of a shared reference means that the refcount is nonzero.
64+
unsafe { bindings::get_task_struct(self.0.get()) };
65+
}
66+
67+
unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
68+
// SAFETY: The safety requirements guarantee that the refcount is nonzero.
69+
unsafe { bindings::put_task_struct(obj.cast().as_ptr()) }
70+
}
71+
}

0 commit comments

Comments
 (0)