aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2017-09-13 17:29:53 -0400
committerThomas Gleixner <tglx@linutronix.de>2017-09-25 14:52:02 -0400
commit2cffad7bad83157f89332872015f4305d2ac09ac (patch)
tree4252f822312b28615986a5caa94c988b2080d4c6
parent464d12309e1b5829597793db551ae8ecaecf4036 (diff)
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 <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/20170913213156.351193962@linutronix.de
-rw-r--r--arch/x86/include/asm/apic.h1
-rw-r--r--arch/x86/include/asm/irq.h4
-rw-r--r--arch/x86/kernel/apic/vector.c32
-rw-r--r--arch/x86/kernel/irq.c99
-rw-r--r--arch/x86/kernel/smpboot.c2
5 files changed, 33 insertions, 105 deletions
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 7a8651921ed5..a9e57f08bfa6 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -386,6 +386,7 @@ extern struct apic *__apicdrivers[], *__apicdrivers_end[];
386 */ 386 */
387#ifdef CONFIG_SMP 387#ifdef CONFIG_SMP
388extern int wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip); 388extern int wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip);
389extern int lapic_can_unplug_cpu(void);
389#endif 390#endif
390 391
391#ifdef CONFIG_X86_LOCAL_APIC 392#ifdef CONFIG_X86_LOCAL_APIC
diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
index 9958ceea2fa3..1002a3e8fccc 100644
--- a/arch/x86/include/asm/irq.h
+++ b/arch/x86/include/asm/irq.h
@@ -25,11 +25,7 @@ extern void irq_ctx_init(int cpu);
25 25
26struct irq_desc; 26struct irq_desc;
27 27
28#ifdef CONFIG_HOTPLUG_CPU
29#include <linux/cpumask.h>
30extern int check_irq_vectors_for_cpu_disable(void);
31extern void fixup_irqs(void); 28extern void fixup_irqs(void);
32#endif
33 29
34#ifdef CONFIG_HAVE_KVM 30#ifdef CONFIG_HAVE_KVM
35extern void kvm_set_posted_intr_wakeup_handler(void (*handler)(void)); 31extern void kvm_set_posted_intr_wakeup_handler(void (*handler)(void));
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index 5e58da8efe77..14b21ca4483c 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -945,7 +945,37 @@ void irq_force_complete_move(struct irq_desc *desc)
945unlock: 945unlock:
946 raw_spin_unlock(&vector_lock); 946 raw_spin_unlock(&vector_lock);
947} 947}
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 */
954int 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 }
973out:
974 raw_spin_unlock(&vector_lock);
975 return ret;
976}
977#endif /* HOTPLUG_CPU */
978#endif /* SMP */
949 979
950static void __init print_APIC_field(int base) 980static void __init print_APIC_field(int base)
951{ 981{
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index 188990c3a514..49cfd9fe7589 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -333,105 +333,6 @@ __visible void smp_kvm_posted_intr_nested_ipi(struct pt_regs *regs)
333 333
334 334
335#ifdef CONFIG_HOTPLUG_CPU 335#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 */
342static 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 */
349int 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
435/* A cpu has been removed from cpu_online_mask. Reset irq affinities. */ 336/* A cpu has been removed from cpu_online_mask. Reset irq affinities. */
436void fixup_irqs(void) 337void fixup_irqs(void)
437{ 338{
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 86739f04701b..92aadfa30d61 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1525,7 +1525,7 @@ int native_cpu_disable(void)
1525{ 1525{
1526 int ret; 1526 int ret;
1527 1527
1528 ret = check_irq_vectors_for_cpu_disable(); 1528 ret = lapic_can_unplug_cpu();
1529 if (ret) 1529 if (ret)
1530 return ret; 1530 return ret;
1531 1531