Skip to content

Commit 5a28fe6

Browse files
committed
Add shared state example.
1 parent a5e8b60 commit 5a28fe6

File tree

2 files changed

+107
-4
lines changed

2 files changed

+107
-4
lines changed

drivers/char/rust_example.rs

+104-4
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,102 @@ 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: `condvar_init!` is called below.
71+
state_changed: unsafe { CondVar::new() },
72+
// SAFETY: `mutex_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(&self, _file: &File, data: &mut UserSlicePtrWriter, offset: u64) -> KernelResult {
103+
// Succeed if the caller doesn't provide a buffer or if not at the start.
104+
if data.is_empty() || offset != 0 {
105+
return Ok(());
106+
}
107+
108+
{
109+
let mut inner = self.shared.inner.lock();
110+
111+
// Wait until we are allowed to decrement the token count or a signal arrives.
112+
while inner.token_count == 0 {
113+
if self.shared.state_changed.wait(&mut inner) {
114+
return Err(Error::EINTR);
115+
}
116+
}
117+
118+
// Consume a token.
119+
inner.token_count -= 1;
120+
}
121+
122+
// Notify a possible writer waiting.
123+
self.shared.state_changed.notify_all();
124+
125+
// Write a one-byte 1 to the reader.
126+
data.write_slice(&[1u8; 1])?;
127+
Ok(())
128+
}
129+
130+
fn write(&self, data: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<isize> {
131+
{
132+
let mut inner = self.shared.inner.lock();
133+
134+
// Wait until we are allowed to increment the token count or a signal arrives.
135+
while inner.token_count == MAX_TOKENS {
136+
if self.shared.state_changed.wait(&mut inner) {
137+
return Err(Error::EINTR);
138+
}
139+
}
140+
141+
// Increment the number of token so that a reader can be released.
142+
inner.token_count += 1;
143+
}
144+
145+
// Notify a possible reader waiting.
146+
self.shared.state_changed.notify_all();
147+
data.read_all()?;
148+
Ok(0)
149+
}
150+
}
151+
54152
struct RustFile;
55153

56154
impl FileOpener<()> for RustFile {
@@ -69,7 +167,7 @@ impl FileOperations for RustFile {
69167
struct RustExample {
70168
message: String,
71169
_chrdev: Pin<Box<chrdev::Registration<2>>>,
72-
_dev: Pin<Box<miscdev::Registration>>,
170+
_dev: Pin<Box<miscdev::Registration<Arc<SharedState>>>>,
73171
}
74172

75173
impl KernelModule for RustExample {
@@ -148,9 +246,11 @@ impl KernelModule for RustExample {
148246
chrdev_reg.as_mut().register::<RustFile>()?;
149247
chrdev_reg.as_mut().register::<RustFile>()?;
150248

249+
let state = SharedState::try_new()?;
250+
151251
Ok(RustExample {
152252
message: "on the heap!".to_owned(),
153-
_dev: miscdev::Registration::new_pinned::<RustFile>(cstr!("rust_miscdev"), None, ())?,
253+
_dev: miscdev::Registration::new_pinned::<Token>(cstr!("rust_miscdev"), None, state)?,
154254
_chrdev: chrdev_reg,
155255
})
156256
}

rust/kernel/error.rs

+3
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)