Skip to content

Commit e134ae0

Browse files
authored
Merge pull request torvalds#114 from Rust-for-Linux/file_operations
Simplify how clients define file operations.
2 parents 58ab534 + b540c45 commit e134ae0

File tree

2 files changed

+72
-37
lines changed

2 files changed

+72
-37
lines changed

drivers/char/rust_example.rs

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ struct RustFile;
5151
impl FileOperations for RustFile {
5252
type Wrapper = Box<Self>;
5353

54+
kernel::declare_file_operations!();
55+
5456
fn open() -> KernelResult<Self::Wrapper> {
5557
println!("rust file was opened!");
5658
Ok(Box::try_new(Self)?)

rust/kernel/file_operations.rs

+70-37
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ unsafe extern "C" fn read_callback<T: FileOperations>(
8686
let f = &*((*file).private_data as *const T);
8787
// No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
8888
// See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
89-
T::READ.unwrap()(f, &File::from_ptr(file), &mut data, (*offset).try_into()?)?;
89+
T::read(f, &File::from_ptr(file), &mut data, (*offset).try_into()?)?;
9090
let written = len - data.len();
9191
(*offset) += bindings::loff_t::try_from(written).unwrap();
9292
Ok(written.try_into().unwrap())
@@ -104,7 +104,7 @@ unsafe extern "C" fn write_callback<T: FileOperations>(
104104
let f = &*((*file).private_data as *const T);
105105
// No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
106106
// See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
107-
T::WRITE.unwrap()(f, &mut data, (*offset).try_into()?)?;
107+
T::write(f, &mut data, (*offset).try_into()?)?;
108108
let read = len - data.len();
109109
(*offset) += bindings::loff_t::try_from(read).unwrap();
110110
Ok(read.try_into().unwrap())
@@ -133,7 +133,7 @@ unsafe extern "C" fn llseek_callback<T: FileOperations>(
133133
_ => return Err(Error::EINVAL),
134134
};
135135
let f = &*((*file).private_data as *const T);
136-
let off = T::SEEK.unwrap()(f, &File::from_ptr(file), off)?;
136+
let off = T::seek(f, &File::from_ptr(file), off)?;
137137
Ok(off as bindings::loff_t)
138138
}
139139
}
@@ -149,7 +149,7 @@ unsafe extern "C" fn fsync_callback<T: FileOperations>(
149149
let end = end.try_into()?;
150150
let datasync = datasync != 0;
151151
let f = &*((*file).private_data as *const T);
152-
let res = T::FSYNC.unwrap()(f, &File::from_ptr(file), start, end, datasync)?;
152+
let res = T::fsync(f, &File::from_ptr(file), start, end, datasync)?;
153153
Ok(res.try_into().unwrap())
154154
}
155155
}
@@ -160,17 +160,17 @@ impl<T: FileOperations> FileOperationsVtable<T> {
160160
pub(crate) const VTABLE: bindings::file_operations = bindings::file_operations {
161161
open: Some(open_callback::<T>),
162162
release: Some(release_callback::<T>),
163-
read: if T::READ.is_some() {
163+
read: if T::TO_USE.read {
164164
Some(read_callback::<T>)
165165
} else {
166166
None
167167
},
168-
write: if T::WRITE.is_some() {
168+
write: if T::TO_USE.write {
169169
Some(write_callback::<T>)
170170
} else {
171171
None
172172
},
173-
llseek: if T::SEEK.is_some() {
173+
llseek: if T::TO_USE.seek {
174174
Some(llseek_callback::<T>)
175175
} else {
176176
None
@@ -184,7 +184,7 @@ impl<T: FileOperations> FileOperationsVtable<T> {
184184
fasync: None,
185185
flock: None,
186186
flush: None,
187-
fsync: if T::FSYNC.is_some() {
187+
fsync: if T::TO_USE.fsync {
188188
Some(fsync_callback::<T>)
189189
} else {
190190
None
@@ -210,26 +210,55 @@ impl<T: FileOperations> FileOperationsVtable<T> {
210210
};
211211
}
212212

213-
/// `read` file operation function type.
214-
pub type ReadFn<T> = Option<fn(&T, &File, &mut UserSlicePtrWriter, u64) -> KernelResult<()>>;
213+
/// Represents which fields of [`struct file_operations`] should be populated with pointers.
214+
pub struct ToUse {
215+
/// The `read` field of [`struct file_operations`].
216+
pub read: bool,
215217

216-
/// `write` file operation function type.
217-
pub type WriteFn<T> = Option<fn(&T, &mut UserSlicePtrReader, u64) -> KernelResult<()>>;
218+
/// The `write` field of [`struct file_operations`].
219+
pub write: bool,
218220

219-
/// `seek` file operation function type.
220-
pub type SeekFn<T> = Option<fn(&T, &File, SeekFrom) -> KernelResult<u64>>;
221+
/// The `llseek` field of [`struct file_operations`].
222+
pub seek: bool,
221223

222-
/// `fsync` file operation function type.
223-
pub type FSync<T> = Option<fn(&T, &File, u64, u64, bool) -> KernelResult<u32>>;
224+
/// The `fsync` field of [`struct file_operations`].
225+
pub fsync: bool,
226+
}
227+
228+
/// A constant version where all values are to set to `false`, that is, all supported fields will
229+
/// be set to null pointers.
230+
pub const USE_NONE: ToUse = ToUse {
231+
read: false,
232+
write: false,
233+
seek: false,
234+
fsync: false,
235+
};
236+
237+
/// Defines the [`FileOperations::TO_USE`] field based on a list of fields to be populated.
238+
#[macro_export]
239+
macro_rules! declare_file_operations {
240+
() => {
241+
const TO_USE: $crate::file_operations::ToUse = $crate::file_operations::USE_NONE;
242+
};
243+
($($i:ident),+) => {
244+
const TO_USE: kernel::file_operations::ToUse =
245+
$crate::file_operations::ToUse {
246+
$($i: true),+ ,
247+
..$crate::file_operations::USE_NONE
248+
};
249+
};
250+
}
224251

225252
/// Corresponds to the kernel's `struct file_operations`.
226253
///
227-
/// You implement this trait whenever you would create a
228-
/// `struct file_operations`.
254+
/// You implement this trait whenever you would create a `struct file_operations`.
229255
///
230-
/// File descriptors may be used from multiple threads/processes concurrently,
231-
/// so your type must be [`Sync`].
256+
/// File descriptors may be used from multiple threads/processes concurrently, so your type must be
257+
/// [`Sync`].
232258
pub trait FileOperations: Sync + Sized {
259+
/// The methods to use to populate [`struct file_operations`].
260+
const TO_USE: ToUse;
261+
233262
/// The pointer type that will be used to hold ourselves.
234263
type Wrapper: PointerWrapper<Self>;
235264

@@ -240,41 +269,46 @@ pub trait FileOperations: Sync + Sized {
240269

241270
/// Cleans up after the last reference to the file goes away.
242271
///
243-
/// Note that the object is moved, so it will be freed automatically unless
244-
/// the implemention moves it elsewhere.
272+
/// Note that the object is moved, so it will be freed automatically unless the implementation
273+
/// moves it elsewhere.
245274
///
246-
/// Corresponds to the `release` function pointer in
247-
/// `struct file_operations`.
275+
/// Corresponds to the `release` function pointer in `struct file_operations`.
248276
fn release(_obj: Self::Wrapper, _file: &File) {}
249277

250278
/// Reads data from this file to userspace.
251279
///
252280
/// Corresponds to the `read` function pointer in `struct file_operations`.
253-
const READ: ReadFn<Self> = None;
281+
fn read(&self, _file: &File, _data: &mut UserSlicePtrWriter, _offset: u64) -> KernelResult<()> {
282+
Err(Error::EINVAL)
283+
}
254284

255285
/// Writes data from userspace to this file.
256286
///
257287
/// Corresponds to the `write` function pointer in `struct file_operations`.
258-
const WRITE: WriteFn<Self> = None;
288+
fn write(&self, _data: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<isize> {
289+
Err(Error::EINVAL)
290+
}
259291

260292
/// Changes the position of the file.
261293
///
262-
/// Corresponds to the `llseek` function pointer in
263-
/// `struct file_operations`.
264-
const SEEK: SeekFn<Self> = None;
294+
/// Corresponds to the `llseek` function pointer in `struct file_operations`.
295+
fn seek(&self, _file: &File, _offset: SeekFrom) -> KernelResult<u64> {
296+
Err(Error::EINVAL)
297+
}
265298

266299
/// Syncs pending changes to this file.
267300
///
268-
/// Corresponds to the `fsync` function pointer in
269-
/// `struct file_operations`.
270-
const FSYNC: FSync<Self> = None;
301+
/// Corresponds to the `fsync` function pointer in `struct file_operations`.
302+
fn fsync(&self, _file: &File, _start: u64, _end: u64, _datasync: bool) -> KernelResult<u32> {
303+
Err(Error::EINVAL)
304+
}
271305
}
272306

273307
/// Used to convert an object into a raw pointer that represents it.
274308
///
275-
/// It can eventually be converted back into the object. This is used to store
276-
/// objects as pointers in kernel data structures, for example, an
277-
/// implementation of `FileOperations` in `struct file::private_data`.
309+
/// It can eventually be converted back into the object. This is used to store objects as pointers
310+
/// in kernel data structures, for example, an implementation of [`FileOperations`] in `struct
311+
/// file::private_data`.
278312
pub trait PointerWrapper<T> {
279313
/// Returns the raw pointer.
280314
fn into_pointer(self) -> *const T;
@@ -283,8 +317,7 @@ pub trait PointerWrapper<T> {
283317
///
284318
/// # Safety
285319
///
286-
/// The passed pointer must come from a previous call to
287-
/// [`PointerWrapper::into_pointer()`].
320+
/// The passed pointer must come from a previous call to [`PointerWrapper::into_pointer()`].
288321
unsafe fn from_pointer(ptr: *const T) -> Self;
289322
}
290323

0 commit comments

Comments
 (0)