Skip to content

Commit 74fc349

Browse files
Darksonngregkh
authored andcommitted
rust: miscdevice: change how f_ops vtable is constructed
I was helping someone with writing a new Rust abstraction, and we were using the miscdevice abstraction as an example. While doing this, it became clear to me that the way I implemented the f_ops vtable is confusing to new Rust users, and that the approach used by the block abstractions is less confusing. Thus, update the miscdevice abstractions to use the same approach as rust/kernel/block/mq/operations.rs. Sorry about the large diff. This changes the indentation of a large amount of code. Reviewed-by: Christian Schrefl <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 264ff84 commit 74fc349

File tree

1 file changed

+143
-154
lines changed

1 file changed

+143
-154
lines changed

rust/kernel/miscdevice.rs

Lines changed: 143 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl MiscDeviceOptions {
3535
let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() };
3636
result.minor = bindings::MISC_DYNAMIC_MINOR as _;
3737
result.name = self.name.as_char_ptr();
38-
result.fops = create_vtable::<T>();
38+
result.fops = MiscdeviceVTable::<T>::build();
3939
result
4040
}
4141
}
@@ -160,171 +160,160 @@ pub trait MiscDevice: Sized {
160160
}
161161
}
162162

163-
const fn create_vtable<T: MiscDevice>() -> &'static bindings::file_operations {
164-
const fn maybe_fn<T: Copy>(check: bool, func: T) -> Option<T> {
165-
if check {
166-
Some(func)
167-
} else {
168-
None
163+
/// A vtable for the file operations of a Rust miscdevice.
164+
struct MiscdeviceVTable<T: MiscDevice>(PhantomData<T>);
165+
166+
impl<T: MiscDevice> MiscdeviceVTable<T> {
167+
/// # Safety
168+
///
169+
/// `file` and `inode` must be the file and inode for a file that is undergoing initialization.
170+
/// The file must be associated with a `MiscDeviceRegistration<T>`.
171+
unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int {
172+
// SAFETY: The pointers are valid and for a file being opened.
173+
let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
174+
if ret != 0 {
175+
return ret;
169176
}
170-
}
171177

172-
struct VtableHelper<T: MiscDevice> {
173-
_t: PhantomData<T>,
174-
}
175-
impl<T: MiscDevice> VtableHelper<T> {
176-
const VTABLE: bindings::file_operations = bindings::file_operations {
177-
open: Some(fops_open::<T>),
178-
release: Some(fops_release::<T>),
179-
unlocked_ioctl: maybe_fn(T::HAS_IOCTL, fops_ioctl::<T>),
180-
#[cfg(CONFIG_COMPAT)]
181-
compat_ioctl: if T::HAS_COMPAT_IOCTL {
182-
Some(fops_compat_ioctl::<T>)
183-
} else if T::HAS_IOCTL {
184-
Some(bindings::compat_ptr_ioctl)
185-
} else {
186-
None
187-
},
188-
show_fdinfo: maybe_fn(T::HAS_SHOW_FDINFO, fops_show_fdinfo::<T>),
189-
// SAFETY: All zeros is a valid value for `bindings::file_operations`.
190-
..unsafe { MaybeUninit::zeroed().assume_init() }
191-
};
192-
}
178+
// SAFETY: The open call of a file can access the private data.
179+
let misc_ptr = unsafe { (*raw_file).private_data };
193180

194-
&VtableHelper::<T>::VTABLE
195-
}
181+
// SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
182+
// associated `struct miscdevice` before calling into this method. Furthermore,
183+
// `misc_open()` ensures that the miscdevice can't be unregistered and freed during this
184+
// call to `fops_open`.
185+
let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
196186

197-
/// # Safety
198-
///
199-
/// `file` and `inode` must be the file and inode for a file that is undergoing initialization.
200-
/// The file must be associated with a `MiscDeviceRegistration<T>`.
201-
unsafe extern "C" fn fops_open<T: MiscDevice>(
202-
inode: *mut bindings::inode,
203-
raw_file: *mut bindings::file,
204-
) -> c_int {
205-
// SAFETY: The pointers are valid and for a file being opened.
206-
let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
207-
if ret != 0 {
208-
return ret;
209-
}
187+
// SAFETY:
188+
// * This underlying file is valid for (much longer than) the duration of `T::open`.
189+
// * There is no active fdget_pos region on the file on this thread.
190+
let file = unsafe { File::from_raw_file(raw_file) };
210191

211-
// SAFETY: The open call of a file can access the private data.
212-
let misc_ptr = unsafe { (*raw_file).private_data };
213-
214-
// SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
215-
// associated `struct miscdevice` before calling into this method. Furthermore, `misc_open()`
216-
// ensures that the miscdevice can't be unregistered and freed during this call to `fops_open`.
217-
let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
192+
let ptr = match T::open(file, misc) {
193+
Ok(ptr) => ptr,
194+
Err(err) => return err.to_errno(),
195+
};
218196

219-
// SAFETY:
220-
// * This underlying file is valid for (much longer than) the duration of `T::open`.
221-
// * There is no active fdget_pos region on the file on this thread.
222-
let file = unsafe { File::from_raw_file(raw_file) };
197+
// This overwrites the private data with the value specified by the user, changing the type
198+
// of this file's private data. All future accesses to the private data is performed by
199+
// other fops_* methods in this file, which all correctly cast the private data to the new
200+
// type.
201+
//
202+
// SAFETY: The open call of a file can access the private data.
203+
unsafe { (*raw_file).private_data = ptr.into_foreign() };
223204

224-
let ptr = match T::open(file, misc) {
225-
Ok(ptr) => ptr,
226-
Err(err) => return err.to_errno(),
227-
};
228-
229-
// This overwrites the private data with the value specified by the user, changing the type of
230-
// this file's private data. All future accesses to the private data is performed by other
231-
// fops_* methods in this file, which all correctly cast the private data to the new type.
232-
//
233-
// SAFETY: The open call of a file can access the private data.
234-
unsafe { (*raw_file).private_data = ptr.into_foreign() };
205+
0
206+
}
235207

236-
0
237-
}
208+
/// # Safety
209+
///
210+
/// `file` and `inode` must be the file and inode for a file that is being released. The file
211+
/// must be associated with a `MiscDeviceRegistration<T>`.
212+
unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int {
213+
// SAFETY: The release call of a file owns the private data.
214+
let private = unsafe { (*file).private_data };
215+
// SAFETY: The release call of a file owns the private data.
216+
let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
217+
218+
// SAFETY:
219+
// * The file is valid for the duration of this call.
220+
// * There is no active fdget_pos region on the file on this thread.
221+
T::release(ptr, unsafe { File::from_raw_file(file) });
222+
223+
0
224+
}
238225

239-
/// # Safety
240-
///
241-
/// `file` and `inode` must be the file and inode for a file that is being released. The file must
242-
/// be associated with a `MiscDeviceRegistration<T>`.
243-
unsafe extern "C" fn fops_release<T: MiscDevice>(
244-
_inode: *mut bindings::inode,
245-
file: *mut bindings::file,
246-
) -> c_int {
247-
// SAFETY: The release call of a file owns the private data.
248-
let private = unsafe { (*file).private_data };
249-
// SAFETY: The release call of a file owns the private data.
250-
let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
251-
252-
// SAFETY:
253-
// * The file is valid for the duration of this call.
254-
// * There is no active fdget_pos region on the file on this thread.
255-
T::release(ptr, unsafe { File::from_raw_file(file) });
256-
257-
0
258-
}
226+
/// # Safety
227+
///
228+
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
229+
unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long {
230+
// SAFETY: The ioctl call of a file can access the private data.
231+
let private = unsafe { (*file).private_data };
232+
// SAFETY: Ioctl calls can borrow the private data of the file.
233+
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
234+
235+
// SAFETY:
236+
// * The file is valid for the duration of this call.
237+
// * There is no active fdget_pos region on the file on this thread.
238+
let file = unsafe { File::from_raw_file(file) };
239+
240+
match T::ioctl(device, file, cmd, arg) {
241+
Ok(ret) => ret as c_long,
242+
Err(err) => err.to_errno() as c_long,
243+
}
244+
}
259245

260-
/// # Safety
261-
///
262-
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
263-
unsafe extern "C" fn fops_ioctl<T: MiscDevice>(
264-
file: *mut bindings::file,
265-
cmd: c_uint,
266-
arg: c_ulong,
267-
) -> c_long {
268-
// SAFETY: The ioctl call of a file can access the private data.
269-
let private = unsafe { (*file).private_data };
270-
// SAFETY: Ioctl calls can borrow the private data of the file.
271-
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
272-
273-
// SAFETY:
274-
// * The file is valid for the duration of this call.
275-
// * There is no active fdget_pos region on the file on this thread.
276-
let file = unsafe { File::from_raw_file(file) };
277-
278-
match T::ioctl(device, file, cmd, arg) {
279-
Ok(ret) => ret as c_long,
280-
Err(err) => err.to_errno() as c_long,
246+
/// # Safety
247+
///
248+
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
249+
#[cfg(CONFIG_COMPAT)]
250+
unsafe extern "C" fn compat_ioctl(
251+
file: *mut bindings::file,
252+
cmd: c_uint,
253+
arg: c_ulong,
254+
) -> c_long {
255+
// SAFETY: The compat ioctl call of a file can access the private data.
256+
let private = unsafe { (*file).private_data };
257+
// SAFETY: Ioctl calls can borrow the private data of the file.
258+
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
259+
260+
// SAFETY:
261+
// * The file is valid for the duration of this call.
262+
// * There is no active fdget_pos region on the file on this thread.
263+
let file = unsafe { File::from_raw_file(file) };
264+
265+
match T::compat_ioctl(device, file, cmd, arg) {
266+
Ok(ret) => ret as c_long,
267+
Err(err) => err.to_errno() as c_long,
268+
}
281269
}
282-
}
283270

284-
/// # Safety
285-
///
286-
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
287-
#[cfg(CONFIG_COMPAT)]
288-
unsafe extern "C" fn fops_compat_ioctl<T: MiscDevice>(
289-
file: *mut bindings::file,
290-
cmd: c_uint,
291-
arg: c_ulong,
292-
) -> c_long {
293-
// SAFETY: The compat ioctl call of a file can access the private data.
294-
let private = unsafe { (*file).private_data };
295-
// SAFETY: Ioctl calls can borrow the private data of the file.
296-
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
297-
298-
// SAFETY:
299-
// * The file is valid for the duration of this call.
300-
// * There is no active fdget_pos region on the file on this thread.
301-
let file = unsafe { File::from_raw_file(file) };
302-
303-
match T::compat_ioctl(device, file, cmd, arg) {
304-
Ok(ret) => ret as c_long,
305-
Err(err) => err.to_errno() as c_long,
271+
/// # Safety
272+
///
273+
/// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
274+
/// - `seq_file` must be a valid `struct seq_file` that we can write to.
275+
unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) {
276+
// SAFETY: The release call of a file owns the private data.
277+
let private = unsafe { (*file).private_data };
278+
// SAFETY: Ioctl calls can borrow the private data of the file.
279+
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
280+
// SAFETY:
281+
// * The file is valid for the duration of this call.
282+
// * There is no active fdget_pos region on the file on this thread.
283+
let file = unsafe { File::from_raw_file(file) };
284+
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in
285+
// which this method is called.
286+
let m = unsafe { SeqFile::from_raw(seq_file) };
287+
288+
T::show_fdinfo(device, m, file);
306289
}
307-
}
308290

309-
/// # Safety
310-
///
311-
/// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
312-
/// - `seq_file` must be a valid `struct seq_file` that we can write to.
313-
unsafe extern "C" fn fops_show_fdinfo<T: MiscDevice>(
314-
seq_file: *mut bindings::seq_file,
315-
file: *mut bindings::file,
316-
) {
317-
// SAFETY: The release call of a file owns the private data.
318-
let private = unsafe { (*file).private_data };
319-
// SAFETY: Ioctl calls can borrow the private data of the file.
320-
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
321-
// SAFETY:
322-
// * The file is valid for the duration of this call.
323-
// * There is no active fdget_pos region on the file on this thread.
324-
let file = unsafe { File::from_raw_file(file) };
325-
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
326-
// this method is called.
327-
let m = unsafe { SeqFile::from_raw(seq_file) };
328-
329-
T::show_fdinfo(device, m, file);
291+
const VTABLE: bindings::file_operations = bindings::file_operations {
292+
open: Some(Self::open),
293+
release: Some(Self::release),
294+
unlocked_ioctl: if T::HAS_IOCTL {
295+
Some(Self::ioctl)
296+
} else {
297+
None
298+
},
299+
#[cfg(CONFIG_COMPAT)]
300+
compat_ioctl: if T::HAS_COMPAT_IOCTL {
301+
Some(Self::compat_ioctl)
302+
} else if T::HAS_IOCTL {
303+
Some(bindings::compat_ptr_ioctl)
304+
} else {
305+
None
306+
},
307+
show_fdinfo: if T::HAS_SHOW_FDINFO {
308+
Some(Self::show_fdinfo)
309+
} else {
310+
None
311+
},
312+
// SAFETY: All zeros is a valid value for `bindings::file_operations`.
313+
..unsafe { MaybeUninit::zeroed().assume_init() }
314+
};
315+
316+
const fn build() -> &'static bindings::file_operations {
317+
&Self::VTABLE
318+
}
330319
}

0 commit comments

Comments
 (0)