]> git.baikalelectronics.ru Git - kernel.git/commitdiff
genirq/msi: Make interrupt allocation less convoluted
authorThomas Gleixner <tglx@linutronix.de>
Mon, 6 Dec 2021 22:51:44 +0000 (23:51 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 16 Dec 2021 21:22:20 +0000 (22:22 +0100)
There is no real reason to do several loops over the MSI descriptors
instead of just doing one loop. In case of an error everything is undone
anyway so it does not matter whether it's a partial or a full rollback.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Michael Kelley <mikelley@microsoft.com>
Tested-by: Nishanth Menon <nm@ti.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/20211206210749.010234767@linutronix.de
.clang-format
include/linux/msi.h
kernel/irq/msi.c

index 15d4eaabc6b531bb26053ae25f6e2602d21659e4..fa959436bcfd0fdc41d69e15496289bc998a07e6 100644 (file)
@@ -216,7 +216,6 @@ ForEachMacros:
   - 'for_each_migratetype_order'
   - 'for_each_msi_entry'
   - 'for_each_msi_entry_safe'
-  - 'for_each_msi_vector'
   - 'for_each_net'
   - 'for_each_net_continue_reverse'
   - 'for_each_netdev'
index 17e47ab8d57a5236ca94bba97c34eb673ede281a..e8dd0be17e8995b4cc54fb5006d6c816702362e5 100644 (file)
@@ -206,12 +206,6 @@ struct msi_desc *msi_next_desc(struct device *dev, enum msi_desc_filter filter);
        list_for_each_entry((desc), dev_to_msi_list((dev)), list)
 #define for_each_msi_entry_safe(desc, tmp, dev)        \
        list_for_each_entry_safe((desc), (tmp), dev_to_msi_list((dev)), list)
-#define for_each_msi_vector(desc, __irq, dev)                          \
-       for_each_msi_entry((desc), (dev))                               \
-               if ((desc)->irq)                                        \
-                       for (__irq = (desc)->irq;                       \
-                            __irq < ((desc)->irq + (desc)->nvec_used); \
-                            __irq++)
 
 #ifdef CONFIG_IRQ_MSI_IOMMU
 static inline const void *msi_desc_get_iommu_cookie(struct msi_desc *desc)
index 09f34e17e8918771f888fc58dfe8a1b2faf07e14..bbe36e20a986e309f6978544e84d53ec1dd72009 100644 (file)
@@ -828,23 +828,74 @@ static int msi_handle_pci_fail(struct irq_domain *domain, struct msi_desc *desc,
        return allocated ? allocated : -ENOSPC;
 }
 
+#define VIRQ_CAN_RESERVE       0x01
+#define VIRQ_ACTIVATE          0x02
+#define VIRQ_NOMASK_QUIRK      0x04
+
+static int msi_init_virq(struct irq_domain *domain, int virq, unsigned int vflags)
+{
+       struct irq_data *irqd = irq_domain_get_irq_data(domain, virq);
+       int ret;
+
+       if (!(vflags & VIRQ_CAN_RESERVE)) {
+               irqd_clr_can_reserve(irqd);
+               if (vflags & VIRQ_NOMASK_QUIRK)
+                       irqd_set_msi_nomask_quirk(irqd);
+       }
+
+       if (!(vflags & VIRQ_ACTIVATE))
+               return 0;
+
+       ret = irq_domain_activate_irq(irqd, vflags & VIRQ_CAN_RESERVE);
+       if (ret)
+               return ret;
+       /*
+        * If the interrupt uses reservation mode, clear the activated bit
+        * so request_irq() will assign the final vector.
+        */
+       if (vflags & VIRQ_CAN_RESERVE)
+               irqd_clr_activated(irqd);
+       return 0;
+}
+
 int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
                            int nvec)
 {
        struct msi_domain_info *info = domain->host_data;
        struct msi_domain_ops *ops = info->ops;
-       struct irq_data *irq_data;
-       struct msi_desc *desc;
        msi_alloc_info_t arg = { };
+       unsigned int vflags = 0;
+       struct msi_desc *desc;
        int allocated = 0;
        int i, ret, virq;
-       bool can_reserve;
 
        ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg);
        if (ret)
                return ret;
 
-       for_each_msi_entry(desc, dev) {
+       /*
+        * This flag is set by the PCI layer as we need to activate
+        * the MSI entries before the PCI layer enables MSI in the
+        * card. Otherwise the card latches a random msi message.
+        */
+       if (info->flags & MSI_FLAG_ACTIVATE_EARLY)
+               vflags |= VIRQ_ACTIVATE;
+
+       /*
+        * Interrupt can use a reserved vector and will not occupy
+        * a real device vector until the interrupt is requested.
+        */
+       if (msi_check_reservation_mode(domain, info, dev)) {
+               vflags |= VIRQ_CAN_RESERVE;
+               /*
+                * MSI affinity setting requires a special quirk (X86) when
+                * reservation mode is active.
+                */
+               if (domain->flags & IRQ_DOMAIN_MSI_NOMASK_QUIRK)
+                       vflags |= VIRQ_NOMASK_QUIRK;
+       }
+
+       msi_for_each_desc(desc, dev, MSI_DESC_NOTASSOCIATED) {
                ops->set_desc(&arg, desc);
 
                virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
@@ -856,49 +907,12 @@ int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
                for (i = 0; i < desc->nvec_used; i++) {
                        irq_set_msi_desc_off(virq, i, desc);
                        irq_debugfs_copy_devname(virq + i, dev);
+                       ret = msi_init_virq(domain, virq + i, vflags);
+                       if (ret)
+                               return ret;
                }
                allocated++;
        }
-
-       can_reserve = msi_check_reservation_mode(domain, info, dev);
-
-       /*
-        * This flag is set by the PCI layer as we need to activate
-        * the MSI entries before the PCI layer enables MSI in the
-        * card. Otherwise the card latches a random msi message.
-        */
-       if (!(info->flags & MSI_FLAG_ACTIVATE_EARLY))
-               goto skip_activate;
-
-       for_each_msi_vector(desc, i, dev) {
-               if (desc->irq == i) {
-                       virq = desc->irq;
-                       dev_dbg(dev, "irq [%d-%d] for MSI\n",
-                               virq, virq + desc->nvec_used - 1);
-               }
-
-               irq_data = irq_domain_get_irq_data(domain, i);
-               if (!can_reserve) {
-                       irqd_clr_can_reserve(irq_data);
-                       if (domain->flags & IRQ_DOMAIN_MSI_NOMASK_QUIRK)
-                               irqd_set_msi_nomask_quirk(irq_data);
-               }
-               ret = irq_domain_activate_irq(irq_data, can_reserve);
-               if (ret)
-                       return ret;
-       }
-
-skip_activate:
-       /*
-        * If these interrupts use reservation mode, clear the activated bit
-        * so request_irq() will assign the final vector.
-        */
-       if (can_reserve) {
-               for_each_msi_vector(desc, i, dev) {
-                       irq_data = irq_domain_get_irq_data(domain, i);
-                       irqd_clr_activated(irq_data);
-               }
-       }
        return 0;
 }
 
@@ -976,26 +990,21 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nve
 
 void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
 {
-       struct irq_data *irq_data;
+       struct irq_data *irqd;
        struct msi_desc *desc;
        int i;
 
-       for_each_msi_vector(desc, i, dev) {
-               irq_data = irq_domain_get_irq_data(domain, i);
-               if (irqd_is_activated(irq_data))
-                       irq_domain_deactivate_irq(irq_data);
-       }
-
-       for_each_msi_entry(desc, dev) {
-               /*
-                * We might have failed to allocate an MSI early
-                * enough that there is no IRQ associated to this
-                * entry. If that's the case, don't do anything.
-                */
-               if (desc->irq) {
-                       irq_domain_free_irqs(desc->irq, desc->nvec_used);
-                       desc->irq = 0;
+       /* Only handle MSI entries which have an interrupt associated */
+       msi_for_each_desc(desc, dev, MSI_DESC_ASSOCIATED) {
+               /* Make sure all interrupts are deactivated */
+               for (i = 0; i < desc->nvec_used; i++) {
+                       irqd = irq_domain_get_irq_data(domain, desc->irq + i);
+                       if (irqd && irqd_is_activated(irqd))
+                               irq_domain_deactivate_irq(irqd);
                }
+
+               irq_domain_free_irqs(desc->irq, desc->nvec_used);
+               desc->irq = 0;
        }
 }