Skip to content

Commit

Permalink
xen/events/fifo: reset control block and local HEADs on resume
Browse files Browse the repository at this point in the history
When using the FIFO-based event channel ABI, if the control block or
the local HEADs are not reset after resuming the guest may see stale
HEAD values and will fail to traverse the FIFO correctly.

This may prevent one or more VCPUs from receiving any events following
a resume.

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: stable@vger.kernel.org
  • Loading branch information
David Vrabel committed Jul 31, 2014
1 parent a91c777 commit c12784c
Showing 1 changed file with 28 additions and 20 deletions.
48 changes: 28 additions & 20 deletions drivers/xen/events/events_fifo.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,25 @@ static unsigned evtchn_fifo_nr_channels(void)
return event_array_pages * EVENT_WORDS_PER_PAGE;
}

static int init_control_block(int cpu,
struct evtchn_fifo_control_block *control_block)
{
struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
struct evtchn_init_control init_control;
unsigned int i;

/* Reset the control block and the local HEADs. */
clear_page(control_block);
for (i = 0; i < EVTCHN_FIFO_MAX_QUEUES; i++)
q->head[i] = 0;

init_control.control_gfn = virt_to_mfn(control_block);
init_control.offset = 0;
init_control.vcpu = cpu;

return HYPERVISOR_event_channel_op(EVTCHNOP_init_control, &init_control);
}

static void free_unused_array_pages(void)
{
unsigned i;
Expand Down Expand Up @@ -324,7 +343,6 @@ static void evtchn_fifo_resume(void)

for_each_possible_cpu(cpu) {
void *control_block = per_cpu(cpu_control_block, cpu);
struct evtchn_init_control init_control;
int ret;

if (!control_block)
Expand All @@ -341,12 +359,7 @@ static void evtchn_fifo_resume(void)
continue;
}

init_control.control_gfn = virt_to_mfn(control_block);
init_control.offset = 0;
init_control.vcpu = cpu;

ret = HYPERVISOR_event_channel_op(EVTCHNOP_init_control,
&init_control);
ret = init_control_block(cpu, control_block);
if (ret < 0)
BUG();
}
Expand Down Expand Up @@ -374,30 +387,25 @@ static const struct evtchn_ops evtchn_ops_fifo = {
.resume = evtchn_fifo_resume,
};

static int evtchn_fifo_init_control_block(unsigned cpu)
static int evtchn_fifo_alloc_control_block(unsigned cpu)
{
struct page *control_block = NULL;
struct evtchn_init_control init_control;
void *control_block = NULL;
int ret = -ENOMEM;

control_block = alloc_page(GFP_KERNEL|__GFP_ZERO);
control_block = (void *)__get_free_page(GFP_KERNEL);
if (control_block == NULL)
goto error;

init_control.control_gfn = virt_to_mfn(page_address(control_block));
init_control.offset = 0;
init_control.vcpu = cpu;

ret = HYPERVISOR_event_channel_op(EVTCHNOP_init_control, &init_control);
ret = init_control_block(cpu, control_block);
if (ret < 0)
goto error;

per_cpu(cpu_control_block, cpu) = page_address(control_block);
per_cpu(cpu_control_block, cpu) = control_block;

return 0;

error:
__free_page(control_block);
free_page((unsigned long)control_block);
return ret;
}

Expand All @@ -411,7 +419,7 @@ static int evtchn_fifo_cpu_notification(struct notifier_block *self,
switch (action) {
case CPU_UP_PREPARE:
if (!per_cpu(cpu_control_block, cpu))
ret = evtchn_fifo_init_control_block(cpu);
ret = evtchn_fifo_alloc_control_block(cpu);
break;
default:
break;
Expand All @@ -428,7 +436,7 @@ int __init xen_evtchn_fifo_init(void)
int cpu = get_cpu();
int ret;

ret = evtchn_fifo_init_control_block(cpu);
ret = evtchn_fifo_alloc_control_block(cpu);
if (ret < 0)
goto out;

Expand Down

0 comments on commit c12784c

Please sign in to comment.