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}