Subject: Fix for spllower()
To: None <port-xen@netbsd.org>
From: Mathieu Ropert <mro@adviseo.fr>
List: port-xen
Date: 05/03/2006 11:44:52
Hi,

I've noticed a potential bug in the spllower() implementation for Xen:
the original code saves flags through read_psl(), disable interrupts 
('cli' on x86) and then even run Xspllower() (if pending interrupts) or 
set new level and restore flags (with write_psl()).
On x86, saving and restoring EFLAGS includes restoring the 'IF' flag (if 
it was previously set) cleared by disable_intr(), thus reenabling 
interrupts.
As the 'IF' flag isn't virtualized on Xen, we may end spllower() without 
reenabling interrupts.

A quick (and a bit dirty) fix could be:

mask = HYPERVISOR_shared_info->vcpu_info[cpu_number()].evtchn_upcall_mask;
psl = read_psl();
disable_intr();
if (ci->ci_ipending & imask) {
	Xspllower(nlevel);
	/* Xspllower does enable_intr() */
} else {
	ci->ci_ilevel = nlevel;
	write_psl(psl);
	HYPERVISOR_shared_info->vcpu_info[cpu_number()].evtchn_upcall_mask = mask;
}

Two notes on this:
1/ reading mask and disabling interrupts not atomically shouldn't be an 
issue as any interrupt that may occur between should not change the 
upcall_mask value.
2/ anyway, i'd prefer using a pair of new wrappers like 
xen_block_events() and xen_unblock_events() which would be something like

#define xen_block_events()   \
    atomic_xchg8(                     \
    &HYPERVISOR_shared_info->vcpu_info[cpu_number()].evtchn_upcall_mask, 1)

#define xen_unblock_events()   \
    atomic_xchg8(                     \
    &HYPERVISOR_shared_info->vcpu_info[cpu_number()].evtchn_upcall_mask, 0)

i'm sure whether the save/restore flags trick is used anywhere (i hope 
this is restricted to arch-dependent code), but that may be worth checking.
By the way, has anyone experienced troubles that may be related to this bug?

Cheers,
Mathieu