File Index
Files and directories
Sign in
FILE

irq.rs

File preview with syntax highlighting.

linux-6.19.14/rust/kernel/pci/irq.rs RUST
  1// SPDX-License-Identifier: GPL-2.0
  2
  3//! PCI interrupt infrastructure.
  4
  5use super::Device;
  6use crate::{
  7    bindings,
  8    device,
  9    device::Bound,
 10    devres,
 11    error::to_result,
 12    irq::{
 13        self,
 14        IrqRequest, //
 15    },
 16    prelude::*,
 17    str::CStr,
 18    sync::aref::ARef, //
 19};
 20use core::ops::RangeInclusive;
 21
 22/// IRQ type flags for PCI interrupt allocation.
 23#[derive(Debug, Clone, Copy)]
 24pub enum IrqType {
 25    /// INTx interrupts.
 26    Intx,
 27    /// Message Signaled Interrupts (MSI).
 28    Msi,
 29    /// Extended Message Signaled Interrupts (MSI-X).
 30    MsiX,
 31}
 32
 33impl IrqType {
 34    /// Convert to the corresponding kernel flags.
 35    const fn as_raw(self) -> u32 {
 36        match self {
 37            IrqType::Intx => bindings::PCI_IRQ_INTX,
 38            IrqType::Msi => bindings::PCI_IRQ_MSI,
 39            IrqType::MsiX => bindings::PCI_IRQ_MSIX,
 40        }
 41    }
 42}
 43
 44/// Set of IRQ types that can be used for PCI interrupt allocation.
 45#[derive(Debug, Clone, Copy, Default)]
 46pub struct IrqTypes(u32);
 47
 48impl IrqTypes {
 49    /// Create a set containing all IRQ types (MSI-X, MSI, and INTx).
 50    pub const fn all() -> Self {
 51        Self(bindings::PCI_IRQ_ALL_TYPES)
 52    }
 53
 54    /// Build a set of IRQ types.
 55    ///
 56    /// # Examples
 57    ///
 58    /// ```ignore
 59    /// // Create a set with only MSI and MSI-X (no INTx interrupts).
 60    /// let msi_only = IrqTypes::default()
 61    ///     .with(IrqType::Msi)
 62    ///     .with(IrqType::MsiX);
 63    /// ```
 64    pub const fn with(self, irq_type: IrqType) -> Self {
 65        Self(self.0 | irq_type.as_raw())
 66    }
 67
 68    /// Get the raw flags value.
 69    const fn as_raw(self) -> u32 {
 70        self.0
 71    }
 72}
 73
 74/// Represents an allocated IRQ vector for a specific PCI device.
 75///
 76/// This type ties an IRQ vector to the device it was allocated for,
 77/// ensuring the vector is only used with the correct device.
 78#[derive(Clone, Copy)]
 79pub struct IrqVector<'a> {
 80    dev: &'a Device<Bound>,
 81    index: u32,
 82}
 83
 84impl<'a> IrqVector<'a> {
 85    /// Creates a new [`IrqVector`] for the given device and index.
 86    ///
 87    /// # Safety
 88    ///
 89    /// - `index` must be a valid IRQ vector index for `dev`.
 90    /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors.
 91    unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self {
 92        Self { dev, index }
 93    }
 94
 95    /// Returns the raw vector index.
 96    fn index(&self) -> u32 {
 97        self.index
 98    }
 99}
100
101impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> {
102    type Error = Error;
103
104    fn try_into(self) -> Result<IrqRequest<'a>> {
105        // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`.
106        let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) };
107        if irq < 0 {
108            return Err(crate::error::Error::from_errno(irq));
109        }
110        // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
111        Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) })
112    }
113}
114
115/// Represents an IRQ vector allocation for a PCI device.
116///
117/// This type ensures that IRQ vectors are properly allocated and freed by
118/// tying the allocation to the lifetime of this registration object.
119///
120/// # Invariants
121///
122/// The [`Device`] has successfully allocated IRQ vectors.
123struct IrqVectorRegistration {
124    dev: ARef<Device>,
125}
126
127impl IrqVectorRegistration {
128    /// Allocate and register IRQ vectors for the given PCI device.
129    ///
130    /// Allocates IRQ vectors and registers them with devres for automatic cleanup.
131    /// Returns a range of valid IRQ vectors.
132    fn register<'a>(
133        dev: &'a Device<Bound>,
134        min_vecs: u32,
135        max_vecs: u32,
136        irq_types: IrqTypes,
137    ) -> Result<RangeInclusive<IrqVector<'a>>> {
138        // SAFETY:
139        // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
140        //   by the type invariant of `Device`.
141        // - `pci_alloc_irq_vectors` internally validates all other parameters
142        //   and returns error codes.
143        let ret = unsafe {
144            bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
145        };
146
147        to_result(ret)?;
148        let count = ret as u32;
149
150        // SAFETY:
151        // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success.
152        // - Vectors are 0-based, so valid indices are [0, count-1].
153        // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and
154        //   `count - 1` are valid IRQ vector indices for `dev`.
155        let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) };
156
157        // INVARIANT: The IRQ vector allocation for `dev` above was successful.
158        let irq_vecs = Self { dev: dev.into() };
159        devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?;
160
161        Ok(range)
162    }
163}
164
165impl Drop for IrqVectorRegistration {
166    fn drop(&mut self) {
167        // SAFETY:
168        // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
169        // - `self.dev` has successfully allocated IRQ vectors.
170        unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
171    }
172}
173
174impl Device<device::Bound> {
175    /// Returns a [`kernel::irq::Registration`] for the given IRQ vector.
176    pub fn request_irq<'a, T: crate::irq::Handler + 'static>(
177        &'a self,
178        vector: IrqVector<'a>,
179        flags: irq::Flags,
180        name: &'static CStr,
181        handler: impl PinInit<T, Error> + 'a,
182    ) -> impl PinInit<irq::Registration<T>, Error> + 'a {
183        pin_init::pin_init_scope(move || {
184            let request = vector.try_into()?;
185
186            Ok(irq::Registration::<T>::new(request, flags, name, handler))
187        })
188    }
189
190    /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector.
191    pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>(
192        &'a self,
193        vector: IrqVector<'a>,
194        flags: irq::Flags,
195        name: &'static CStr,
196        handler: impl PinInit<T, Error> + 'a,
197    ) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a {
198        pin_init::pin_init_scope(move || {
199            let request = vector.try_into()?;
200
201            Ok(irq::ThreadedRegistration::<T>::new(
202                request, flags, name, handler,
203            ))
204        })
205    }
206
207    /// Allocate IRQ vectors for this PCI device with automatic cleanup.
208    ///
209    /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
210    /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
211    /// parameter and hardware capabilities. When multiple types are specified, the kernel
212    /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.
213    ///
214    /// The allocated vectors are automatically freed when the device is unbound, using the
215    /// devres (device resource management) system.
216    ///
217    /// # Arguments
218    ///
219    /// * `min_vecs` - Minimum number of vectors required.
220    /// * `max_vecs` - Maximum number of vectors to allocate.
221    /// * `irq_types` - Types of interrupts that can be used.
222    ///
223    /// # Returns
224    ///
225    /// Returns a range of IRQ vectors that were successfully allocated, or an error if the
226    /// allocation fails or cannot meet the minimum requirement.
227    ///
228    /// # Examples
229    ///
230    /// ```
231    /// # use kernel::{ device::Bound, pci};
232    /// # fn no_run(dev: &pci::Device<Bound>) -> Result {
233    /// // Allocate using any available interrupt type in the order mentioned above.
234    /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?;
235    ///
236    /// // Allocate MSI or MSI-X only (no INTx interrupts).
237    /// let msi_only = pci::IrqTypes::default()
238    ///     .with(pci::IrqType::Msi)
239    ///     .with(pci::IrqType::MsiX);
240    /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?;
241    /// # Ok(())
242    /// # }
243    /// ```
244    pub fn alloc_irq_vectors(
245        &self,
246        min_vecs: u32,
247        max_vecs: u32,
248        irq_types: IrqTypes,
249    ) -> Result<RangeInclusive<IrqVector<'_>>> {
250        IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types)
251    }
252}
~/ Read-only file preview.