Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: pci: Allocate and manage PCI interrupt vectors

Add support to PCI rust module to allocate, free and manage IRQ vectors.
Integrate with devres for managing the allocated resources.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[ Add links in doc-comments; add missing invariant comment; re-format
multiple safety requirements as list and fix missing backticks;
refactor the example of alloc_irq_vectors() to compile. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Joel Fernandes and committed by
Danilo Krummrich
340ccc97 3a866087

+201 -13
+201 -13
rust/kernel/pci.rs
··· 6 6 7 7 use crate::{ 8 8 bindings, container_of, device, 9 + device::Bound, 9 10 device_id::{RawDeviceId, RawDeviceIdIndex}, 10 - devres::Devres, 11 + devres::{self, Devres}, 11 12 driver, 12 13 error::{from_result, to_result, Result}, 13 14 io::{Io, IoRaw}, ··· 20 19 }; 21 20 use core::{ 22 21 marker::PhantomData, 23 - ops::Deref, 22 + ops::{Deref, RangeInclusive}, 24 23 ptr::{addr_of_mut, NonNull}, 25 24 }; 26 25 use kernel::prelude::*; ··· 28 27 mod id; 29 28 30 29 pub use self::id::{Class, ClassMask, Vendor}; 30 + 31 + /// IRQ type flags for PCI interrupt allocation. 32 + #[derive(Debug, Clone, Copy)] 33 + pub enum IrqType { 34 + /// INTx interrupts. 35 + Intx, 36 + /// Message Signaled Interrupts (MSI). 37 + Msi, 38 + /// Extended Message Signaled Interrupts (MSI-X). 39 + MsiX, 40 + } 41 + 42 + impl IrqType { 43 + /// Convert to the corresponding kernel flags. 44 + const fn as_raw(self) -> u32 { 45 + match self { 46 + IrqType::Intx => bindings::PCI_IRQ_INTX, 47 + IrqType::Msi => bindings::PCI_IRQ_MSI, 48 + IrqType::MsiX => bindings::PCI_IRQ_MSIX, 49 + } 50 + } 51 + } 52 + 53 + /// Set of IRQ types that can be used for PCI interrupt allocation. 54 + #[derive(Debug, Clone, Copy, Default)] 55 + pub struct IrqTypes(u32); 56 + 57 + impl IrqTypes { 58 + /// Create a set containing all IRQ types (MSI-X, MSI, and Legacy). 59 + pub const fn all() -> Self { 60 + Self(bindings::PCI_IRQ_ALL_TYPES) 61 + } 62 + 63 + /// Build a set of IRQ types. 64 + /// 65 + /// # Examples 66 + /// 67 + /// ```ignore 68 + /// // Create a set with only MSI and MSI-X (no legacy interrupts). 69 + /// let msi_only = IrqTypes::default() 70 + /// .with(IrqType::Msi) 71 + /// .with(IrqType::MsiX); 72 + /// ``` 73 + pub const fn with(self, irq_type: IrqType) -> Self { 74 + Self(self.0 | irq_type.as_raw()) 75 + } 76 + 77 + /// Get the raw flags value. 78 + const fn as_raw(self) -> u32 { 79 + self.0 80 + } 81 + } 31 82 32 83 /// An adapter for the registration of PCI drivers. 33 84 pub struct Adapter<T: Driver>(T); ··· 569 516 } 570 517 } 571 518 519 + /// Represents an allocated IRQ vector for a specific PCI device. 520 + /// 521 + /// This type ties an IRQ vector to the device it was allocated for, 522 + /// ensuring the vector is only used with the correct device. 523 + #[derive(Clone, Copy)] 524 + pub struct IrqVector<'a> { 525 + dev: &'a Device<Bound>, 526 + index: u32, 527 + } 528 + 529 + impl<'a> IrqVector<'a> { 530 + /// Creates a new [`IrqVector`] for the given device and index. 531 + /// 532 + /// # Safety 533 + /// 534 + /// - `index` must be a valid IRQ vector index for `dev`. 535 + /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors. 536 + unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self { 537 + Self { dev, index } 538 + } 539 + 540 + /// Returns the raw vector index. 541 + fn index(&self) -> u32 { 542 + self.index 543 + } 544 + } 545 + 546 + /// Represents an IRQ vector allocation for a PCI device. 547 + /// 548 + /// This type ensures that IRQ vectors are properly allocated and freed by 549 + /// tying the allocation to the lifetime of this registration object. 550 + /// 551 + /// # Invariants 552 + /// 553 + /// The [`Device`] has successfully allocated IRQ vectors. 554 + struct IrqVectorRegistration { 555 + dev: ARef<Device>, 556 + } 557 + 558 + impl IrqVectorRegistration { 559 + /// Allocate and register IRQ vectors for the given PCI device. 560 + /// 561 + /// Allocates IRQ vectors and registers them with devres for automatic cleanup. 562 + /// Returns a range of valid IRQ vectors. 563 + fn register<'a>( 564 + dev: &'a Device<Bound>, 565 + min_vecs: u32, 566 + max_vecs: u32, 567 + irq_types: IrqTypes, 568 + ) -> Result<RangeInclusive<IrqVector<'a>>> { 569 + // SAFETY: 570 + // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev` 571 + // by the type invariant of `Device`. 572 + // - `pci_alloc_irq_vectors` internally validates all other parameters 573 + // and returns error codes. 574 + let ret = unsafe { 575 + bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw()) 576 + }; 577 + 578 + to_result(ret)?; 579 + let count = ret as u32; 580 + 581 + // SAFETY: 582 + // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success. 583 + // - Vectors are 0-based, so valid indices are [0, count-1]. 584 + // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and 585 + // `count - 1` are valid IRQ vector indices for `dev`. 586 + let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) }; 587 + 588 + // INVARIANT: The IRQ vector allocation for `dev` above was successful. 589 + let irq_vecs = Self { dev: dev.into() }; 590 + devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?; 591 + 592 + Ok(range) 593 + } 594 + } 595 + 596 + impl Drop for IrqVectorRegistration { 597 + fn drop(&mut self) { 598 + // SAFETY: 599 + // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`. 600 + // - `self.dev` has successfully allocated IRQ vectors. 601 + unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) }; 602 + } 603 + } 604 + 572 605 impl Device<device::Bound> { 573 606 /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks 574 607 /// can be performed on compile time for offsets (plus the requested type size) < SIZE. ··· 675 536 self.iomap_region_sized::<0>(bar, name) 676 537 } 677 538 678 - /// Returns an [`IrqRequest`] for the IRQ vector at the given index, if any. 679 - pub fn irq_vector(&self, index: u32) -> Result<IrqRequest<'_>> { 539 + /// Returns an [`IrqRequest`] for the given IRQ vector. 540 + pub fn irq_vector(&self, vector: IrqVector<'_>) -> Result<IrqRequest<'_>> { 541 + // Verify that the vector belongs to this device. 542 + if !core::ptr::eq(vector.dev.as_raw(), self.as_raw()) { 543 + return Err(EINVAL); 544 + } 545 + 680 546 // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. 681 - let irq = unsafe { crate::bindings::pci_irq_vector(self.as_raw(), index) }; 547 + let irq = unsafe { crate::bindings::pci_irq_vector(self.as_raw(), vector.index()) }; 682 548 if irq < 0 { 683 549 return Err(crate::error::Error::from_errno(irq)); 684 550 } ··· 691 547 Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) }) 692 548 } 693 549 694 - /// Returns a [`kernel::irq::Registration`] for the IRQ vector at the given 695 - /// index. 550 + /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. 696 551 pub fn request_irq<'a, T: crate::irq::Handler + 'static>( 697 552 &'a self, 698 - index: u32, 553 + vector: IrqVector<'_>, 699 554 flags: irq::Flags, 700 555 name: &'static CStr, 701 556 handler: impl PinInit<T, Error> + 'a, 702 557 ) -> Result<impl PinInit<irq::Registration<T>, Error> + 'a> { 703 - let request = self.irq_vector(index)?; 558 + let request = self.irq_vector(vector)?; 704 559 705 560 Ok(irq::Registration::<T>::new(request, flags, name, handler)) 706 561 } 707 562 708 - /// Returns a [`kernel::irq::ThreadedRegistration`] for the IRQ vector at 709 - /// the given index. 563 + /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. 710 564 pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>( 711 565 &'a self, 712 - index: u32, 566 + vector: IrqVector<'_>, 713 567 flags: irq::Flags, 714 568 name: &'static CStr, 715 569 handler: impl PinInit<T, Error> + 'a, 716 570 ) -> Result<impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a> { 717 - let request = self.irq_vector(index)?; 571 + let request = self.irq_vector(vector)?; 718 572 719 573 Ok(irq::ThreadedRegistration::<T>::new( 720 574 request, flags, name, handler, 721 575 )) 576 + } 577 + 578 + /// Allocate IRQ vectors for this PCI device with automatic cleanup. 579 + /// 580 + /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device. 581 + /// The allocation will use MSI-X, MSI, or legacy interrupts based on the `irq_types` 582 + /// parameter and hardware capabilities. When multiple types are specified, the kernel 583 + /// will try them in order of preference: MSI-X first, then MSI, then legacy interrupts. 584 + /// 585 + /// The allocated vectors are automatically freed when the device is unbound, using the 586 + /// devres (device resource management) system. 587 + /// 588 + /// # Arguments 589 + /// 590 + /// * `min_vecs` - Minimum number of vectors required. 591 + /// * `max_vecs` - Maximum number of vectors to allocate. 592 + /// * `irq_types` - Types of interrupts that can be used. 593 + /// 594 + /// # Returns 595 + /// 596 + /// Returns a range of IRQ vectors that were successfully allocated, or an error if the 597 + /// allocation fails or cannot meet the minimum requirement. 598 + /// 599 + /// # Examples 600 + /// 601 + /// ``` 602 + /// # use kernel::{ device::Bound, pci}; 603 + /// # fn no_run(dev: &pci::Device<Bound>) -> Result { 604 + /// // Allocate using any available interrupt type in the order mentioned above. 605 + /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; 606 + /// 607 + /// // Allocate MSI or MSI-X only (no legacy interrupts). 608 + /// let msi_only = pci::IrqTypes::default() 609 + /// .with(pci::IrqType::Msi) 610 + /// .with(pci::IrqType::MsiX); 611 + /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?; 612 + /// # Ok(()) 613 + /// # } 614 + /// ``` 615 + pub fn alloc_irq_vectors( 616 + &self, 617 + min_vecs: u32, 618 + max_vecs: u32, 619 + irq_types: IrqTypes, 620 + ) -> Result<RangeInclusive<IrqVector<'_>>> { 621 + IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types) 722 622 } 723 623 } 724 624