Skip to content

Commit

Permalink
xen/evtchn: rework per event channel lock
Browse files Browse the repository at this point in the history
Currently the lock for a single event channel needs to be taken with
interrupts off, which causes deadlocks in some cases.

Rework the per event channel lock to be non-blocking for the case of
sending an event and removing the need for disabling interrupts for
taking the lock.

The lock is needed for avoiding races between event channel state
changes (creation, closing, binding) against normal operations (set
pending, [un]masking, priority changes).

Use a rwlock, but with some restrictions:

- Changing the state of an event channel (creation, closing, binding)
  needs to use write_lock(), with ASSERT()ing that the lock is taken as
  writer only when the state of the event channel is either before or
  after the locked region appropriate (either free or unbound).

- Sending an event needs to use read_trylock() mostly, in case of not
  obtaining the lock the operation is omitted. This is needed as
  sending an event can happen with interrupts off (at least in some
  cases).

- Dumping the event channel state for debug purposes is using
  read_trylock(), too, in order to avoid blocking in case the lock is
  taken as writer for a long time.

- All other cases can use read_lock().

Fixes: e045199 ("evtchn: address races with evtchn_reset()")
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Julien Grall <jgrall@amazon.com>
  • Loading branch information
jgross1 authored and jbeulich committed Nov 10, 2020
1 parent 3059178 commit 5f2df45
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 75 deletions.
6 changes: 2 additions & 4 deletions xen/arch/x86/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -2495,14 +2495,12 @@ static void dump_irqs(unsigned char key)
pirq = domain_irq_to_pirq(d, irq);
info = pirq_info(d, pirq);
evtchn = evtchn_from_port(d, info->evtchn);
local_irq_disable();
if ( spin_trylock(&evtchn->lock) )
if ( evtchn_read_trylock(evtchn) )
{
pending = evtchn_is_pending(d, evtchn);
masked = evtchn_is_masked(d, evtchn);
spin_unlock(&evtchn->lock);
evtchn_read_unlock(evtchn);
}
local_irq_enable();
printk("d%d:%3d(%c%c%c)%c",
d->domain_id, pirq, "-P?"[pending],
"-M?"[masked], info->masked ? 'M' : '-',
Expand Down
9 changes: 5 additions & 4 deletions xen/arch/x86/pv/shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -660,11 +660,12 @@ void pv_shim_inject_evtchn(unsigned int port)
if ( port_is_valid(guest, port) )
{
struct evtchn *chn = evtchn_from_port(guest, port);
unsigned long flags;

spin_lock_irqsave(&chn->lock, flags);
evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn);
spin_unlock_irqrestore(&chn->lock, flags);
if ( evtchn_read_trylock(chn) )
{
evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn);
evtchn_read_unlock(chn);
}
}
}

Expand Down

0 comments on commit 5f2df45

Please sign in to comment.