aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/irq.h1
-rw-r--r--arch/x86/kernel/irq.c70
-rw-r--r--arch/x86/kernel/smpboot.c6
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>
28extern int check_irq_vectors_for_cpu_disable(void);
28extern void fixup_irqs(void); 29extern void fixup_irqs(void);
29extern void irq_force_complete_move(int); 30extern 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)
262EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq); 262EXPORT_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 */
270int 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. */
266void fixup_irqs(void) 336void 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
1313int native_cpu_disable(void) 1313int 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();