xref: /linux/rust/kernel/pci/irq.rs (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
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         // 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 
165 impl 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 
174 impl 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 }
235