aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen
diff options
context:
space:
mode:
authorRoss Lagerwall <ross.lagerwall@citrix.com>2015-07-31 09:30:42 -0400
committerDavid Vrabel <david.vrabel@citrix.com>2015-08-04 10:41:59 -0400
commitfcdf31a7c162de0c93a2bee51df4688ab0a348f8 (patch)
treea93d841ce0104c8e19e6e37253df8512e0113a6e /drivers/xen
parent929423fa83e5b75e94101b280738b9a5a376a0e1 (diff)
xen/events/fifo: Handle linked events when closing a port
An event channel bound to a CPU that was offlined may still be linked on that CPU's queue. If this event channel is closed and reused, subsequent events will be lost because the event channel is never unlinked and thus cannot be linked onto the correct queue. When a channel is closed and the event is still linked into a queue, ensure that it is unlinked before completing. If the CPU to which the event channel bound is online, spin until the event is handled by that CPU. If that CPU is offline, it can't handle the event, so clear the event queue during the close, dropping the events. This fixes the missing interrupts (and subsequent disk stalls etc.) when offlining a CPU. Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com> Cc: <stable@vger.kernel.org> Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r--drivers/xen/events/events_base.c10
-rw-r--r--drivers/xen/events/events_fifo.c45
-rw-r--r--drivers/xen/events/events_internal.h7
3 files changed, 53 insertions, 9 deletions
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 96093ae369a5..1495eccb1617 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -452,10 +452,12 @@ static void xen_free_irq(unsigned irq)
452 irq_free_desc(irq); 452 irq_free_desc(irq);
453} 453}
454 454
455static void xen_evtchn_close(unsigned int port) 455static void xen_evtchn_close(unsigned int port, unsigned int cpu)
456{ 456{
457 struct evtchn_close close; 457 struct evtchn_close close;
458 458
459 xen_evtchn_op_close(port, cpu);
460
459 close.port = port; 461 close.port = port;
460 if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0) 462 if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0)
461 BUG(); 463 BUG();
@@ -544,7 +546,7 @@ out:
544 546
545err: 547err:
546 pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc); 548 pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc);
547 xen_evtchn_close(evtchn); 549 xen_evtchn_close(evtchn, NR_CPUS);
548 return 0; 550 return 0;
549} 551}
550 552
@@ -565,7 +567,7 @@ static void shutdown_pirq(struct irq_data *data)
565 return; 567 return;
566 568
567 mask_evtchn(evtchn); 569 mask_evtchn(evtchn);
568 xen_evtchn_close(evtchn); 570 xen_evtchn_close(evtchn, cpu_from_evtchn(evtchn));
569 xen_irq_info_cleanup(info); 571 xen_irq_info_cleanup(info);
570} 572}
571 573
@@ -609,7 +611,7 @@ static void __unbind_from_irq(unsigned int irq)
609 if (VALID_EVTCHN(evtchn)) { 611 if (VALID_EVTCHN(evtchn)) {
610 unsigned int cpu = cpu_from_irq(irq); 612 unsigned int cpu = cpu_from_irq(irq);
611 613
612 xen_evtchn_close(evtchn); 614 xen_evtchn_close(evtchn, cpu);
613 615
614 switch (type_from_irq(irq)) { 616 switch (type_from_irq(irq)) {
615 case IRQT_VIRQ: 617 case IRQT_VIRQ:
diff --git a/drivers/xen/events/events_fifo.c b/drivers/xen/events/events_fifo.c
index ed673e1acd61..6df8aac966b9 100644
--- a/drivers/xen/events/events_fifo.c
+++ b/drivers/xen/events/events_fifo.c
@@ -255,6 +255,12 @@ static void evtchn_fifo_unmask(unsigned port)
255 } 255 }
256} 256}
257 257
258static bool evtchn_fifo_is_linked(unsigned port)
259{
260 event_word_t *word = event_word_from_port(port);
261 return sync_test_bit(EVTCHN_FIFO_BIT(LINKED, word), BM(word));
262}
263
258static uint32_t clear_linked(volatile event_word_t *word) 264static uint32_t clear_linked(volatile event_word_t *word)
259{ 265{
260 event_word_t new, old, w; 266 event_word_t new, old, w;
@@ -281,7 +287,8 @@ static void handle_irq_for_port(unsigned port)
281 287
282static void consume_one_event(unsigned cpu, 288static void consume_one_event(unsigned cpu,
283 struct evtchn_fifo_control_block *control_block, 289 struct evtchn_fifo_control_block *control_block,
284 unsigned priority, unsigned long *ready) 290 unsigned priority, unsigned long *ready,
291 bool drop)
285{ 292{
286 struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu); 293 struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
287 uint32_t head; 294 uint32_t head;
@@ -313,13 +320,15 @@ static void consume_one_event(unsigned cpu,
313 if (head == 0) 320 if (head == 0)
314 clear_bit(priority, ready); 321 clear_bit(priority, ready);
315 322
316 if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) 323 if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) {
317 handle_irq_for_port(port); 324 if (likely(!drop))
325 handle_irq_for_port(port);
326 }
318 327
319 q->head[priority] = head; 328 q->head[priority] = head;
320} 329}
321 330
322static void evtchn_fifo_handle_events(unsigned cpu) 331static void __evtchn_fifo_handle_events(unsigned cpu, bool drop)
323{ 332{
324 struct evtchn_fifo_control_block *control_block; 333 struct evtchn_fifo_control_block *control_block;
325 unsigned long ready; 334 unsigned long ready;
@@ -331,11 +340,16 @@ static void evtchn_fifo_handle_events(unsigned cpu)
331 340
332 while (ready) { 341 while (ready) {
333 q = find_first_bit(&ready, EVTCHN_FIFO_MAX_QUEUES); 342 q = find_first_bit(&ready, EVTCHN_FIFO_MAX_QUEUES);
334 consume_one_event(cpu, control_block, q, &ready); 343 consume_one_event(cpu, control_block, q, &ready, drop);
335 ready |= xchg(&control_block->ready, 0); 344 ready |= xchg(&control_block->ready, 0);
336 } 345 }
337} 346}
338 347
348static void evtchn_fifo_handle_events(unsigned cpu)
349{
350 __evtchn_fifo_handle_events(cpu, false);
351}
352
339static void evtchn_fifo_resume(void) 353static void evtchn_fifo_resume(void)
340{ 354{
341 unsigned cpu; 355 unsigned cpu;
@@ -371,6 +385,26 @@ static void evtchn_fifo_resume(void)
371 event_array_pages = 0; 385 event_array_pages = 0;
372} 386}
373 387
388static void evtchn_fifo_close(unsigned port, unsigned int cpu)
389{
390 if (cpu == NR_CPUS)
391 return;
392
393 get_online_cpus();
394 if (cpu_online(cpu)) {
395 if (WARN_ON(irqs_disabled()))
396 goto out;
397
398 while (evtchn_fifo_is_linked(port))
399 cpu_relax();
400 } else {
401 __evtchn_fifo_handle_events(cpu, true);
402 }
403
404out:
405 put_online_cpus();
406}
407
374static const struct evtchn_ops evtchn_ops_fifo = { 408static const struct evtchn_ops evtchn_ops_fifo = {
375 .max_channels = evtchn_fifo_max_channels, 409 .max_channels = evtchn_fifo_max_channels,
376 .nr_channels = evtchn_fifo_nr_channels, 410 .nr_channels = evtchn_fifo_nr_channels,
@@ -384,6 +418,7 @@ static const struct evtchn_ops evtchn_ops_fifo = {
384 .unmask = evtchn_fifo_unmask, 418 .unmask = evtchn_fifo_unmask,
385 .handle_events = evtchn_fifo_handle_events, 419 .handle_events = evtchn_fifo_handle_events,
386 .resume = evtchn_fifo_resume, 420 .resume = evtchn_fifo_resume,
421 .close = evtchn_fifo_close,
387}; 422};
388 423
389static int evtchn_fifo_alloc_control_block(unsigned cpu) 424static int evtchn_fifo_alloc_control_block(unsigned cpu)
diff --git a/drivers/xen/events/events_internal.h b/drivers/xen/events/events_internal.h
index 50c2050a1e32..d18e12315ec0 100644
--- a/drivers/xen/events/events_internal.h
+++ b/drivers/xen/events/events_internal.h
@@ -68,6 +68,7 @@ struct evtchn_ops {
68 bool (*test_and_set_mask)(unsigned port); 68 bool (*test_and_set_mask)(unsigned port);
69 void (*mask)(unsigned port); 69 void (*mask)(unsigned port);
70 void (*unmask)(unsigned port); 70 void (*unmask)(unsigned port);
71 void (*close)(unsigned port, unsigned cpu);
71 72
72 void (*handle_events)(unsigned cpu); 73 void (*handle_events)(unsigned cpu);
73 void (*resume)(void); 74 void (*resume)(void);
@@ -145,6 +146,12 @@ static inline void xen_evtchn_resume(void)
145 evtchn_ops->resume(); 146 evtchn_ops->resume();
146} 147}
147 148
149static inline void xen_evtchn_op_close(unsigned port, unsigned cpu)
150{
151 if (evtchn_ops->close)
152 return evtchn_ops->close(port, cpu);
153}
154
148void xen_evtchn_2l_init(void); 155void xen_evtchn_2l_init(void);
149int xen_evtchn_fifo_init(void); 156int xen_evtchn_fifo_init(void);
150 157