genirq/msi: Provide domain flags to allocate/free MSI descriptors automatically
authorThomas Gleixner <tglx@linutronix.de>
Mon, 6 Dec 2021 22:51:12 +0000 (23:51 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 16 Dec 2021 21:22:17 +0000 (22:22 +0100)
Provide domain info flags which tell the core to allocate simple
descriptors or to free descriptors when the interrupts are freed and
implement the required functionality.

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/20211206210747.928198636@linutronix.de
include/linux/msi.h
kernel/irq/msi.c

index bbb8c1e..17e47ab 100644 (file)
@@ -105,6 +105,8 @@ struct pci_msi_desc {
        };
 };
 
+#define MSI_MAX_INDEX          ((unsigned int)USHRT_MAX)
+
 /**
  * struct msi_desc - Descriptor structure for MSI based interrupts
  * @list:      List head for management
@@ -248,6 +250,17 @@ static inline void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg)
 #endif /* CONFIG_PCI_MSI */
 
 int msi_add_msi_desc(struct device *dev, struct msi_desc *init_desc);
+void msi_free_msi_descs_range(struct device *dev, enum msi_desc_filter filter,
+                             unsigned int first_index, unsigned int last_index);
+
+/**
+ * msi_free_msi_descs - Free MSI descriptors of a device
+ * @dev:       Device to free the descriptors
+ */
+static inline void msi_free_msi_descs(struct device *dev)
+{
+       msi_free_msi_descs_range(dev, MSI_DESC_ALL, 0, MSI_MAX_INDEX);
+}
 
 struct msi_desc *alloc_msi_entry(struct device *dev, int nvec,
                                 const struct irq_affinity_desc *affinity);
@@ -408,6 +421,10 @@ enum {
        MSI_FLAG_DEV_SYSFS              = (1 << 7),
        /* MSI-X entries must be contiguous */
        MSI_FLAG_MSIX_CONTIGUOUS        = (1 << 8),
+       /* Allocate simple MSI descriptors */
+       MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS = (1 << 9),
+       /* Free MSI descriptors */
+       MSI_FLAG_FREE_MSI_DESCS         = (1 << 10),
 };
 
 int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
index 6ffe75e..b511dc1 100644 (file)
@@ -119,6 +119,32 @@ fail:
        return -ENOMEM;
 }
 
+/**
+ * msi_free_msi_descs_range - Free MSI descriptors of a device
+ * @dev:               Device to free the descriptors
+ * @filter:            Descriptor state filter
+ * @first_index:       Index to start freeing from
+ * @last_index:                Last index to be freed
+ */
+void msi_free_msi_descs_range(struct device *dev, enum msi_desc_filter filter,
+                             unsigned int first_index, unsigned int last_index)
+{
+       struct msi_desc *desc;
+
+       lockdep_assert_held(&dev->msi.data->mutex);
+
+       msi_for_each_desc(desc, dev, filter) {
+               /*
+                * Stupid for now to handle MSI device domain until the
+                * storage is switched over to an xarray.
+                */
+               if (desc->msi_index < first_index || desc->msi_index > last_index)
+                       continue;
+               list_del(&desc->list);
+               free_msi_entry(desc);
+       }
+}
+
 void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
 {
        *msg = entry->msg;
@@ -879,6 +905,16 @@ skip_activate:
        return 0;
 }
 
+static int msi_domain_add_simple_msi_descs(struct msi_domain_info *info,
+                                          struct device *dev,
+                                          unsigned int num_descs)
+{
+       if (!(info->flags & MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS))
+               return 0;
+
+       return msi_add_simple_msi_descs(dev, 0, num_descs);
+}
+
 /**
  * msi_domain_alloc_irqs_descs_locked - Allocate interrupts from a MSI interrupt domain
  * @domain:    The domain to allocate from
@@ -901,6 +937,10 @@ int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device
 
        lockdep_assert_held(&dev->msi.data->mutex);
 
+       ret = msi_domain_add_simple_msi_descs(info, dev, nvec);
+       if (ret)
+               return ret;
+
        ret = ops->domain_alloc_irqs(domain, dev, nvec);
        if (ret)
                goto cleanup;
@@ -962,6 +1002,13 @@ void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
        }
 }
 
+static void msi_domain_free_msi_descs(struct msi_domain_info *info,
+                                     struct device *dev)
+{
+       if (info->flags & MSI_FLAG_FREE_MSI_DESCS)
+               msi_free_msi_descs(dev);
+}
+
 /**
  * msi_domain_free_irqs_descs_locked - Free interrupts from a MSI interrupt @domain associated to @dev
  * @domain:    The domain to managing the interrupts
@@ -982,6 +1029,7 @@ void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device
        if (info->flags & MSI_FLAG_DEV_SYSFS)
                msi_device_destroy_sysfs(dev);
        ops->domain_free_irqs(domain, dev);
+       msi_domain_free_msi_descs(info, dev);
 }
 
 /**