| From 81b1e6e6a8590a19257e37a1633bec098d499c57 Mon Sep 17 00:00:00 2001 |
| From: Miquel Raynal <miquel.raynal@bootlin.com> |
| Date: Thu, 11 Oct 2018 11:12:34 +0200 |
| Subject: platform-msi: Free descriptors in platform_msi_domain_free() |
| |
| From: Miquel Raynal <miquel.raynal@bootlin.com> |
| |
| commit 81b1e6e6a8590a19257e37a1633bec098d499c57 upstream. |
| |
| Since the addition of platform MSI support, there were two helpers |
| supposed to allocate/free IRQs for a device: |
| |
| platform_msi_domain_alloc_irqs() |
| platform_msi_domain_free_irqs() |
| |
| In these helpers, IRQ descriptors are allocated in the "alloc" routine |
| while they are freed in the "free" one. |
| |
| Later, two other helpers have been added to handle IRQ domains on top |
| of MSI domains: |
| |
| platform_msi_domain_alloc() |
| platform_msi_domain_free() |
| |
| Seen from the outside, the logic is pretty close with the former |
| helpers and people used it with the same logic as before: a |
| platform_msi_domain_alloc() call should be balanced with a |
| platform_msi_domain_free() call. While this is probably what was |
| intended to do, the platform_msi_domain_free() does not remove/free |
| the IRQ descriptor(s) created/inserted in |
| platform_msi_domain_alloc(). |
| |
| One effect of such situation is that removing a module that requested |
| an IRQ will let one orphaned IRQ descriptor (with an allocated MSI |
| entry) in the device descriptors list. Next time the module will be |
| inserted back, one will observe that the allocation will happen twice |
| in the MSI domain, one time for the remaining descriptor, one time for |
| the new one. It also has the side effect to quickly overshoot the |
| maximum number of allocated MSI and then prevent any module requesting |
| an interrupt in the same domain to be inserted anymore. |
| |
| This situation has been met with loops of insertion/removal of the |
| mvpp2.ko module (requesting 15 MSIs each time). |
| |
| Fixes: 552c494a7666 ("platform-msi: Allow creation of a MSI-based stacked irq domain") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> |
| Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/base/platform-msi.c | 6 ++++-- |
| include/linux/msi.h | 2 ++ |
| 2 files changed, 6 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/base/platform-msi.c |
| +++ b/drivers/base/platform-msi.c |
| @@ -375,14 +375,16 @@ void platform_msi_domain_free(struct irq |
| unsigned int nvec) |
| { |
| struct platform_msi_priv_data *data = domain->host_data; |
| - struct msi_desc *desc; |
| - for_each_msi_entry(desc, data->dev) { |
| + struct msi_desc *desc, *tmp; |
| + for_each_msi_entry_safe(desc, tmp, data->dev) { |
| if (WARN_ON(!desc->irq || desc->nvec_used != 1)) |
| return; |
| if (!(desc->irq >= virq && desc->irq < (virq + nvec))) |
| continue; |
| |
| irq_domain_free_irqs_common(domain, desc->irq, 1); |
| + list_del(&desc->list); |
| + free_msi_entry(desc); |
| } |
| } |
| |
| --- a/include/linux/msi.h |
| +++ b/include/linux/msi.h |
| @@ -108,6 +108,8 @@ struct msi_desc { |
| list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list) |
| #define for_each_msi_entry(desc, dev) \ |
| 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) |
| |
| #ifdef CONFIG_PCI_MSI |
| #define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev) |