mirror of
https://github.com/qemu/qemu.git
synced 2024-12-13 22:43:32 +08:00
d25f89c9e9
Derived from kvm-tool patch http://thread.gmane.org/gmane.comp.emulators.kvm.devel/74309 Ingo Molnar pointed out that sending the timer signal to the whole process, just blocking it everywhere, is suboptimal with an increasing number of threads. QEMU is also using this pattern so far. Linux provides a (non-portable) way to restrict the signal to a single thread: We can use SIGEV_THREAD_ID unless we are forced to emulate signalfd via an additional thread. That case could theoretically be optimized as well, but it doesn't look worth bothering. Reviewed-by: Richard Henderson <rth@twiddle.net> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
129 lines
2.5 KiB
C
129 lines
2.5 KiB
C
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#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);
|
|
pthread_sigmask(SIG_BLOCK, &all, NULL);
|
|
|
|
while (1) {
|
|
int sig;
|
|
int err;
|
|
|
|
err = sigwait(&info->mask, &sig);
|
|
if (err != 0) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
struct qemu_signalfd_siginfo buffer;
|
|
size_t offset = 0;
|
|
|
|
memset(&buffer, 0, sizeof(buffer));
|
|
buffer.ssi_signo = sig;
|
|
|
|
while (offset < sizeof(buffer)) {
|
|
ssize_t len;
|
|
|
|
len = write(info->fd, (char *)&buffer + offset,
|
|
sizeof(buffer) - offset);
|
|
if (len == -1 && errno == EINTR)
|
|
continue;
|
|
|
|
if (len <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
offset += len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
bool qemu_signalfd_available(void)
|
|
{
|
|
#ifdef CONFIG_SIGNALFD
|
|
errno = 0;
|
|
syscall(SYS_signalfd, -1, NULL, _NSIG / 8);
|
|
return errno != ENOSYS;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|