xref: /linux/rust/kernel/pci/irq.rs (revision 14c5eb685cdefbd32e73d2723071ecbd8effbce9)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! PCI interrupt infrastructure.
4 
5 use super::Device;
6 use crate::{
7     bindings,
8     device,
9     device::Bound,
10     error::to_result,
11     irq::IrqRequest,
12     prelude::*, //
13 };
14 use core::num::NonZero;
15 
16 /// IRQ type flags for PCI interrupt allocation.
17 #[derive(Debug, Clone, Copy)]
18 pub 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 
27 impl 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)]
50 pub struct IrqTypes(u32);
51 
52 impl 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).
82 pub struct IrqVector<'a> {
83     request: IrqRequest<'a>,
84     reg: &'a IrqVectorRegistration<'a>,
85 }
86 
87 impl<'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 
111 impl<'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.
126 pub struct IrqVectorRegistration<'a> {
127     dev: &'a Device<Bound>,
128     len: NonZero<usize>,
129 }
130 
131 impl<'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 
167 impl 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 
176 impl 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 }
237