Skip to main content

kernel/pci/
irq.rs

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    error::to_result,
11    irq::IrqRequest,
12    prelude::*, //
13};
14use core::num::NonZero;
15
16/// IRQ type flags for PCI interrupt allocation.
17#[derive(Debug, Clone, Copy)]
18pub enum IrqType {
19    /// INTx interrupts.
20    Intx,
21    /// Message Signaled Interrupts (MSI).
22    Msi,
23    /// Extended Message Signaled Interrupts (MSI-X).
24    MsiX,
25}
26
27impl IrqType {
28    /// Convert to the corresponding kernel flags.
29    const fn as_raw(self) -> u32 {
30        match self {
31            IrqType::Intx => bindings::PCI_IRQ_INTX,
32            IrqType::Msi => bindings::PCI_IRQ_MSI,
33            IrqType::MsiX => bindings::PCI_IRQ_MSIX,
34        }
35    }
36
37    /// Construct from raw value.
38    #[inline]
39    const fn from_raw(raw: u32) -> Self {
40        match raw {
41            bindings::PCI_IRQ_MSIX => IrqType::MsiX,
42            bindings::PCI_IRQ_MSI => IrqType::Msi,
43            _ => IrqType::Intx,
44        }
45    }
46}
47
48/// Set of IRQ types that can be used for PCI interrupt allocation.
49#[derive(Debug, Clone, Copy, Default)]
50pub struct IrqTypes(u32);
51
52impl IrqTypes {
53    /// Create a set containing all IRQ types (MSI-X, MSI, and INTx).
54    pub const fn all() -> Self {
55        Self(bindings::PCI_IRQ_ALL_TYPES)
56    }
57
58    /// Build a set of IRQ types.
59    ///
60    /// # Examples
61    ///
62    /// ```ignore
63    /// // Create a set with only MSI and MSI-X (no INTx interrupts).
64    /// let msi_only = IrqTypes::default()
65    ///     .with(IrqType::Msi)
66    ///     .with(IrqType::MsiX);
67    /// ```
68    pub const fn with(self, irq_type: IrqType) -> Self {
69        Self(self.0 | irq_type.as_raw())
70    }
71
72    /// Get the raw flags value.
73    const fn as_raw(self) -> u32 {
74        self.0
75    }
76}
77
78/// A resolved IRQ vector from a PCI interrupt vector allocation.
79///
80/// Created by [`IrqVectorRegistration::index`]. Convert to [`IrqRequest`] via [`From`] to register
81/// a handler with [`irq::Registration::new`](crate::irq::Registration::new).
82pub struct IrqVector<'a> {
83    request: IrqRequest<'a>,
84    reg: &'a IrqVectorRegistration<'a>,
85}
86
87impl<'a> IrqVector<'a> {
88    /// Creates a new [`IrqVector`] with an already resolved [`IrqRequest`].
89    ///
90    /// # Safety
91    ///
92    /// `request` must have been resolved from `reg`.
93    #[inline]
94    unsafe fn new(request: IrqRequest<'a>, reg: &'a IrqVectorRegistration<'a>) -> Self {
95        Self { request, reg }
96    }
97
98    /// Returns the [`IrqVectorRegistration`] this vector was derived from.
99    #[inline]
100    pub fn vectors(&self) -> &'a IrqVectorRegistration<'a> {
101        self.reg
102    }
103
104    /// Returns the interrupt type the PCI core selected for this vector's allocation.
105    #[inline]
106    pub fn irq_type(&self) -> IrqType {
107        self.reg.irq_type()
108    }
109}
110
111impl<'a> From<IrqVector<'a>> for IrqRequest<'a> {
112    #[inline]
113    fn from(vector: IrqVector<'a>) -> Self {
114        vector.request
115    }
116}
117
118/// An allocation of PCI interrupt vectors for a device.
119///
120/// This type owns the vector allocation; dropping it frees the vectors. IRQ handlers borrow from
121/// this registration and must be dropped before it is.
122///
123/// # Invariants
124///
125/// `dev` has an allocation of `len` interrupt vectors.
126pub struct IrqVectorRegistration<'a> {
127    dev: &'a Device<Bound>,
128    len: NonZero<usize>,
129}
130
131impl<'a> IrqVectorRegistration<'a> {
132    /// Returns the number of allocated vectors.
133    ///
134    /// This is at least the `min_vecs` that [`Device::alloc_irq_vectors`] was asked for.
135    #[inline]
136    #[allow(clippy::len_without_is_empty)]
137    pub fn len(&self) -> usize {
138        self.len.get()
139    }
140
141    /// Returns the interrupt type the PCI core selected for this allocation.
142    #[inline]
143    pub fn irq_type(&self) -> IrqType {
144        // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
145        IrqType::from_raw(unsafe { bindings::pci_irq_type(self.dev.as_raw()) })
146    }
147
148    /// Returns the [`IrqVector`] at `index`.
149    ///
150    /// Returns [`EINVAL`] if the `index` is out of bounds for the length reported by
151    /// [`Self::len()`].
152    #[inline]
153    pub fn index(&self, index: usize) -> Result<IrqVector<'_>> {
154        // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
155        let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index as u32) };
156        if irq < 0 {
157            return Err(Error::from_errno(irq));
158        }
159
160        // SAFETY: `irq` is a valid IRQ number for `self.dev`, resolved from this registration.
161        Ok(unsafe { IrqVector::new(IrqRequest::new(self.dev.as_ref(), irq as u32), self) })
162    }
163}
164
165impl Drop for IrqVectorRegistration<'_> {
166    #[inline]
167    fn drop(&mut self) {
168        // SAFETY: By the type invariant, `self.dev.as_raw()` is a valid pointer to a
169        // `struct pci_dev` that has successfully allocated IRQ vectors.
170        unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
171    }
172}
173
174impl Device<device::Bound> {
175    /// Allocate IRQ vectors for this PCI device.
176    ///
177    /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
178    /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
179    /// parameter and hardware capabilities. When multiple types are specified, the kernel
180    /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.
181    ///
182    /// The allocated vectors are freed when the returned [`IrqVectorRegistration`] is dropped.
183    /// Use [`IrqVectorRegistration::index`] to obtain an [`IrqVector`] for a given vector
184    /// index.
185    ///
186    /// # Arguments
187    ///
188    /// * `min_vecs` - Minimum number of vectors required.
189    /// * `max_vecs` - Maximum number of vectors to allocate.
190    /// * `irq_types` - Types of interrupts that can be used.
191    ///
192    /// # Returns
193    ///
194    /// Returns the IRQ vector registration, or an error if `min_vecs` vectors cannot be
195    /// allocated.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// # use kernel::{ device::Bound, pci};
201    /// # fn no_run(dev: &pci::Device<Bound>) -> Result {
202    /// // Allocate using any available interrupt type in the order mentioned above.
203    /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?;
204    ///
205    /// // Allocate MSI or MSI-X only (no INTx interrupts).
206    /// let msi_only = pci::IrqTypes::default()
207    ///     .with(pci::IrqType::Msi)
208    ///     .with(pci::IrqType::MsiX);
209    /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?;
210    /// # Ok(())
211    /// # }
212    /// ```
213    pub fn alloc_irq_vectors(
214        &self,
215        min_vecs: u32,
216        max_vecs: u32,
217        irq_types: IrqTypes,
218    ) -> Result<IrqVectorRegistration<'_>> {
219        // SAFETY:
220        // - `self.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
221        //   by the type invariant of `Device`.
222        // - `pci_alloc_irq_vectors` internally validates all other parameters
223        //   and returns error codes.
224        let ret = unsafe {
225            bindings::pci_alloc_irq_vectors(self.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
226        };
227        to_result(ret)?;
228
229        let len = NonZero::new(ret as usize).ok_or(EINVAL)?;
230
231        // INVARIANT: `pci_alloc_irq_vectors()` allocated `len` vectors for `self`.
232        Ok(IrqVectorRegistration { dev: self, len })
233    }
234}