diff options
Diffstat (limited to 'drivers/xen/events.c')
| -rw-r--r-- | drivers/xen/events.c | 20 | 
1 files changed, 15 insertions, 5 deletions
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index d17aa41a904..aa85881d17b 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -403,11 +403,23 @@ static void unmask_evtchn(int port)  	if (unlikely((cpu != cpu_from_evtchn(port))))  		do_hypercall = 1; -	else +	else { +		/* +		 * Need to clear the mask before checking pending to +		 * avoid a race with an event becoming pending. +		 * +		 * EVTCHNOP_unmask will only trigger an upcall if the +		 * mask bit was set, so if a hypercall is needed +		 * remask the event. +		 */ +		sync_clear_bit(port, BM(&s->evtchn_mask[0]));  		evtchn_pending = sync_test_bit(port, BM(&s->evtchn_pending[0])); -	if (unlikely(evtchn_pending && xen_hvm_domain())) -		do_hypercall = 1; +		if (unlikely(evtchn_pending && xen_hvm_domain())) { +			sync_set_bit(port, BM(&s->evtchn_mask[0])); +			do_hypercall = 1; +		} +	}  	/* Slow path (hypercall) if this is a non-local port or if this is  	 * an hvm domain and an event is pending (hvm domains don't have @@ -418,8 +430,6 @@ static void unmask_evtchn(int port)  	} else {  		struct vcpu_info *vcpu_info = __this_cpu_read(xen_vcpu); -		sync_clear_bit(port, BM(&s->evtchn_mask[0])); -  		/*  		 * The following is basically the equivalent of  		 * 'hw_resend_irq'. Just like a real IO-APIC we 'lose  |