x86/MSI: Conserve interrupt resources when using multiple-MSIs
authorAlexander Gordeev <agordeev@redhat.com>
Mon, 13 May 2013 09:06:17 +0000 (11:06 +0200)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 3 Jun 2013 20:40:44 +0000 (14:40 -0600)
Current multiple-MSI implementation does not take into account actual
number of requested MSIs and always rounds that number to a larger
power-of-two value.  Yet, the number of MSIs a PCI device could send (and
therefore the number of messages a device driver could request) may be
smaller.  As result, resources allocated for extra MSIs are just wasted.

This update takes advantage of 'msi_desc::nvec_used' field introduced with
generic MSI code to track the number of requested and used MSIs.  As
result, resources associated with interrupts are conserved.  Of those
resources most noticeable are x86 interrupt vectors.

The initial version of this fix also conserved IRTEs, but Jan noticed that
a malfunctioning PCI device might send a message number it did not claim
and thus refer to an IRTE it does not own.  To avoid this security hole,
as many IRTEs are reserved as the device could possibly send.

[bhelgaas: changelog, rename to "nvec_used"]
Signed-off-by: Alexander Gordeev <agordeev@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/iommu/irq_remapping.c

index dcfea4e39be766810a327d0b402b7a539b71900c..39f81aeefcd698f6389f0cf12799491be8e2867d 100644 (file)
@@ -51,26 +51,27 @@ static void irq_remapping_disable_io_apic(void)
 
 static int do_setup_msi_irqs(struct pci_dev *dev, int nvec)
 {
-       int node, ret, sub_handle, index = 0;
+       int node, ret, sub_handle, nvec_pow2, index = 0;
        unsigned int irq;
        struct msi_desc *msidesc;
 
-       nvec = __roundup_pow_of_two(nvec);
-
        WARN_ON(!list_is_singular(&dev->msi_list));
        msidesc = list_entry(dev->msi_list.next, struct msi_desc, list);
        WARN_ON(msidesc->irq);
        WARN_ON(msidesc->msi_attrib.multiple);
+       WARN_ON(msidesc->nvec_used);
 
        node = dev_to_node(&dev->dev);
        irq = __create_irqs(get_nr_irqs_gsi(), nvec, node);
        if (irq == 0)
                return -ENOSPC;
 
-       msidesc->msi_attrib.multiple = ilog2(nvec);
+       nvec_pow2 = __roundup_pow_of_two(nvec);
+       msidesc->nvec_used = nvec;
+       msidesc->msi_attrib.multiple = ilog2(nvec_pow2);
        for (sub_handle = 0; sub_handle < nvec; sub_handle++) {
                if (!sub_handle) {
-                       index = msi_alloc_remapped_irq(dev, irq, nvec);
+                       index = msi_alloc_remapped_irq(dev, irq, nvec_pow2);
                        if (index < 0) {
                                ret = index;
                                goto error;
@@ -95,6 +96,7 @@ error:
         * IRQs from tearing down again in default_teardown_msi_irqs()
         */
        msidesc->irq = 0;
+       msidesc->nvec_used = 0;
        msidesc->msi_attrib.multiple = 0;
 
        return ret;