Skip to content

Commit 2cffad7

Browse files
committed
x86/irq: Simplify hotplug vector accounting
Before a CPU is taken offline the number of active interrupt vectors on the outgoing CPU and the number of vectors which are available on the other online CPUs are counted and compared. If the active vectors are more than the available vectors on the other CPUs then the CPU hot-unplug operation is aborted. This again uses loop based search and is inaccurate. The bitmap matrix allocator has accurate accounting information and can tell exactly whether the vector space is sufficient or not. Emit a message when the number of globaly reserved (unallocated) vectors is larger than the number of available vectors after offlining a CPU because after that point request_irq() might fail. Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Juergen Gross <[email protected]> Tested-by: Yu Chen <[email protected]> Acked-by: Juergen Gross <[email protected]> Cc: Boris Ostrovsky <[email protected]> Cc: Tony Luck <[email protected]> Cc: Marc Zyngier <[email protected]> Cc: Alok Kataria <[email protected]> Cc: Joerg Roedel <[email protected]> Cc: "Rafael J. Wysocki" <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Paolo Bonzini <[email protected]> Cc: Rui Zhang <[email protected]> Cc: "K. Y. Srinivasan" <[email protected]> Cc: Arjan van de Ven <[email protected]> Cc: Dan Williams <[email protected]> Cc: Len Brown <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 464d123 commit 2cffad7

File tree

5 files changed

+33
-105
lines changed

5 files changed

+33
-105
lines changed

arch/x86/include/asm/apic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ extern struct apic *__apicdrivers[], *__apicdrivers_end[];
386386
*/
387387
#ifdef CONFIG_SMP
388388
extern int wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip);
389+
extern int lapic_can_unplug_cpu(void);
389390
#endif
390391

391392
#ifdef CONFIG_X86_LOCAL_APIC

arch/x86/include/asm/irq.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ extern void irq_ctx_init(int cpu);
2525

2626
struct irq_desc;
2727

28-
#ifdef CONFIG_HOTPLUG_CPU
29-
#include <linux/cpumask.h>
30-
extern int check_irq_vectors_for_cpu_disable(void);
3128
extern void fixup_irqs(void);
32-
#endif
3329

3430
#ifdef CONFIG_HAVE_KVM
3531
extern void kvm_set_posted_intr_wakeup_handler(void (*handler)(void));

arch/x86/kernel/apic/vector.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,37 @@ void irq_force_complete_move(struct irq_desc *desc)
945945
unlock:
946946
raw_spin_unlock(&vector_lock);
947947
}
948-
#endif
948+
949+
#ifdef CONFIG_HOTPLUG_CPU
950+
/*
951+
* Note, this is not accurate accounting, but at least good enough to
952+
* prevent that the actual interrupt move will run out of vectors.
953+
*/
954+
int lapic_can_unplug_cpu(void)
955+
{
956+
unsigned int rsvd, avl, tomove, cpu = smp_processor_id();
957+
int ret = 0;
958+
959+
raw_spin_lock(&vector_lock);
960+
tomove = irq_matrix_allocated(vector_matrix);
961+
avl = irq_matrix_available(vector_matrix, true);
962+
if (avl < tomove) {
963+
pr_warn("CPU %u has %u vectors, %u available. Cannot disable CPU\n",
964+
cpu, tomove, avl);
965+
ret = -ENOSPC;
966+
goto out;
967+
}
968+
rsvd = irq_matrix_reserved(vector_matrix);
969+
if (avl < rsvd) {
970+
pr_warn("Reserved vectors %u > available %u. IRQ request may fail\n",
971+
rsvd, avl);
972+
}
973+
out:
974+
raw_spin_unlock(&vector_lock);
975+
return ret;
976+
}
977+
#endif /* HOTPLUG_CPU */
978+
#endif /* SMP */
949979

950980
static void __init print_APIC_field(int base)
951981
{

arch/x86/kernel/irq.c

Lines changed: 0 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -333,105 +333,6 @@ __visible void smp_kvm_posted_intr_nested_ipi(struct pt_regs *regs)
333333

334334

335335
#ifdef CONFIG_HOTPLUG_CPU
336-
337-
/* These two declarations are only used in check_irq_vectors_for_cpu_disable()
338-
* below, which is protected by stop_machine(). Putting them on the stack
339-
* results in a stack frame overflow. Dynamically allocating could result in a
340-
* failure so declare these two cpumasks as global.
341-
*/
342-
static struct cpumask affinity_new, online_new;
343-
344-
/*
345-
* This cpu is going to be removed and its vectors migrated to the remaining
346-
* online cpus. Check to see if there are enough vectors in the remaining cpus.
347-
* This function is protected by stop_machine().
348-
*/
349-
int check_irq_vectors_for_cpu_disable(void)
350-
{
351-
unsigned int this_cpu, vector, this_count, count;
352-
struct irq_desc *desc;
353-
struct irq_data *data;
354-
int cpu;
355-
356-
this_cpu = smp_processor_id();
357-
cpumask_copy(&online_new, cpu_online_mask);
358-
cpumask_clear_cpu(this_cpu, &online_new);
359-
360-
this_count = 0;
361-
for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS; vector++) {
362-
desc = __this_cpu_read(vector_irq[vector]);
363-
if (IS_ERR_OR_NULL(desc))
364-
continue;
365-
/*
366-
* Protect against concurrent action removal, affinity
367-
* changes etc.
368-
*/
369-
raw_spin_lock(&desc->lock);
370-
data = irq_desc_get_irq_data(desc);
371-
cpumask_copy(&affinity_new,
372-
irq_data_get_affinity_mask(data));
373-
cpumask_clear_cpu(this_cpu, &affinity_new);
374-
375-
/* Do not count inactive or per-cpu irqs. */
376-
if (!irq_desc_has_action(desc) || irqd_is_per_cpu(data)) {
377-
raw_spin_unlock(&desc->lock);
378-
continue;
379-
}
380-
381-
raw_spin_unlock(&desc->lock);
382-
/*
383-
* A single irq may be mapped to multiple cpu's
384-
* vector_irq[] (for example IOAPIC cluster mode). In
385-
* this case we have two possibilities:
386-
*
387-
* 1) the resulting affinity mask is empty; that is
388-
* this the down'd cpu is the last cpu in the irq's
389-
* affinity mask, or
390-
*
391-
* 2) the resulting affinity mask is no longer a
392-
* subset of the online cpus but the affinity mask is
393-
* not zero; that is the down'd cpu is the last online
394-
* cpu in a user set affinity mask.
395-
*/
396-
if (cpumask_empty(&affinity_new) ||
397-
!cpumask_subset(&affinity_new, &online_new))
398-
this_count++;
399-
}
400-
/* No need to check any further. */
401-
if (!this_count)
402-
return 0;
403-
404-
count = 0;
405-
for_each_online_cpu(cpu) {
406-
if (cpu == this_cpu)
407-
continue;
408-
/*
409-
* We scan from FIRST_EXTERNAL_VECTOR to first system
410-
* vector. If the vector is marked in the used vectors
411-
* bitmap or an irq is assigned to it, we don't count
412-
* it as available.
413-
*
414-
* As this is an inaccurate snapshot anyway, we can do
415-
* this w/o holding vector_lock.
416-
*/
417-
for (vector = FIRST_EXTERNAL_VECTOR;
418-
vector < FIRST_SYSTEM_VECTOR; vector++) {
419-
if (!test_bit(vector, system_vectors) &&
420-
IS_ERR_OR_NULL(per_cpu(vector_irq, cpu)[vector])) {
421-
if (++count == this_count)
422-
return 0;
423-
}
424-
}
425-
}
426-
427-
if (count < this_count) {
428-
pr_warn("CPU %d disable failed: CPU has %u vectors assigned and there are only %u available.\n",
429-
this_cpu, this_count, count);
430-
return -ERANGE;
431-
}
432-
return 0;
433-
}
434-
435336
/* A cpu has been removed from cpu_online_mask. Reset irq affinities. */
436337
void fixup_irqs(void)
437338
{

arch/x86/kernel/smpboot.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,7 @@ int native_cpu_disable(void)
15251525
{
15261526
int ret;
15271527

1528-
ret = check_irq_vectors_for_cpu_disable();
1528+
ret = lapic_can_unplug_cpu();
15291529
if (ret)
15301530
return ret;
15311531

0 commit comments

Comments
 (0)