diff options
Diffstat (limited to 'arch/x86/xen/smp.c')
-rw-r--r-- | arch/x86/xen/smp.c | 133 |
1 files changed, 46 insertions, 87 deletions
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 94e69000f982..b3786e749b8e 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c | |||
@@ -36,27 +36,14 @@ | |||
36 | #include "mmu.h" | 36 | #include "mmu.h" |
37 | 37 | ||
38 | static cpumask_t xen_cpu_initialized_map; | 38 | static cpumask_t xen_cpu_initialized_map; |
39 | static DEFINE_PER_CPU(int, resched_irq) = -1; | ||
40 | static DEFINE_PER_CPU(int, callfunc_irq) = -1; | ||
41 | static DEFINE_PER_CPU(int, debug_irq) = -1; | ||
42 | |||
43 | /* | ||
44 | * Structure and data for smp_call_function(). This is designed to minimise | ||
45 | * static memory requirements. It also looks cleaner. | ||
46 | */ | ||
47 | static DEFINE_SPINLOCK(call_lock); | ||
48 | 39 | ||
49 | struct call_data_struct { | 40 | static DEFINE_PER_CPU(int, resched_irq); |
50 | void (*func) (void *info); | 41 | static DEFINE_PER_CPU(int, callfunc_irq); |
51 | void *info; | 42 | static DEFINE_PER_CPU(int, callfuncsingle_irq); |
52 | atomic_t started; | 43 | static DEFINE_PER_CPU(int, debug_irq) = -1; |
53 | atomic_t finished; | ||
54 | int wait; | ||
55 | }; | ||
56 | 44 | ||
57 | static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id); | 45 | static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id); |
58 | 46 | static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id); | |
59 | static struct call_data_struct *call_data; | ||
60 | 47 | ||
61 | /* | 48 | /* |
62 | * Reschedule call back. Nothing to do, | 49 | * Reschedule call back. Nothing to do, |
@@ -122,6 +109,17 @@ static int xen_smp_intr_init(unsigned int cpu) | |||
122 | goto fail; | 109 | goto fail; |
123 | per_cpu(debug_irq, cpu) = rc; | 110 | per_cpu(debug_irq, cpu) = rc; |
124 | 111 | ||
112 | callfunc_name = kasprintf(GFP_KERNEL, "callfuncsingle%d", cpu); | ||
113 | rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_SINGLE_VECTOR, | ||
114 | cpu, | ||
115 | xen_call_function_single_interrupt, | ||
116 | IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, | ||
117 | callfunc_name, | ||
118 | NULL); | ||
119 | if (rc < 0) | ||
120 | goto fail; | ||
121 | per_cpu(callfuncsingle_irq, cpu) = rc; | ||
122 | |||
125 | return 0; | 123 | return 0; |
126 | 124 | ||
127 | fail: | 125 | fail: |
@@ -131,6 +129,9 @@ static int xen_smp_intr_init(unsigned int cpu) | |||
131 | unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); | 129 | unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); |
132 | if (per_cpu(debug_irq, cpu) >= 0) | 130 | if (per_cpu(debug_irq, cpu) >= 0) |
133 | unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL); | 131 | unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL); |
132 | if (per_cpu(callfuncsingle_irq, cpu) >= 0) | ||
133 | unbind_from_irqhandler(per_cpu(callfuncsingle_irq, cpu), NULL); | ||
134 | |||
134 | return rc; | 135 | return rc; |
135 | } | 136 | } |
136 | 137 | ||
@@ -338,7 +339,6 @@ void xen_smp_send_reschedule(int cpu) | |||
338 | xen_send_IPI_one(cpu, XEN_RESCHEDULE_VECTOR); | 339 | xen_send_IPI_one(cpu, XEN_RESCHEDULE_VECTOR); |
339 | } | 340 | } |
340 | 341 | ||
341 | |||
342 | static void xen_send_IPI_mask(cpumask_t mask, enum ipi_vector vector) | 342 | static void xen_send_IPI_mask(cpumask_t mask, enum ipi_vector vector) |
343 | { | 343 | { |
344 | unsigned cpu; | 344 | unsigned cpu; |
@@ -349,83 +349,42 @@ static void xen_send_IPI_mask(cpumask_t mask, enum ipi_vector vector) | |||
349 | xen_send_IPI_one(cpu, vector); | 349 | xen_send_IPI_one(cpu, vector); |
350 | } | 350 | } |
351 | 351 | ||
352 | void xen_smp_send_call_function_ipi(cpumask_t mask) | ||
353 | { | ||
354 | int cpu; | ||
355 | |||
356 | xen_send_IPI_mask(mask, XEN_CALL_FUNCTION_VECTOR); | ||
357 | |||
358 | /* Make sure other vcpus get a chance to run if they need to. */ | ||
359 | for_each_cpu_mask(cpu, mask) { | ||
360 | if (xen_vcpu_stolen(cpu)) { | ||
361 | HYPERVISOR_sched_op(SCHEDOP_yield, 0); | ||
362 | break; | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | |||
367 | void xen_smp_send_call_function_single_ipi(int cpu) | ||
368 | { | ||
369 | xen_send_IPI_mask(cpumask_of_cpu(cpu), XEN_CALL_FUNCTION_SINGLE_VECTOR); | ||
370 | } | ||
371 | |||
352 | static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id) | 372 | static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id) |
353 | { | 373 | { |
354 | void (*func) (void *info) = call_data->func; | ||
355 | void *info = call_data->info; | ||
356 | int wait = call_data->wait; | ||
357 | |||
358 | /* | ||
359 | * Notify initiating CPU that I've grabbed the data and am | ||
360 | * about to execute the function | ||
361 | */ | ||
362 | mb(); | ||
363 | atomic_inc(&call_data->started); | ||
364 | /* | ||
365 | * At this point the info structure may be out of scope unless wait==1 | ||
366 | */ | ||
367 | irq_enter(); | 374 | irq_enter(); |
368 | (*func)(info); | 375 | generic_smp_call_function_interrupt(); |
369 | __get_cpu_var(irq_stat).irq_call_count++; | 376 | __get_cpu_var(irq_stat).irq_call_count++; |
370 | irq_exit(); | 377 | irq_exit(); |
371 | 378 | ||
372 | if (wait) { | ||
373 | mb(); /* commit everything before setting finished */ | ||
374 | atomic_inc(&call_data->finished); | ||
375 | } | ||
376 | |||
377 | return IRQ_HANDLED; | 379 | return IRQ_HANDLED; |
378 | } | 380 | } |
379 | 381 | ||
380 | int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *), | 382 | static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id) |
381 | void *info, int wait) | ||
382 | { | 383 | { |
383 | struct call_data_struct data; | 384 | irq_enter(); |
384 | int cpus, cpu; | 385 | generic_smp_call_function_single_interrupt(); |
385 | bool yield; | 386 | __get_cpu_var(irq_stat).irq_call_count++; |
386 | 387 | irq_exit(); | |
387 | /* Holding any lock stops cpus from going down. */ | ||
388 | spin_lock(&call_lock); | ||
389 | |||
390 | cpu_clear(smp_processor_id(), mask); | ||
391 | |||
392 | cpus = cpus_weight(mask); | ||
393 | if (!cpus) { | ||
394 | spin_unlock(&call_lock); | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | /* Can deadlock when called with interrupts disabled */ | ||
399 | WARN_ON(irqs_disabled()); | ||
400 | |||
401 | data.func = func; | ||
402 | data.info = info; | ||
403 | atomic_set(&data.started, 0); | ||
404 | data.wait = wait; | ||
405 | if (wait) | ||
406 | atomic_set(&data.finished, 0); | ||
407 | |||
408 | call_data = &data; | ||
409 | mb(); /* write everything before IPI */ | ||
410 | |||
411 | /* Send a message to other CPUs and wait for them to respond */ | ||
412 | xen_send_IPI_mask(mask, XEN_CALL_FUNCTION_VECTOR); | ||
413 | |||
414 | /* Make sure other vcpus get a chance to run if they need to. */ | ||
415 | yield = false; | ||
416 | for_each_cpu_mask(cpu, mask) | ||
417 | if (xen_vcpu_stolen(cpu)) | ||
418 | yield = true; | ||
419 | |||
420 | if (yield) | ||
421 | HYPERVISOR_sched_op(SCHEDOP_yield, 0); | ||
422 | |||
423 | /* Wait for response */ | ||
424 | while (atomic_read(&data.started) != cpus || | ||
425 | (wait && atomic_read(&data.finished) != cpus)) | ||
426 | cpu_relax(); | ||
427 | |||
428 | spin_unlock(&call_lock); | ||
429 | 388 | ||
430 | return 0; | 389 | return IRQ_HANDLED; |
431 | } | 390 | } |