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