X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=main-loop.c;h=6a7f8d30bd2a87380b928bc084e523bd18d4fb35;hb=d3d74d6fe095e2e49d030e0c163cecfb9c20f1d4;hp=eb80ff369f9ef9e327a2170201086702ba9b4db2;hpb=5f3aa1ff4781f39e05b9892d58319a09fedc8918;p=mirror_qemu.git diff --git a/main-loop.c b/main-loop.c index eb80ff369f..6a7f8d30bd 100644 --- a/main-loop.c +++ b/main-loop.c @@ -22,9 +22,13 @@ * THE SOFTWARE. */ -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" #include "qemu/timer.h" -#include "slirp/slirp.h" +#include "qemu/sockets.h" // struct in_addr needed for libslirp.h +#include "sysemu/qtest.h" +#include "slirp/libslirp.h" #include "qemu/main-loop.h" #include "block/aio.h" @@ -82,6 +86,11 @@ static int qemu_signal_init(void) sigaddset(&set, SIGIO); sigaddset(&set, SIGALRM); sigaddset(&set, SIGBUS); + /* SIGINT cannot be handled via signalfd, so that ^C can be used + * to interrupt QEMU when it is being run under gdb. SIGHUP and + * SIGTERM are also handled asynchronously, even though it is not + * strictly necessary, because they use the same handler as SIGINT. + */ pthread_sigmask(SIG_BLOCK, &set, NULL); sigdelset(&set, SIG_IPI); @@ -93,8 +102,7 @@ static int qemu_signal_init(void) fcntl_setfl(sigfd, O_NONBLOCK); - qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL, - (void *)(intptr_t)sigfd); + qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); return 0; } @@ -108,6 +116,14 @@ static int qemu_signal_init(void) #endif static AioContext *qemu_aio_context; +static QEMUBH *qemu_notify_bh; + +static void notify_event_cb(void *opaque) +{ + /* No need to do anything; this bottom half is only used to + * kick the kernel out of ppoll/poll/WaitForMultipleObjects. + */ +} AioContext *qemu_get_aio_context(void) { @@ -119,32 +135,37 @@ void qemu_notify_event(void) if (!qemu_aio_context) { return; } - aio_notify(qemu_aio_context); + qemu_bh_schedule(qemu_notify_bh); } static GArray *gpollfds; -int qemu_init_main_loop(void) +int qemu_init_main_loop(Error **errp) { int ret; GSource *src; + Error *local_error = NULL; init_clocks(); - if (init_timer_alarm() < 0) { - fprintf(stderr, "could not initialize alarm timer\n"); - exit(1); - } ret = qemu_signal_init(); if (ret) { return ret; } + qemu_aio_context = aio_context_new(&local_error); + if (!qemu_aio_context) { + error_propagate(errp, local_error); + return -EMFILE; + } + qemu_notify_bh = qemu_bh_new(notify_event_cb, NULL); gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); - qemu_aio_context = aio_context_new(); src = aio_get_g_source(qemu_aio_context); g_source_attach(src, NULL); g_source_unref(src); + src = iohandler_get_g_source(); + g_source_attach(src, NULL); + g_source_unref(src); return 0; } @@ -154,10 +175,11 @@ static int max_priority; static int glib_pollfds_idx; static int glib_n_poll_fds; -static void glib_pollfds_fill(uint32_t *cur_timeout) +static void glib_pollfds_fill(int64_t *cur_timeout) { GMainContext *context = g_main_context_default(); int timeout = 0; + int64_t timeout_ns; int n; g_main_context_prepare(context, &max_priority); @@ -173,9 +195,13 @@ static void glib_pollfds_fill(uint32_t *cur_timeout) glib_n_poll_fds); } while (n != glib_n_poll_fds); - if (timeout >= 0 && timeout < *cur_timeout) { - *cur_timeout = timeout; + if (timeout < 0) { + timeout_ns = -1; + } else { + timeout_ns = (int64_t)timeout * (int64_t)SCALE_MS; } + + *cur_timeout = qemu_soonest_timeout(timeout_ns, *cur_timeout); } static void glib_pollfds_poll(void) @@ -188,19 +214,44 @@ static void glib_pollfds_poll(void) } } -static int os_host_main_loop_wait(uint32_t timeout) +#define MAX_MAIN_LOOP_SPIN (1000) + +static int os_host_main_loop_wait(int64_t timeout) { int ret; + static int spin_counter; glib_pollfds_fill(&timeout); - if (timeout > 0) { + /* If the I/O thread is very busy or we are incorrectly busy waiting in + * the I/O thread, this can lead to starvation of the BQL such that the + * VCPU threads never run. To make sure we can detect the later case, + * print a message to the screen. If we run into this condition, create + * a fake timeout in order to give the VCPU threads a chance to run. + */ + if (!timeout && (spin_counter > MAX_MAIN_LOOP_SPIN)) { + static bool notified; + + if (!notified && !qtest_driver()) { + fprintf(stderr, + "main-loop: WARNING: I/O thread spun for %d iterations\n", + MAX_MAIN_LOOP_SPIN); + notified = true; + } + + timeout = SCALE_MS; + } + + if (timeout) { + spin_counter = 0; qemu_mutex_unlock_iothread(); + } else { + spin_counter++; } - ret = g_poll((GPollFD *)gpollfds->data, gpollfds->len, timeout); + ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); - if (timeout > 0) { + if (timeout) { qemu_mutex_lock_iothread(); } @@ -308,11 +359,11 @@ static int pollfds_fill(GArray *pollfds, fd_set *rfds, fd_set *wfds, GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); int fd = pfd->fd; int events = pfd->events; - if (events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) { + if (events & G_IO_IN) { FD_SET(fd, rfds); nfds = MAX(nfds, fd); } - if (events & (G_IO_OUT | G_IO_ERR)) { + if (events & G_IO_OUT) { FD_SET(fd, wfds); nfds = MAX(nfds, fd); } @@ -335,10 +386,10 @@ static void pollfds_poll(GArray *pollfds, int nfds, fd_set *rfds, int revents = 0; if (FD_ISSET(fd, rfds)) { - revents |= G_IO_IN | G_IO_HUP | G_IO_ERR; + revents |= G_IO_IN; } if (FD_ISSET(fd, wfds)) { - revents |= G_IO_OUT | G_IO_ERR; + revents |= G_IO_OUT; } if (FD_ISSET(fd, xfds)) { revents |= G_IO_PRI; @@ -347,7 +398,7 @@ static void pollfds_poll(GArray *pollfds, int nfds, fd_set *rfds, } } -static int os_host_main_loop_wait(uint32_t timeout) +static int os_host_main_loop_wait(int64_t timeout) { GMainContext *context = g_main_context_default(); GPollFD poll_fds[1024 * 2]; /* this is probably overkill */ @@ -356,6 +407,7 @@ static int os_host_main_loop_wait(uint32_t timeout) PollingEntry *pe; WaitObjects *w = &wait_objects; gint poll_timeout; + int64_t poll_timeout_ns; static struct timeval tv0; fd_set rfds, wfds, xfds; int nfds; @@ -369,6 +421,20 @@ static int os_host_main_loop_wait(uint32_t timeout) return ret; } + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + nfds = pollfds_fill(gpollfds, &rfds, &wfds, &xfds); + if (nfds >= 0) { + select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0); + if (select_ret != 0) { + timeout = 0; + } + if (select_ret > 0) { + pollfds_poll(gpollfds, nfds, &rfds, &wfds, &xfds); + } + } + g_main_context_prepare(context, &max_priority); n_poll_fds = g_main_context_query(context, max_priority, &poll_timeout, poll_fds, ARRAY_SIZE(poll_fds)); @@ -379,12 +445,17 @@ static int os_host_main_loop_wait(uint32_t timeout) poll_fds[n_poll_fds + i].events = G_IO_IN; } - if (poll_timeout < 0 || timeout < poll_timeout) { - poll_timeout = timeout; + if (poll_timeout < 0) { + poll_timeout_ns = -1; + } else { + poll_timeout_ns = (int64_t)poll_timeout * (int64_t)SCALE_MS; } + poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout); + qemu_mutex_unlock_iothread(); - g_poll_ret = g_poll(poll_fds, n_poll_fds + w->num, poll_timeout); + g_poll_ret = qemu_poll_ns(poll_fds, n_poll_fds + w->num, poll_timeout_ns); + qemu_mutex_lock_iothread(); if (g_poll_ret > 0) { for (i = 0; i < w->num; i++) { @@ -401,24 +472,6 @@ static int os_host_main_loop_wait(uint32_t timeout) g_main_context_dispatch(context); } - /* Call select after g_poll to avoid a useless iteration and therefore - * improve socket latency. - */ - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&xfds); - nfds = pollfds_fill(gpollfds, &rfds, &wfds, &xfds); - if (nfds >= 0) { - select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0); - if (select_ret != 0) { - timeout = 0; - } - if (select_ret > 0) { - pollfds_poll(gpollfds, nfds, &rfds, &wfds, &xfds); - } - } - return select_ret || g_poll_ret; } #endif @@ -427,6 +480,7 @@ int main_loop_wait(int nonblocking) { int ret; uint32_t timeout = UINT32_MAX; + int64_t timeout_ns; if (nonblocking) { timeout = 0; @@ -436,17 +490,28 @@ int main_loop_wait(int nonblocking) g_array_set_size(gpollfds, 0); /* reset for new iteration */ /* XXX: separate device handlers from system ones */ #ifdef CONFIG_SLIRP - slirp_update_timeout(&timeout); - slirp_pollfds_fill(gpollfds); + slirp_pollfds_fill(gpollfds, &timeout); #endif - qemu_iohandler_fill(gpollfds); - ret = os_host_main_loop_wait(timeout); - qemu_iohandler_poll(gpollfds, ret); + + if (timeout == UINT32_MAX) { + timeout_ns = -1; + } else { + timeout_ns = (uint64_t)timeout * (int64_t)(SCALE_MS); + } + + timeout_ns = qemu_soonest_timeout(timeout_ns, + timerlistgroup_deadline_ns( + &main_loop_tlg)); + + ret = os_host_main_loop_wait(timeout_ns); #ifdef CONFIG_SLIRP slirp_pollfds_poll(gpollfds, (ret < 0)); #endif - qemu_run_all_timers(); + /* CPU thread can infinitely wait for event after + missing the warp */ + qemu_start_warp_timer(); + qemu_clock_run_all_timers(); return ret; } @@ -457,27 +522,3 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) { return aio_bh_new(qemu_aio_context, cb, opaque); } - -bool qemu_aio_wait(void) -{ - return aio_poll(qemu_aio_context, true); -} - -#ifdef CONFIG_POSIX -void qemu_aio_set_fd_handler(int fd, - IOHandler *io_read, - IOHandler *io_write, - AioFlushHandler *io_flush, - void *opaque) -{ - aio_set_fd_handler(qemu_aio_context, fd, io_read, io_write, io_flush, - opaque); -} -#endif - -void qemu_aio_set_event_notifier(EventNotifier *notifier, - EventNotifierHandler *io_read, - AioFlushEventNotifierHandler *io_flush) -{ - aio_set_event_notifier(qemu_aio_context, notifier, io_read, io_flush); -}