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        let index = u32::try_from(index)?;
155
156        // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
157        let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index) };
158        if irq < 0 {
159            return Err(Error::from_errno(irq));
160        }
161
162        // SAFETY: `irq` is a valid IRQ number for `self.dev`, resolved from this registration.
163        Ok(unsafe { IrqVector::new(IrqRequest::new(self.dev.as_ref(), irq as u32), self) })
164    }
165}
166
167impl Drop for IrqVectorRegistration<'_> {
168    #[inline]
169    fn drop(&mut self) {
170        // SAFETY: By the type invariant, `self.dev.as_raw()` is a valid pointer to a
171        // `struct pci_dev` that has successfully allocated IRQ vectors.
172        unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
173    }
174}
175
176impl Device<device::Bound> {
177    /// Allocate IRQ vectors for this PCI device.
178    ///
179    /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
180    /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
181    /// parameter and hardware capabilities. When multiple types are specified, the kernel
182    /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.
183    ///
184    /// The allocated vectors are freed when the returned [`IrqVectorRegistration`] is dropped.
185    /// Use [`IrqVectorRegistration::index`] to obtain an [`IrqVector`] for a given vector
186    /// index.
187    ///
188    /// # Arguments
189    ///
190    /// * `min_vecs` - Minimum number of vectors required.
191    /// * `max_vecs` - Maximum number of vectors to allocate.
192    /// * `irq_types` - Types of interrupts that can be used.
193    ///
194    /// # Returns
195    ///
196    /// Returns the IRQ vector registration, or an error if `min_vecs` vectors cannot be
197    /// allocated.
198    ///
199    /// # Examples
200    ///
201    /// ```
202    /// # use kernel::{ device::Bound, pci};
203    /// # fn no_run(dev: &pci::Device<Bound>) -> Result {
204    /// // Allocate using any available interrupt type in the order mentioned above.
205    /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?;
206    ///
207    /// // Allocate MSI or MSI-X only (no INTx interrupts).
208    /// let msi_only = pci::IrqTypes::default()
209    ///     .with(pci::IrqType::Msi)
210    ///     .with(pci::IrqType::MsiX);
211    /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?;
212    /// # Ok(())
213    /// # }
214    /// ```
215    pub fn alloc_irq_vectors(
216        &self,
217        min_vecs: u32,
218        max_vecs: u32,
219        irq_types: IrqTypes,
220    ) -> Result<IrqVectorRegistration<'_>> {
221        // SAFETY:
222        // - `self.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
223        //   by the type invariant of `Device`.
224        // - `pci_alloc_irq_vectors` internally validates all other parameters
225        //   and returns error codes.
226        let ret = unsafe {
227            bindings::pci_alloc_irq_vectors(self.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
228        };
229        to_result(ret)?;
230
231        let len = NonZero::new(ret as usize).ok_or(EINVAL)?;
232
233        // INVARIANT: `pci_alloc_irq_vectors()` allocated `len` vectors for `self`.
234        Ok(IrqVectorRegistration { dev: self, len })
235    }
236}