about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-02-10 11:37:34 +0000
committerEric Wong <normalperson@yhbt.net>2013-02-11 00:39:16 +0000
commit5537c96848f483d403da1ed663809681e7b09f3b (patch)
tree6a1afef2cc6ac2af758cbad65385c7c8323ca2c2
parent955991aae8c3da5a13e34e929188db3fd9216a0e (diff)
downloadcmogstored-5537c96848f483d403da1ed663809681e7b09f3b.tar.gz
eventfd uses fewer resources than a pipe, so create less
overhead for our users by using eventfd instead of a pipe.
-rw-r--r--Makefile.am3
-rw-r--r--cmogstored.h8
-rw-r--r--configure.ac3
-rw-r--r--notify.c69
-rw-r--r--notify.h16
-rw-r--r--selfwake.c40
-rw-r--r--selfwake_eventfd.h36
-rw-r--r--selfwake_pipe.h35
8 files changed, 151 insertions, 59 deletions
diff --git a/Makefile.am b/Makefile.am
index 557ecca..8ec860c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -77,6 +77,9 @@ mog_src += queue_kqueue.c
 mog_src += queue_kqueue.h
 mog_src += queue_loop.c
 mog_src += queue_step.c
+mog_src += selfwake_eventfd.h
+mog_src += selfwake_pipe.h
+mog_src += selfwake.c
 mog_src += sig.c
 mog_src += svc.c
 mog_src += svc_dev.c
diff --git a/cmogstored.h b/cmogstored.h
index 769825b..2fe0f05 100644
--- a/cmogstored.h
+++ b/cmogstored.h
@@ -246,8 +246,8 @@ enum mog_fd_type {
         MOG_FD_TYPE_HTTPGET,
         MOG_FD_TYPE_MGMT,
         MOG_FD_TYPE_IOSTAT,
-        MOG_FD_TYPE_NOTIFYRD,
-        MOG_FD_TYPE_NOTIFYWR,
+        MOG_FD_TYPE_SELFWAKE,
+        MOG_FD_TYPE_SELFPIPE,
         MOG_FD_TYPE_ACCEPT,
         MOG_FD_TYPE_FILE,
         MOG_FD_TYPE_QUEUE,
@@ -264,8 +264,8 @@ struct mog_fd {
                 struct mog_mgmt mgmt;
                 struct mog_http http;
                 struct mog_iostat iostat;
-                struct mog_notifyrd notifyrd;
-                struct mog_notifywr notifywr;
+                struct mog_selfwake selfwake;
+                struct mog_selfpipe selfpipe;
                 struct mog_file file;
                 struct mog_queue queue;
                 struct mog_svc *svc;
diff --git a/configure.ac b/configure.ac
index b63ab40..ca202da 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,7 +34,8 @@ AC_C_FLEXIBLE_ARRAY_MEMBER
 dnl gnulib *at functions aren't thread-safe, ask for the real thing
 AC_CHECK_FUNCS([openat renameat mkdirat fstatat unlinkat])
 
-AC_CHECK_FUNCS([epoll_wait])
+AC_CHECK_HEADERS_ONCE([sys/eventfd.h])
+AC_CHECK_FUNCS([epoll_wait eventfd])
 
 dnl libkqueue should work in the future
 AC_CHECK_FUNCS([kqueue])
diff --git a/notify.c b/notify.c
index 5178d25..7d7872a 100644
--- a/notify.c
+++ b/notify.c
@@ -4,8 +4,8 @@
  */
 #include "cmogstored.h"
 
-static struct mog_fd *notifywr_mfd;
-static struct mog_notifywr *notifywr;
+static struct mog_fd *notify_mfd;
+static struct mog_selfwake *notify;
 static time_t usage_file_updated_at;
 static time_t usage_file_interval = 10;
 struct mog_queue *mog_notify_queue;
@@ -13,11 +13,6 @@ struct mog_queue *mog_notify_queue;
 void mog_notify_init(void)
 {
         const char *interval = getenv("MOG_DISK_USAGE_INTERVAL");
-        struct mog_fd *mfd;
-        int self_pipe[2];
-
-        if (pipe2(self_pipe, O_NONBLOCK | O_CLOEXEC) < 0)
-                die_errno("failed to init self-pipe");
 
         if (interval) {
                 int i = atoi(interval);
@@ -27,16 +22,16 @@ void mog_notify_init(void)
         }
 
         assert(mog_notify_queue == NULL && "notify queue already initialized");
-        mog_notify_queue = mog_queue_new();
-
-        mfd = mog_fd_init(self_pipe[0], MOG_FD_TYPE_NOTIFYRD);
-        mfd->as.notifyrd.queue = mog_notify_queue;
-        mog_idleq_add(mog_notify_queue, mfd, MOG_QEV_RD);
+        assert(notify == NULL && "notify already initialized");
+        assert(notify_mfd == NULL && "notify_mfd already initialized");
 
-        notifywr_mfd = mfd = mog_fd_init(self_pipe[1], MOG_FD_TYPE_NOTIFYWR);
-        notifywr = &mfd->as.notifywr;
-        notifywr->queue = mog_notify_queue;
-        memset(notifywr->notes, 0, sizeof(notifywr->notes));
+        mog_notify_queue = mog_queue_new();
+        notify_mfd = mog_selfwake_new();
+        notify = &notify_mfd->as.selfwake;
+        assert(notify->writer && "notify writer not initialized");
+        notify->queue = mog_notify_queue;
+        mog_idleq_add(notify->queue, notify_mfd, MOG_QEV_RD);
+        memset(&notify->as.notes, 0, sizeof(notify->as.notes));
 }
 
 static void global_mkusage(void)
@@ -47,22 +42,13 @@ static void global_mkusage(void)
 
 static inline bool note_xchg(enum mog_notification note, int from, int to)
 {
-        return __sync_bool_compare_and_swap(&notifywr->notes[note], from, to);
+        return __sync_bool_compare_and_swap(&notify->as.notes[note], from, to);
 }
 
 /* drain the pipe and process notifications */
-static void notifyrd_queue_step(struct mog_fd *mfd)
+static void note_queue_step(struct mog_fd *mfd)
 {
-        char buf[128];
-        ssize_t r;
-        struct mog_notifyrd *notifyrd = &mfd->as.notifyrd;
-
-        do {
-                r = read(mfd->fd, buf, sizeof(buf));
-        } while (r > 0);
-        assert(r < 0 &&
-                (errno == EAGAIN || errno == EINTR)
-                && "self-pipe read failed");
+        mog_selfwake_drain(mfd);
 
         if (note_xchg(MOG_NOTIFY_DEVICE_REFRESH, 1, 0))
                 global_mkusage();
@@ -70,13 +56,13 @@ static void notifyrd_queue_step(struct mog_fd *mfd)
         if (note_xchg(MOG_NOTIFY_SET_N_THREADS, 1, 0))
                 mog_thrpool_process_queue();
 
-        mog_idleq_push(notifyrd->queue, mfd, MOG_QEV_RD);
+        mog_idleq_push(mfd->as.selfwake.queue, mfd, MOG_QEV_RD);
 }
 
 static void notify_queue_step(struct mog_fd *mfd)
 {
         switch (mfd->fd_type) {
-        case MOG_FD_TYPE_NOTIFYRD: notifyrd_queue_step(mfd); return;
+        case MOG_FD_TYPE_SELFWAKE: note_queue_step(mfd); return;
         case MOG_FD_TYPE_IOSTAT: mog_iostat_queue_step(mfd); return;
         default:
                 assert(0 && mfd->fd_type && "bad fd_type in queue");
@@ -98,7 +84,7 @@ void mog_notify_wait(bool need_usage_file)
          * epoll_wait() with timeout==0 can avoid some slow paths,
          * so take anything that's already ready before sleeping
          */
-        while ((mfd = mog_idleq_wait(notifywr->queue, 0)))
+        while ((mfd = mog_idleq_wait(notify->queue, 0)))
                 notify_queue_step(mfd);
 
         if (need_usage_file == false)
@@ -109,7 +95,7 @@ void mog_notify_wait(bool need_usage_file)
                 timeout = 0;
 
         mog_intr_enable();
-        mfd = mog_idleq_wait(notifywr->queue, timeout);
+        mfd = mog_idleq_wait(notify->queue, timeout);
         mog_intr_disable();
         if (mfd)
                 notify_queue_step(mfd);
@@ -118,8 +104,6 @@ void mog_notify_wait(bool need_usage_file)
 /* this is async-signal safe */
 void mog_notify(enum mog_notification note)
 {
-        ssize_t w;
-
         switch (note) {
         case MOG_NOTIFY_DEVICE_REFRESH:
         case MOG_NOTIFY_SET_N_THREADS:
@@ -128,20 +112,5 @@ void mog_notify(enum mog_notification note)
         case MOG_NOTIFY_SIGNAL: break;
         default: assert(0 && "bad note passed");
         }
-
-retry:
-        w = write(notifywr_mfd->fd, "^", 1);
-        if (w >= 0) return;
-
-        switch (errno) {
-        case_EAGAIN: return; /* somebody already woke this up */
-        case EINTR: goto retry; /* just in case... */
-        }
-
-        /*
-         * we're screwed anyways, at least try this even though syslog()
-         * isn't safe inside a signal handler.
-         */
-        syslog(LOG_CRIT, "mog_notify write() to pipe failed: %m");
-        abort();
+        mog_selfwake_trigger(notify_mfd);
 }
diff --git a/notify.h b/notify.h
index 3136151..0e0eb53 100644
--- a/notify.h
+++ b/notify.h
@@ -9,13 +9,21 @@ enum mog_notification {
         MOG_NOTIFY_MAX
 };
 
-struct mog_notifyrd {
+struct mog_selfwake {
         struct mog_queue *queue;
+        struct mog_fd *writer;
+        union {
+                int notes[MOG_NOTIFY_MAX];
+        } as;
 };
 
-struct mog_notifywr {
-        struct mog_queue *queue;
-        int notes[MOG_NOTIFY_MAX];
+/* only for pipe */
+struct mog_selfpipe {
+        struct mog_fd *reader; /* points to mog_selfwake */
 };
 
 extern struct mog_queue *mog_notify_queue;
+
+struct mog_fd * mog_selfwake_new(void);
+void mog_selfwake_trigger(struct mog_fd *);
+void mog_selfwake_drain(struct mog_fd *);
diff --git a/selfwake.c b/selfwake.c
new file mode 100644
index 0000000..d0fbf44
--- /dev/null
+++ b/selfwake.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012-2013, Eric Wong <normalperson@yhbt.net>
+ * License: GPLv3 or later (see COPYING for details)
+ */
+#include "cmogstored.h"
+#include "selfwake_eventfd.h"
+#include "selfwake_pipe.h"
+
+/* this is async-signal safe (except in the case of bugs) */
+void mog_selfwake_trigger(struct mog_fd *mfd)
+{
+        ssize_t w;
+
+retry:
+        w = selfwake_write(mfd);
+        if (w >= 0) return;
+
+        switch (errno) {
+        case_EAGAIN: return; /* somebody already woke this up */
+        case EINTR: goto retry; /* just in case... */
+        }
+
+        /*
+         * we're screwed anyways, at least try this even though syslog()
+         * isn't safe inside a signal handler.
+         */
+        syslog(LOG_CRIT, "mog_selfwake_trigger write() failed: %m");
+        abort();
+}
+
+void mog_selfwake_drain(struct mog_fd *mfd)
+{
+        ssize_t r;
+
+        do {
+                r = selfwake_drain(mfd);
+        } while (r > 0);
+        assert(r < 0 && (errno == EAGAIN || errno == EINTR)
+               && "selfwake read failed");
+}
diff --git a/selfwake_eventfd.h b/selfwake_eventfd.h
new file mode 100644
index 0000000..baca39f
--- /dev/null
+++ b/selfwake_eventfd.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012-2013, Eric Wong <normalperson@yhbt.net>
+ * License: GPLv3 or later (see COPYING for details)
+ */
+#if defined(HAVE_SYS_EVENTFD_H) && defined(HAVE_EVENTFD)
+#include <sys/eventfd.h>
+#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
+static ssize_t selfwake_write(struct mog_fd *mfd)
+{
+        static const uint64_t buf = 1;
+
+        return write(mfd->fd, &buf, sizeof(buf));
+}
+
+struct mog_fd * mog_selfwake_new(void)
+{
+        struct mog_fd *mfd;
+        int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+
+        if (efd < 0)
+                die_errno("failed to create eventfd");
+
+        mfd = mog_fd_init(efd, MOG_FD_TYPE_SELFWAKE);
+        mfd->as.selfwake.writer = mfd;
+
+        return mfd;
+}
+
+static ssize_t selfwake_drain(struct mog_fd *mfd)
+{
+        uint64_t buf;
+
+        return read(mfd->fd, &buf, sizeof(buf));
+}
+#endif /* EFD_CLOEXEC+EFD_NONBLOCK */
+#endif /* HAVE_SYS_EVENTFD_H  */
diff --git a/selfwake_pipe.h b/selfwake_pipe.h
new file mode 100644
index 0000000..763332a
--- /dev/null
+++ b/selfwake_pipe.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012-2013, Eric Wong <normalperson@yhbt.net>
+ * License: GPLv3 or later (see COPYING for details)
+ */
+#ifndef HAVE_EVENTFD
+static ssize_t selfwake_write(struct mog_fd *mfd)
+{
+        return write(mfd->as.selfwake.writer->fd, "", 1);
+}
+
+struct mog_fd * mog_selfwake_new(void)
+{
+        struct mog_fd *reader, *writer;
+        struct mog_selfwake *selfwake;
+        int self_pipe[2];
+
+        if (pipe2(self_pipe, O_NONBLOCK | O_CLOEXEC) < 0)
+                die_errno("failed to init self-pipe");
+
+        reader = mog_fd_init(self_pipe[0], MOG_FD_TYPE_SELFWAKE);
+        selfwake = &reader->as.selfwake;
+
+        writer = mog_fd_init(self_pipe[1], MOG_FD_TYPE_SELFPIPE);
+        writer->as.selfpipe.reader = reader;
+        selfwake->writer = writer;
+
+        return reader;
+}
+
+static ssize_t selfwake_drain(struct mog_fd *mfd)
+{
+        char buf[64];
+        return read(mfd->fd, buf, sizeof(buf));
+}
+#endif /* !HAVE_EVENTFD */