xref: /linux/rust/kernel/pci/irq.rs (revision 416f99c3b16f582a3fc6d64a1f77f39d94b76de5)
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     devres,
11     error::to_result,
12     irq::{
13         self,
14         IrqRequest, //
15     },
16     prelude::*,
17     str::CStr,
18     sync::aref::ARef, //
19 };
20 use core::ops::RangeInclusive;
21 
22 /// IRQ type flags for PCI interrupt allocation.
23 #[derive(Debug, Clone, Copy)]
24 pub enum IrqType {
25     /// INTx interrupts.
26     Intx,
27     /// Message Signaled Interrupts (MSI).
28     Msi,
29     /// Extended Message Signaled Interrupts (MSI-X).
30     MsiX,
31 }
32 
33 impl IrqType {
34     /// Convert to the corresponding kernel flags.
as_raw(self) -> u3235     const fn as_raw(self) -> u32 {
36         match self {
37             IrqType::Intx => bindings::PCI_IRQ_INTX,
38             IrqType::Msi => bindings::PCI_IRQ_MSI,
39             IrqType::MsiX => bindings::PCI_IRQ_MSIX,
40         }
41     }
42 }
43 
44 /// Set of IRQ types that can be used for PCI interrupt allocation.
45 #[derive(Debug, Clone, Copy, Default)]
46 pub struct IrqTypes(u32);
47 
48 impl IrqTypes {
49     /// Create a set containing all IRQ types (MSI-X, MSI, and INTx).
all() -> Self50     pub const fn all() -> Self {
51         Self(bindings::PCI_IRQ_ALL_TYPES)
52     }
53 
54     /// Build a set of IRQ types.
55     ///
56     /// # Examples
57     ///
58     /// ```ignore
59     /// // Create a set with only MSI and MSI-X (no INTx interrupts).
60     /// let msi_only = IrqTypes::default()
61     ///     .with(IrqType::Msi)
62     ///     .with(IrqType::MsiX);
63     /// ```
with(self, irq_type: IrqType) -> Self64     pub const fn with(self, irq_type: IrqType) -> Self {
65         Self(self.0 | irq_type.as_raw())
66     }
67 
68     /// Get the raw flags value.
as_raw(self) -> u3269     const fn as_raw(self) -> u32 {
70         self.0
71     }
72 }
73 
74 /// Represents an allocated IRQ vector for a specific PCI device.
75 ///
76 /// This type ties an IRQ vector to the device it was allocated for,
77 /// ensuring the vector is only used with the correct device.
78 #[derive(Clone, Copy)]
79 pub struct IrqVector<'a> {
80     dev: &'a Device<Bound>,
81     index: u32,
82 }
83 
84 impl<'a> IrqVector<'a> {
85     /// Creates a new [`IrqVector`] for the given device and index.
86     ///
87     /// # Safety
88     ///
89     /// - `index` must be a valid IRQ vector index for `dev`.
90     /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors.
new(dev: &'a Device<Bound>, index: u32) -> Self91     unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self {
92         Self { dev, index }
93     }
94 
95     /// Returns the raw vector index.
index(&self) -> u3296     fn index(&self) -> u32 {
97         self.index
98     }
99 }
100 
101 impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> {
102     type Error = Error;
103 
try_into(self) -> Result<IrqRequest<'a>>104     fn try_into(self) -> Result<IrqRequest<'a>> {
105         // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`.
106         let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) };
107         if irq < 0 {
108             return Err(crate::error::Error::from_errno(irq));
109         }
110         // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
111         Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) })
112     }
113 }
114 
115 /// Represents an IRQ vector allocation for a PCI device.
116 ///
117 /// This type ensures that IRQ vectors are properly allocated and freed by
118 /// tying the allocation to the lifetime of this registration object.
119 ///
120 /// # Invariants
121 ///
122 /// The [`Device`] has successfully allocated IRQ vectors.
123 struct IrqVectorRegistration {
124     dev: ARef<Device>,
125 }
126 
127 impl IrqVectorRegistration {
128     /// Allocate and register IRQ vectors for the given PCI device.
129     ///
130     /// Allocates IRQ vectors and registers them with devres for automatic cleanup.
131     /// Returns a range of valid IRQ vectors.
register<'a>( dev: &'a Device<Bound>, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'a>>>132     fn register<'a>(
133         dev: &'a Device<Bound>,
134         min_vecs: u32,
135         max_vecs: u32,
136         irq_types: IrqTypes,
137     ) -> Result<RangeInclusive<IrqVector<'a>>> {
138         // SAFETY:
139         // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
140         //   by the type invariant of `Device`.
141         // - `pci_alloc_irq_vectors` internally validates all other parameters
142         //   and returns error codes.
143         let ret = unsafe {
144             bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
145         };
146 
147         to_result(ret)?;
148         let count = ret as u32;
149 
150         // SAFETY:
151         // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success.
152         // - Vectors are 0-based, so valid indices are [0, count-1].
153         // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and
154         //   `count - 1` are valid IRQ vector indices for `dev`.
155         let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) };
156 
157         // INVARIANT: The IRQ vector allocation for `dev` above was successful.
158         let irq_vecs = Self { dev: dev.into() };
159         devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?;
160 
161         Ok(range)
162     }
163 }
164 
165 impl Drop for IrqVectorRegistration {
drop(&mut self)166     fn drop(&mut self) {
167         // SAFETY:
168         // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
169         // - `self.dev` 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     /// Returns a [`kernel::irq::Registration`] for the given IRQ vector.
request_irq<'a, T: crate::irq::Handler + 'static>( &'a self, vector: IrqVector<'a>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit<T, Error> + 'a, ) -> impl PinInit<irq::Registration<T>, Error> + 'a176     pub fn request_irq<'a, T: crate::irq::Handler + 'static>(
177         &'a self,
178         vector: IrqVector<'a>,
179         flags: irq::Flags,
180         name: &'static CStr,
181         handler: impl PinInit<T, Error> + 'a,
182     ) -> impl PinInit<irq::Registration<T>, Error> + 'a {
183         pin_init::pin_init_scope(move || {
184             let request = vector.try_into()?;
185 
186             Ok(irq::Registration::<T>::new(request, flags, name, handler))
187         })
188     }
189 
190     /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector.
request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>( &'a self, vector: IrqVector<'a>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit<T, Error> + 'a, ) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a191     pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>(
192         &'a self,
193         vector: IrqVector<'a>,
194         flags: irq::Flags,
195         name: &'static CStr,
196         handler: impl PinInit<T, Error> + 'a,
197     ) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a {
198         pin_init::pin_init_scope(move || {
199             let request = vector.try_into()?;
200 
201             Ok(irq::ThreadedRegistration::<T>::new(
202                 request, flags, name, handler,
203             ))
204         })
205     }
206 
207     /// Allocate IRQ vectors for this PCI device with automatic cleanup.
208     ///
209     /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
210     /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
211     /// parameter and hardware capabilities. When multiple types are specified, the kernel
212     /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.
213     ///
214     /// The allocated vectors are automatically freed when the device is unbound, using the
215     /// devres (device resource management) system.
216     ///
217     /// # Arguments
218     ///
219     /// * `min_vecs` - Minimum number of vectors required.
220     /// * `max_vecs` - Maximum number of vectors to allocate.
221     /// * `irq_types` - Types of interrupts that can be used.
222     ///
223     /// # Returns
224     ///
225     /// Returns a range of IRQ vectors that were successfully allocated, or an error if the
226     /// allocation fails or cannot meet the minimum requirement.
227     ///
228     /// # Examples
229     ///
230     /// ```
231     /// # use kernel::{ device::Bound, pci};
232     /// # fn no_run(dev: &pci::Device<Bound>) -> Result {
233     /// // Allocate using any available interrupt type in the order mentioned above.
234     /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?;
235     ///
236     /// // Allocate MSI or MSI-X only (no INTx interrupts).
237     /// let msi_only = pci::IrqTypes::default()
238     ///     .with(pci::IrqType::Msi)
239     ///     .with(pci::IrqType::MsiX);
240     /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?;
241     /// # Ok(())
242     /// # }
243     /// ```
alloc_irq_vectors( &self, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'_>>>244     pub fn alloc_irq_vectors(
245         &self,
246         min_vecs: u32,
247         max_vecs: u32,
248         irq_types: IrqTypes,
249     ) -> Result<RangeInclusive<IrqVector<'_>>> {
250         IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types)
251     }
252 }
253