Skip to content

Commit 08d85f3

Browse files
Marc ZyngierKAGA-KOKO
authored andcommitted
irqdomain: Avoid activating interrupts more than once
Since commit f3b0946 ("genirq/msi: Make sure PCI MSIs are activated early"), we can end-up activating a PCI/MSI twice (once at allocation time, and once at startup time). This is normally of no consequences, except that there is some HW out there that may misbehave if activate is used more than once (the GICv3 ITS, for example, uses the activate callback to issue the MAPVI command, and the architecture spec says that "If there is an existing mapping for the EventID-DeviceID combination, behavior is UNPREDICTABLE"). While this could be worked around in each individual driver, it may make more sense to tackle the issue at the core level. In order to avoid getting in that situation, let's have a per-interrupt flag to remember if we have already activated that interrupt or not. Fixes: f3b0946 ("genirq/msi: Make sure PCI MSIs are activated early") Reported-and-tested-by: Andre Przywara <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 566cf87 commit 08d85f3

File tree

2 files changed

+47
-14
lines changed

2 files changed

+47
-14
lines changed

include/linux/irq.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ struct irq_data {
184184
*
185185
* IRQD_TRIGGER_MASK - Mask for the trigger type bits
186186
* IRQD_SETAFFINITY_PENDING - Affinity setting is pending
187+
* IRQD_ACTIVATED - Interrupt has already been activated
187188
* IRQD_NO_BALANCING - Balancing disabled for this IRQ
188189
* IRQD_PER_CPU - Interrupt is per cpu
189190
* IRQD_AFFINITY_SET - Interrupt affinity was set
@@ -202,6 +203,7 @@ struct irq_data {
202203
enum {
203204
IRQD_TRIGGER_MASK = 0xf,
204205
IRQD_SETAFFINITY_PENDING = (1 << 8),
206+
IRQD_ACTIVATED = (1 << 9),
205207
IRQD_NO_BALANCING = (1 << 10),
206208
IRQD_PER_CPU = (1 << 11),
207209
IRQD_AFFINITY_SET = (1 << 12),
@@ -312,6 +314,21 @@ static inline bool irqd_affinity_is_managed(struct irq_data *d)
312314
return __irqd_to_state(d) & IRQD_AFFINITY_MANAGED;
313315
}
314316

317+
static inline bool irqd_is_activated(struct irq_data *d)
318+
{
319+
return __irqd_to_state(d) & IRQD_ACTIVATED;
320+
}
321+
322+
static inline void irqd_set_activated(struct irq_data *d)
323+
{
324+
__irqd_to_state(d) |= IRQD_ACTIVATED;
325+
}
326+
327+
static inline void irqd_clr_activated(struct irq_data *d)
328+
{
329+
__irqd_to_state(d) &= ~IRQD_ACTIVATED;
330+
}
331+
315332
#undef __irqd_to_state
316333

317334
static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)

kernel/irq/irqdomain.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,30 @@ void irq_domain_free_irqs_parent(struct irq_domain *domain,
13461346
}
13471347
EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent);
13481348

1349+
static void __irq_domain_activate_irq(struct irq_data *irq_data)
1350+
{
1351+
if (irq_data && irq_data->domain) {
1352+
struct irq_domain *domain = irq_data->domain;
1353+
1354+
if (irq_data->parent_data)
1355+
__irq_domain_activate_irq(irq_data->parent_data);
1356+
if (domain->ops->activate)
1357+
domain->ops->activate(domain, irq_data);
1358+
}
1359+
}
1360+
1361+
static void __irq_domain_deactivate_irq(struct irq_data *irq_data)
1362+
{
1363+
if (irq_data && irq_data->domain) {
1364+
struct irq_domain *domain = irq_data->domain;
1365+
1366+
if (domain->ops->deactivate)
1367+
domain->ops->deactivate(domain, irq_data);
1368+
if (irq_data->parent_data)
1369+
__irq_domain_deactivate_irq(irq_data->parent_data);
1370+
}
1371+
}
1372+
13491373
/**
13501374
* irq_domain_activate_irq - Call domain_ops->activate recursively to activate
13511375
* interrupt
@@ -1356,13 +1380,9 @@ EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent);
13561380
*/
13571381
void irq_domain_activate_irq(struct irq_data *irq_data)
13581382
{
1359-
if (irq_data && irq_data->domain) {
1360-
struct irq_domain *domain = irq_data->domain;
1361-
1362-
if (irq_data->parent_data)
1363-
irq_domain_activate_irq(irq_data->parent_data);
1364-
if (domain->ops->activate)
1365-
domain->ops->activate(domain, irq_data);
1383+
if (!irqd_is_activated(irq_data)) {
1384+
__irq_domain_activate_irq(irq_data);
1385+
irqd_set_activated(irq_data);
13661386
}
13671387
}
13681388

@@ -1376,13 +1396,9 @@ void irq_domain_activate_irq(struct irq_data *irq_data)
13761396
*/
13771397
void irq_domain_deactivate_irq(struct irq_data *irq_data)
13781398
{
1379-
if (irq_data && irq_data->domain) {
1380-
struct irq_domain *domain = irq_data->domain;
1381-
1382-
if (domain->ops->deactivate)
1383-
domain->ops->deactivate(domain, irq_data);
1384-
if (irq_data->parent_data)
1385-
irq_domain_deactivate_irq(irq_data->parent_data);
1399+
if (irqd_is_activated(irq_data)) {
1400+
__irq_domain_deactivate_irq(irq_data);
1401+
irqd_clr_activated(irq_data);
13861402
}
13871403
}
13881404

0 commit comments

Comments
 (0)