diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2017-09-13 17:29:11 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2017-09-25 14:38:24 -0400 |
commit | bb9b428a5c832d7abb494fbabac37c515c01c6c4 (patch) | |
tree | d5a448d6832547bc940e137bff492c0d88b1786a | |
parent | 72491643469aab736536ae71dcd199b19dabd891 (diff) |
genirq/irqdomain: Allow irq_domain_activate_irq() to fail
Allow irq_domain_activate_irq() to fail. This is required to support a
reservation and late vector assignment scheme.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Juergen Gross <jgross@suse.com>
Tested-by: Yu Chen <yu.c.chen@intel.com>
Acked-by: Juergen Gross <jgross@suse.com>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Alok Kataria <akataria@vmware.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Rui Zhang <rui.zhang@intel.com>
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Len Brown <lenb@kernel.org>
Link: https://lkml.kernel.org/r/20170913213152.933882227@linutronix.de
-rw-r--r-- | include/linux/irqdomain.h | 2 | ||||
-rw-r--r-- | kernel/irq/chip.c | 9 | ||||
-rw-r--r-- | kernel/irq/internals.h | 3 | ||||
-rw-r--r-- | kernel/irq/irqdomain.c | 40 | ||||
-rw-r--r-- | kernel/irq/msi.c | 19 |
5 files changed, 52 insertions, 21 deletions
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 9bc07f59b090..8fb877121984 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h | |||
@@ -441,7 +441,7 @@ extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, | |||
441 | unsigned int nr_irqs, int node, void *arg, | 441 | unsigned int nr_irqs, int node, void *arg, |
442 | bool realloc, const struct cpumask *affinity); | 442 | bool realloc, const struct cpumask *affinity); |
443 | extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs); | 443 | extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs); |
444 | extern void irq_domain_activate_irq(struct irq_data *irq_data); | 444 | extern int irq_domain_activate_irq(struct irq_data *irq_data); |
445 | extern void irq_domain_deactivate_irq(struct irq_data *irq_data); | 445 | extern void irq_domain_deactivate_irq(struct irq_data *irq_data); |
446 | 446 | ||
447 | static inline int irq_domain_alloc_irqs(struct irq_domain *domain, | 447 | static inline int irq_domain_alloc_irqs(struct irq_domain *domain, |
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 37dd34d922f4..cd5b3eb38082 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -219,7 +219,12 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force) | |||
219 | */ | 219 | */ |
220 | return IRQ_STARTUP_ABORT; | 220 | return IRQ_STARTUP_ABORT; |
221 | } | 221 | } |
222 | irq_domain_activate_irq(d); | 222 | /* |
223 | * Managed interrupts have reserved resources, so this should not | ||
224 | * happen. | ||
225 | */ | ||
226 | if (WARN_ON(irq_domain_activate_irq(d))) | ||
227 | return IRQ_STARTUP_ABORT; | ||
223 | return IRQ_STARTUP_MANAGED; | 228 | return IRQ_STARTUP_MANAGED; |
224 | } | 229 | } |
225 | #else | 230 | #else |
@@ -285,7 +290,7 @@ int irq_activate(struct irq_desc *desc) | |||
285 | struct irq_data *d = irq_desc_get_irq_data(desc); | 290 | struct irq_data *d = irq_desc_get_irq_data(desc); |
286 | 291 | ||
287 | if (!irqd_affinity_is_managed(d)) | 292 | if (!irqd_affinity_is_managed(d)) |
288 | irq_domain_activate_irq(d); | 293 | return irq_domain_activate_irq(d); |
289 | return 0; | 294 | return 0; |
290 | } | 295 | } |
291 | 296 | ||
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 8bd131e4c944..e84d0e3899f6 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h | |||
@@ -439,9 +439,10 @@ static inline bool irq_fixup_move_pending(struct irq_desc *desc, bool fclear) | |||
439 | #endif /* !CONFIG_GENERIC_PENDING_IRQ */ | 439 | #endif /* !CONFIG_GENERIC_PENDING_IRQ */ |
440 | 440 | ||
441 | #if !defined(CONFIG_IRQ_DOMAIN) || !defined(CONFIG_IRQ_DOMAIN_HIERARCHY) | 441 | #if !defined(CONFIG_IRQ_DOMAIN) || !defined(CONFIG_IRQ_DOMAIN_HIERARCHY) |
442 | static inline void irq_domain_activate_irq(struct irq_data *data) | 442 | static inline int irq_domain_activate_irq(struct irq_data *data) |
443 | { | 443 | { |
444 | irqd_set_activated(data); | 444 | irqd_set_activated(data); |
445 | return 0; | ||
445 | } | 446 | } |
446 | static inline void irq_domain_deactivate_irq(struct irq_data *data) | 447 | static inline void irq_domain_deactivate_irq(struct irq_data *data) |
447 | { | 448 | { |
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 1423f8a6c75d..47e8ddd9e8cf 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
@@ -1682,28 +1682,35 @@ void irq_domain_free_irqs_parent(struct irq_domain *domain, | |||
1682 | } | 1682 | } |
1683 | EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent); | 1683 | EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent); |
1684 | 1684 | ||
1685 | static void __irq_domain_activate_irq(struct irq_data *irq_data) | 1685 | static void __irq_domain_deactivate_irq(struct irq_data *irq_data) |
1686 | { | 1686 | { |
1687 | if (irq_data && irq_data->domain) { | 1687 | if (irq_data && irq_data->domain) { |
1688 | struct irq_domain *domain = irq_data->domain; | 1688 | struct irq_domain *domain = irq_data->domain; |
1689 | 1689 | ||
1690 | if (domain->ops->deactivate) | ||
1691 | domain->ops->deactivate(domain, irq_data); | ||
1690 | if (irq_data->parent_data) | 1692 | if (irq_data->parent_data) |
1691 | __irq_domain_activate_irq(irq_data->parent_data); | 1693 | __irq_domain_deactivate_irq(irq_data->parent_data); |
1692 | if (domain->ops->activate) | ||
1693 | domain->ops->activate(domain, irq_data, false); | ||
1694 | } | 1694 | } |
1695 | } | 1695 | } |
1696 | 1696 | ||
1697 | static void __irq_domain_deactivate_irq(struct irq_data *irq_data) | 1697 | static int __irq_domain_activate_irq(struct irq_data *irqd) |
1698 | { | 1698 | { |
1699 | if (irq_data && irq_data->domain) { | 1699 | int ret = 0; |
1700 | struct irq_domain *domain = irq_data->domain; | ||
1701 | 1700 | ||
1702 | if (domain->ops->deactivate) | 1701 | if (irqd && irqd->domain) { |
1703 | domain->ops->deactivate(domain, irq_data); | 1702 | struct irq_domain *domain = irqd->domain; |
1704 | if (irq_data->parent_data) | 1703 | |
1705 | __irq_domain_deactivate_irq(irq_data->parent_data); | 1704 | if (irqd->parent_data) |
1705 | ret = __irq_domain_activate_irq(irqd->parent_data); | ||
1706 | if (!ret && domain->ops->activate) { | ||
1707 | ret = domain->ops->activate(domain, irqd, false); | ||
1708 | /* Rollback in case of error */ | ||
1709 | if (ret && irqd->parent_data) | ||
1710 | __irq_domain_deactivate_irq(irqd->parent_data); | ||
1711 | } | ||
1706 | } | 1712 | } |
1713 | return ret; | ||
1707 | } | 1714 | } |
1708 | 1715 | ||
1709 | /** | 1716 | /** |
@@ -1714,12 +1721,15 @@ static void __irq_domain_deactivate_irq(struct irq_data *irq_data) | |||
1714 | * This is the second step to call domain_ops->activate to program interrupt | 1721 | * This is the second step to call domain_ops->activate to program interrupt |
1715 | * controllers, so the interrupt could actually get delivered. | 1722 | * controllers, so the interrupt could actually get delivered. |
1716 | */ | 1723 | */ |
1717 | void irq_domain_activate_irq(struct irq_data *irq_data) | 1724 | int irq_domain_activate_irq(struct irq_data *irq_data) |
1718 | { | 1725 | { |
1719 | if (!irqd_is_activated(irq_data)) { | 1726 | int ret = 0; |
1720 | __irq_domain_activate_irq(irq_data); | 1727 | |
1728 | if (!irqd_is_activated(irq_data)) | ||
1729 | ret = __irq_domain_activate_irq(irq_data); | ||
1730 | if (!ret) | ||
1721 | irqd_set_activated(irq_data); | 1731 | irqd_set_activated(irq_data); |
1722 | } | 1732 | return ret; |
1723 | } | 1733 | } |
1724 | 1734 | ||
1725 | /** | 1735 | /** |
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 6155fff9f647..5ece369950ec 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c | |||
@@ -401,11 +401,26 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, | |||
401 | struct irq_data *irq_data; | 401 | struct irq_data *irq_data; |
402 | 402 | ||
403 | irq_data = irq_domain_get_irq_data(domain, desc->irq); | 403 | irq_data = irq_domain_get_irq_data(domain, desc->irq); |
404 | irq_domain_activate_irq(irq_data); | 404 | ret = irq_domain_activate_irq(irq_data); |
405 | if (ret) | ||
406 | goto cleanup; | ||
405 | } | 407 | } |
406 | } | 408 | } |
407 | |||
408 | return 0; | 409 | return 0; |
410 | |||
411 | cleanup: | ||
412 | for_each_msi_entry(desc, dev) { | ||
413 | struct irq_data *irqd; | ||
414 | |||
415 | if (desc->irq == virq) | ||
416 | break; | ||
417 | |||
418 | irqd = irq_domain_get_irq_data(domain, desc->irq); | ||
419 | if (irqd_is_activated(irqd)) | ||
420 | irq_domain_deactivate_irq(irqd); | ||
421 | } | ||
422 | msi_domain_free_irqs(domain, dev); | ||
423 | return ret; | ||
409 | } | 424 | } |
410 | 425 | ||
411 | /** | 426 | /** |