[PATCH 1/1] pci: msi: Add subdevice irq domains for dynamic MSI-X

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



PoC code only. All sorts of missing checks etc.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx>
---
 arch/x86/kernel/apic/msi.c  |  3 ++
 drivers/pci/msi/api.c       | 14 ++++++
 drivers/pci/msi/irqdomain.c | 87 +++++++++++++++++++++++++++++++++++++
 include/linux/msi.h         |  5 +++
 include/linux/pci.h         |  3 ++
 kernel/irq/msi.c            |  5 +++
 6 files changed, 117 insertions(+)

diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c
index 340769242dea..8ab4391d4559 100644
--- a/arch/x86/kernel/apic/msi.c
+++ b/arch/x86/kernel/apic/msi.c
@@ -218,6 +218,9 @@ static bool x86_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
 	case DOMAIN_BUS_DMAR:
 	case DOMAIN_BUS_AMDVI:
 		break;
+	case DOMAIN_BUS_PCI_DEVICE_MSIX:
+		/* Currently needed just for the PCI MSI-X subdevice handling */
+		break;
 	default:
 		WARN_ON_ONCE(1);
 		return false;
diff --git a/drivers/pci/msi/api.c b/drivers/pci/msi/api.c
index b956ce591f96..2b4c15102671 100644
--- a/drivers/pci/msi/api.c
+++ b/drivers/pci/msi/api.c
@@ -179,6 +179,20 @@ void pci_msix_free_irq(struct pci_dev *dev, struct msi_map map)
 }
 EXPORT_SYMBOL_GPL(pci_msix_free_irq);
 
+struct msi_map pci_subdev_msix_alloc_irq_at(struct device *dev, unsigned int index,
+					const struct irq_affinity_desc *affdesc)
+{
+	//missing sanity checks
+	return msi_domain_alloc_irq_at(dev, MSI_DEFAULT_DOMAIN, index, affdesc, NULL);
+}
+EXPORT_SYMBOL_GPL(pci_subdev_msix_alloc_irq_at);
+
+void pci_subdev_msix_free_irq(struct device *dev, struct msi_map map)
+{
+	msi_domain_free_irqs_range(dev, MSI_DEFAULT_DOMAIN, map.index, map.index);
+}
+EXPORT_SYMBOL_GPL(pci_subdev_msix_free_irq);
+
 /**
  * pci_disable_msix() - Disable MSI-X interrupt mode on device
  * @dev: the PCI device to operate on
diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
index 569125726b3e..48357a8054ff 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -444,3 +444,90 @@ struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
 					     DOMAIN_BUS_PCI_MSI);
 	return dom;
 }
+
+static const struct msi_parent_ops pci_msix_parent_ops = {
+	.supported_flags = MSI_FLAG_PCI_MSIX | MSI_FLAG_PCI_MSIX_ALLOC_DYN,
+	.prefix = "PCI-MSIX-PROXY-",
+	.init_dev_msi_info = msi_parent_init_dev_msi_info,
+};
+
+int pci_msi_enable_parent_domain(struct pci_dev *pdev)
+{
+	struct irq_domain *msix_dom;
+	/* TGLX: Validate has v2 parent domain */
+	/* TGLX: validate msix enabled */
+	/* TGLX: Validate msix domain supports dynamics msi-x */
+
+	/* Enable PCI device parent domain */
+	msix_dom = dev_msi_get_msix_device_domain(&pdev->dev);
+	msix_dom->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+	msix_dom->msi_parent_ops = &pci_msix_parent_ops;
+	return 0;
+}
+
+void pci_msi_init_subdevice(struct pci_dev *pdev, struct device *dev)
+{
+	dev_set_msi_domain(dev, dev_msi_get_msix_device_domain(&pdev->dev));
+}
+
+static bool pci_subdev_create_device_domain(struct device *dev, const struct msi_domain_template *tmpl,
+				     unsigned int hwsize)
+{
+	struct irq_domain *domain = dev_get_msi_domain(dev);
+
+	if (!domain || !irq_domain_is_msi_parent(domain))
+		return true;
+
+	return msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, tmpl,
+					    hwsize, NULL, NULL);
+}
+
+static void pci_subdev_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg,
+					struct msi_desc *desc)
+{
+	struct device *parent = desc->dev->parent;
+
+	if (!desc->pci.mask_base) {
+		/* Not elegant - but needed for irq affinity to work? */
+		desc->dev = parent;
+		msix_prepare_msi_desc(to_pci_dev(parent), desc);
+	}
+}
+
+static const struct msi_domain_template subdev_template = {
+	.chip = {
+		.name = "SUBDEV",
+		.irq_mask = irq_chip_unmask_parent,
+		.irq_unmask = irq_chip_unmask_parent,
+		.irq_write_msi_msg = pci_msi_domain_write_msg,
+		.irq_set_affinity = irq_chip_set_affinity_parent,
+		.flags = IRQCHIP_ONESHOT_SAFE,
+	},
+	.ops = {
+		/*
+		 * RFC: Sets the desc->dev to the parent PCI device
+		 *       Needed for
+		 *       irq_setup_affinity() ->
+		 *          msi_set_affinity() ->
+		 *             parent = irq_d->parent_data;
+		 *             parent->chip->irq_set_affinity() to work.
+		 *      That could be made more flexible perhaps as
+		 *      currently it makes assumption that parent of
+		 *      the MSI device is the one to set the affinity on.
+		 */
+		.prepare_desc = pci_subdev_msix_prepare_desc,
+		/* Works because the desc->dev is the parent PCI device */
+		.set_desc = pci_msi_domain_set_desc,
+	},
+	.info = {
+		.flags = MSI_FLAG_PCI_MSIX | MSI_FLAG_PCI_MSIX_ALLOC_DYN |
+		MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_USE_DEF_DOM_OPS,
+		.bus_token = DOMAIN_BUS_PCI_DEVICE_MSIX,
+	},
+};
+
+bool pci_subdev_setup_device_domain(struct device *dev, unsigned int hwsize)
+{
+	return pci_subdev_create_device_domain(dev, &subdev_template, hwsize);
+}
+EXPORT_SYMBOL_GPL(pci_subdev_setup_device_domain);
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 944979763825..ff81b4dcc1d9 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -656,8 +656,13 @@ void pci_msi_unmask_irq(struct irq_data *data);
 struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
 					     struct msi_domain_info *info,
 					     struct irq_domain *parent);
+int pci_msi_enable_parent_domain(struct pci_dev *pdev);
+struct irq_domain *pci_msi_get_msix_device_domain(struct device *dev);
 u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
 struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
+struct irq_domain *dev_msi_get_msix_device_domain(struct device *dev);
+void pci_msi_init_subdevice(struct pci_dev *pdev, struct device *dev);
+bool pci_subdev_setup_device_domain(struct device *dev, unsigned int hwsize);
 #else /* CONFIG_PCI_MSI */
 static inline struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
 {
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 225de9be3006..460551f1bd6e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1679,6 +1679,9 @@ struct msi_map pci_msix_alloc_irq_at(struct pci_dev *dev, unsigned int index,
 				     const struct irq_affinity_desc *affdesc);
 void pci_msix_free_irq(struct pci_dev *pdev, struct msi_map map);
 
+struct msi_map pci_subdev_msix_alloc_irq_at(struct device *dev, unsigned int index,
+					const struct irq_affinity_desc *affdesc);
+void pci_subdev_msix_free_irq(struct device *dev, struct msi_map map);
 void pci_free_irq_vectors(struct pci_dev *dev);
 int pci_irq_vector(struct pci_dev *dev, unsigned int nr);
 const struct cpumask *pci_irq_get_affinity(struct pci_dev *pdev, int vec);
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 5fa0547ece0c..d55a91c7a496 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -1720,3 +1720,8 @@ bool msi_device_has_isolated_msi(struct device *dev)
 	return arch_is_isolated_msi();
 }
 EXPORT_SYMBOL_GPL(msi_device_has_isolated_msi);
+struct irq_domain *dev_msi_get_msix_device_domain(struct device *dev)
+{
+	return dev->msi.data->__domains[0].domain;
+}
+
-- 
2.43.0







[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux