2010-10-11 20:31:15 +02:00
|
|
|
/*
|
|
|
|
* signalfd/eventfd compatibility
|
|
|
|
*
|
|
|
|
* Copyright IBM, Corp. 2008
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
|
|
|
* the COPYING file in the top-level directory.
|
|
|
|
*
|
2012-01-13 17:44:23 +01:00
|
|
|
* Contributions after 2012-01-13 are licensed under the terms of the
|
|
|
|
* GNU GPL, version 2 or (at your option) any later version.
|
2010-10-11 20:31:15 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "qemu-common.h"
|
|
|
|
#include "compatfd.h"
|
|
|
|
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
struct sigfd_compat_info
|
|
|
|
{
|
|
|
|
sigset_t mask;
|
|
|
|
int fd;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void *sigwait_compat(void *opaque)
|
|
|
|
{
|
|
|
|
struct sigfd_compat_info *info = opaque;
|
|
|
|
sigset_t all;
|
|
|
|
|
|
|
|
sigfillset(&all);
|
sigfd: use pthread_sigmask
Qemu uses signalfd to figure out, if a signal occured without the need
to actually receive the signal. Instead, it can read from the fd to receive
its news.
Now, we obviously don't always have signalfd around. Especially not on
non-Linux systems. So what we do there is that we create a new thread,
block that thread on all signals and simply call sigwait to wait for a
signal we're interested in to occur.
This all sounds great, but what we're really doing is:
sigset_t all;
sigfillset(&all);
sigprocmask(SIG_BLOCK, &all, NULL);
which - on Darwin - blocks all signals on the current _process_, not only
on the current thread. To block signals on the thread, we can use
pthread_sigmask().
This patch does that, assuming that my above analysis is correct, and thus
renders Qemu useable on Darwin again.
Reported-by: Andreas Färber <andreas.faerber@web.de>
Acked-by: Paolo Bonizni <pbonzini@redhat.com>
CC: Jan Kiszka <jan.kiszka@siemens.com>
CC: Anthony Liguori <anthony@codemonkey.ws>
Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
2011-06-09 00:55:37 +02:00
|
|
|
pthread_sigmask(SIG_BLOCK, &all, NULL);
|
2010-10-11 20:31:15 +02:00
|
|
|
|
2011-02-18 14:17:16 +01:00
|
|
|
while (1) {
|
|
|
|
int sig;
|
|
|
|
int err;
|
2010-10-11 20:31:15 +02:00
|
|
|
|
2011-02-18 14:17:16 +01:00
|
|
|
err = sigwait(&info->mask, &sig);
|
|
|
|
if (err != 0) {
|
|
|
|
if (errno == EINTR) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct qemu_signalfd_siginfo buffer;
|
2010-10-11 20:31:15 +02:00
|
|
|
size_t offset = 0;
|
|
|
|
|
2011-02-18 14:17:16 +01:00
|
|
|
memset(&buffer, 0, sizeof(buffer));
|
|
|
|
buffer.ssi_signo = sig;
|
|
|
|
|
2010-10-11 20:31:15 +02:00
|
|
|
while (offset < sizeof(buffer)) {
|
|
|
|
ssize_t len;
|
|
|
|
|
2011-02-18 14:17:16 +01:00
|
|
|
len = write(info->fd, (char *)&buffer + offset,
|
2010-10-11 20:31:15 +02:00
|
|
|
sizeof(buffer) - offset);
|
|
|
|
if (len == -1 && errno == EINTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (len <= 0) {
|
2011-02-18 14:17:16 +01:00
|
|
|
return NULL;
|
2010-10-11 20:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
offset += len;
|
|
|
|
}
|
|
|
|
}
|
2011-02-18 14:17:16 +01:00
|
|
|
}
|
2010-10-11 20:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qemu_signalfd_compat(const sigset_t *mask)
|
|
|
|
{
|
|
|
|
pthread_attr_t attr;
|
|
|
|
pthread_t tid;
|
|
|
|
struct sigfd_compat_info *info;
|
|
|
|
int fds[2];
|
|
|
|
|
|
|
|
info = malloc(sizeof(*info));
|
|
|
|
if (info == NULL) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pipe(fds) == -1) {
|
|
|
|
free(info);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_set_cloexec(fds[0]);
|
|
|
|
qemu_set_cloexec(fds[1]);
|
|
|
|
|
|
|
|
memcpy(&info->mask, mask, sizeof(*mask));
|
|
|
|
info->fd = fds[1];
|
|
|
|
|
|
|
|
pthread_attr_init(&attr);
|
|
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
|
|
|
|
pthread_create(&tid, &attr, sigwait_compat, info);
|
|
|
|
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
|
|
|
|
return fds[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemu_signalfd(const sigset_t *mask)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_SIGNALFD)
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);
|
|
|
|
if (ret != -1) {
|
|
|
|
qemu_set_cloexec(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return qemu_signalfd_compat(mask);
|
|
|
|
}
|
2011-06-17 11:25:49 +02:00
|
|
|
|
|
|
|
bool qemu_signalfd_available(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_SIGNALFD
|
2011-10-13 19:45:37 +02:00
|
|
|
sigset_t mask;
|
|
|
|
int fd;
|
|
|
|
bool ok;
|
|
|
|
sigemptyset(&mask);
|
2011-06-17 11:25:49 +02:00
|
|
|
errno = 0;
|
2011-10-13 19:45:37 +02:00
|
|
|
fd = syscall(SYS_signalfd, -1, &mask, _NSIG / 8);
|
|
|
|
ok = (errno != ENOSYS);
|
|
|
|
if (fd >= 0) {
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
return ok;
|
2011-06-17 11:25:49 +02:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|