]> git.karo-electronics.de Git - linux-beck.git/commitdiff
PCI: Allocate only as many MSI vectors as requested by driver
authorAlexander Gordeev <agordeev@redhat.com>
Mon, 13 May 2013 09:05:48 +0000 (11:05 +0200)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 28 May 2013 17:31:16 +0000 (11:31 -0600)
Because of the encoding of the "Multiple Message Capable" and "Multiple
Message Enable" fields, a device can only advertise that it's capable of a
power-of-two number of vectors, and the OS can only enable a power-of-two
number.

For example, a device that's limited internally to using 18 vectors would
have to advertise that it's capable of 32.  The 14 extra vectors consume
vector numbers and IRQ descriptors even though the device can't actually
use them.

This fix introduces a 'msi_desc::nvec_used' field to address this issue.
When non-zero, it is the actual number of MSIs the device will send, as
requested by the device driver.  This value should be used by architectures
to set up and tear down only as many interrupt resources as the device will
actually use.

Note, although the existing 'msi_desc::multiple' field might seem
redundant, in fact it is not.  The number of MSIs advertised need not be
the smallest power-of-two larger than the number of MSIs the device will
send.  Thus, it is not always possible to derive the former from the
latter, so we need to keep them both to handle this case.

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

index 2c1075213beceac6bb0eb75be6aeef7adc8f34d7..aca7578b05e56205d854c6f76a5aac88c77d326e 100644 (file)
@@ -81,7 +81,10 @@ void default_teardown_msi_irqs(struct pci_dev *dev)
                int i, nvec;
                if (entry->irq == 0)
                        continue;
-               nvec = 1 << entry->msi_attrib.multiple;
+               if (entry->nvec_used)
+                       nvec = entry->nvec_used;
+               else
+                       nvec = 1 << entry->msi_attrib.multiple;
                for (i = 0; i < nvec; i++)
                        arch_teardown_msi_irq(entry->irq + i);
        }
@@ -336,7 +339,10 @@ static void free_msi_irqs(struct pci_dev *dev)
                int i, nvec;
                if (!entry->irq)
                        continue;
-               nvec = 1 << entry->msi_attrib.multiple;
+               if (entry->nvec_used)
+                       nvec = entry->nvec_used;
+               else
+                       nvec = 1 << entry->msi_attrib.multiple;
 #ifdef CONFIG_GENERIC_HARDIRQS
                for (i = 0; i < nvec; i++)
                        BUG_ON(irq_has_action(entry->irq + i));
index 20c2d6dd5d259f6467baf0939e6837ed85e936b3..ee66f3a12fb6ca3f1cd067a5b9c0350767bc11e2 100644 (file)
@@ -35,6 +35,7 @@ struct msi_desc {
 
        u32 masked;                     /* mask bits */
        unsigned int irq;
+       unsigned int nvec_used;         /* number of messages */
        struct list_head list;
 
        union {