Skip to content

Commit 33df7ba

Browse files
committed
Add "semaphore" sample driver in C and Rust.
1 parent 7ed5aa1 commit 33df7ba

File tree

4 files changed

+364
-0
lines changed

4 files changed

+364
-0
lines changed

samples/rust/Kconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,20 @@ config SAMPLE_RUST_STACK_PROBING
7070

7171
If unsure, say N.
7272

73+
config SAMPLE_SEMAPHORE
74+
tristate "Semaphore in C"
75+
help
76+
This option builds the C semaphore sample.
77+
78+
To compile this as a module, choose M here:
79+
the module will be called semaphore.
80+
81+
config SAMPLE_RUST_SEMAPHORE
82+
tristate "Semaphore in Rust"
83+
help
84+
This option builds the Rust semaphore sample.
85+
86+
To compile this as a module, choose M here:
87+
the module will be called rust_semaphore.
88+
7389
endif # SAMPLES_RUST

samples/rust/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ obj-$(CONFIG_SAMPLE_RUST_SYNC) += rust_sync.o
66
obj-$(CONFIG_SAMPLE_RUST_CHRDEV) += rust_chrdev.o
77
obj-$(CONFIG_SAMPLE_RUST_MISCDEV) += rust_miscdev.o
88
obj-$(CONFIG_SAMPLE_RUST_STACK_PROBING) += rust_stack_probing.o
9+
obj-$(CONFIG_SAMPLE_SEMAPHORE) += semaphore.o
10+
obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE) += rust_semaphore.o

samples/rust/rust_semaphore.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#![no_std]
4+
#![feature(allocator_api, global_asm)]
5+
6+
use alloc::{boxed::Box, sync::Arc};
7+
use core::{
8+
pin::Pin,
9+
sync::atomic::{AtomicU64, Ordering},
10+
};
11+
use kernel::{
12+
condvar_init, cstr, declare_file_operations,
13+
file_operations::{File, FileOpener, FileOperations, IoctlCommand, IoctlHandler},
14+
miscdev::Registration,
15+
mutex_init,
16+
prelude::*,
17+
sync::{CondVar, Mutex},
18+
user_ptr::{UserSlicePtrReader, UserSlicePtrWriter},
19+
Error,
20+
};
21+
22+
module! {
23+
type: RustSemaphoreModule,
24+
name: b"rust_semaphore",
25+
author: b"Rust for Linux Contributors",
26+
description: b"An example kernel module written in Rust",
27+
license: b"GPL v2",
28+
params: {},
29+
}
30+
31+
struct SemaphoreInner {
32+
count: usize,
33+
max_seen: usize,
34+
}
35+
36+
struct Semaphore {
37+
changed: CondVar,
38+
inner: Mutex<SemaphoreInner>,
39+
}
40+
41+
struct FileState {
42+
read_count: AtomicU64,
43+
shared: Arc<Semaphore>,
44+
}
45+
46+
impl FileState {
47+
fn consume(&self) -> KernelResult {
48+
let mut inner = self.shared.inner.lock();
49+
while inner.count == 0 {
50+
if self.shared.changed.wait(&mut inner) {
51+
return Err(Error::EINTR);
52+
}
53+
}
54+
inner.count -= 1;
55+
Ok(())
56+
}
57+
}
58+
59+
impl FileOpener<Arc<Semaphore>> for FileState {
60+
fn open(shared: &Arc<Semaphore>) -> KernelResult<Box<Self>> {
61+
Ok(Box::try_new(Self {
62+
read_count: AtomicU64::new(0),
63+
shared: shared.clone(),
64+
})?)
65+
}
66+
}
67+
68+
impl FileOperations for FileState {
69+
type Wrapper = Box<Self>;
70+
71+
declare_file_operations!(read, write, ioctl);
72+
73+
fn read(&self, _: &File, data: &mut UserSlicePtrWriter, offset: u64) -> KernelResult<usize> {
74+
if data.is_empty() || offset > 0 {
75+
return Ok(0);
76+
}
77+
self.consume()?;
78+
data.write_slice(&[0u8; 1])?;
79+
self.read_count.fetch_add(1, Ordering::Relaxed);
80+
Ok(1)
81+
}
82+
83+
fn write(&self, data: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<usize> {
84+
{
85+
let mut inner = self.shared.inner.lock();
86+
inner.count = inner.count.saturating_add(data.len());
87+
if inner.count > inner.max_seen {
88+
inner.max_seen = inner.count;
89+
}
90+
}
91+
92+
self.shared.changed.notify_all();
93+
Ok(data.len())
94+
}
95+
96+
fn ioctl(&self, file: &File, cmd: &mut IoctlCommand) -> KernelResult<i32> {
97+
cmd.dispatch(self, file)
98+
}
99+
100+
fn release(_obj: Box<Self>, _file: &File) {}
101+
}
102+
103+
struct RustSemaphoreModule {
104+
_dev: Pin<Box<Registration<Arc<Semaphore>>>>,
105+
}
106+
107+
impl KernelModule for RustSemaphoreModule {
108+
fn init() -> KernelResult<Self> {
109+
let sema = Arc::try_new(Semaphore {
110+
// SAFETY: `condvar_init!` is called below.
111+
changed: unsafe { CondVar::new() },
112+
// SAFETY: `mutex_init!` is called below.
113+
inner: unsafe {
114+
Mutex::new(SemaphoreInner {
115+
count: 0,
116+
max_seen: 0,
117+
})
118+
},
119+
})?;
120+
// SAFETY: `changed` is pinned behind `Arc`.
121+
condvar_init!(Pin::new_unchecked(&sema.changed), "Semaphore::changed");
122+
// SAFETY: `inner` is pinned behind `Arc`.
123+
mutex_init!(Pin::new_unchecked(&sema.inner), "Semaphore::inner");
124+
Ok(Self {
125+
_dev: Registration::new_pinned::<FileState>(cstr!("rust_semaphore"), None, sema)?,
126+
})
127+
}
128+
}
129+
130+
const IOCTL_GET_READ_COUNT: u32 = 0x80086301;
131+
const IOCTL_SET_READ_COUNT: u32 = 0x40086301;
132+
133+
impl IoctlHandler for FileState {
134+
fn read(&self, _: &File, cmd: u32, writer: &mut UserSlicePtrWriter) -> KernelResult<i32> {
135+
match cmd {
136+
IOCTL_GET_READ_COUNT => {
137+
writer.write(&self.read_count.load(Ordering::Relaxed))?;
138+
Ok(0)
139+
}
140+
_ => Err(Error::EINVAL),
141+
}
142+
}
143+
144+
fn write(&self, _: &File, cmd: u32, reader: &mut UserSlicePtrReader) -> KernelResult<i32> {
145+
match cmd {
146+
IOCTL_SET_READ_COUNT => {
147+
self.read_count.store(reader.read()?, Ordering::Relaxed);
148+
Ok(0)
149+
}
150+
_ => Err(Error::EINVAL),
151+
}
152+
}
153+
}

samples/rust/semaphore.c

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+
#include <linux/miscdevice.h>
4+
#include <linux/module.h>
5+
#include <linux/fs.h>
6+
#include <linux/slab.h>
7+
#include <linux/refcount.h>
8+
#include <linux/wait.h>
9+
10+
#define IOCTL_GET_READ_COUNT _IOR('c', 1, u64)
11+
#define IOCTL_SET_READ_COUNT _IOW('c', 1, u64)
12+
13+
struct semaphore_state {
14+
refcount_t ref;
15+
struct miscdevice miscdev;
16+
wait_queue_head_t changed;
17+
struct mutex mutex;
18+
size_t count;
19+
size_t max_seen;
20+
};
21+
22+
struct file_state {
23+
atomic64_t read_count;
24+
struct semaphore_state *shared;
25+
};
26+
27+
static int semaphore_consume(struct semaphore_state *state)
28+
{
29+
DEFINE_WAIT(wait);
30+
31+
mutex_lock(&state->mutex);
32+
while (state->count == 0) {
33+
prepare_to_wait(&state->changed, &wait, TASK_INTERRUPTIBLE);
34+
mutex_unlock(&state->mutex);
35+
schedule();
36+
finish_wait(&state->changed, &wait);
37+
if (signal_pending(current))
38+
return -EINTR;
39+
mutex_lock(&state->mutex);
40+
}
41+
42+
state->count--;
43+
mutex_unlock(&state->mutex);
44+
45+
return 0;
46+
}
47+
48+
static int semaphore_open(struct inode *nodp, struct file *filp)
49+
{
50+
struct semaphore_state *shared =
51+
container_of(filp->private_data, struct semaphore_state, miscdev);
52+
struct file_state *state;
53+
54+
state = kzalloc(sizeof(*state), GFP_KERNEL);
55+
if (!state)
56+
return -ENOMEM;
57+
58+
refcount_inc(&shared->ref);
59+
state->shared = shared;
60+
atomic64_set(&state->read_count, 0);
61+
62+
filp->private_data = state;
63+
64+
return 0;
65+
}
66+
67+
static ssize_t semaphore_write(struct file *filp, const char __user *buffer, size_t count,
68+
loff_t *ppos)
69+
{
70+
struct file_state *state = filp->private_data;
71+
struct semaphore_state *shared = state->shared;
72+
73+
mutex_lock(&shared->mutex);
74+
75+
shared->count += count;
76+
if (shared->count < count)
77+
shared->count = SIZE_MAX;
78+
79+
if (shared->count > shared->max_seen)
80+
shared->max_seen = shared->count;
81+
82+
mutex_unlock(&shared->mutex);
83+
84+
wake_up_all(&shared->changed);
85+
86+
return count;
87+
}
88+
89+
static ssize_t semaphore_read(struct file *filp, char __user *buffer,
90+
size_t count, loff_t *ppos)
91+
{
92+
struct file_state *state = filp->private_data;
93+
char c = 0;
94+
int ret;
95+
96+
if (count == 0 || *ppos > 0)
97+
return 0;
98+
99+
ret = semaphore_consume(state->shared);
100+
if (ret)
101+
return ret;
102+
103+
if (copy_to_user(buffer, &c, sizeof(c)))
104+
return -EFAULT;
105+
106+
atomic64_add(1, &state->read_count);
107+
*ppos += 1;
108+
return 1;
109+
}
110+
111+
static long semaphore_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
112+
{
113+
struct file_state *state = filp->private_data;
114+
void __user *buffer = (void __user *)arg;
115+
u64 value;
116+
117+
switch (cmd) {
118+
case IOCTL_GET_READ_COUNT:
119+
value = atomic64_read(&state->read_count);
120+
if (copy_to_user(buffer, &value, sizeof(value)))
121+
return -EFAULT;
122+
return 0;
123+
case IOCTL_SET_READ_COUNT:
124+
if (copy_from_user(&value, buffer, sizeof(value)))
125+
return -EFAULT;
126+
atomic64_set(&state->read_count, value);
127+
return 0;
128+
default:
129+
return -EINVAL;
130+
}
131+
}
132+
133+
static int semaphore_release(struct inode *nodp, struct file *filp)
134+
{
135+
struct file_state *state = filp->private_data;
136+
137+
if (refcount_dec_and_test(&state->shared->ref))
138+
kfree(state->shared);
139+
140+
kfree(state);
141+
return 0;
142+
}
143+
144+
static const struct file_operations semaphore_fops = {
145+
.owner = THIS_MODULE,
146+
.open = semaphore_open,
147+
.read = semaphore_read,
148+
.write = semaphore_write,
149+
.compat_ioctl = semaphore_ioctl,
150+
.release = semaphore_release,
151+
};
152+
153+
static struct semaphore_state *device;
154+
155+
static int __init semaphore_init(void)
156+
{
157+
int ret;
158+
struct semaphore_state *state;
159+
160+
state = kzalloc(sizeof(*state), GFP_KERNEL);
161+
if (!state)
162+
return -ENOMEM;
163+
164+
mutex_init(&state->mutex);
165+
refcount_set(&state->ref, 1);
166+
init_waitqueue_head(&state->changed);
167+
168+
state->miscdev.fops = &semaphore_fops;
169+
state->miscdev.minor = MISC_DYNAMIC_MINOR;
170+
state->miscdev.name = "semaphore";
171+
172+
ret = misc_register(&state->miscdev);
173+
if (ret < 0) {
174+
kfree(state);
175+
return ret;
176+
}
177+
178+
device = state;
179+
180+
return 0;
181+
}
182+
183+
static void __exit semaphore_exit(void)
184+
{
185+
misc_deregister(&device->miscdev);
186+
if (refcount_dec_and_test(&device->ref))
187+
kfree(device);
188+
}
189+
190+
module_init(semaphore_init);
191+
module_exit(semaphore_exit);
192+
193+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)