From dadf17334f3820e2f8c561011706b6fb99bf9860 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 28 Dec 2016 08:25:04 -0800 Subject: PCI/MSI: Fix msi_capability_init() kernel-doc warnings Fix kernel-doc warnings in pci/msi.c: ..//drivers/pci/msi.c:623: warning: No description found for parameter 'affd' ..//drivers/pci/msi.c:623: warning: Excess function parameter 'affinity' description in 'msi_capability_init' Signed-off-by: Randy Dunlap Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 50c5003295ca..ca9112afd53a 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -610,7 +610,7 @@ static int msi_verify_entries(struct pci_dev *dev) * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * @nvec: number of interrupts to allocate - * @affinity: flag to indicate cpu irq affinity mask should be set + * @affd: description of automatic irq affinity assignments (may be %NULL) * * Setup the MSI capability structure of the device with the requested * number of interrupts. A return value of zero indicates the successful -- cgit v1.2.3 From e8e8dd6d20fa55ff974460ad742fcbf35b301062 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 9 Jan 2017 21:37:38 +0100 Subject: [media] cobalt: use pci_irq_allocate_vectors() Simplify the interrupt setup by using the new PCI layer helpers. Despite using pci_enable_msi_range(), this driver was only requesting a single MSI vector anyway. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- drivers/media/pci/cobalt/cobalt-driver.c | 8 ++------ drivers/media/pci/cobalt/cobalt-driver.h | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index 979634000597..d5c911c09e2b 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -308,9 +308,7 @@ static void cobalt_pci_iounmap(struct cobalt *cobalt, struct pci_dev *pci_dev) static void cobalt_free_msi(struct cobalt *cobalt, struct pci_dev *pci_dev) { free_irq(pci_dev->irq, (void *)cobalt); - - if (cobalt->msi_enabled) - pci_disable_msi(pci_dev); + pci_free_irq_vectors(pci_dev); } static int cobalt_setup_pci(struct cobalt *cobalt, struct pci_dev *pci_dev, @@ -387,14 +385,12 @@ static int cobalt_setup_pci(struct cobalt *cobalt, struct pci_dev *pci_dev, from being generated. */ cobalt_set_interrupt(cobalt, false); - if (pci_enable_msi_range(pci_dev, 1, 1) < 1) { + if (pci_alloc_irq_vectors(pci_dev, 1, 1, PCI_IRQ_MSI) < 1) { cobalt_err("Could not enable MSI\n"); - cobalt->msi_enabled = false; ret = -EIO; goto err_release; } msi_config_show(cobalt, pci_dev); - cobalt->msi_enabled = true; /* Register IRQ */ if (request_irq(pci_dev->irq, cobalt_irq_handler, IRQF_SHARED, diff --git a/drivers/media/pci/cobalt/cobalt-driver.h b/drivers/media/pci/cobalt/cobalt-driver.h index ed00dc9d9399..00f773ec359a 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.h +++ b/drivers/media/pci/cobalt/cobalt-driver.h @@ -287,8 +287,6 @@ struct cobalt { u32 irq_none; u32 irq_full_fifo; - bool msi_enabled; - /* omnitek dma */ int dma_channels; int first_fifo_channel; -- cgit v1.2.3 From e75377404726be171d66c154f8ea1e6cf840811d Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Fri, 13 Jan 2017 09:05:53 +0100 Subject: amd-xgbe: Update PCI support to use new IRQ functions Some of the PCI MSI/MSI-X functions have been deprecated and it is recommended to use the new pci_alloc_irq_vectors() function. Convert the code over to use the new function. Also, modify the way in which the IRQs are requested - try for multiple MSI-X/MSI first, then a single MSI/legacy interrupt. Signed-off-by: Tom Lendacky Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 128 +++++++++---------------------- drivers/net/ethernet/amd/xgbe/xgbe.h | 8 +- 2 files changed, 41 insertions(+), 95 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index e76b7f65b805..e43690288c59 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -122,104 +122,40 @@ #include "xgbe.h" #include "xgbe-common.h" -static int xgbe_config_msi(struct xgbe_prv_data *pdata) +static int xgbe_config_multi_msi(struct xgbe_prv_data *pdata) { - unsigned int msi_count; + unsigned int vector_count; unsigned int i, j; int ret; - msi_count = XGBE_MSIX_BASE_COUNT; - msi_count += max(pdata->rx_ring_count, - pdata->tx_ring_count); - msi_count = roundup_pow_of_two(msi_count); + vector_count = XGBE_MSI_BASE_COUNT; + vector_count += max(pdata->rx_ring_count, + pdata->tx_ring_count); - ret = pci_enable_msi_exact(pdata->pcidev, msi_count); + ret = pci_alloc_irq_vectors(pdata->pcidev, XGBE_MSI_MIN_COUNT, + vector_count, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (ret < 0) { - dev_info(pdata->dev, "MSI request for %u interrupts failed\n", - msi_count); - - ret = pci_enable_msi(pdata->pcidev); - if (ret < 0) { - dev_info(pdata->dev, "MSI enablement failed\n"); - return ret; - } - - msi_count = 1; - } - - pdata->irq_count = msi_count; - - pdata->dev_irq = pdata->pcidev->irq; - - if (msi_count > 1) { - pdata->ecc_irq = pdata->pcidev->irq + 1; - pdata->i2c_irq = pdata->pcidev->irq + 2; - pdata->an_irq = pdata->pcidev->irq + 3; - - for (i = XGBE_MSIX_BASE_COUNT, j = 0; - (i < msi_count) && (j < XGBE_MAX_DMA_CHANNELS); - i++, j++) - pdata->channel_irq[j] = pdata->pcidev->irq + i; - pdata->channel_irq_count = j; - - pdata->per_channel_irq = 1; - pdata->channel_irq_mode = XGBE_IRQ_MODE_LEVEL; - } else { - pdata->ecc_irq = pdata->pcidev->irq; - pdata->i2c_irq = pdata->pcidev->irq; - pdata->an_irq = pdata->pcidev->irq; - } - - if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "MSI interrupts enabled\n"); - - return 0; -} - -static int xgbe_config_msix(struct xgbe_prv_data *pdata) -{ - unsigned int msix_count; - unsigned int i, j; - int ret; - - msix_count = XGBE_MSIX_BASE_COUNT; - msix_count += max(pdata->rx_ring_count, - pdata->tx_ring_count); - - pdata->msix_entries = devm_kcalloc(pdata->dev, msix_count, - sizeof(struct msix_entry), - GFP_KERNEL); - if (!pdata->msix_entries) - return -ENOMEM; - - for (i = 0; i < msix_count; i++) - pdata->msix_entries[i].entry = i; - - ret = pci_enable_msix_range(pdata->pcidev, pdata->msix_entries, - XGBE_MSIX_MIN_COUNT, msix_count); - if (ret < 0) { - dev_info(pdata->dev, "MSI-X enablement failed\n"); - devm_kfree(pdata->dev, pdata->msix_entries); - pdata->msix_entries = NULL; + dev_info(pdata->dev, "multi MSI/MSI-X enablement failed\n"); return ret; } pdata->irq_count = ret; - pdata->dev_irq = pdata->msix_entries[0].vector; - pdata->ecc_irq = pdata->msix_entries[1].vector; - pdata->i2c_irq = pdata->msix_entries[2].vector; - pdata->an_irq = pdata->msix_entries[3].vector; + pdata->dev_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->ecc_irq = pci_irq_vector(pdata->pcidev, 1); + pdata->i2c_irq = pci_irq_vector(pdata->pcidev, 2); + pdata->an_irq = pci_irq_vector(pdata->pcidev, 3); - for (i = XGBE_MSIX_BASE_COUNT, j = 0; i < ret; i++, j++) - pdata->channel_irq[j] = pdata->msix_entries[i].vector; + for (i = XGBE_MSI_BASE_COUNT, j = 0; i < ret; i++, j++) + pdata->channel_irq[j] = pci_irq_vector(pdata->pcidev, i); pdata->channel_irq_count = j; pdata->per_channel_irq = 1; pdata->channel_irq_mode = XGBE_IRQ_MODE_LEVEL; if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "MSI-X interrupts enabled\n"); + dev_dbg(pdata->dev, "multi %s interrupts enabled\n", + pdata->pcidev->msix_enabled ? "MSI-X" : "MSI"); return 0; } @@ -228,21 +164,28 @@ static int xgbe_config_irqs(struct xgbe_prv_data *pdata) { int ret; - ret = xgbe_config_msix(pdata); + ret = xgbe_config_multi_msi(pdata); if (!ret) goto out; - ret = xgbe_config_msi(pdata); - if (!ret) - goto out; + ret = pci_alloc_irq_vectors(pdata->pcidev, 1, 1, + PCI_IRQ_LEGACY | PCI_IRQ_MSI); + if (ret < 0) { + dev_info(pdata->dev, "single IRQ enablement failed\n"); + return ret; + } pdata->irq_count = 1; - pdata->irq_shared = 1; + pdata->channel_irq_count = 1; + + pdata->dev_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->ecc_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->i2c_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->an_irq = pci_irq_vector(pdata->pcidev, 0); - pdata->dev_irq = pdata->pcidev->irq; - pdata->ecc_irq = pdata->pcidev->irq; - pdata->i2c_irq = pdata->pcidev->irq; - pdata->an_irq = pdata->pcidev->irq; + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "single %s interrupt enabled\n", + pdata->pcidev->msi_enabled ? "MSI" : "legacy"); out: if (netif_msg_probe(pdata)) { @@ -412,12 +355,15 @@ static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Configure the netdev resource */ ret = xgbe_config_netdev(pdata); if (ret) - goto err_pci_enable; + goto err_irq_vectors; netdev_notice(pdata->netdev, "net device enabled\n"); return 0; +err_irq_vectors: + pci_free_irq_vectors(pdata->pcidev); + err_pci_enable: xgbe_free_pdata(pdata); @@ -433,6 +379,8 @@ static void xgbe_pci_remove(struct pci_dev *pdev) xgbe_deconfig_netdev(pdata); + pci_free_irq_vectors(pdata->pcidev); + xgbe_free_pdata(pdata); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index f52a9bd05bac..99f1c87df818 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -211,9 +211,9 @@ #define XGBE_MAC_PROP_OFFSET 0x1d000 #define XGBE_I2C_CTRL_OFFSET 0x1e000 -/* PCI MSIx support */ -#define XGBE_MSIX_BASE_COUNT 4 -#define XGBE_MSIX_MIN_COUNT (XGBE_MSIX_BASE_COUNT + 1) +/* PCI MSI/MSIx support */ +#define XGBE_MSI_BASE_COUNT 4 +#define XGBE_MSI_MIN_COUNT (XGBE_MSI_BASE_COUNT + 1) /* PCI clock frequencies */ #define XGBE_V2_DMA_CLOCK_FREQ 500000000 /* 500 MHz */ @@ -980,14 +980,12 @@ struct xgbe_prv_data { unsigned int desc_ded_count; unsigned int desc_sec_count; - struct msix_entry *msix_entries; int dev_irq; int ecc_irq; int i2c_irq; int channel_irq[XGBE_MAX_DMA_CHANNELS]; unsigned int per_channel_irq; - unsigned int irq_shared; unsigned int irq_count; unsigned int channel_irq_count; unsigned int channel_irq_mode; -- cgit v1.2.3 From 4fe0395550aeb6709ea5332f46de3644aef7d328 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 9 Jan 2017 21:37:40 +0100 Subject: PCI/MSI: Remove pci_enable_msi_{exact,range}() All multi-MSI allocations are now done through pci_irq_alloc_vectors(), so remove the old pci_enable_msi_range() and pci_enable_msi_exact() interfaces. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- Documentation/PCI/MSI-HOWTO.txt | 6 ++---- drivers/pci/msi.c | 26 +++++++++----------------- include/linux/pci.h | 16 ++-------------- 3 files changed, 13 insertions(+), 35 deletions(-) diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index cd9c9f6a7cd9..1e37138027a3 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -162,8 +162,6 @@ The following old APIs to enable and disable MSI or MSI-X interrupts should not be used in new code: pci_enable_msi() /* deprecated */ - pci_enable_msi_range() /* deprecated */ - pci_enable_msi_exact() /* deprecated */ pci_disable_msi() /* deprecated */ pci_enable_msix_range() /* deprecated */ pci_enable_msix_exact() /* deprecated */ @@ -268,5 +266,5 @@ or disabled (0). If 0 is found in any of the msi_bus files belonging to bridges between the PCI root and the device, MSIs are disabled. It is also worth checking the device driver to see whether it supports MSIs. -For example, it may contain calls to pci_enable_msi_range() or -pci_enable_msix_range(). +For example, it may contain calls to pci_irq_alloc_vectors() with the +PCI_IRQ_MSI or PCI_IRQ_MSIX flags. diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index ca9112afd53a..b6785c8be44d 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1109,23 +1109,15 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, } } -/** - * pci_enable_msi_range - configure device's MSI capability structure - * @dev: device to configure - * @minvec: minimal number of interrupts to configure - * @maxvec: maximum number of interrupts to configure - * - * This function tries to allocate a maximum possible number of interrupts in a - * range between @minvec and @maxvec. It returns a negative errno if an error - * occurs. If it succeeds, it returns the actual number of interrupts allocated - * and updates the @dev's irq member to the lowest new interrupt number; - * the other interrupt numbers allocated to this device are consecutive. - **/ -int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) +/* deprecated, don't use */ +int pci_enable_msi(struct pci_dev *dev) { - return __pci_enable_msi_range(dev, minvec, maxvec, NULL); + int rc = __pci_enable_msi_range(dev, 1, 1, NULL); + if (rc < 0) + return rc; + return 0; } -EXPORT_SYMBOL(pci_enable_msi_range); +EXPORT_SYMBOL(pci_enable_msi); static int __pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec, @@ -1381,7 +1373,7 @@ int pci_msi_domain_check_cap(struct irq_domain *domain, { struct msi_desc *desc = first_pci_msi_entry(to_pci_dev(dev)); - /* Special handling to support pci_enable_msi_range() */ + /* Special handling to support __pci_enable_msi_range() */ if (pci_msi_desc_is_multi_msi(desc) && !(info->flags & MSI_FLAG_MULTI_PCI_MSI)) return 1; @@ -1394,7 +1386,7 @@ int pci_msi_domain_check_cap(struct irq_domain *domain, static int pci_msi_domain_handle_error(struct irq_domain *domain, struct msi_desc *desc, int error) { - /* Special handling to support pci_enable_msi_range() */ + /* Special handling to support __pci_enable_msi_range() */ if (pci_msi_desc_is_multi_msi(desc) && error == -ENOSPC) return 1; diff --git a/include/linux/pci.h b/include/linux/pci.h index e2d1a124216a..2159376dc673 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1309,14 +1309,7 @@ void pci_msix_shutdown(struct pci_dev *dev); void pci_disable_msix(struct pci_dev *dev); void pci_restore_msi_state(struct pci_dev *dev); int pci_msi_enabled(void); -int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec); -static inline int pci_enable_msi_exact(struct pci_dev *dev, int nvec) -{ - int rc = pci_enable_msi_range(dev, nvec, nvec); - if (rc < 0) - return rc; - return 0; -} +int pci_enable_msi(struct pci_dev *dev); int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec, int maxvec); static inline int pci_enable_msix_exact(struct pci_dev *dev, @@ -1347,10 +1340,7 @@ static inline void pci_msix_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msix(struct pci_dev *dev) { } static inline void pci_restore_msi_state(struct pci_dev *dev) { } static inline int pci_msi_enabled(void) { return 0; } -static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec, - int maxvec) -{ return -ENOSYS; } -static inline int pci_enable_msi_exact(struct pci_dev *dev, int nvec) +static inline int pci_enable_msi(struct pci_dev *dev) { return -ENOSYS; } static inline int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec, int maxvec) @@ -1426,8 +1416,6 @@ static inline void pcie_set_ecrc_checking(struct pci_dev *dev) { } static inline void pcie_ecrc_get_policy(char *str) { } #endif -#define pci_enable_msi(pdev) pci_enable_msi_exact(pdev, 1) - #ifdef CONFIG_HT_IRQ /* The functions a driver should call */ int ht_create_irq(struct pci_dev *dev, int idx); -- cgit v1.2.3 From 3adfb572f2978a980b250a9e1a56f84f3a031001 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 27 Jan 2017 16:14:53 +0100 Subject: PCI/MSI: Return failure when msix_setup_entries() fails If alloc_msi_entry() fails, we free resources and set ret = -ENOMEM. However, msix_setup_entries() returns 0 unconditionally. Return the error code instead. Fixes: e75eafb9b039 ("genirq/msi: Switch to new irq spreading infrastructure") Signed-off-by: Christophe JAILLET Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index b6785c8be44d..0f77b38f03dd 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -731,7 +731,7 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, ret = 0; out: kfree(masks); - return 0; + return ret; } static void msix_program_entries(struct pci_dev *dev, -- cgit v1.2.3 From 47feb41888bc82ba5c9268c344775adc483d6de7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Feb 2017 18:17:43 +0100 Subject: PCI/MSI: Remove unused pci_msi_create_default_irq_domain() pci_msi_create_default_irq_domain() is never called in the whole tree, so remove it as well as all the supporting code for a default PCI MSI domain. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas Reviewed-by: Thomas Gleixner --- drivers/pci/msi.c | 50 ++------------------------------------------------ include/linux/msi.h | 3 --- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 0f77b38f03dd..79f20e4cb7bf 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -32,30 +32,11 @@ int pci_msi_ignore_mask; #define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -static struct irq_domain *pci_msi_default_domain; -static DEFINE_MUTEX(pci_msi_domain_lock); - -struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev) -{ - return pci_msi_default_domain; -} - -static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev) -{ - struct irq_domain *domain; - - domain = dev_get_msi_domain(&dev->dev); - if (domain) - return domain; - - return arch_get_pci_msi_domain(dev); -} - static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) { struct irq_domain *domain; - domain = pci_msi_get_domain(dev); + domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); @@ -66,7 +47,7 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) { struct irq_domain *domain; - domain = pci_msi_get_domain(dev); + domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) pci_msi_domain_free_irqs(domain, dev); else @@ -1499,33 +1480,6 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) msi_domain_free_irqs(domain, &dev->dev); } -/** - * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain - * @fwnode: Optional fwnode of the interrupt controller - * @info: MSI domain info - * @parent: Parent irq domain - * - * Returns: A domain pointer or NULL in case of failure. If successful - * the default PCI/MSI irqdomain pointer is updated. - */ -struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, struct irq_domain *parent) -{ - struct irq_domain *domain; - - mutex_lock(&pci_msi_domain_lock); - if (pci_msi_default_domain) { - pr_err("PCI: default irq domain for PCI MSI has already been created.\n"); - domain = NULL; - } else { - domain = pci_msi_create_irq_domain(fwnode, info, parent); - pci_msi_default_domain = domain; - } - mutex_unlock(&pci_msi_domain_lock); - - return domain; -} - static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) { u32 *pa = data; diff --git a/include/linux/msi.h b/include/linux/msi.h index 0db320b7bb15..18b8566b3ce3 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -319,9 +319,6 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, int nvec, int type); void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev); -struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, struct irq_domain *parent); - irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, struct msi_desc *desc); int pci_msi_domain_check_cap(struct irq_domain *domain, -- cgit v1.2.3 From 699c4cec238731a4c466f73fe6e9e45ab6f49a41 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Feb 2017 18:17:44 +0100 Subject: PCI/MSI: Remove pci_msi_domain_{alloc,free}_irqs() Just call the msi_* version directly instead of having trivial wrappers for one or two callsites. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas Reviewed-by: Thomas Gleixner --- arch/x86/kernel/apic/msi.c | 2 +- drivers/pci/msi.c | 30 ++---------------------------- include/linux/msi.h | 3 --- 3 files changed, 3 insertions(+), 32 deletions(-) diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index 015bbf30e3e3..c61aec7e65f4 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c @@ -82,7 +82,7 @@ int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) if (domain == NULL) return -ENOSYS; - return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); + return msi_domain_alloc_irqs(domain, &dev->dev, nvec); } void native_teardown_msi_irq(unsigned int irq) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 79f20e4cb7bf..b44ad7c21b29 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -38,7 +38,7 @@ static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) - return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); + return msi_domain_alloc_irqs(domain, &dev->dev, nvec); return arch_setup_msi_irqs(dev, nvec, type); } @@ -49,7 +49,7 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) - pci_msi_domain_free_irqs(domain, dev); + msi_domain_free_irqs(domain, &dev->dev); else arch_teardown_msi_irqs(dev); } @@ -1454,32 +1454,6 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain); -/** - * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain - * @domain: The interrupt domain to allocate from - * @dev: The device for which to allocate - * @nvec: The number of interrupts to allocate - * @type: Unused to allow simpler migration from the arch_XXX interfaces - * - * Returns: - * A virtual interrupt number or an error code in case of failure - */ -int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, - int nvec, int type) -{ - return msi_domain_alloc_irqs(domain, &dev->dev, nvec); -} - -/** - * pci_msi_domain_free_irqs - Free interrupts for @dev in @domain - * @domain: The interrupt domain - * @dev: The device for which to free interrupts - */ -void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) -{ - msi_domain_free_irqs(domain, &dev->dev); -} - static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) { u32 *pa = data; diff --git a/include/linux/msi.h b/include/linux/msi.h index 18b8566b3ce3..1b6f3ebbe876 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -316,9 +316,6 @@ void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg); struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, struct msi_domain_info *info, struct irq_domain *parent); -int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, - int nvec, int type); -void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev); irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, struct msi_desc *desc); int pci_msi_domain_check_cap(struct irq_domain *domain, -- cgit v1.2.3 From 862290f9e23c39051e59bf12ce65707a8ec8b911 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 1 Feb 2017 14:41:42 +0100 Subject: PCI/MSI: Check that we have a legacy interrupt line before using it It seems like there are some devices (e.g. the PCIe root port driver) that may not always have a INTx interrupt. Check for dev->irq before returning a legacy interrupt in pci_irq_alloc_vectors to properly handle this case. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index b44ad7c21b29..52ac675f0bd7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1198,9 +1198,11 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, } /* use legacy irq if allowed */ - if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1) { - pci_intx(dev, 1); - return 1; + if (flags & PCI_IRQ_LEGACY) { + if (min_vecs == 1 && dev->irq) { + pci_intx(dev, 1); + return 1; + } } return vecs; -- cgit v1.2.3 From 3674cc49da9a8fc55bf1dec2ab03a66c77f2dcdf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 1 Feb 2017 14:41:43 +0100 Subject: PCI/portdrv: Use pci_irq_alloc_vectors() Use pci_irq_alloc_vectors() and greatly simplify the code by managing the vector number for the subservices directly. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 161 ++++++++++++---------------------------- 1 file changed, 48 insertions(+), 113 deletions(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 9698289f105c..cea504f6f478 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -43,53 +43,17 @@ static void release_pcie_device(struct device *dev) kfree(to_pcie_device(dev)); } -/** - * pcie_port_msix_add_entry - add entry to given array of MSI-X entries - * @entries: Array of MSI-X entries - * @new_entry: Index of the entry to add to the array - * @nr_entries: Number of entries already in the array - * - * Return value: Position of the added entry in the array - */ -static int pcie_port_msix_add_entry( - struct msix_entry *entries, int new_entry, int nr_entries) -{ - int j; - - for (j = 0; j < nr_entries; j++) - if (entries[j].entry == new_entry) - return j; - - entries[j].entry = new_entry; - return j; -} - /** * pcie_port_enable_msix - try to set up MSI-X as interrupt mode for given port * @dev: PCI Express port to handle - * @vectors: Array of interrupt vectors to populate + * @irqs: Array of interrupt vectors to populate * @mask: Bitmask of port capabilities returned by get_port_device_capability() * * Return value: 0 on success, error code on failure */ -static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) +static int pcie_port_enable_msix(struct pci_dev *dev, int *irqs, int mask) { - struct msix_entry *msix_entries; - int idx[PCIE_PORT_DEVICE_MAXSERVICES]; - int nr_entries, status, pos, i, nvec; - u16 reg16; - u32 reg32; - - nr_entries = pci_msix_vec_count(dev); - if (nr_entries < 0) - return nr_entries; - BUG_ON(!nr_entries); - if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES) - nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES; - - msix_entries = kzalloc(sizeof(*msix_entries) * nr_entries, GFP_KERNEL); - if (!msix_entries) - return -ENOMEM; + int nr_entries, entry, nvec = 0; /* * Allocate as many entries as the port wants, so that we can check @@ -97,20 +61,13 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) * equal to the number of entries this port actually uses, we'll happily * go through without any tricks. */ - for (i = 0; i < nr_entries; i++) - msix_entries[i].entry = i; - - status = pci_enable_msix_exact(dev, msix_entries, nr_entries); - if (status) - goto Exit; - - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) - idx[i] = -1; - status = -EIO; - nvec = 0; + nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSIX_ENTRIES, + PCI_IRQ_MSIX); + if (nr_entries < 0) + return nr_entries; if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { - int entry; + u16 reg16; /* * The code below follows the PCI Express Base Specification 2.0 @@ -125,18 +82,16 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16); entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9; if (entry >= nr_entries) - goto Error; + goto out_free_irqs; - i = pcie_port_msix_add_entry(msix_entries, entry, nvec); - if (i == nvec) - nvec++; + irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry); + irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry); - idx[PCIE_PORT_SERVICE_PME_SHIFT] = i; - idx[PCIE_PORT_SERVICE_HP_SHIFT] = i; + nvec = max(nvec, entry + 1); } if (mask & PCIE_PORT_SERVICE_AER) { - int entry; + u32 reg32, pos; /* * The code below follows Section 7.10.10 of the PCI Express @@ -151,13 +106,11 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); entry = reg32 >> 27; if (entry >= nr_entries) - goto Error; + goto out_free_irqs; - i = pcie_port_msix_add_entry(msix_entries, entry, nvec); - if (i == nvec) - nvec++; + irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry); - idx[PCIE_PORT_SERVICE_AER_SHIFT] = i; + nvec = max(nvec, entry + 1); } /* @@ -165,41 +118,39 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) * what we have. Otherwise, the port has some extra entries not for the * services we know and we need to work around that. */ - if (nvec == nr_entries) { - status = 0; - } else { + if (nvec != nr_entries) { /* Drop the temporary MSI-X setup */ - pci_disable_msix(dev); + pci_free_irq_vectors(dev); /* Now allocate the MSI-X vectors for real */ - status = pci_enable_msix_exact(dev, msix_entries, nvec); - if (status) - goto Exit; + nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec, + PCI_IRQ_MSIX); + if (nr_entries < 0) + return nr_entries; } - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) - vectors[i] = idx[i] >= 0 ? msix_entries[idx[i]].vector : -1; - - Exit: - kfree(msix_entries); - return status; + return 0; - Error: - pci_disable_msix(dev); - goto Exit; +out_free_irqs: + pci_free_irq_vectors(dev); + return -EIO; } /** - * init_service_irqs - initialize irqs for PCI Express port services + * pcie_init_service_irqs - initialize irqs for PCI Express port services * @dev: PCI Express port to handle * @irqs: Array of irqs to populate * @mask: Bitmask of port capabilities returned by get_port_device_capability() * * Return value: Interrupt mode associated with the port */ -static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) +static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) { - int i, irq = -1; + unsigned flags = PCI_IRQ_LEGACY | PCI_IRQ_MSI; + int ret, i; + + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) + irqs[i] = -1; /* * If MSI cannot be used for PCIe PME or hotplug, we have to use @@ -207,41 +158,25 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) */ if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) || ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) { - if (dev->irq) - irq = dev->irq; - goto no_msi; + flags &= ~PCI_IRQ_MSI; + } else { + /* Try to use MSI-X if supported */ + if (!pcie_port_enable_msix(dev, irqs, mask)) + return 0; } - /* Try to use MSI-X if supported */ - if (!pcie_port_enable_msix(dev, irqs, mask)) - return 0; - - /* - * We're not going to use MSI-X, so try MSI and fall back to INTx. - * If neither MSI/MSI-X nor INTx available, try other interrupt. On - * some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode. - */ - if (!pci_enable_msi(dev) || dev->irq) - irq = dev->irq; + ret = pci_alloc_irq_vectors(dev, 1, 1, flags); + if (ret < 0) + return -ENODEV; - no_msi: - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) - irqs[i] = irq; - irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1; + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { + if (i != PCIE_PORT_SERVICE_VC_SHIFT) + irqs[i] = pci_irq_vector(dev, 0); + } - if (irq < 0) - return -ENODEV; return 0; } -static void cleanup_service_irqs(struct pci_dev *dev) -{ - if (dev->msix_enabled) - pci_disable_msix(dev); - else if (dev->msi_enabled) - pci_disable_msi(dev); -} - /** * get_port_device_capability - discover capabilities of a PCI Express port * @dev: PCI Express port to examine @@ -378,7 +313,7 @@ int pcie_port_device_register(struct pci_dev *dev) * that can be used in the absence of irqs. Allow them to determine * if that is to be used. */ - status = init_service_irqs(dev, irqs, capabilities); + status = pcie_init_service_irqs(dev, irqs, capabilities); if (status) { capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP; if (!capabilities) @@ -401,7 +336,7 @@ int pcie_port_device_register(struct pci_dev *dev) return 0; error_cleanup_irqs: - cleanup_service_irqs(dev); + pci_free_irq_vectors(dev); error_disable: pci_disable_device(dev); return status; @@ -469,7 +404,7 @@ static int remove_iter(struct device *dev, void *data) void pcie_port_device_remove(struct pci_dev *dev) { device_for_each_child(&dev->dev, NULL, remove_iter); - cleanup_service_irqs(dev); + pci_free_irq_vectors(dev); pci_disable_device(dev); } -- cgit v1.2.3 From 948b7620c15411444167a62cfc14cdd4b0e44682 Mon Sep 17 00:00:00 2001 From: Dennis Chen Date: Thu, 1 Dec 2016 10:15:04 +0800 Subject: PCI/MSI: Return -ENOSPC if pci_enable_msi_range() can't get enough vectors If device doesn't support as many MSI vectors as the driver requested, we previously returned -EINVAL from __pci_enable_msi_range() and pci_enable_msi_range(). In other similar situations in both __pci_enable_msi_range() and __pci_enable_msix_range(), we returned -ENOSPC. Return -ENOSPC from __pci_enable_msi_range() so we do it consistently. [bhelgaas: changelog] Signed-off-by: Dennis Chen Signed-off-by: Bjorn Helgaas CC: Tejun Heo CC: Christoph Hellwig CC: Tom Long Nguyen CC: Greg Kroah-Hartman CC: Marc Zyngier CC: Lorenzo Pieralisi CC: Steve Capper --- drivers/pci/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 52ac675f0bd7..93cc268c6ff1 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1065,7 +1065,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, if (nvec < 0) return nvec; if (nvec < minvec) - return -EINVAL; + return -ENOSPC; if (nvec > maxvec) nvec = maxvec; -- cgit v1.2.3 From c3cf2c61ddc1410424da0ea87717edf16fc296c5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 15 Feb 2017 08:58:22 +0100 Subject: PCI/MSI: Document pci_alloc_irq_vectors(), deprecate pci_enable_msi() Document pci_alloc_irq_vectors() instead of the deprecated pci_enable_msi() and pci_enable_msix() APIs. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- Documentation/PCI/pci.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Documentation/PCI/pci.txt b/Documentation/PCI/pci.txt index 77f49dc5be23..611a75e4366e 100644 --- a/Documentation/PCI/pci.txt +++ b/Documentation/PCI/pci.txt @@ -382,18 +382,18 @@ The fundamental difference between MSI and MSI-X is how multiple "vectors" get allocated. MSI requires contiguous blocks of vectors while MSI-X can allocate several individual ones. -MSI capability can be enabled by calling pci_enable_msi() or -pci_enable_msix() before calling request_irq(). This causes -the PCI support to program CPU vector data into the PCI device -capability registers. - -If your PCI device supports both, try to enable MSI-X first. -Only one can be enabled at a time. Many architectures, chip-sets, -or BIOSes do NOT support MSI or MSI-X and the call to pci_enable_msi/msix -will fail. This is important to note since many drivers have -two (or more) interrupt handlers: one for MSI/MSI-X and another for IRQs. -They choose which handler to register with request_irq() based on the -return value from pci_enable_msi/msix(). +MSI capability can be enabled by calling pci_alloc_irq_vectors() with the +PCI_IRQ_MSI and/or PCI_IRQ_MSIX flags before calling request_irq(). This +causes the PCI support to program CPU vector data into the PCI device +capability registers. Many architectures, chip-sets, or BIOSes do NOT +support MSI or MSI-X and a call to pci_alloc_irq_vectors with just +the PCI_IRQ_MSI and PCI_IRQ_MSIX flags will fail, so try to always +specify PCI_IRQ_LEGACY as well. + +Drivers that have different interrupt handlers for MSI/MSI-X and +legacy INTx should chose the right one based on the msi_enabled +and msix_enabled flags in the pci_dev structure after calling +pci_alloc_irq_vectors. There are (at least) two really good reasons for using MSI: 1) MSI is an exclusive interrupt vector by definition. -- cgit v1.2.3 From e4e7d59704d485f272061cea9057798dda3cfd99 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 15 Feb 2017 08:58:23 +0100 Subject: PCI/MSI: Update MSI/MSI-X bits in PCIEBUS-HOWTO Update the MSI/MSI-X bits in PCIEBUS-HOWTO. Stop talking about low-level details that mention deprecated APIs and concentrate on what service drivers should do and why. Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- Documentation/PCI/PCIEBUS-HOWTO.txt | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/Documentation/PCI/PCIEBUS-HOWTO.txt b/Documentation/PCI/PCIEBUS-HOWTO.txt index 6bd5f372adec..15f0bb3b5045 100644 --- a/Documentation/PCI/PCIEBUS-HOWTO.txt +++ b/Documentation/PCI/PCIEBUS-HOWTO.txt @@ -161,21 +161,13 @@ Since all service drivers of a PCI-PCI Bridge Port device are allowed to run simultaneously, below lists a few of possible resource conflicts with proposed solutions. -6.1 MSI Vector Resource - -The MSI capability structure enables a device software driver to call -pci_enable_msi to request MSI based interrupts. Once MSI interrupts -are enabled on a device, it stays in this mode until a device driver -calls pci_disable_msi to disable MSI interrupts and revert back to -INTx emulation mode. Since service drivers of the same PCI-PCI Bridge -port share the same physical device, if an individual service driver -calls pci_enable_msi/pci_disable_msi it may result unpredictable -behavior. For example, two service drivers run simultaneously on the -same physical Root Port. Both service drivers call pci_enable_msi to -request MSI based interrupts. A service driver may not know whether -any other service drivers have run on this Root Port. If either one -of them calls pci_disable_msi, it puts the other service driver -in a wrong interrupt mode. +6.1 MSI and MSI-X Vector Resource + +Once MSI or MSI-X interrupts are enabled on a device, it stays in this +mode until they are disabled again. Since service drivers of the same +PCI-PCI Bridge port share the same physical device, if an individual +service driver enables or disables MSI/MSI-X mode it may result +unpredictable behavior. To avoid this situation all service drivers are not permitted to switch interrupt mode on its device. The PCI Express Port Bus driver @@ -187,17 +179,6 @@ driver. Service drivers should use (struct pcie_device*)dev->irq to call request_irq/free_irq. In addition, the interrupt mode is stored in the field interrupt_mode of struct pcie_device. -6.2 MSI-X Vector Resources - -Similar to the MSI a device driver for an MSI-X capable device can -call pci_enable_msix to request MSI-X interrupts. All service drivers -are not permitted to switch interrupt mode on its device. The PCI -Express Port Bus driver is responsible for determining the interrupt -mode and this should be transparent to service drivers. Any attempt -by service driver to call pci_enable_msix/pci_disable_msix may -result unpredictable behavior. Service drivers should use -(struct pcie_device*)dev->irq and call request_irq/free_irq. - 6.3 PCI Memory/IO Mapped Regions Service drivers for PCI Express Power Management (PME), Advanced -- cgit v1.2.3