rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Benno Lossin" <lossin@kernel.org>
To: "Daniel Almeida" <daniel.almeida@collabora.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <benno.lossin@proton.me>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	"Thomas Gleixner" <tglx@linutronix.de>
Cc: <linux-kernel@vger.kernel.org>, <rust-for-linux@vger.kernel.org>
Subject: Re: [PATCH v3 1/2] rust: irq: add support for request_irq()
Date: Wed, 14 May 2025 22:04:43 +0200	[thread overview]
Message-ID: <D9W5IX9Z7QMU.3DL48O2KYTN1Z@kernel.org> (raw)
In-Reply-To: <20250514-topics-tyr-request_irq-v3-1-d6fcc2591a88@collabora.com>

On Wed May 14, 2025 at 9:20 PM CEST, Daniel Almeida wrote:
> Add support for registering IRQ handlers in Rust.
>
> IRQ handlers are extensively used in drivers when some peripheral wants
> to obtain the CPU attention. Registering a handler will make the system
> invoke the passed-in function whenever the chosen IRQ line is triggered.
>
> Both regular and threaded IRQ handlers are supported through a Handler
> (or ThreadedHandler) trait that is meant to be implemented by a type
> that:
>
> a) provides a function to be run by the system when the IRQ fires and,
>
> b) holds the shared data (i.e.: `T`) between process and IRQ contexts.
>
> The requirement that T is Sync derives from the fact that handlers might
> run concurrently with other processes executing the same driver,
> creating the potential for data races.
>
> Ideally, some interior mutability must be in place if T is to be
> mutated. This should usually be done through the in-flight SpinLockIrq
> type.
>
> Co-developed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
> ---
>  rust/bindings/bindings_helper.h |   1 +
>  rust/helpers/helpers.c          |   1 +
>  rust/helpers/irq.c              |   9 +
>  rust/kernel/irq.rs              |  24 +++
>  rust/kernel/irq/flags.rs        | 102 +++++++++
>  rust/kernel/irq/request.rs      | 455 ++++++++++++++++++++++++++++++++++++++++
>  rust/kernel/lib.rs              |   1 +
>  7 files changed, 593 insertions(+)

Could you split this patch into smaller chunks?

> +pub use request::Handler;
> +pub use request::IrqReturn;
> +pub use request::Registration;
> +pub use request::ThreadedHandler;
> +pub use request::ThreadedIrqReturn;
> +pub use request::ThreadedRegistration;

Why not?:

    pub use request::{Handler, ..., ThreadedRegistration};

> diff --git a/rust/kernel/irq/flags.rs b/rust/kernel/irq/flags.rs
> new file mode 100644
> index 0000000000000000000000000000000000000000..3cfaef65ae14f6c02f55ebcf4d52450c0052df30
> --- /dev/null
> +++ b/rust/kernel/irq/flags.rs
> @@ -0,0 +1,102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPDX-FileCopyrightText: Copyright 2025 Collabora ltd.
> +
> +use crate::bindings;
> +
> +/// Flags to be used when registering IRQ handlers.
> +///
> +/// They can be combined with the operators `|`, `&`, and `!`.
> +#[derive(Clone, Copy, PartialEq, Eq)]
> +pub struct Flags(u64);

The constants below seem to all be 32 bit, why did you choose u64?

> +
> +impl Flags {
> +    pub(crate) fn into_inner(self) -> u64 {
> +        self.0
> +    }
> +}
> +pub const NO_DEBUG: Flags = Flags(bindings::IRQF_NO_DEBUG as u64);

> diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs
> new file mode 100644
> index 0000000000000000000000000000000000000000..55f0ea8f9a93dc9ada67ce91af686a9634c8e8ed
> --- /dev/null
> +++ b/rust/kernel/irq/request.rs
> @@ -0,0 +1,455 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPDX-FileCopyrightText: Copyright 2025 Collabora ltd.
> +
> +//! IRQ allocation and handling

Missing `.`.

> +
> +use core::marker::PhantomPinned;
> +use core::ptr::addr_of_mut;
> +
> +use pin_init::pin_init_from_closure;
> +
> +use crate::alloc::Allocator;
> +use crate::error::to_result;
> +use crate::irq::flags::Flags;
> +use crate::prelude::*;
> +use crate::str::CStr;
> +use crate::sync::Arc;
> +
> +/// The value that can be returned from an IrqHandler or a ThreadedIrqHandler.
> +#[repr(u32)]

I think we should let the compiler decide the layout & discriminants, it
might do something smarter when returning this value together with
others. Then we just need this function:

    fn into_inner(self) -> u32 {
        match self {
            Self::None => bindings::irqreturn_IRQ_NONE,
            Self::Handled => bindings::irqreturn_IRQ_HANDLED,
        }
    }

> +pub enum IrqReturn {
> +    /// The interrupt was not from this device or was not handled.
> +    None = bindings::irqreturn_IRQ_NONE,
> +
> +    /// The interrupt was handled by this device.
> +    Handled = bindings::irqreturn_IRQ_HANDLED,
> +}
> +
> +/// Callbacks for an IRQ handler.
> +pub trait Handler: Sync {
> +    /// The actual handler function. As usual, sleeps are not allowed in IRQ
> +    /// context.
> +    fn handle_irq(&self) -> IrqReturn;
> +}
> +
> +impl<T: ?Sized + Handler + Send> Handler for Arc<T> {
> +    fn handle_irq(&self) -> IrqReturn {
> +        T::handle_irq(self)
> +    }
> +}
> +
> +impl<T: ?Sized + Handler, A: Allocator> Handler for Box<T, A> {
> +    fn handle_irq(&self) -> IrqReturn {
> +        T::handle_irq(self)
> +    }
> +}
> +
> +/// A registration of an IRQ handler for a given IRQ line.
> +///
> +/// # Examples
> +///
> +/// The following is an example of using `Registration`. It uses a
> +/// [`SpinLock`](crate::sync::SpinLockIrq) to provide the interior mutability.
> +/// Note that Spinlocks are not safe to use in IRQ context as of now, but may be
> +/// in the future.

Didn't your commit message mention SpinLockIrq?

> +///
> +/// ```
> +/// use kernel::prelude::*;
> +/// use kernel::irq::flags;
> +/// use kernel::irq::Registration;
> +/// use kernel::irq::IrqReturn;
> +/// use kernel::sync::Arc;
> +/// use kernel::sync::SpinLock;
> +/// use kernel::c_str;
> +/// use kernel::alloc::flags::GFP_KERNEL;
> +///
> +/// // Declare a struct that will be passed in when the interrupt fires. The u32
> +/// // merely serves as an example of some internal data.
> +/// struct Data(SpinLock<u32>);
> +///
> +/// // [`handle_irq`] takes &self. This example illustrates interior
> +/// // mutability can be used when share the data between process context and IRQ
> +/// // context.
> +///
> +/// type Handler = Data;
> +///
> +/// impl kernel::irq::request::Handler for Handler {
> +///     // This is executing in IRQ context in some CPU. Other CPUs can still
> +///     // try to access to data.
> +///     fn handle_irq(&self) -> IrqReturn {
> +///         // We now have exclusive access to the data by locking the
> +///         // SpinLock.
> +///         let mut data = self.0.lock();
> +///         *data += 1;
> +///
> +///         IrqReturn::Handled
> +///     }
> +/// }
> +///
> +/// // This is running in process context.
> +/// fn register_irq(irq: u32, handler: Handler) -> Result<Arc<Registration<Handler>>> {
> +///     let registration = Registration::register(irq, flags::SHARED, c_str!("my-device"), handler);
> +///
> +///     // You can have as many references to the registration as you want, so
> +///     // multiple parts of the driver can access it.
> +///     let registration = Arc::pin_init(registration, GFP_KERNEL)?;
> +///
> +///     // The handler may be called immediately after the function above
> +///     // returns, possibly in a different CPU.
> +///
> +///     {
> +///         // The data can be accessed from the process context too.
> +///         let mut data = registration.handler().0.lock();
> +///         *data = 42;
> +///     }
> +///
> +///     Ok(registration)
> +/// }
> +///
> +/// # Ok::<(), Error>(())
> +///```
> +///
> +/// # Invariants
> +///
> +/// * We own an irq handler using `&self` as its private data.
> +///
> +#[pin_data(PinnedDrop)]
> +pub struct Registration<T: Handler> {
> +    irq: u32,
> +    #[pin]
> +    handler: T,
> +    #[pin]
> +    /// Pinned because we need address stability so that we can pass a pointer
> +    /// to the callback.
> +    _pin: PhantomPinned,
> +}
> +
> +impl<T: Handler> Registration<T> {
> +    /// Registers the IRQ handler with the system for the given IRQ number. The
> +    /// handler must be able to be called as soon as this function returns.

The first line of documentation should be a single sentence description
of what the item does. It will get rendered next to it on the summary &
search pages.

What is meant by the second sentence? What about this phrasing?: "The
handler might be called immediately after this function returns.".

> +    pub fn register(
> +        irq: u32,
> +        flags: Flags,
> +        name: &'static CStr,
> +        handler: T,
> +    ) -> impl PinInit<Self, Error> {
> +        let closure = move |slot: *mut Self| {
> +            // SAFETY: The slot passed to pin initializer is valid for writing.
> +            unsafe {
> +                slot.write(Self {
> +                    irq,
> +                    handler,
> +                    _pin: PhantomPinned,
> +                })
> +            };
> +
> +            // SAFETY:
> +            // - The callbacks are valid for use with request_irq.
> +            // - If this succeeds, the slot is guaranteed to be valid until the
> +            // destructor of Self runs, which will deregister the callbacks
> +            // before the memory location becomes invalid.
> +            let res = to_result(unsafe {
> +                bindings::request_irq(
> +                    irq,
> +                    Some(handle_irq_callback::<T>),
> +                    flags.into_inner() as usize,
> +                    name.as_char_ptr(),
> +                    &*slot as *const _ as *mut core::ffi::c_void,

Please don't use `as` casts when possible, instead use `.cast()` on
pointers.

> +                )
> +            });
> +
> +            if res.is_err() {
> +                // SAFETY: We are returning an error, so we can destroy the slot.
> +                unsafe { core::ptr::drop_in_place(addr_of_mut!((*slot).handler)) };
> +            }
> +
> +            res
> +        };
> +
> +        // SAFETY:
> +        // - if this returns Ok, then every field of `slot` is fully
> +        // initialized.
> +        // - if this returns an error, then the slot does not need to remain
> +        // valid.
> +        unsafe { pin_init_from_closure(closure) }

Please don't use `pin_init_from_closure`, instead do this:

    pin_init!(Self {
        irq,
        handler,
        _pin: PhantomPinned
    })
    .pin_chain(|this| {
        // SAFETY: TODO: correct FFI safety requirements
        to_result(unsafe {
            bindings::request_irq(...)
        })
    })

The `pin_chain` function is exactly for this use-case, doing some
operation that might fail after initializing & it will drop the value
when the closure fails.

> +    }
> +
> +    /// Returns a reference to the handler that was registered with the system.
> +    pub fn handler(&self) -> &T {
> +        &self.handler
> +    }
> +}
> +
> +#[pinned_drop]
> +impl<T: Handler> PinnedDrop for Registration<T> {
> +    fn drop(self: Pin<&mut Self>) {
> +        // SAFETY:
> +        // - `self.irq` is the same as the one passed to `reques_irq`.
> +        // -  `&self` was passed to `request_irq` as the cookie. It is
> +        // guaranteed to be unique by the type system, since each call to
> +        // `register` will return a different instance of `Registration`.

This is missing important information: `self` is `!Unpin` **and** was
initializing using pin-init, so it occupied the same memory location for
the entirety of its lifetime.

> +        //
> +        // Notice that this will block until all handlers finish executing,
> +        // i.e.: at no point will &self be invalid while the handler is running.

This is good to know!

> +        unsafe { bindings::free_irq(self.irq, &*self as *const Self as *mut core::ffi::c_void) };
> +    }
> +}
> +
> +/// The value that can be returned from `ThreadedHandler::handle_irq`.
> +#[repr(u32)]
> +pub enum ThreadedIrqReturn {
> +    /// The interrupt was not from this device or was not handled.
> +    None = bindings::irqreturn_IRQ_NONE,
> +
> +    /// The interrupt was handled by this device.
> +    Handled = bindings::irqreturn_IRQ_HANDLED,
> +
> +    /// The handler wants the handler thread to wake up.

How about "The handler wants the handler thread to take care of the
interrupt." or is that incorrect?

> +    WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD,
> +}
> +
> +/// Callbacks for a threaded IRQ handler.

What is the difference to a normal one?

> +pub trait ThreadedHandler: Sync {
> +    /// The actual handler function. As usual, sleeps are not allowed in IRQ
> +    /// context.
> +    fn handle_irq(&self) -> ThreadedIrqReturn;

Why does this `handle_irq` function return a `ThreadedIrqReturn`...

> +
> +    /// The threaded handler function. This function is called from the irq
> +    /// handler thread, which is automatically created by the system.
> +    fn thread_fn(&self) -> IrqReturn;

... and this `thread_fn` an `IrqReturn`? I would have expected it to be
the other way around.

> +}
> +
> +impl<T: ?Sized + ThreadedHandler + Send> ThreadedHandler for Arc<T> {
> +    fn handle_irq(&self) -> ThreadedIrqReturn {
> +        T::handle_irq(self)
> +    }
> +
> +    fn thread_fn(&self) -> IrqReturn {
> +        T::thread_fn(self)
> +    }
> +}
> +
> +impl<T: ?Sized + ThreadedHandler, A: Allocator> ThreadedHandler for Box<T, A> {
> +    fn handle_irq(&self) -> ThreadedIrqReturn {
> +        T::handle_irq(self)
> +    }
> +
> +    fn thread_fn(&self) -> IrqReturn {
> +        T::thread_fn(self)
> +    }
> +}
> +
> +/// A registration of a threaded IRQ handler for a given IRQ line.
> +///
> +/// Two callbacks are required: one to handle the IRQ, and one to handle any
> +/// other work in a separate thread.
> +///
> +/// The thread handler is only called if the IRQ handler returns `WakeThread`.

Ah this is some information that should be on `ThreadedHandler`. (it
also explains the difference in return types above)

> +///
> +/// # Examples
> +///
> +/// The following is an example of using `ThreadedRegistration`. It uses a
> +/// [`SpinLock`](crate::sync::SpinLockIrq) to provide the interior mutability.
> +/// Note that Spinlocks are not safe to use in IRQ context as of now, but may be
> +/// in the future.
> +///
> +/// ```
> +/// use kernel::prelude::*;
> +/// use kernel::irq::flags;
> +/// use kernel::irq::ThreadedIrqReturn;
> +/// use kernel::irq::ThreadedRegistration;
> +/// use kernel::irq::IrqReturn;
> +/// use kernel::sync::Arc;
> +/// use kernel::sync::SpinLock;
> +/// use kernel::alloc::flags::GFP_KERNEL;
> +/// use kernel::c_str;
> +///
> +/// // Declare a struct that will be passed in when the interrupt fires. The u32
> +/// // merely serves as an example of some internal data.
> +/// struct Data(SpinLock<u32>);
> +///
> +/// // [`handle_irq`] takes &self. This example illustrates interior
> +/// // mutability can be used when share the data between process context and IRQ
> +/// // context.
> +///
> +/// type Handler = Data;
> +///
> +/// impl kernel::irq::request::ThreadedHandler for Handler {
> +///     // This is executing in IRQ context in some CPU. Other CPUs can still
> +///     // try to access to data.
> +///     fn handle_irq(&self) -> ThreadedIrqReturn {
> +///         // We now have exclusive access to the data by locking the
> +///         // SpinLockIrq.
> +///         let mut data = self.0.lock();
> +///         *data += 1;
> +///
> +///         // By returning `WakeThread`, we indicate to the system that the
> +///         // thread function should be called. Otherwise, return
> +///         // ThreadedIrqReturn::Handled.
> +///         ThreadedIrqReturn::WakeThread
> +///     }
> +///
> +///     // This will run (in a separate kthread) if and only if `handle_irq`
> +///     // returns `WakeThread`.
> +///     fn thread_fn(&self) -> IrqReturn {
> +///         // We now have exclusive access to the data by locking the SpinLock.
> +///         //
> +///         // Ideally, we should disable interrupts while we are doing this to
> +///         // avoid deadlocks, but this is not currently possible.

Would this be solved by SpinLockIrq?

---
Cheers,
Benno

> +///         let mut data = self.0.lock();
> +///         *data += 1;
> +///
> +///         IrqReturn::Handled
> +///     }
> +/// }

  reply	other threads:[~2025-05-14 20:04 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-14 19:20 [PATCH v3 0/2] rust: add support for request_irq Daniel Almeida
2025-05-14 19:20 ` [PATCH v3 1/2] rust: irq: add support for request_irq() Daniel Almeida
2025-05-14 20:04   ` Benno Lossin [this message]
2025-05-14 20:58     ` Daniel Almeida
2025-05-14 21:03       ` Daniel Almeida
2025-05-15  8:46       ` Benno Lossin
2025-05-15 12:06         ` Daniel Almeida
2025-05-15 12:44           ` Benno Lossin
2025-06-02 15:20     ` Alice Ryhl
2025-06-04  7:36       ` Benno Lossin
2025-06-04  7:48         ` Alice Ryhl
2025-06-04  9:43           ` Benno Lossin
2025-05-14 21:53   ` Danilo Krummrich
2025-05-15 11:54     ` Daniel Almeida
2025-05-15 12:04       ` Danilo Krummrich
2025-05-15 12:27         ` Daniel Almeida
2025-05-15 12:45           ` Danilo Krummrich
2025-05-15 13:16             ` Daniel Almeida
2025-05-15 13:45               ` Danilo Krummrich
2025-05-15 13:52                 ` Danilo Krummrich
2025-06-02 14:40                   ` Daniel Almeida
2025-06-02 17:35                     ` Danilo Krummrich
2025-06-02 16:02                   ` Alice Ryhl
2025-05-15 13:28             ` Benno Lossin
2025-06-02 16:19     ` Alice Ryhl
2025-06-02 17:31       ` Danilo Krummrich
2025-06-03  8:28         ` Alice Ryhl
2025-06-03  8:46           ` Danilo Krummrich
2025-06-03  8:54             ` Alice Ryhl
2025-06-03  9:10               ` Danilo Krummrich
2025-06-03  9:18                 ` Alice Ryhl
2025-06-03  9:43                   ` Danilo Krummrich
2025-06-03  9:57                     ` Alice Ryhl
2025-06-03 10:08                       ` Danilo Krummrich
2025-06-03 10:16                         ` Danilo Krummrich
2025-06-04 18:32                           ` Daniel Almeida
2025-06-04 18:57                             ` Danilo Krummrich
2025-05-18 13:24   ` Alexandre Courbot
2025-05-18 14:07     ` Benno Lossin
2025-05-14 19:20 ` [PATCH v3 2/2] rust: platform: add irq accessors Daniel Almeida
2025-05-14 20:06   ` Benno Lossin
2025-05-19 10:41   ` Danilo Krummrich
2025-06-02 14:56     ` Daniel Almeida
2025-06-02 17:45       ` Danilo Krummrich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=D9W5IX9Z7QMU.3DL48O2KYTN1Z@kernel.org \
    --to=lossin@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=benno.lossin@proton.me \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=gary@garyguo.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rafael@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tglx@linutronix.de \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).