xen: fix low-level interrupt handling with osv

The Xen code registers a function that calls semaphore::signal as
an interrupt handler, however that function is not smp safe and may crash,
and in events it generates are likely to be ignored, since they are just
appended to the reactor queue without any real wakeup to the reactor thread.

Switch to using an eventfd.  That's still unsafe, but a little better, since
its signalling is smp safe, and will cause the reactor thread to wake up
in case it was asleep.

With this, we are able to receive multiple packets.
This commit is contained in:
Avi Kivity
2014-11-03 15:15:22 +02:00
parent 6e193b2874
commit a9a87c8dbd

View File

@@ -77,6 +77,7 @@ void userspace_evtchn::umask(int *port, unsigned count)
#ifdef HAVE_OSV
class kernel_evtchn: public evtchn {
static void make_ready(void *arg);
static void process_interrupts(readable_eventfd* fd, semaphore* sem);
public:
kernel_evtchn(unsigned otherend) : evtchn(otherend) {}
virtual int bind() override;
@@ -85,8 +86,9 @@ public:
void kernel_evtchn::make_ready(void *arg) {
printf("Got an interrupt!\n");
semaphore *sem = reinterpret_cast<semaphore *>(arg);
sem->signal();
int fd = reinterpret_cast<uintptr_t>(arg);
uint64_t one = 1;
::write(fd, &one, sizeof(one));
}
int kernel_evtchn::bind() {
@@ -95,12 +97,25 @@ int kernel_evtchn::bind() {
int port;
irq = bind_listening_port_to_irq(_otherend, &port);
intr_add_handler("", irq, NULL, make_ready, init_port(port), 0, 0);
// We need to convert extra-seastar events to intra-seastar events
// (in this case, the semaphore interface of evtchn). The only
// way to do that currently is via eventfd.
semaphore* sem = init_port(port);
auto fd = new readable_eventfd;
auto wfd = fd->get_write_fd();
intr_add_handler("", irq, NULL, make_ready, reinterpret_cast<void*>(uintptr_t(wfd)), 0, 0);
unmask_evtchn(port);
process_interrupts(fd, sem);
return evtchn_from_irq(irq);
}
void kernel_evtchn::process_interrupts(readable_eventfd* fd, semaphore* sem) {
fd->wait().then([fd, sem] (size_t ignore) {
sem->signal();
process_interrupts(fd, sem);
});
}
void kernel_evtchn::notify(int port) {
notify_remote_via_evtchn(port);
}