Skip to content

Commit fcdf31a

Browse files
rosslagerwallDavid Vrabel
authored andcommitted
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 <[email protected]> Cc: <[email protected]> Signed-off-by: David Vrabel <[email protected]>
1 parent 929423f commit fcdf31a

File tree

3 files changed

+53
-9
lines changed

3 files changed

+53
-9
lines changed

drivers/xen/events/events_base.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,12 @@ static void xen_free_irq(unsigned irq)
452452
irq_free_desc(irq);
453453
}
454454

455-
static void xen_evtchn_close(unsigned int port)
455+
static void xen_evtchn_close(unsigned int port, unsigned int cpu)
456456
{
457457
struct evtchn_close close;
458458

459+
xen_evtchn_op_close(port, cpu);
460+
459461
close.port = port;
460462
if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0)
461463
BUG();
@@ -544,7 +546,7 @@ static unsigned int __startup_pirq(unsigned int irq)
544546

545547
err:
546548
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);
548550
return 0;
549551
}
550552

@@ -565,7 +567,7 @@ static void shutdown_pirq(struct irq_data *data)
565567
return;
566568

567569
mask_evtchn(evtchn);
568-
xen_evtchn_close(evtchn);
570+
xen_evtchn_close(evtchn, cpu_from_evtchn(evtchn));
569571
xen_irq_info_cleanup(info);
570572
}
571573

@@ -609,7 +611,7 @@ static void __unbind_from_irq(unsigned int irq)
609611
if (VALID_EVTCHN(evtchn)) {
610612
unsigned int cpu = cpu_from_irq(irq);
611613

612-
xen_evtchn_close(evtchn);
614+
xen_evtchn_close(evtchn, cpu);
613615

614616
switch (type_from_irq(irq)) {
615617
case IRQT_VIRQ:

drivers/xen/events/events_fifo.c

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,12 @@ static void evtchn_fifo_unmask(unsigned port)
255255
}
256256
}
257257

258+
static 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+
258264
static uint32_t clear_linked(volatile event_word_t *word)
259265
{
260266
event_word_t new, old, w;
@@ -281,7 +287,8 @@ static void handle_irq_for_port(unsigned port)
281287

282288
static void consume_one_event(unsigned cpu,
283289
struct evtchn_fifo_control_block *control_block,
284-
unsigned priority, unsigned long *ready)
290+
unsigned priority, unsigned long *ready,
291+
bool drop)
285292
{
286293
struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
287294
uint32_t head;
@@ -313,13 +320,15 @@ static void consume_one_event(unsigned cpu,
313320
if (head == 0)
314321
clear_bit(priority, ready);
315322

316-
if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port))
317-
handle_irq_for_port(port);
323+
if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) {
324+
if (likely(!drop))
325+
handle_irq_for_port(port);
326+
}
318327

319328
q->head[priority] = head;
320329
}
321330

322-
static void evtchn_fifo_handle_events(unsigned cpu)
331+
static void __evtchn_fifo_handle_events(unsigned cpu, bool drop)
323332
{
324333
struct evtchn_fifo_control_block *control_block;
325334
unsigned long ready;
@@ -331,11 +340,16 @@ static void evtchn_fifo_handle_events(unsigned cpu)
331340

332341
while (ready) {
333342
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);
335344
ready |= xchg(&control_block->ready, 0);
336345
}
337346
}
338347

348+
static void evtchn_fifo_handle_events(unsigned cpu)
349+
{
350+
__evtchn_fifo_handle_events(cpu, false);
351+
}
352+
339353
static void evtchn_fifo_resume(void)
340354
{
341355
unsigned cpu;
@@ -371,6 +385,26 @@ static void evtchn_fifo_resume(void)
371385
event_array_pages = 0;
372386
}
373387

388+
static 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+
404+
out:
405+
put_online_cpus();
406+
}
407+
374408
static const struct evtchn_ops evtchn_ops_fifo = {
375409
.max_channels = evtchn_fifo_max_channels,
376410
.nr_channels = evtchn_fifo_nr_channels,
@@ -384,6 +418,7 @@ static const struct evtchn_ops evtchn_ops_fifo = {
384418
.unmask = evtchn_fifo_unmask,
385419
.handle_events = evtchn_fifo_handle_events,
386420
.resume = evtchn_fifo_resume,
421+
.close = evtchn_fifo_close,
387422
};
388423

389424
static int evtchn_fifo_alloc_control_block(unsigned cpu)

drivers/xen/events/events_internal.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct evtchn_ops {
6868
bool (*test_and_set_mask)(unsigned port);
6969
void (*mask)(unsigned port);
7070
void (*unmask)(unsigned port);
71+
void (*close)(unsigned port, unsigned cpu);
7172

7273
void (*handle_events)(unsigned cpu);
7374
void (*resume)(void);
@@ -145,6 +146,12 @@ static inline void xen_evtchn_resume(void)
145146
evtchn_ops->resume();
146147
}
147148

149+
static 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+
148155
void xen_evtchn_2l_init(void);
149156
int xen_evtchn_fifo_init(void);
150157

0 commit comments

Comments
 (0)