Skip to content

Commit 64d2e99

Browse files
authored
Merge pull request #173 from wedsonaf/example
Add "semaphore" sample driver in C and Rust.
2 parents 02c8ac6 + ca33751 commit 64d2e99

File tree

4 files changed

+382
-0
lines changed

4 files changed

+382
-0
lines changed

samples/rust/Kconfig

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

8181
If unsure, say N.
8282

83+
config SAMPLE_SEMAPHORE
84+
tristate "Semaphore in C"
85+
help
86+
This option builds the C semaphore sample.
87+
88+
To compile this as a module, choose M here:
89+
the module will be called semaphore.
90+
91+
config SAMPLE_RUST_SEMAPHORE
92+
tristate "Semaphore in Rust"
93+
help
94+
This option builds the Rust semaphore sample.
95+
96+
To compile this as a module, choose M here:
97+
the module will be called rust_semaphore.
98+
8399
endif # SAMPLES_RUST

samples/rust/Makefile

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

samples/rust/rust_semaphore.rs

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

samples/rust/semaphore.c

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

0 commit comments

Comments
 (0)