aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/apic/vector.c63
1 files changed, 53 insertions, 10 deletions
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index 5f7883578880..3b670df4ba7b 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -120,7 +120,12 @@ static int __assign_irq_vector(int irq, struct apic_chip_data *d,
120 static int current_offset = VECTOR_OFFSET_START % 16; 120 static int current_offset = VECTOR_OFFSET_START % 16;
121 int cpu, vector; 121 int cpu, vector;
122 122
123 if (d->move_in_progress) 123 /*
124 * If there is still a move in progress or the previous move has not
125 * been cleaned up completely, tell the caller to come back later.
126 */
127 if (d->move_in_progress ||
128 cpumask_intersects(d->old_domain, cpu_online_mask))
124 return -EBUSY; 129 return -EBUSY;
125 130
126 /* Only try and allocate irqs on cpus that are present */ 131 /* Only try and allocate irqs on cpus that are present */
@@ -259,7 +264,12 @@ static void clear_irq_vector(int irq, struct apic_chip_data *data)
259 data->cfg.vector = 0; 264 data->cfg.vector = 0;
260 cpumask_clear(data->domain); 265 cpumask_clear(data->domain);
261 266
262 if (likely(!data->move_in_progress)) 267 /*
268 * If move is in progress or the old_domain mask is not empty,
269 * i.e. the cleanup IPI has not been processed yet, we need to remove
270 * the old references to desc from all cpus vector tables.
271 */
272 if (!data->move_in_progress && cpumask_empty(data->old_domain))
263 return; 273 return;
264 274
265 desc = irq_to_desc(irq); 275 desc = irq_to_desc(irq);
@@ -579,12 +589,25 @@ asmlinkage __visible void smp_irq_move_cleanup_interrupt(void)
579 goto unlock; 589 goto unlock;
580 590
581 /* 591 /*
582 * Check if the irq migration is in progress. If so, we 592 * Nothing to cleanup if irq migration is in progress
583 * haven't received the cleanup request yet for this irq. 593 * or this cpu is not set in the cleanup mask.
584 */ 594 */
585 if (data->move_in_progress) 595 if (data->move_in_progress ||
596 !cpumask_test_cpu(me, data->old_domain))
586 goto unlock; 597 goto unlock;
587 598
599 /*
600 * We have two cases to handle here:
601 * 1) vector is unchanged but the target mask got reduced
602 * 2) vector and the target mask has changed
603 *
604 * #1 is obvious, but in #2 we have two vectors with the same
605 * irq descriptor: the old and the new vector. So we need to
606 * make sure that we only cleanup the old vector. The new
607 * vector has the current @vector number in the config and
608 * this cpu is part of the target mask. We better leave that
609 * one alone.
610 */
588 if (vector == data->cfg.vector && 611 if (vector == data->cfg.vector &&
589 cpumask_test_cpu(me, data->domain)) 612 cpumask_test_cpu(me, data->domain))
590 goto unlock; 613 goto unlock;
@@ -602,6 +625,7 @@ asmlinkage __visible void smp_irq_move_cleanup_interrupt(void)
602 goto unlock; 625 goto unlock;
603 } 626 }
604 __this_cpu_write(vector_irq[vector], VECTOR_UNUSED); 627 __this_cpu_write(vector_irq[vector], VECTOR_UNUSED);
628 cpumask_clear_cpu(me, data->old_domain);
605unlock: 629unlock:
606 raw_spin_unlock(&desc->lock); 630 raw_spin_unlock(&desc->lock);
607 } 631 }
@@ -645,13 +669,32 @@ void irq_force_complete_move(struct irq_desc *desc)
645 __irq_complete_move(cfg, cfg->vector); 669 __irq_complete_move(cfg, cfg->vector);
646 670
647 /* 671 /*
648 * Remove this cpu from the cleanup mask. The IPI might have been sent 672 * This is tricky. If the cleanup of @data->old_domain has not been
649 * just before the cpu was removed from the offline mask, but has not 673 * done yet, then the following setaffinity call will fail with
650 * been processed because the CPU has interrupts disabled and is on 674 * -EBUSY. This can leave the interrupt in a stale state.
651 * the way out. 675 *
676 * The cleanup cannot make progress because we hold @desc->lock. So in
677 * case @data->old_domain is not yet cleaned up, we need to drop the
678 * lock and acquire it again. @desc cannot go away, because the
679 * hotplug code holds the sparse irq lock.
652 */ 680 */
653 raw_spin_lock(&vector_lock); 681 raw_spin_lock(&vector_lock);
654 cpumask_clear_cpu(smp_processor_id(), data->old_domain); 682 /* Clean out all offline cpus (including ourself) first. */
683 cpumask_and(data->old_domain, data->old_domain, cpu_online_mask);
684 while (!cpumask_empty(data->old_domain)) {
685 raw_spin_unlock(&vector_lock);
686 raw_spin_unlock(&desc->lock);
687 cpu_relax();
688 raw_spin_lock(&desc->lock);
689 /*
690 * Reevaluate apic_chip_data. It might have been cleared after
691 * we dropped @desc->lock.
692 */
693 data = apic_chip_data(irqdata);
694 if (!data)
695 return;
696 raw_spin_lock(&vector_lock);
697 }
655 raw_spin_unlock(&vector_lock); 698 raw_spin_unlock(&vector_lock);
656} 699}
657#endif 700#endif