Skip to content

Commit d189aae

Browse files
authored
Merge pull request #89 from Rust-for-Linux/pin-chrdev
Use Pin<T> in the chrdev API
2 parents 9ba918a + 5bcc52f commit d189aae

File tree

2 files changed

+93
-70
lines changed

2 files changed

+93
-70
lines changed

drivers/char/rust_example.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use alloc::boxed::Box;
88
use core::pin::Pin;
99
use kernel::prelude::*;
10-
use kernel::{cstr, file_operations::FileOperations, miscdev};
10+
use kernel::{chrdev, cstr, file_operations::FileOperations, miscdev};
1111

1212
module! {
1313
type: RustExample,
@@ -47,6 +47,7 @@ impl FileOperations for RustFile {
4747

4848
struct RustExample {
4949
message: String,
50+
_chrdev: Pin<Box<chrdev::Registration<2>>>,
5051
_dev: Pin<Box<miscdev::Registration>>,
5152
}
5253

@@ -72,9 +73,15 @@ impl KernelModule for RustExample {
7273
let x: [u64; 1028] = core::hint::black_box([5; 1028]);
7374
println!("Large array has length: {}", x.len());
7475

76+
let mut chrdev_reg = chrdev::Registration::new_pinned(
77+
cstr!("rust_chrdev"), 0, &THIS_MODULE)?;
78+
chrdev_reg.as_mut().register::<RustFile>()?;
79+
chrdev_reg.as_mut().register::<RustFile>()?;
80+
7581
Ok(RustExample {
7682
message: "on the heap!".to_owned(),
7783
_dev: miscdev::Registration::new_pinned::<RustFile>(cstr!("rust_miscdev"), None)?,
84+
_chrdev: chrdev_reg,
7885
})
7986
}
8087
}

rust/kernel/chrdev.rs

+85-69
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,116 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3-
use core::convert::TryInto;
4-
use core::mem;
5-
use core::ops::Range;
6-
73
use alloc::boxed::Box;
8-
use alloc::vec;
9-
use alloc::vec::Vec;
4+
use core::convert::TryInto;
5+
use core::marker::PhantomPinned;
6+
use core::mem::MaybeUninit;
7+
use core::pin::Pin;
108

119
use crate::bindings;
1210
use crate::c_types;
1311
use crate::error::{Error, KernelResult};
1412
use crate::file_operations;
1513
use crate::types::CStr;
1614

17-
pub fn builder(name: CStr<'static>, minors: Range<u16>) -> KernelResult<Builder> {
18-
Ok(Builder {
19-
name,
20-
minors,
21-
file_ops: vec![],
22-
})
15+
struct RegistrationInner<const N: usize> {
16+
dev: bindings::dev_t,
17+
used: usize,
18+
cdevs: [MaybeUninit<bindings::cdev>; N],
19+
_pin: PhantomPinned,
2320
}
2421

25-
pub struct Builder {
22+
/// chrdev registration. May contain up to a fixed number (`N`) of devices.
23+
/// Must be pinned.
24+
pub struct Registration<const N: usize> {
2625
name: CStr<'static>,
27-
minors: Range<u16>,
28-
file_ops: Vec<&'static bindings::file_operations>,
26+
minors_start: u16,
27+
this_module: &'static crate::ThisModule,
28+
inner: Option<RegistrationInner<N>>,
2929
}
3030

31-
impl Builder {
32-
pub fn register_device<T: file_operations::FileOperations>(mut self) -> Builder {
33-
if self.file_ops.len() >= self.minors.len() {
34-
panic!("More devices registered than minor numbers allocated.")
31+
impl<const N: usize> Registration<{ N }> {
32+
pub fn new(
33+
name: CStr<'static>,
34+
minors_start: u16,
35+
this_module: &'static crate::ThisModule,
36+
) -> Self {
37+
Registration {
38+
name,
39+
minors_start,
40+
this_module,
41+
inner: None,
3542
}
36-
self.file_ops
37-
.push(&file_operations::FileOperationsVtable::<T>::VTABLE);
38-
self
3943
}
4044

41-
pub fn build(self, this_module: &'static crate::ThisModule) -> KernelResult<Registration> {
42-
let mut dev: bindings::dev_t = 0;
43-
let res = unsafe {
44-
bindings::alloc_chrdev_region(
45-
&mut dev,
46-
self.minors.start.into(),
47-
self.minors.len().try_into()?,
48-
self.name.as_ptr() as *const c_types::c_char,
49-
)
50-
};
51-
if res != 0 {
52-
return Err(Error::from_kernel_errno(res));
45+
pub fn new_pinned(
46+
name: CStr<'static>,
47+
minors_start: u16,
48+
this_module: &'static crate::ThisModule,
49+
) -> KernelResult<Pin<Box<Self>>> {
50+
Ok(Pin::from(Box::try_new(Self::new(name, minors_start, this_module))?))
51+
}
52+
/// Register a character device with this range. Call this once per device
53+
/// type (up to `N` times).
54+
pub fn register<T: file_operations::FileOperations>(self: Pin<&mut Self>) -> KernelResult<()> {
55+
// SAFETY: we must ensure that we never move out of `this`.
56+
let this = unsafe { self.get_unchecked_mut() };
57+
if this.inner.is_none() {
58+
let mut dev: bindings::dev_t = 0;
59+
// SAFETY: Calling unsafe function. `this.name` has 'static
60+
// lifetime
61+
let res = unsafe {
62+
bindings::alloc_chrdev_region(
63+
&mut dev,
64+
this.minors_start.into(),
65+
N.try_into()?,
66+
this.name.as_ptr() as *const c_types::c_char,
67+
)
68+
};
69+
if res != 0 {
70+
return Err(Error::from_kernel_errno(res));
71+
}
72+
this.inner = Some(RegistrationInner {
73+
dev,
74+
used: 0,
75+
cdevs: [MaybeUninit::<bindings::cdev>::uninit(); N],
76+
_pin: PhantomPinned,
77+
});
5378
}
5479

55-
// Turn this into a boxed slice immediately because the kernel stores pointers into it, and
56-
// so that data should never be moved.
57-
let mut cdevs = vec![unsafe { mem::zeroed() }; self.file_ops.len()].into_boxed_slice();
58-
for (i, file_op) in self.file_ops.iter().enumerate() {
59-
unsafe {
60-
bindings::cdev_init(&mut cdevs[i], *file_op);
61-
cdevs[i].owner = this_module.0;
62-
let rc = bindings::cdev_add(&mut cdevs[i], dev + i as bindings::dev_t, 1);
63-
if rc != 0 {
64-
// Clean up the ones that were allocated.
65-
for j in 0..=i {
66-
bindings::cdev_del(&mut cdevs[j]);
67-
}
68-
bindings::unregister_chrdev_region(dev, self.minors.len() as _);
69-
return Err(Error::from_kernel_errno(rc));
70-
}
80+
let mut inner = this.inner.as_mut().unwrap();
81+
if inner.used == N {
82+
return Err(Error::EINVAL);
83+
}
84+
let cdev = inner.cdevs[inner.used].as_mut_ptr();
85+
// SAFETY: calling unsafe functions and manipulating MaybeUninit ptr.
86+
unsafe {
87+
bindings::cdev_init(cdev, &file_operations::FileOperationsVtable::<T>::VTABLE);
88+
(*cdev).owner = this.this_module.0;
89+
let rc = bindings::cdev_add(cdev, inner.dev + inner.used as bindings::dev_t, 1);
90+
if rc != 0 {
91+
return Err(Error::from_kernel_errno(rc));
7192
}
7293
}
73-
74-
Ok(Registration {
75-
dev,
76-
count: self.minors.len(),
77-
cdevs,
78-
})
94+
inner.used += 1;
95+
Ok(())
7996
}
8097
}
8198

82-
pub struct Registration {
83-
dev: bindings::dev_t,
84-
count: usize,
85-
cdevs: Box<[bindings::cdev]>,
86-
}
87-
88-
// This is safe because Registration doesn't actually expose any methods.
89-
unsafe impl Sync for Registration {}
99+
// SAFETY: `Registration` doesn't expose any of its state across threads (it's
100+
// fine for multiple threads to have a shared reference to it).
101+
unsafe impl<const N: usize> Sync for Registration<{ N }> {}
90102

91-
impl Drop for Registration {
103+
impl<const N: usize> Drop for Registration<{ N }> {
92104
fn drop(&mut self) {
93-
unsafe {
94-
for dev in self.cdevs.iter_mut() {
95-
bindings::cdev_del(dev);
105+
if let Some(inner) = self.inner.as_mut() {
106+
// SAFETY: calling unsafe functions, `0..inner.used` of
107+
// `inner.cdevs` are initialized in `Registration::register`.
108+
unsafe {
109+
for i in 0..inner.used {
110+
bindings::cdev_del(inner.cdevs[i].as_mut_ptr());
111+
}
112+
bindings::unregister_chrdev_region(inner.dev, N.try_into().unwrap());
96113
}
97-
bindings::unregister_chrdev_region(self.dev, self.count as _);
98114
}
99115
}
100116
}

0 commit comments

Comments
 (0)