[patch 01/10] genirq/msi: Add range argument to alloc/free MSI domain ops

From: Thomas Gleixner
Date: Fri Nov 26 2021 - 21:17:03 EST


In preparation for supporting range allocations for MSI-X, add a range
argument to the MSI domain alloc/free function pointers and fixup all
affected places.

The range is supplied via a pointer to a struct msi_range which contains
the first and last MSI index and the number of vectors to allocate/free.

To support the sparse MSI-X allocations via pci_enable_msix_range() and
pci_enable_msix_exact() the number of vectors can be smaller than the range
defined by the first and last MSI index. This can be cleaned up later once
the code is converted by converting these sparse allocations to an initial
allocation on enable and expansion of the vector space at the required
indices.

Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
---
arch/powerpc/platforms/pseries/msi.c | 6 +++---
arch/x86/pci/xen.c | 10 +++++-----
include/linux/msi.h | 30 +++++++++++++++++++++++-------
kernel/irq/msi.c | 12 ++++++------
4 files changed, 37 insertions(+), 21 deletions(-)

--- a/arch/powerpc/platforms/pseries/msi.c
+++ b/arch/powerpc/platforms/pseries/msi.c
@@ -450,13 +450,13 @@ static void pseries_msi_ops_msi_free(str
* RTAS can not disable one MSI at a time. It's all or nothing. Do it
* at the end after all IRQs have been freed.
*/
-static void pseries_msi_domain_free_irqs(struct irq_domain *domain,
- struct device *dev)
+static void pseries_msi_domain_free_irqs(struct irq_domain *domain, struct device *dev,
+ struct msi_range *range)
{
if (WARN_ON_ONCE(!dev_is_pci(dev)))
return;

- __msi_domain_free_irqs(domain, dev);
+ __msi_domain_free_irqs(domain, dev, range);

rtas_disable_msi(to_pci_dev(dev));
}
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -407,8 +407,8 @@ static void xen_pv_teardown_msi_irqs(str
xen_teardown_msi_irqs(dev);
}

-static int xen_msi_domain_alloc_irqs(struct irq_domain *domain,
- struct device *dev, int nvec)
+static int xen_msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
+ struct msi_range *range)
{
int type;

@@ -420,11 +420,11 @@ static int xen_msi_domain_alloc_irqs(str
else
type = PCI_CAP_ID_MSI;

- return xen_msi_ops.setup_msi_irqs(to_pci_dev(dev), nvec, type);
+ return xen_msi_ops.setup_msi_irqs(to_pci_dev(dev), range->ndesc, type);
}

-static void xen_msi_domain_free_irqs(struct irq_domain *domain,
- struct device *dev)
+static void xen_msi_domain_free_irqs(struct irq_domain *domain, struct device *dev,
+ struct msi_range *range)
{
if (WARN_ON_ONCE(!dev_is_pci(dev)))
return;
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -191,6 +191,23 @@ struct msi_device_data {
enum msi_desc_filter __iter_filter;
};

+/**
+ * msi_range - Descriptor for a MSI index range
+ * @first: First index
+ * @last: Last index (inclusive)
+ * @ndesc: Number of descriptors for allocations
+ *
+ * @first = 0 and @last = UINT_MAX is the full range for an operation.
+ *
+ * Note: @ndesc can be less than the range defined by @first and @last to
+ * support sparse allocations from PCI/MSI-X.
+ */
+struct msi_range {
+ unsigned int first;
+ unsigned int last;
+ unsigned int ndesc;
+};
+
int msi_setup_device_data(struct device *dev);

/* MSI device properties */
@@ -415,10 +432,10 @@ struct msi_domain_ops {
msi_alloc_info_t *arg);
void (*set_desc)(msi_alloc_info_t *arg,
struct msi_desc *desc);
- int (*domain_alloc_irqs)(struct irq_domain *domain,
- struct device *dev, int nvec);
- void (*domain_free_irqs)(struct irq_domain *domain,
- struct device *dev);
+ int (*domain_alloc_irqs)(struct irq_domain *domain, struct device *dev,
+ struct msi_range *range);
+ void (*domain_free_irqs)(struct irq_domain *domain, struct device *dev,
+ struct msi_range *range);
};

/**
@@ -484,13 +501,12 @@ int msi_domain_set_affinity(struct irq_d
struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
struct msi_domain_info *info,
struct irq_domain *parent);
-int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
- int nvec);
+int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, struct msi_range *range);
int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device *dev,
int nvec);
int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
int nvec);
-void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
+void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev, struct msi_range *range);
void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev);
void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -869,8 +869,7 @@ static int msi_init_virq(struct irq_doma
return 0;
}

-int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
- int nvec)
+int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, struct msi_range *range)
{
struct msi_domain_info *info = domain->host_data;
struct msi_domain_ops *ops = info->ops;
@@ -880,7 +879,7 @@ int __msi_domain_alloc_irqs(struct irq_d
int allocated = 0;
int i, ret, virq;

- ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg);
+ ret = msi_domain_prepare_irqs(domain, dev, range->ndesc, &arg);
if (ret)
return ret;

@@ -960,6 +959,7 @@ int msi_domain_alloc_irqs_descs_locked(s
int nvec)
{
struct msi_domain_info *info = domain->host_data;
+ struct msi_range range = { .ndesc = nvec };
struct msi_domain_ops *ops = info->ops;
int ret;

@@ -969,7 +969,7 @@ int msi_domain_alloc_irqs_descs_locked(s
if (ret)
return ret;

- ret = ops->domain_alloc_irqs(domain, dev, nvec);
+ ret = ops->domain_alloc_irqs(domain, dev, &range);
if (ret)
msi_domain_free_irqs_descs_locked(domain, dev);
return ret;
@@ -994,7 +994,7 @@ int msi_domain_alloc_irqs(struct irq_dom
return ret;
}

-void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
+void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev, struct msi_range *range)
{
struct msi_domain_info *info = domain->host_data;
struct irq_data *irqd;
@@ -1041,7 +1041,7 @@ void msi_domain_free_irqs_descs_locked(s

lockdep_assert_held(&dev->msi.data->mutex);

- ops->domain_free_irqs(domain, dev);
+ ops->domain_free_irqs(domain, dev, NULL);
msi_domain_free_msi_descs(info, dev);
}