summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/apic/vector.c56
1 files changed, 33 insertions, 23 deletions
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index 46a9ae921819..5e58da8efe77 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -258,17 +258,6 @@ static int assign_irq_vector_any_locked(struct irq_data *irqd)
return assign_vector_locked(irqd, cpu_online_mask);
}
-static int assign_irq_vector_any(struct irq_data *irqd)
-{
- unsigned long flags;
- int ret;
-
- raw_spin_lock_irqsave(&vector_lock, flags);
- ret = assign_irq_vector_any_locked(irqd);
- raw_spin_unlock_irqrestore(&vector_lock, flags);
- return ret;
-}
-
static int
assign_irq_vector_policy(struct irq_data *irqd, struct irq_alloc_info *info)
{
@@ -276,10 +265,10 @@ assign_irq_vector_policy(struct irq_data *irqd, struct irq_alloc_info *info)
return reserve_managed_vector(irqd);
if (info->mask)
return assign_irq_vector(irqd, info->mask);
- if (info->type != X86_IRQ_ALLOC_TYPE_MSI &&
- info->type != X86_IRQ_ALLOC_TYPE_MSIX)
- return assign_irq_vector_any(irqd);
- /* For MSI(X) make only a global reservation with no guarantee */
+ /*
+ * Make only a global reservation with no guarantee. A real vector
+ * is associated at activation time.
+ */
return reserve_irq_vector(irqd);
}
@@ -456,13 +445,39 @@ static void x86_vector_free_irqs(struct irq_domain *domain,
}
}
+static bool vector_configure_legacy(unsigned int virq, struct irq_data *irqd,
+ struct apic_chip_data *apicd)
+{
+ unsigned long flags;
+ bool realloc = false;
+
+ apicd->vector = ISA_IRQ_VECTOR(virq);
+ apicd->cpu = 0;
+
+ raw_spin_lock_irqsave(&vector_lock, flags);
+ /*
+ * If the interrupt is activated, then it must stay at this vector
+ * position. That's usually the timer interrupt (0).
+ */
+ if (irqd_is_activated(irqd)) {
+ trace_vector_setup(virq, true, 0);
+ apic_update_irq_cfg(irqd, apicd->vector, apicd->cpu);
+ } else {
+ /* Release the vector */
+ apicd->can_reserve = true;
+ clear_irq_vector(irqd);
+ realloc = true;
+ }
+ raw_spin_unlock_irqrestore(&vector_lock, flags);
+ return realloc;
+}
+
static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
struct irq_alloc_info *info = arg;
struct apic_chip_data *apicd;
struct irq_data *irqd;
- unsigned long flags;
int i, err, node;
if (disable_apic)
@@ -496,13 +511,8 @@ static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq,
* config.
*/
if (info->flags & X86_IRQ_ALLOC_LEGACY) {
- apicd->vector = ISA_IRQ_VECTOR(virq + i);
- apicd->cpu = 0;
- trace_vector_setup(virq + i, true, 0);
- raw_spin_lock_irqsave(&vector_lock, flags);
- apic_update_irq_cfg(irqd, apicd->vector, apicd->cpu);
- raw_spin_unlock_irqrestore(&vector_lock, flags);
- continue;
+ if (!vector_configure_legacy(virq + i, irqd, apicd))
+ continue;
}
err = assign_irq_vector_policy(irqd, info);