| /* |
| * event notifier support |
| * |
| * Copyright Red Hat, Inc. 2010 |
| * |
| * Authors: |
| * Michael S. Tsirkin <mst@redhat.com> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/cutils.h" |
| #include "qemu/event_notifier.h" |
| #include "qemu/main-loop.h" |
| |
| #ifdef CONFIG_EVENTFD |
| #include <sys/eventfd.h> |
| #endif |
| |
| #ifdef CONFIG_EVENTFD |
| /* |
| * Initialize @e with existing file descriptor @fd. |
| * @fd must be a genuine eventfd object, emulation with pipe won't do. |
| */ |
| void event_notifier_init_fd(EventNotifier *e, int fd) |
| { |
| e->rfd = fd; |
| e->wfd = fd; |
| e->initialized = true; |
| } |
| #endif |
| |
| int event_notifier_init(EventNotifier *e, int active) |
| { |
| int fds[2]; |
| int ret; |
| |
| #ifdef CONFIG_EVENTFD |
| ret = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); |
| #else |
| ret = -1; |
| errno = ENOSYS; |
| #endif |
| if (ret >= 0) { |
| e->rfd = e->wfd = ret; |
| } else { |
| if (errno != ENOSYS) { |
| return -errno; |
| } |
| if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { |
| return -errno; |
| } |
| if (!g_unix_set_fd_nonblocking(fds[0], true, NULL)) { |
| ret = -errno; |
| goto fail; |
| } |
| if (!g_unix_set_fd_nonblocking(fds[1], true, NULL)) { |
| ret = -errno; |
| goto fail; |
| } |
| e->rfd = fds[0]; |
| e->wfd = fds[1]; |
| } |
| e->initialized = true; |
| if (active) { |
| event_notifier_set(e); |
| } |
| return 0; |
| |
| fail: |
| close(fds[0]); |
| close(fds[1]); |
| return ret; |
| } |
| |
| void event_notifier_cleanup(EventNotifier *e) |
| { |
| if (!e->initialized) { |
| return; |
| } |
| |
| if (e->rfd != e->wfd) { |
| close(e->rfd); |
| } |
| |
| e->rfd = -1; |
| close(e->wfd); |
| e->wfd = -1; |
| e->initialized = false; |
| } |
| |
| int event_notifier_get_fd(const EventNotifier *e) |
| { |
| return e->rfd; |
| } |
| |
| int event_notifier_get_wfd(const EventNotifier *e) |
| { |
| return e->wfd; |
| } |
| |
| int event_notifier_set(EventNotifier *e) |
| { |
| static const uint64_t value = 1; |
| ssize_t ret; |
| |
| if (!e->initialized) { |
| return -1; |
| } |
| |
| do { |
| ret = write(e->wfd, &value, sizeof(value)); |
| } while (ret < 0 && errno == EINTR); |
| |
| /* EAGAIN is fine, a read must be pending. */ |
| if (ret < 0 && errno != EAGAIN) { |
| return -errno; |
| } |
| return 0; |
| } |
| |
| int event_notifier_test_and_clear(EventNotifier *e) |
| { |
| int value; |
| ssize_t len; |
| char buffer[512]; |
| |
| if (!e->initialized) { |
| return 0; |
| } |
| |
| /* Drain the notify pipe. For eventfd, only 8 bytes will be read. */ |
| value = 0; |
| do { |
| len = read(e->rfd, buffer, sizeof(buffer)); |
| value |= (len > 0); |
| } while ((len == -1 && errno == EINTR) || len == sizeof(buffer)); |
| |
| return value; |
| } |