diff options
| -rw-r--r-- | arch/x86/include/asm/irq.h | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/irq.c | 70 | ||||
| -rw-r--r-- | arch/x86/kernel/smpboot.c | 6 |
3 files changed, 77 insertions, 0 deletions
diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h index 0ea10f27d613..cb6cfcd034cf 100644 --- a/arch/x86/include/asm/irq.h +++ b/arch/x86/include/asm/irq.h | |||
| @@ -25,6 +25,7 @@ extern void irq_ctx_init(int cpu); | |||
| 25 | 25 | ||
| 26 | #ifdef CONFIG_HOTPLUG_CPU | 26 | #ifdef CONFIG_HOTPLUG_CPU |
| 27 | #include <linux/cpumask.h> | 27 | #include <linux/cpumask.h> |
| 28 | extern int check_irq_vectors_for_cpu_disable(void); | ||
| 28 | extern void fixup_irqs(void); | 29 | extern void fixup_irqs(void); |
| 29 | extern void irq_force_complete_move(int); | 30 | extern void irq_force_complete_move(int); |
| 30 | #endif | 31 | #endif |
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 22d0687e7fda..4207e8d1a094 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c | |||
| @@ -262,6 +262,76 @@ __visible void smp_trace_x86_platform_ipi(struct pt_regs *regs) | |||
| 262 | EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq); | 262 | EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq); |
| 263 | 263 | ||
| 264 | #ifdef CONFIG_HOTPLUG_CPU | 264 | #ifdef CONFIG_HOTPLUG_CPU |
| 265 | /* | ||
| 266 | * This cpu is going to be removed and its vectors migrated to the remaining | ||
| 267 | * online cpus. Check to see if there are enough vectors in the remaining cpus. | ||
| 268 | * This function is protected by stop_machine(). | ||
| 269 | */ | ||
| 270 | int check_irq_vectors_for_cpu_disable(void) | ||
| 271 | { | ||
| 272 | int irq, cpu; | ||
| 273 | unsigned int this_cpu, vector, this_count, count; | ||
| 274 | struct irq_desc *desc; | ||
| 275 | struct irq_data *data; | ||
| 276 | struct cpumask affinity_new, online_new; | ||
| 277 | |||
| 278 | this_cpu = smp_processor_id(); | ||
| 279 | cpumask_copy(&online_new, cpu_online_mask); | ||
| 280 | cpu_clear(this_cpu, online_new); | ||
| 281 | |||
| 282 | this_count = 0; | ||
| 283 | for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS; vector++) { | ||
| 284 | irq = __this_cpu_read(vector_irq[vector]); | ||
| 285 | if (irq >= 0) { | ||
| 286 | desc = irq_to_desc(irq); | ||
| 287 | data = irq_desc_get_irq_data(desc); | ||
| 288 | cpumask_copy(&affinity_new, data->affinity); | ||
| 289 | cpu_clear(this_cpu, affinity_new); | ||
| 290 | |||
| 291 | /* Do not count inactive or per-cpu irqs. */ | ||
| 292 | if (!irq_has_action(irq) || irqd_is_per_cpu(data)) | ||
| 293 | continue; | ||
| 294 | |||
| 295 | /* | ||
| 296 | * A single irq may be mapped to multiple | ||
| 297 | * cpu's vector_irq[] (for example IOAPIC cluster | ||
| 298 | * mode). In this case we have two | ||
| 299 | * possibilities: | ||
| 300 | * | ||
| 301 | * 1) the resulting affinity mask is empty; that is | ||
| 302 | * this the down'd cpu is the last cpu in the irq's | ||
| 303 | * affinity mask, or | ||
| 304 | * | ||
| 305 | * 2) the resulting affinity mask is no longer | ||
| 306 | * a subset of the online cpus but the affinity | ||
| 307 | * mask is not zero; that is the down'd cpu is the | ||
| 308 | * last online cpu in a user set affinity mask. | ||
| 309 | */ | ||
| 310 | if (cpumask_empty(&affinity_new) || | ||
| 311 | !cpumask_subset(&affinity_new, &online_new)) | ||
| 312 | this_count++; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | count = 0; | ||
| 317 | for_each_online_cpu(cpu) { | ||
| 318 | if (cpu == this_cpu) | ||
| 319 | continue; | ||
| 320 | for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS; | ||
| 321 | vector++) { | ||
| 322 | if (per_cpu(vector_irq, cpu)[vector] < 0) | ||
| 323 | count++; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | if (count < this_count) { | ||
| 328 | pr_warn("CPU %d disable failed: CPU has %u vectors assigned and there are only %u available.\n", | ||
| 329 | this_cpu, this_count, count); | ||
| 330 | return -ERANGE; | ||
| 331 | } | ||
| 332 | return 0; | ||
| 333 | } | ||
| 334 | |||
| 265 | /* A cpu has been removed from cpu_online_mask. Reset irq affinities. */ | 335 | /* A cpu has been removed from cpu_online_mask. Reset irq affinities. */ |
| 266 | void fixup_irqs(void) | 336 | void fixup_irqs(void) |
| 267 | { | 337 | { |
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 85dc05a3aa02..391ea529dc26 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c | |||
| @@ -1312,6 +1312,12 @@ void cpu_disable_common(void) | |||
| 1312 | 1312 | ||
| 1313 | int native_cpu_disable(void) | 1313 | int native_cpu_disable(void) |
| 1314 | { | 1314 | { |
| 1315 | int ret; | ||
| 1316 | |||
| 1317 | ret = check_irq_vectors_for_cpu_disable(); | ||
| 1318 | if (ret) | ||
| 1319 | return ret; | ||
| 1320 | |||
| 1315 | clear_local_APIC(); | 1321 | clear_local_APIC(); |
| 1316 | 1322 | ||
| 1317 | cpu_disable_common(); | 1323 | cpu_disable_common(); |
