Skip to content

Commit 6824175

Browse files
committed
Add shared state example.
1 parent 2f028fd commit 6824175

File tree

2 files changed

+111
-4
lines changed

2 files changed

+111
-4
lines changed

drivers/char/rust_example.rs

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
#![feature(allocator_api, global_asm)]
77
#![feature(test)]
88

9-
use alloc::boxed::Box;
9+
use alloc::{boxed::Box, sync::Arc};
1010
use core::pin::Pin;
1111
use kernel::prelude::*;
1212
use kernel::{
1313
chrdev, condvar_init, cstr,
14-
file_operations::{FileOpener, FileOperations},
14+
file_operations::{File, FileOpener, FileOperations},
1515
miscdev, mutex_init, spinlock_init,
1616
sync::{CondVar, Mutex, SpinLock},
17+
user_ptr::{UserSlicePtrReader, UserSlicePtrWriter},
18+
Error,
1719
};
1820

1921
module! {
@@ -51,6 +53,106 @@ module! {
5153
},
5254
}
5355

56+
const MAX_TOKENS: usize = 3;
57+
58+
struct SharedStateInner {
59+
token_count: usize,
60+
}
61+
62+
struct SharedState {
63+
state_changed: CondVar,
64+
inner: Mutex<SharedStateInner>,
65+
}
66+
67+
impl SharedState {
68+
fn try_new() -> KernelResult<Arc<Self>> {
69+
let state = Arc::try_new(Self {
70+
// SAFETY: Init is called below.
71+
state_changed: unsafe { CondVar::new() },
72+
// SAFETY: Init is called below.
73+
inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) },
74+
})?;
75+
// SAFETY: `state_changed` is pinned behind `Arc`.
76+
let state_changed = unsafe { Pin::new_unchecked(&state.state_changed) };
77+
kernel::condvar_init!(state_changed, "SharedState::state_changed");
78+
// SAFETY: `inner` is pinned behind `Arc`.
79+
let inner = unsafe { Pin::new_unchecked(&state.inner) };
80+
kernel::mutex_init!(inner, "SharedState::inner");
81+
Ok(state)
82+
}
83+
}
84+
85+
struct Token {
86+
shared: Arc<SharedState>,
87+
}
88+
89+
impl FileOpener<Arc<SharedState>> for Token {
90+
fn open(shared: &Arc<SharedState>) -> KernelResult<Self::Wrapper> {
91+
Ok(Box::try_new(Self {
92+
shared: shared.clone(),
93+
})?)
94+
}
95+
}
96+
97+
impl FileOperations for Token {
98+
type Wrapper = Box<Self>;
99+
100+
kernel::declare_file_operations!(read, write);
101+
102+
fn read(
103+
&self,
104+
_file: &File,
105+
data: &mut UserSlicePtrWriter,
106+
offset: u64,
107+
) -> KernelResult<usize> {
108+
// Succeed if the caller doesn't provide a buffer or if not at the start.
109+
if data.is_empty() || offset != 0 {
110+
return Ok(0);
111+
}
112+
113+
{
114+
let mut inner = self.shared.inner.lock();
115+
116+
// Wait until we are allowed to decrement the token count or a signal arrives.
117+
while inner.token_count == 0 {
118+
if self.shared.state_changed.wait(&mut inner) {
119+
return Err(Error::EINTR);
120+
}
121+
}
122+
123+
// Consume a token.
124+
inner.token_count -= 1;
125+
}
126+
127+
// Notify a possible writer waiting.
128+
self.shared.state_changed.notify_all();
129+
130+
// Write a one-byte 1 to the reader.
131+
data.write_slice(&[1u8; 1])?;
132+
Ok(1)
133+
}
134+
135+
fn write(&self, data: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<usize> {
136+
{
137+
let mut inner = self.shared.inner.lock();
138+
139+
// Wait until we are allowed to increment the token count or a signal arrives.
140+
while inner.token_count == MAX_TOKENS {
141+
if self.shared.state_changed.wait(&mut inner) {
142+
return Err(Error::EINTR);
143+
}
144+
}
145+
146+
// Increment the number of token so that a reader can be released.
147+
inner.token_count += 1;
148+
}
149+
150+
// Notify a possible reader waiting.
151+
self.shared.state_changed.notify_all();
152+
Ok(data.len())
153+
}
154+
}
155+
54156
struct RustFile;
55157

56158
impl FileOpener<()> for RustFile {
@@ -69,7 +171,7 @@ impl FileOperations for RustFile {
69171
struct RustExample {
70172
message: String,
71173
_chrdev: Pin<Box<chrdev::Registration<2>>>,
72-
_dev: Pin<Box<miscdev::Registration>>,
174+
_dev: Pin<Box<miscdev::Registration<Arc<SharedState>>>>,
73175
}
74176

75177
impl KernelModule for RustExample {
@@ -148,9 +250,11 @@ impl KernelModule for RustExample {
148250
chrdev_reg.as_mut().register::<RustFile>()?;
149251
chrdev_reg.as_mut().register::<RustFile>()?;
150252

253+
let state = SharedState::try_new()?;
254+
151255
Ok(RustExample {
152256
message: "on the heap!".to_owned(),
153-
_dev: miscdev::Registration::new_pinned::<RustFile>(cstr!("rust_miscdev"), None, ())?,
257+
_dev: miscdev::Registration::new_pinned::<Token>(cstr!("rust_miscdev"), None, state)?,
154258
_chrdev: chrdev_reg,
155259
})
156260
}

rust/kernel/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ impl Error {
4545
/// No such file or directory.
4646
pub const ENOENT: Self = Error(-(bindings::ENOENT as i32));
4747

48+
/// Interrupted system call.
49+
pub const EINTR: Self = Error(-(bindings::EINTR as i32));
50+
4851
/// Creates an [`Error`] from a kernel error code.
4952
pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
5053
Error(errno)

0 commit comments

Comments
 (0)