提交 0f91f2ed 编写于 作者: M Miguel Ojeda 提交者: Zheng Zengkai

samples: add Rust examples

maillist inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I5J75G
CVE: NA

Reference: https://lore.kernel.org/rust-for-linux/CANiq72nDcJLSB3pLhkdqGdLitfmqqCUVVfkY5EjP9AcwVv9B4A@mail.gmail.com/T/#t

--------------------------------

A set of Rust modules that showcase how Rust modules look like
and how to use the abstracted kernel features, as well as
an example of a Rust host program with several modules.

These samples also double as tests in the CI.

The semaphore sample comes with a C version for comparison.
Co-developed-by: NAlex Gaynor <alex.gaynor@gmail.com>
Signed-off-by: NAlex Gaynor <alex.gaynor@gmail.com>
Co-developed-by: NFinn Behrens <me@kloenk.de>
Signed-off-by: NFinn Behrens <me@kloenk.de>
Co-developed-by: NWedson Almeida Filho <wedsonaf@google.com>
Signed-off-by: NWedson Almeida Filho <wedsonaf@google.com>
Co-developed-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Co-developed-by: NSven Van Asbroeck <thesven73@gmail.com>
Signed-off-by: NSven Van Asbroeck <thesven73@gmail.com>
Co-developed-by: NGary Guo <gary@garyguo.net>
Signed-off-by: NGary Guo <gary@garyguo.net>
Co-developed-by: NBoris-Chengbiao Zhou <bobo1239@web.de>
Signed-off-by: NBoris-Chengbiao Zhou <bobo1239@web.de>
Co-developed-by: NAyaan Zaidi <zaidi.ayaan@gmail.com>
Signed-off-by: NAyaan Zaidi <zaidi.ayaan@gmail.com>
Co-developed-by: NMilan Landaverde <milan@mdaverde.com>
Signed-off-by: NMilan Landaverde <milan@mdaverde.com>
Signed-off-by: NMiguel Ojeda <ojeda@kernel.org>
Signed-off-by: NWeilong Chen <chenweilong@huawei.com>
上级 bdfae9f3
......@@ -216,4 +216,6 @@ config SAMPLE_WATCH_QUEUE
Build example userspace program to use the new mount_notify(),
sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
source "samples/rust/Kconfig"
endif # SAMPLES
......@@ -29,3 +29,4 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/
subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog
subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/
obj-$(CONFIG_SAMPLES_RUST) += rust/
# SPDX-License-Identifier: GPL-2.0
menuconfig SAMPLES_RUST
bool "Rust samples"
depends on RUST
help
You can build sample Rust kernel code here.
If unsure, say N.
if SAMPLES_RUST
config SAMPLE_RUST_MINIMAL
tristate "Minimal"
help
This option builds the Rust minimal module sample.
To compile this as a module, choose M here:
the module will be called rust_minimal.
If unsure, say N.
config SAMPLE_RUST_PRINT
tristate "Printing macros"
help
This option builds the Rust printing macros sample.
To compile this as a module, choose M here:
the module will be called rust_print.
If unsure, say N.
config SAMPLE_RUST_MODULE_PARAMETERS
tristate "Module parameters"
help
This option builds the Rust module parameters sample.
To compile this as a module, choose M here:
the module will be called rust_module_parameters.
If unsure, say N.
config SAMPLE_RUST_SYNC
tristate "Synchronisation primitives"
help
This option builds the Rust synchronisation primitives sample.
To compile this as a module, choose M here:
the module will be called rust_sync.
If unsure, say N.
config SAMPLE_RUST_CHRDEV
tristate "Character device"
help
This option builds the Rust character device sample.
To compile this as a module, choose M here:
the module will be called rust_chrdev.
If unsure, say N.
config SAMPLE_RUST_MISCDEV
tristate "Miscellaneous device"
help
This option builds the Rust miscellaneous device sample.
To compile this as a module, choose M here:
the module will be called rust_miscdev.
If unsure, say N.
config SAMPLE_RUST_STACK_PROBING
tristate "Stack probing"
help
This option builds the Rust stack probing sample.
To compile this as a module, choose M here:
the module will be called rust_stack_probing.
If unsure, say N.
config SAMPLE_RUST_SEMAPHORE
tristate "Semaphore"
help
This option builds the Rust semaphore sample.
To compile this as a module, choose M here:
the module will be called rust_semaphore.
If unsure, say N.
config SAMPLE_RUST_SEMAPHORE_C
tristate "Semaphore (in C, for comparison)"
help
This option builds the Rust semaphore sample (in C, for comparison).
To compile this as a module, choose M here:
the module will be called rust_semaphore_c.
If unsure, say N.
config SAMPLE_RUST_RANDOM
tristate "Random"
help
This option builds the Rust random sample.
To compile this as a module, choose M here:
the module will be called rust_random.
If unsure, say N.
config SAMPLE_RUST_PLATFORM
tristate "Platform device driver"
help
This option builds the Rust platform device driver sample.
To compile this as a module, choose M here:
the module will be called rust_platform.
If unsure, say N.
config SAMPLE_RUST_NETFILTER
tristate "Network filter module"
help
This option builds the Rust netfilter module sample.
To compile this as a module, choose M here:
the module will be called rust_netfilter.
If unsure, say N.
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
This option builds the Rust host program samples.
If unsure, say N.
endif # SAMPLES_RUST
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
obj-$(CONFIG_SAMPLE_RUST_MODULE_PARAMETERS) += rust_module_parameters.o
obj-$(CONFIG_SAMPLE_RUST_SYNC) += rust_sync.o
obj-$(CONFIG_SAMPLE_RUST_CHRDEV) += rust_chrdev.o
obj-$(CONFIG_SAMPLE_RUST_MISCDEV) += rust_miscdev.o
obj-$(CONFIG_SAMPLE_RUST_STACK_PROBING) += rust_stack_probing.o
obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE) += rust_semaphore.o
obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE_C) += rust_semaphore_c.o
obj-$(CONFIG_SAMPLE_RUST_RANDOM) += rust_random.o
obj-$(CONFIG_SAMPLE_RUST_PLATFORM) += rust_platform.o
obj-$(CONFIG_SAMPLE_RUST_NETFILTER) += rust_netfilter.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
# SPDX-License-Identifier: GPL-2.0
single
# SPDX-License-Identifier: GPL-2.0
hostprogs-always-y := single
single-rust := y
// SPDX-License-Identifier: GPL-2.0
//! Rust single host program sample: module `a`.
pub(crate) fn f(x: i32) {
println!("The number is {}.", x);
}
// SPDX-License-Identifier: GPL-2.0
//! Rust single host program sample: module `b`.
pub(crate) const CONSTANT: i32 = 42;
// SPDX-License-Identifier: GPL-2.0
//! Rust single host program sample.
mod a;
mod b;
fn main() {
println!("Hello world!");
a::f(b::CONSTANT);
}
// SPDX-License-Identifier: GPL-2.0
//! Rust character device sample.
use kernel::prelude::*;
use kernel::{chrdev, file};
module! {
type: RustChrdev,
name: b"rust_chrdev",
author: b"Rust for Linux Contributors",
description: b"Rust character device sample",
license: b"GPL",
}
struct RustFile;
impl file::Operations for RustFile {
kernel::declare_file_operations!();
fn open(_shared: &(), _file: &file::File) -> Result {
Ok(())
}
}
struct RustChrdev {
_dev: Pin<Box<chrdev::Registration<2>>>,
}
impl kernel::Module for RustChrdev {
fn init(name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust character device sample (init)\n");
let mut chrdev_reg = chrdev::Registration::new_pinned(name, 0, module)?;
// Register the same kind of device twice, we're just demonstrating
// that you can use multiple minors. There are two minors in this case
// because its type is `chrdev::Registration<2>`
chrdev_reg.as_mut().register::<RustFile>()?;
chrdev_reg.as_mut().register::<RustFile>()?;
Ok(RustChrdev { _dev: chrdev_reg })
}
}
impl Drop for RustChrdev {
fn drop(&mut self) {
pr_info!("Rust character device sample (exit)\n");
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust minimal sample.
use kernel::prelude::*;
module! {
type: RustMinimal,
name: b"rust_minimal",
author: b"Rust for Linux Contributors",
description: b"Rust minimal sample",
license: b"GPL",
}
struct RustMinimal {
message: String,
}
impl kernel::Module for RustMinimal {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust minimal sample (init)\n");
pr_info!("Am I built-in? {}\n", !cfg!(MODULE));
Ok(RustMinimal {
message: "on the heap!".try_to_owned()?,
})
}
}
impl Drop for RustMinimal {
fn drop(&mut self) {
pr_info!("My message is {}\n", self.message);
pr_info!("Rust minimal sample (exit)\n");
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust miscellaneous device sample.
use kernel::prelude::*;
use kernel::{
file::{self, File},
io_buffer::{IoBufferReader, IoBufferWriter},
miscdev,
sync::{CondVar, Mutex, Ref, RefBorrow, UniqueRef},
};
module! {
type: RustMiscdev,
name: b"rust_miscdev",
author: b"Rust for Linux Contributors",
description: b"Rust miscellaneous device sample",
license: b"GPL",
}
const MAX_TOKENS: usize = 3;
struct SharedStateInner {
token_count: usize,
}
struct SharedState {
state_changed: CondVar,
inner: Mutex<SharedStateInner>,
}
impl SharedState {
fn try_new() -> Result<Ref<Self>> {
let mut state = Pin::from(UniqueRef::try_new(Self {
// SAFETY: `condvar_init!` is called below.
state_changed: unsafe { CondVar::new() },
// SAFETY: `mutex_init!` is called below.
inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) },
})?);
// SAFETY: `state_changed` is pinned when `state` is.
let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.state_changed) };
kernel::condvar_init!(pinned, "SharedState::state_changed");
// SAFETY: `inner` is pinned when `state` is.
let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.inner) };
kernel::mutex_init!(pinned, "SharedState::inner");
Ok(state.into())
}
}
struct Token;
impl file::Operations for Token {
type Data = Ref<SharedState>;
type OpenData = Ref<SharedState>;
kernel::declare_file_operations!(read, write);
fn open(shared: &Ref<SharedState>, _file: &File) -> Result<Self::Data> {
Ok(shared.clone())
}
fn read(
shared: RefBorrow<'_, SharedState>,
_: &File,
data: &mut impl IoBufferWriter,
offset: u64,
) -> Result<usize> {
// Succeed if the caller doesn't provide a buffer or if not at the start.
if data.is_empty() || offset != 0 {
return Ok(0);
}
{
let mut inner = shared.inner.lock();
// Wait until we are allowed to decrement the token count or a signal arrives.
while inner.token_count == 0 {
if shared.state_changed.wait(&mut inner) {
return Err(EINTR);
}
}
// Consume a token.
inner.token_count -= 1;
}
// Notify a possible writer waiting.
shared.state_changed.notify_all();
// Write a one-byte 1 to the reader.
data.write_slice(&[1u8; 1])?;
Ok(1)
}
fn write(
shared: RefBorrow<'_, SharedState>,
_: &File,
data: &mut impl IoBufferReader,
_offset: u64,
) -> Result<usize> {
{
let mut inner = shared.inner.lock();
// Wait until we are allowed to increment the token count or a signal arrives.
while inner.token_count == MAX_TOKENS {
if shared.state_changed.wait(&mut inner) {
return Err(EINTR);
}
}
// Increment the number of token so that a reader can be released.
inner.token_count += 1;
}
// Notify a possible reader waiting.
shared.state_changed.notify_all();
Ok(data.len())
}
}
struct RustMiscdev {
_dev: Pin<Box<miscdev::Registration<Token>>>,
}
impl kernel::Module for RustMiscdev {
fn init(name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust miscellaneous device sample (init)\n");
let state = SharedState::try_new()?;
Ok(RustMiscdev {
_dev: miscdev::Registration::new_pinned(fmt!("{name}"), state)?,
})
}
}
impl Drop for RustMiscdev {
fn drop(&mut self) {
pr_info!("Rust miscellaneous device sample (exit)\n");
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust module parameters sample.
use kernel::prelude::*;
module! {
type: RustModuleParameters,
name: b"rust_module_parameters",
author: b"Rust for Linux Contributors",
description: b"Rust module parameters sample",
license: b"GPL",
params: {
my_bool: bool {
default: true,
permissions: 0,
description: b"Example of bool",
},
my_i32: i32 {
default: 42,
permissions: 0o644,
description: b"Example of i32",
},
my_str: str {
default: b"default str val",
permissions: 0o644,
description: b"Example of a string param",
},
my_usize: usize {
default: 42,
permissions: 0o644,
description: b"Example of usize",
},
my_array: ArrayParam<i32, 3> {
default: [0, 1],
permissions: 0,
description: b"Example of array",
},
},
}
struct RustModuleParameters;
impl kernel::Module for RustModuleParameters {
fn init(_name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust module parameters sample (init)\n");
{
let lock = module.kernel_param_lock();
pr_info!("Parameters:\n");
pr_info!(" my_bool: {}\n", my_bool.read());
pr_info!(" my_i32: {}\n", my_i32.read(&lock));
pr_info!(
" my_str: {}\n",
core::str::from_utf8(my_str.read(&lock))?
);
pr_info!(" my_usize: {}\n", my_usize.read(&lock));
pr_info!(" my_array: {:?}\n", my_array.read());
}
Ok(RustModuleParameters)
}
}
impl Drop for RustModuleParameters {
fn drop(&mut self) {
pr_info!("Rust module parameters sample (exit)\n");
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust netfilter sample.
use kernel::net;
use kernel::net::filter::{self as netfilter, inet, Disposition, Family};
use kernel::prelude::*;
module! {
type: RustNetfilter,
name: b"rust_netfilter",
author: b"Rust for Linux Contributors",
description: b"Rust netfilter sample",
license: b"GPL",
}
struct RustNetfilter {
_in: Pin<Box<netfilter::Registration<Self>>>,
_out: Pin<Box<netfilter::Registration<Self>>>,
}
impl netfilter::Filter for RustNetfilter {
fn filter(_: (), skb: &net::SkBuff) -> Disposition {
let data = skb.head_data();
pr_info!(
"packet headlen={}, len={}, first bytes={:02x?}\n",
data.len(),
skb.len(),
&data[..core::cmp::min(10, data.len())]
);
Disposition::Accept
}
}
impl kernel::Module for RustNetfilter {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
Ok(Self {
_in: netfilter::Registration::new_pinned(
Family::INet(inet::Hook::PreRouting),
0,
net::init_ns().into(),
None,
(),
)?,
_out: netfilter::Registration::new_pinned(
Family::INet(inet::Hook::PostRouting),
0,
net::init_ns().into(),
None,
(),
)?,
})
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust platform device driver sample.
use kernel::{module_platform_driver, of, platform, prelude::*};
module_platform_driver! {
type: Driver,
name: b"rust_platform",
license: b"GPL",
}
struct Driver;
impl platform::Driver for Driver {
kernel::define_of_id_table! {(), [
(of::DeviceId::Compatible(b"rust,sample"), None),
]}
fn probe(_dev: &mut platform::Device, _id_info: Option<&Self::IdInfo>) -> Result {
Ok(())
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust printing macros sample.
use kernel::prelude::*;
use kernel::{pr_cont, str::CStr, ThisModule};
module! {
type: RustPrint,
name: b"rust_print",
author: b"Rust for Linux Contributors",
description: b"Rust printing macros sample",
license: b"GPL",
}
struct RustPrint;
impl kernel::Module for RustPrint {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust printing macros sample (init)\n");
pr_emerg!("Emergency message (level 0) without args\n");
pr_alert!("Alert message (level 1) without args\n");
pr_crit!("Critical message (level 2) without args\n");
pr_err!("Error message (level 3) without args\n");
pr_warn!("Warning message (level 4) without args\n");
pr_notice!("Notice message (level 5) without args\n");
pr_info!("Info message (level 6) without args\n");
pr_info!("A line that");
pr_cont!(" is continued");
pr_cont!(" without args\n");
pr_emerg!("{} message (level {}) with args\n", "Emergency", 0);
pr_alert!("{} message (level {}) with args\n", "Alert", 1);
pr_crit!("{} message (level {}) with args\n", "Critical", 2);
pr_err!("{} message (level {}) with args\n", "Error", 3);
pr_warn!("{} message (level {}) with args\n", "Warning", 4);
pr_notice!("{} message (level {}) with args\n", "Notice", 5);
pr_info!("{} message (level {}) with args\n", "Info", 6);
pr_info!("A {} that", "line");
pr_cont!(" is {}", "continued");
pr_cont!(" with {}\n", "args");
Ok(RustPrint)
}
}
impl Drop for RustPrint {
fn drop(&mut self) {
pr_info!("Rust printing macros sample (exit)\n");
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust random device.
//!
//! Adapted from Alex Gaynor's original available at
//! <https://github.com/alex/just-use/blob/master/src/lib.rs>.
use kernel::{
file::{self, File},
io_buffer::{IoBufferReader, IoBufferWriter},
prelude::*,
};
module_misc_device! {
type: RandomFile,
name: b"rust_random",
author: b"Rust for Linux Contributors",
description: b"Just use /dev/urandom: Now with early-boot safety",
license: b"GPL",
}
struct RandomFile;
impl file::Operations for RandomFile {
kernel::declare_file_operations!(read, write, read_iter, write_iter);
fn open(_data: &(), _file: &File) -> Result {
Ok(())
}
fn read(_this: (), file: &File, buf: &mut impl IoBufferWriter, _: u64) -> Result<usize> {
let total_len = buf.len();
let mut chunkbuf = [0; 256];
while !buf.is_empty() {
let len = chunkbuf.len().min(buf.len());
let chunk = &mut chunkbuf[0..len];
if file.is_blocking() {
kernel::random::getrandom(chunk)?;
} else {
kernel::random::getrandom_nonblock(chunk)?;
}
buf.write_slice(chunk)?;
}
Ok(total_len)
}
fn write(_this: (), _file: &File, buf: &mut impl IoBufferReader, _: u64) -> Result<usize> {
let total_len = buf.len();
let mut chunkbuf = [0; 256];
while !buf.is_empty() {
let len = chunkbuf.len().min(buf.len());
let chunk = &mut chunkbuf[0..len];
buf.read_slice(chunk)?;
kernel::random::add_randomness(chunk);
}
Ok(total_len)
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust semaphore sample.
//!
//! A counting semaphore that can be used by userspace.
//!
//! The count is incremented by writes to the device. A write of `n` bytes results in an increment
//! of `n`. It is decremented by reads; each read results in the count being decremented by 1. If
//! the count is already zero, a read will block until another write increments it.
//!
//! This can be used in user space from the shell for example as follows (assuming a node called
//! `semaphore`): `cat semaphore` decrements the count by 1 (waiting for it to become non-zero
//! before decrementing); `echo -n 123 > semaphore` increments the semaphore by 3, potentially
//! unblocking up to 3 blocked readers.
use core::sync::atomic::{AtomicU64, Ordering};
use kernel::{
condvar_init, declare_file_operations,
file::{self, File, IoctlCommand, IoctlHandler},
io_buffer::{IoBufferReader, IoBufferWriter},
miscdev::Registration,
mutex_init,
prelude::*,
sync::{CondVar, Mutex, Ref, UniqueRef},
user_ptr::{UserSlicePtrReader, UserSlicePtrWriter},
};
module! {
type: RustSemaphore,
name: b"rust_semaphore",
author: b"Rust for Linux Contributors",
description: b"Rust semaphore sample",
license: b"GPL",
}
struct SemaphoreInner {
count: usize,
max_seen: usize,
}
struct Semaphore {
changed: CondVar,
inner: Mutex<SemaphoreInner>,
}
struct FileState {
read_count: AtomicU64,
shared: Ref<Semaphore>,
}
impl FileState {
fn consume(&self) -> Result {
let mut inner = self.shared.inner.lock();
while inner.count == 0 {
if self.shared.changed.wait(&mut inner) {
return Err(EINTR);
}
}
inner.count -= 1;
Ok(())
}
}
impl file::Operations for FileState {
type Data = Box<Self>;
type OpenData = Ref<Semaphore>;
declare_file_operations!(read, write, ioctl);
fn open(shared: &Ref<Semaphore>, _file: &File) -> Result<Box<Self>> {
Ok(Box::try_new(Self {
read_count: AtomicU64::new(0),
shared: shared.clone(),
})?)
}
fn read(this: &Self, _: &File, data: &mut impl IoBufferWriter, offset: u64) -> Result<usize> {
if data.is_empty() || offset > 0 {
return Ok(0);
}
this.consume()?;
data.write_slice(&[0u8; 1])?;
this.read_count.fetch_add(1, Ordering::Relaxed);
Ok(1)
}
fn write(this: &Self, _: &File, data: &mut impl IoBufferReader, _offs: u64) -> Result<usize> {
{
let mut inner = this.shared.inner.lock();
inner.count = inner.count.saturating_add(data.len());
if inner.count > inner.max_seen {
inner.max_seen = inner.count;
}
}
this.shared.changed.notify_all();
Ok(data.len())
}
fn ioctl(this: &Self, file: &File, cmd: &mut IoctlCommand) -> Result<i32> {
cmd.dispatch::<Self>(this, file)
}
}
struct RustSemaphore {
_dev: Pin<Box<Registration<FileState>>>,
}
impl kernel::Module for RustSemaphore {
fn init(name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust semaphore sample (init)\n");
let mut sema = Pin::from(UniqueRef::try_new(Semaphore {
// SAFETY: `condvar_init!` is called below.
changed: unsafe { CondVar::new() },
// SAFETY: `mutex_init!` is called below.
inner: unsafe {
Mutex::new(SemaphoreInner {
count: 0,
max_seen: 0,
})
},
})?);
// SAFETY: `changed` is pinned when `sema` is.
let pinned = unsafe { sema.as_mut().map_unchecked_mut(|s| &mut s.changed) };
condvar_init!(pinned, "Semaphore::changed");
// SAFETY: `inner` is pinned when `sema` is.
let pinned = unsafe { sema.as_mut().map_unchecked_mut(|s| &mut s.inner) };
mutex_init!(pinned, "Semaphore::inner");
Ok(Self {
_dev: Registration::new_pinned(fmt!("{name}"), sema.into())?,
})
}
}
impl Drop for RustSemaphore {
fn drop(&mut self) {
pr_info!("Rust semaphore sample (exit)\n");
}
}
const IOCTL_GET_READ_COUNT: u32 = 0x80086301;
const IOCTL_SET_READ_COUNT: u32 = 0x40086301;
impl IoctlHandler for FileState {
type Target<'a> = &'a Self;
fn read(this: &Self, _: &File, cmd: u32, writer: &mut UserSlicePtrWriter) -> Result<i32> {
match cmd {
IOCTL_GET_READ_COUNT => {
writer.write(&this.read_count.load(Ordering::Relaxed))?;
Ok(0)
}
_ => Err(EINVAL),
}
}
fn write(this: &Self, _: &File, cmd: u32, reader: &mut UserSlicePtrReader) -> Result<i32> {
match cmd {
IOCTL_SET_READ_COUNT => {
this.read_count.store(reader.read()?, Ordering::Relaxed);
Ok(0)
}
_ => Err(EINVAL),
}
}
}
// SPDX-License-Identifier: GPL-2.0
/*
* Rust semaphore sample (in C, for comparison)
*
* This is a C implementation of `rust_semaphore.rs`. Refer to the description
* in that file for details on the device.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/wait.h>
#define IOCTL_GET_READ_COUNT _IOR('c', 1, u64)
#define IOCTL_SET_READ_COUNT _IOW('c', 1, u64)
struct semaphore_state {
struct kref ref;
struct miscdevice miscdev;
wait_queue_head_t changed;
struct mutex mutex;
size_t count;
size_t max_seen;
};
struct file_state {
atomic64_t read_count;
struct semaphore_state *shared;
};
static int semaphore_consume(struct semaphore_state *state)
{
DEFINE_WAIT(wait);
mutex_lock(&state->mutex);
while (state->count == 0) {
prepare_to_wait(&state->changed, &wait, TASK_INTERRUPTIBLE);
mutex_unlock(&state->mutex);
schedule();
finish_wait(&state->changed, &wait);
if (signal_pending(current))
return -EINTR;
mutex_lock(&state->mutex);
}
state->count--;
mutex_unlock(&state->mutex);
return 0;
}
static int semaphore_open(struct inode *nodp, struct file *filp)
{
struct semaphore_state *shared =
container_of(filp->private_data, struct semaphore_state, miscdev);
struct file_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
kref_get(&shared->ref);
state->shared = shared;
atomic64_set(&state->read_count, 0);
filp->private_data = state;
return 0;
}
static ssize_t semaphore_write(struct file *filp, const char __user *buffer, size_t count,
loff_t *ppos)
{
struct file_state *state = filp->private_data;
struct semaphore_state *shared = state->shared;
mutex_lock(&shared->mutex);
shared->count += count;
if (shared->count < count)
shared->count = SIZE_MAX;
if (shared->count > shared->max_seen)
shared->max_seen = shared->count;
mutex_unlock(&shared->mutex);
wake_up_all(&shared->changed);
return count;
}
static ssize_t semaphore_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct file_state *state = filp->private_data;
char c = 0;
int ret;
if (count == 0 || *ppos > 0)
return 0;
ret = semaphore_consume(state->shared);
if (ret)
return ret;
if (copy_to_user(buffer, &c, sizeof(c)))
return -EFAULT;
atomic64_add(1, &state->read_count);
*ppos += 1;
return 1;
}
static long semaphore_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct file_state *state = filp->private_data;
void __user *buffer = (void __user *)arg;
u64 value;
switch (cmd) {
case IOCTL_GET_READ_COUNT:
value = atomic64_read(&state->read_count);
if (copy_to_user(buffer, &value, sizeof(value)))
return -EFAULT;
return 0;
case IOCTL_SET_READ_COUNT:
if (copy_from_user(&value, buffer, sizeof(value)))
return -EFAULT;
atomic64_set(&state->read_count, value);
return 0;
default:
return -EINVAL;
}
}
static void semaphore_free(struct kref *kref)
{
struct semaphore_state *device;
device = container_of(kref, struct semaphore_state, ref);
kfree(device);
}
static int semaphore_release(struct inode *nodp, struct file *filp)
{
struct file_state *state = filp->private_data;
kref_put(&state->shared->ref, semaphore_free);
kfree(state);
return 0;
}
static const struct file_operations semaphore_fops = {
.owner = THIS_MODULE,
.open = semaphore_open,
.read = semaphore_read,
.write = semaphore_write,
.compat_ioctl = semaphore_ioctl,
.release = semaphore_release,
};
static struct semaphore_state *device;
static int __init semaphore_init(void)
{
int ret;
struct semaphore_state *state;
pr_info("Rust semaphore sample (in C, for comparison) (init)\n");
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
mutex_init(&state->mutex);
kref_init(&state->ref);
init_waitqueue_head(&state->changed);
state->miscdev.fops = &semaphore_fops;
state->miscdev.minor = MISC_DYNAMIC_MINOR;
state->miscdev.name = "semaphore";
ret = misc_register(&state->miscdev);
if (ret < 0) {
kfree(state);
return ret;
}
device = state;
return 0;
}
static void __exit semaphore_exit(void)
{
pr_info("Rust semaphore sample (in C, for comparison) (exit)\n");
misc_deregister(&device->miscdev);
kref_put(&device->ref, semaphore_free);
}
module_init(semaphore_init);
module_exit(semaphore_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rust for Linux Contributors");
MODULE_DESCRIPTION("Rust semaphore sample (in C, for comparison)");
// SPDX-License-Identifier: GPL-2.0
//! Rust stack probing sample.
use kernel::prelude::*;
module! {
type: RustStackProbing,
name: b"rust_stack_probing",
author: b"Rust for Linux Contributors",
description: b"Rust stack probing sample",
license: b"GPL",
}
struct RustStackProbing;
impl kernel::Module for RustStackProbing {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust stack probing sample (init)\n");
// Including this large variable on the stack will trigger
// stack probing on the supported archs.
// This will verify that stack probing does not lead to
// any errors if we need to link `__rust_probestack`.
let x: [u64; 514] = core::hint::black_box([5; 514]);
pr_info!("Large array has length: {}\n", x.len());
Ok(RustStackProbing)
}
}
impl Drop for RustStackProbing {
fn drop(&mut self) {
pr_info!("Rust stack probing sample (exit)\n");
}
}
// SPDX-License-Identifier: GPL-2.0
//! Rust synchronisation primitives sample.
use kernel::prelude::*;
use kernel::{
condvar_init, mutex_init, spinlock_init,
sync::{CondVar, Mutex, SpinLock},
};
module! {
type: RustSync,
name: b"rust_sync",
author: b"Rust for Linux Contributors",
description: b"Rust synchronisation primitives sample",
license: b"GPL",
}
kernel::init_static_sync! {
static SAMPLE_MUTEX: Mutex<u32> = 10;
static SAMPLE_CONDVAR: CondVar;
}
struct RustSync;
impl kernel::Module for RustSync {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust synchronisation primitives sample (init)\n");
// Test mutexes.
{
// SAFETY: `init` is called below.
let mut data = Pin::from(Box::try_new(unsafe { Mutex::new(0) })?);
mutex_init!(data.as_mut(), "RustSync::init::data1");
*data.lock() = 10;
pr_info!("Value: {}\n", *data.lock());
// SAFETY: `init` is called below.
let mut cv = Pin::from(Box::try_new(unsafe { CondVar::new() })?);
condvar_init!(cv.as_mut(), "RustSync::init::cv1");
{
let mut guard = data.lock();
while *guard != 10 {
let _ = cv.wait(&mut guard);
}
}
cv.notify_one();
cv.notify_all();
cv.free_waiters();
}
// Test static mutex + condvar.
*SAMPLE_MUTEX.lock() = 20;
{
let mut guard = SAMPLE_MUTEX.lock();
while *guard != 20 {
let _ = SAMPLE_CONDVAR.wait(&mut guard);
}
}
// Test spinlocks.
{
// SAFETY: `init` is called below.
let mut data = Pin::from(Box::try_new(unsafe { SpinLock::new(0) })?);
spinlock_init!(data.as_mut(), "RustSync::init::data2");
*data.lock() = 10;
pr_info!("Value: {}\n", *data.lock());
// SAFETY: `init` is called below.
let mut cv = Pin::from(Box::try_new(unsafe { CondVar::new() })?);
condvar_init!(cv.as_mut(), "RustSync::init::cv2");
{
let mut guard = data.lock();
while *guard != 10 {
let _ = cv.wait(&mut guard);
}
}
cv.notify_one();
cv.notify_all();
cv.free_waiters();
}
Ok(RustSync)
}
}
impl Drop for RustSync {
fn drop(&mut self) {
pr_info!("Rust synchronisation primitives sample (exit)\n");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册