diff options
author | Jeremy Fitzhardinge <jeremy@goop.org> | 2008-08-20 17:02:18 -0700 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-08-21 13:52:57 +0200 |
commit | 168d2f464ab9860f0d1e66cf1f9684973222f1c6 (patch) | |
tree | c3a4199cf2aa4c6322b7860c431fd5bfdc4220ef /drivers | |
parent | 7708ad64a24a674f7905aa7a5099a50f055debec (diff) | |
download | linux-168d2f464ab9860f0d1e66cf1f9684973222f1c6.tar.gz linux-168d2f464ab9860f0d1e66cf1f9684973222f1c6.tar.bz2 linux-168d2f464ab9860f0d1e66cf1f9684973222f1c6.zip |
xen: save previous spinlock when blocking
A spinlock can be interrupted while spinning, so make sure we preserve
the previous lock of interest if we're taking a lock from within an
interrupt handler.
We also need to deal with the case where the blocking path gets
interrupted between testing to see if the lock is free and actually
blocking. If we get interrupted there and end up in the state where
the lock is free but the irq isn't pending, then we'll block
indefinitely in the hypervisor. This fix is to make sure that any
nested lock-takers will always leave the irq pending if there's any
chance the outer lock became free.
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Acked-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/xen/events.c | 25 |
1 files changed, 25 insertions, 0 deletions
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index a0837036d898..b6c2b8f16bee 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -164,6 +164,12 @@ static inline void set_evtchn(int port) sync_set_bit(port, &s->evtchn_pending[0]); } +static inline int test_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + return sync_test_bit(port, &s->evtchn_pending[0]); +} + /** * notify_remote_via_irq - send event to remote end of event channel via irq @@ -732,6 +738,25 @@ void xen_clear_irq_pending(int irq) clear_evtchn(evtchn); } +void xen_set_irq_pending(int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + set_evtchn(evtchn); +} + +bool xen_test_irq_pending(int irq) +{ + int evtchn = evtchn_from_irq(irq); + bool ret = false; + + if (VALID_EVTCHN(evtchn)) + ret = test_evtchn(evtchn); + + return ret; +} + /* Poll waiting for an irq to become pending. In the usual case, the irq will be disabled so it won't deliver an interrupt. */ void xen_poll_irq(int irq) |