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: irq: add &Device<Bound> argument to irq callbacks

When working with a bus device, many operations are only possible while
the device is still bound. The &Device<Bound> type represents a proof in
the type system that you are in a scope where the device is guaranteed
to still be bound. Since we deregister irq callbacks when unbinding a
device, if an irq callback is running, that implies that the device has
not yet been unbound.

To allow drivers to take advantage of that, add an additional argument
to irq callbacks.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Link: https://lore.kernel.org/r/20250811-topics-tyr-request_irq2-v9-7-0485dcd9bcbf@collabora.com
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Alice Ryhl and committed by
Danilo Krummrich
29e16fcd 9b6d4fb9

+57 -38
+57 -38
rust/kernel/irq/request.rs
··· 36 36 /// All work that does not necessarily need to be executed from 37 37 /// interrupt context, should be deferred to a threaded handler. 38 38 /// See also [`ThreadedRegistration`]. 39 - fn handle(&self) -> IrqReturn; 39 + fn handle(&self, device: &Device<Bound>) -> IrqReturn; 40 40 } 41 41 42 42 impl<T: ?Sized + Handler + Send> Handler for Arc<T> { 43 - fn handle(&self) -> IrqReturn { 44 - T::handle(self) 43 + fn handle(&self, device: &Device<Bound>) -> IrqReturn { 44 + T::handle(self, device) 45 45 } 46 46 } 47 47 48 48 impl<T: ?Sized + Handler, A: Allocator> Handler for Box<T, A> { 49 - fn handle(&self) -> IrqReturn { 50 - T::handle(self) 49 + fn handle(&self, device: &Device<Bound>) -> IrqReturn { 50 + T::handle(self, device) 51 51 } 52 52 } 53 53 ··· 140 140 /// 141 141 /// ``` 142 142 /// use kernel::c_str; 143 - /// use kernel::device::Bound; 143 + /// use kernel::device::{Bound, Device}; 144 144 /// use kernel::irq::{self, Flags, IrqRequest, IrqReturn, Registration}; 145 145 /// use kernel::prelude::*; 146 146 /// use kernel::sync::{Arc, Completion}; ··· 154 154 /// 155 155 /// impl irq::Handler for Data { 156 156 /// // Executed in IRQ context. 157 - /// fn handle(&self) -> IrqReturn { 157 + /// fn handle(&self, _dev: &Device<Bound>) -> IrqReturn { 158 158 /// self.completion.complete_all(); 159 159 /// IrqReturn::Handled 160 160 /// } ··· 180 180 /// 181 181 /// # Invariants 182 182 /// 183 - /// * We own an irq handler using `&self.handler` as its private data. 183 + /// * We own an irq handler whose cookie is a pointer to `Self`. 184 184 #[pin_data] 185 185 pub struct Registration<T: Handler + 'static> { 186 186 #[pin] ··· 208 208 inner <- Devres::new( 209 209 request.dev, 210 210 try_pin_init!(RegistrationInner { 211 - // SAFETY: `this` is a valid pointer to the `Registration` instance 212 - cookie: unsafe { &raw mut (*this.as_ptr()).handler }.cast(), 211 + // INVARIANT: `this` is a valid pointer to the `Registration` instance 212 + cookie: this.as_ptr().cast::<c_void>(), 213 213 irq: { 214 214 // SAFETY: 215 215 // - The callbacks are valid for use with request_irq. 216 216 // - If this succeeds, the slot is guaranteed to be valid until the 217 217 // destructor of Self runs, which will deregister the callbacks 218 218 // before the memory location becomes invalid. 219 + // - When request_irq is called, everything that handle_irq_callback will 220 + // touch has already been initialized, so it's safe for the callback to 221 + // be called immediately. 219 222 to_result(unsafe { 220 223 bindings::request_irq( 221 224 request.irq, 222 225 Some(handle_irq_callback::<T>), 223 226 flags.into_inner(), 224 227 name.as_char_ptr(), 225 - (&raw mut (*this.as_ptr()).handler).cast(), 228 + this.as_ptr().cast::<c_void>(), 226 229 ) 227 230 })?; 228 231 request.irq ··· 262 259 /// 263 260 /// This function should be only used as the callback in `request_irq`. 264 261 unsafe extern "C" fn handle_irq_callback<T: Handler>(_irq: i32, ptr: *mut c_void) -> c_uint { 265 - // SAFETY: `ptr` is a pointer to T set in `Registration::new` 266 - let handler = unsafe { &*(ptr as *const T) }; 267 - T::handle(handler) as c_uint 262 + // SAFETY: `ptr` is a pointer to `Registration<T>` set in `Registration::new` 263 + let registration = unsafe { &*(ptr as *const Registration<T>) }; 264 + // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq 265 + // callback is running implies that the device has not yet been unbound. 266 + let device = unsafe { registration.inner.device().as_bound() }; 267 + 268 + T::handle(&registration.handler, device) as c_uint 268 269 } 269 270 270 271 /// The value that can be returned from [`ThreadedHandler::handle`]. ··· 294 287 /// handler, i.e. [`ThreadedHandler::handle_threaded`]. 295 288 /// 296 289 /// The default implementation returns [`ThreadedIrqReturn::WakeThread`]. 297 - fn handle(&self) -> ThreadedIrqReturn { 290 + #[expect(unused_variables)] 291 + fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn { 298 292 ThreadedIrqReturn::WakeThread 299 293 } 300 294 ··· 303 295 /// 304 296 /// This is executed in process context. The kernel creates a dedicated 305 297 /// `kthread` for this purpose. 306 - fn handle_threaded(&self) -> IrqReturn; 298 + fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn; 307 299 } 308 300 309 301 impl<T: ?Sized + ThreadedHandler + Send> ThreadedHandler for Arc<T> { 310 - fn handle(&self) -> ThreadedIrqReturn { 311 - T::handle(self) 302 + fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn { 303 + T::handle(self, device) 312 304 } 313 305 314 - fn handle_threaded(&self) -> IrqReturn { 315 - T::handle_threaded(self) 306 + fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn { 307 + T::handle_threaded(self, device) 316 308 } 317 309 } 318 310 319 311 impl<T: ?Sized + ThreadedHandler, A: Allocator> ThreadedHandler for Box<T, A> { 320 - fn handle(&self) -> ThreadedIrqReturn { 321 - T::handle(self) 312 + fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn { 313 + T::handle(self, device) 322 314 } 323 315 324 - fn handle_threaded(&self) -> IrqReturn { 325 - T::handle_threaded(self) 316 + fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn { 317 + T::handle_threaded(self, device) 326 318 } 327 319 } 328 320 ··· 341 333 /// 342 334 /// ``` 343 335 /// use kernel::c_str; 344 - /// use kernel::device::Bound; 336 + /// use kernel::device::{Bound, Device}; 345 337 /// use kernel::irq::{ 346 338 /// self, Flags, IrqRequest, IrqReturn, ThreadedHandler, ThreadedIrqReturn, 347 339 /// ThreadedRegistration, ··· 365 357 /// // This will run (in a separate kthread) if and only if 366 358 /// // [`ThreadedHandler::handle`] returns [`WakeThread`], which it does by 367 359 /// // default. 368 - /// fn handle_threaded(&self) -> IrqReturn { 360 + /// fn handle_threaded(&self, _dev: &Device<Bound>) -> IrqReturn { 369 361 /// let mut data = self.value.lock(); 370 362 /// *data += 1; 371 363 /// IrqReturn::Handled ··· 398 390 /// 399 391 /// # Invariants 400 392 /// 401 - /// * We own an irq handler using `&T` as its private data. 393 + /// * We own an irq handler whose cookie is a pointer to `Self`. 402 394 #[pin_data] 403 395 pub struct ThreadedRegistration<T: ThreadedHandler + 'static> { 404 396 #[pin] ··· 426 418 inner <- Devres::new( 427 419 request.dev, 428 420 try_pin_init!(RegistrationInner { 429 - // SAFETY: `this` is a valid pointer to the `ThreadedRegistration` instance. 430 - cookie: unsafe { &raw mut (*this.as_ptr()).handler }.cast(), 421 + // INVARIANT: `this` is a valid pointer to the `ThreadedRegistration` instance. 422 + cookie: this.as_ptr().cast::<c_void>(), 431 423 irq: { 432 424 // SAFETY: 433 425 // - The callbacks are valid for use with request_threaded_irq. 434 426 // - If this succeeds, the slot is guaranteed to be valid until the 435 - // destructor of Self runs, which will deregister the callbacks 436 - // before the memory location becomes invalid. 427 + // destructor of Self runs, which will deregister the callbacks 428 + // before the memory location becomes invalid. 429 + // - When request_threaded_irq is called, everything that the two callbacks 430 + // will touch has already been initialized, so it's safe for the 431 + // callbacks to be called immediately. 437 432 to_result(unsafe { 438 433 bindings::request_threaded_irq( 439 434 request.irq, ··· 444 433 Some(thread_fn_callback::<T>), 445 434 flags.into_inner(), 446 435 name.as_char_ptr(), 447 - (&raw mut (*this.as_ptr()).handler).cast(), 436 + this.as_ptr().cast::<c_void>(), 448 437 ) 449 438 })?; 450 439 request.irq ··· 484 473 _irq: i32, 485 474 ptr: *mut c_void, 486 475 ) -> c_uint { 487 - // SAFETY: `ptr` is a pointer to T set in `ThreadedRegistration::new` 488 - let handler = unsafe { &*(ptr as *const T) }; 489 - T::handle(handler) as c_uint 476 + // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new` 477 + let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) }; 478 + // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq 479 + // callback is running implies that the device has not yet been unbound. 480 + let device = unsafe { registration.inner.device().as_bound() }; 481 + 482 + T::handle(&registration.handler, device) as c_uint 490 483 } 491 484 492 485 /// # Safety 493 486 /// 494 487 /// This function should be only used as the callback in `request_threaded_irq`. 495 488 unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint { 496 - // SAFETY: `ptr` is a pointer to T set in `ThreadedRegistration::new` 497 - let handler = unsafe { &*(ptr as *const T) }; 498 - T::handle_threaded(handler) as c_uint 489 + // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new` 490 + let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) }; 491 + // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq 492 + // callback is running implies that the device has not yet been unbound. 493 + let device = unsafe { registration.inner.device().as_bound() }; 494 + 495 + T::handle_threaded(&registration.handler, device) as c_uint 499 496 }