Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <chrisi.schrefl@gmail.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20250227-miscdevice-fops-change-v1-1-c9e9b75d67eb@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alice Ryhl and committed by
Greg Kroah-Hartman
74fc3493 264ff841

+145 -156
+145 -156
rust/kernel/miscdevice.rs
··· 35 35 let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() }; 36 36 result.minor = bindings::MISC_DYNAMIC_MINOR as _; 37 37 result.name = self.name.as_char_ptr(); 38 - result.fops = create_vtable::<T>(); 38 + result.fops = MiscdeviceVTable::<T>::build(); 39 39 result 40 40 } 41 41 } ··· 160 160 } 161 161 } 162 162 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; 176 + } 177 + 178 + // SAFETY: The open call of a file can access the private data. 179 + let misc_ptr = unsafe { (*raw_file).private_data }; 180 + 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>>() }; 186 + 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) }; 191 + 192 + let ptr = match T::open(file, misc) { 193 + Ok(ptr) => ptr, 194 + Err(err) => return err.to_errno(), 195 + }; 196 + 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() }; 204 + 205 + 0 206 + } 207 + 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 + } 225 + 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, 169 243 } 170 244 } 171 245 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 - } 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) }; 193 259 194 - &VtableHelper::<T>::VTABLE 195 - } 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) }; 196 264 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; 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 + } 209 269 } 210 270 211 - // SAFETY: The open call of a file can access the private data. 212 - let misc_ptr = unsafe { (*raw_file).private_data }; 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) }; 213 287 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>>() }; 288 + T::show_fdinfo(device, m, file); 289 + } 218 290 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) }; 223 - 224 - let ptr = match T::open(file, misc) { 225 - Ok(ptr) => ptr, 226 - Err(err) => return err.to_errno(), 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() } 227 314 }; 228 315 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() }; 235 - 236 - 0 237 - } 238 - 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 - } 259 - 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, 316 + const fn build() -> &'static bindings::file_operations { 317 + &Self::VTABLE 281 318 } 282 - } 283 - 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, 306 - } 307 - } 308 - 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); 330 319 }