Export epoch time via proc_global_info (XCC)
[akaros.git] / user / benchutil / alarm.c
index 3bbec75..0d5bc59 100644 (file)
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
  *
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
  *
- * Userspace alarm service, based off a slimmed down version of the kernel
- * alarms.  Under the hood, it uses the kernel alarm service for the root of
- * the alarm chain.
+ * Userspace alarms.  There are lower level helpers to build your own alarms
+ * from the #alarm device and an alarm service, based off a slimmed down version
+ * of the kernel alarms.  Under the hood, the user alarm uses the #alarm service
+ * for the root of the alarm chain.
  *
  * There's only one timer chain, unlike in the kernel, for the entire process.
  * If you want one-off timers unrelated to the chain (and sent to other vcores),
  *
  * There's only one timer chain, unlike in the kernel, for the entire process.
  * If you want one-off timers unrelated to the chain (and sent to other vcores),
- * use #A directly.
+ * use #alarm directly.
  *
  * Your handlers will run from vcore context.
  *
  * Code differences from the kernel (for future porting):
  *
  * Your handlers will run from vcore context.
  *
  * Code differences from the kernel (for future porting):
- * - init_alarm_service, run once out of init_awaiter (or wherever).
+ * - init_alarm_service, run as a constructor
  * - set_alarm() and friends are __tc_set_alarm(), passing global_tchain.
  * - set_alarm() and friends are __tc_set_alarm(), passing global_tchain.
- * - reset_tchain_interrupt() uses #A
+ * - reset_tchain_interrupt() uses #alarm
  * - removed anything related to semaphores or kthreads
  * - spinlocks -> spin_pdr_locks
  * - removed anything related to semaphores or kthreads
  * - spinlocks -> spin_pdr_locks
- * - ev_q wrappers for converting #A events to __triggers
+ * - ev_q wrappers for converting #alarm events to __triggers
  * - printks, and other minor stuff. */
 
 #include <sys/queue.h>
 #include <sys/time.h>
  * - printks, and other minor stuff. */
 
 #include <sys/queue.h>
 #include <sys/time.h>
-#include <alarm.h>
+#include <benchutil/alarm.h>
 #include <stdio.h>
 #include <stdio.h>
-#include <assert.h>
+#include <parlib/assert.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <parlib.h>
-#include <event.h>
-#include <measure.h>
-#include <uthread.h>
-#include <spinlock.h>
-#include <timing.h>
+#include <parlib/parlib.h>
+#include <parlib/event.h>
+#include <benchutil/measure.h>
+#include <parlib/uthread.h>
+#include <parlib/spinlock.h>
+#include <parlib/timing.h>
+#include <sys/plan9_helpers.h>
+
+/* Helper to get your own alarm.   If you don't care about a return value, pass
+ * 0 and it'll be ignored.  The alarm is built, but has no evq or timer set. */
+int devalarm_get_fds(int *ctlfd_r, int *timerfd_r, int *alarmid_r)
+{
+       int ctlfd, timerfd, alarmid, ret;
+       char buf[20];
+       char path[32];
+
+       ctlfd = open("#alarm/clone", O_RDWR | O_CLOEXEC);
+       if (ctlfd < 0)
+               return -1;
+       ret = read(ctlfd, buf, sizeof(buf) - 1);
+       if (ret <= 0)
+               return -1;
+       buf[ret] = 0;
+       alarmid = atoi(buf);
+       snprintf(path, sizeof(path), "#alarm/a%s/timer", buf);
+       timerfd = open(path, O_RDWR | O_CLOEXEC);
+       if (timerfd < 0)
+               return -1;
+       if (ctlfd_r)
+               *ctlfd_r = ctlfd;
+       else
+               close(ctlfd);
+       if (timerfd_r)
+               *timerfd_r = timerfd;
+       else
+               close(timerfd);
+       if (alarmid_r)
+               *alarmid_r = alarmid;
+       return 0;
+}
+
+int devalarm_set_evq(int timerfd, struct event_queue *ev_q, int alarmid)
+{
+       struct fd_tap_req tap_req = {0};
+
+       tap_req.fd = timerfd;
+       tap_req.cmd = FDTAP_CMD_ADD;
+       tap_req.filter = FDTAP_FILT_WRITTEN;
+       tap_req.ev_id = EV_ALARM;
+       tap_req.ev_q = ev_q;
+       tap_req.data = (void*)(long)alarmid;
+       if (sys_tap_fds(&tap_req, 1) != 1)
+               return -1;
+       return 0;
+}
+
+int devalarm_set_time(int timerfd, uint64_t tsc_time)
+{
+       return write_hex_to_fd(timerfd, tsc_time);
+}
+
+int devalarm_get_id(struct event_msg *ev_msg)
+{
+       if (!ev_msg)
+               return -1;
+       return (int)(long)ev_msg->ev_arg3;
+}
+
+int devalarm_disable(int timerfd)
+{
+       return write_hex_to_fd(timerfd, 0);
+}
 
 /* Helpers, basically renamed kernel interfaces, with the *tchain. */
 static void __tc_locked_set_alarm(struct timer_chain *tchain,
 
 /* Helpers, basically renamed kernel interfaces, with the *tchain. */
 static void __tc_locked_set_alarm(struct timer_chain *tchain,
@@ -54,21 +121,6 @@ static void handle_user_alarm(struct event_msg *ev_msg, unsigned int ev_type,
 /* One chain to rule them all. */
 struct timer_chain global_tchain;
 
 /* One chain to rule them all. */
 struct timer_chain global_tchain;
 
-/* Unix time offsets so we can allow people to specify an absolute unix time to
- * an alarm, rather than an absolute time in terms of raw tsc ticks.  This
- * value is initialized when the timer service is started. */
-static struct {
-       uint64_t tod; // The initial time of day in microseconds
-       uint64_t tsc; // The initial value of the tsc counter
-} unixtime_offsets;
-static inline void init_unixtime_offsets()
-{
-       struct timeval tv;
-       gettimeofday(&tv, NULL);
-       unixtime_offsets.tsc = read_tsc();
-       unixtime_offsets.tod = tv.tv_sec*1000000 + tv.tv_usec;
-}
-
 /* Helper, resets the earliest/latest times, based on the elements of the list.
  * If the list is empty, we set the times to be the 12345 poison time.  Since
  * the list is empty, the alarm shouldn't be going off. */
 /* Helper, resets the earliest/latest times, based on the elements of the list.
  * If the list is empty, we set the times to be the 12345 poison time.  Since
  * the list is empty, the alarm shouldn't be going off. */
@@ -84,58 +136,34 @@ static void reset_tchain_times(struct timer_chain *tchain)
        }
 }
 
        }
 }
 
-static void init_alarm_service(void)
+static void __attribute__((constructor)) init_alarm_service(void)
 {
 {
-       int ctlfd, timerfd, alarmid, ret;
-       char buf[20];
-       char path[32];
+       int ctlfd, timerfd, alarmid;
        struct event_queue *ev_q;
 
        struct event_queue *ev_q;
 
-       /* Initialize the unixtime_offsets */
-       init_unixtime_offsets();
-
        /* Sets up timer chain (only one chain per process) */
        spin_pdr_init(&global_tchain.lock);
        TAILQ_INIT(&global_tchain.waiters);
        reset_tchain_times(&global_tchain);
 
        /* Sets up timer chain (only one chain per process) */
        spin_pdr_init(&global_tchain.lock);
        TAILQ_INIT(&global_tchain.waiters);
        reset_tchain_times(&global_tchain);
 
-       ctlfd = open("#A/clone", O_RDWR | O_CLOEXEC);
-       if (ctlfd < 0) {
-               perror("Useralarm: Can't clone an alarm");
-               return;
-       }
-       ret = read(ctlfd, buf, sizeof(buf) - 1);
-       if (ret <= 0) {
-               if (!ret)
-                       printf("Useralarm: Got early EOF from ctl\n");
-               else
-                       perror("Useralarm: Can't read ctl");
-               return;
-       }
-       buf[ret] = 0;
-       alarmid = atoi(buf);
-       snprintf(path, sizeof(path), "#A/a%s/timer", buf);
-       timerfd = open(path, O_RDWR | O_CLOEXEC);
-       if (timerfd < 0) {
-               perror("Useralarm: Can't open timer");
+       if (devalarm_get_fds(&ctlfd, &timerfd, &alarmid)) {
+               perror("Useralarm: devalarm_get_fds");
                return;
        }
        /* Since we're doing SPAM_PUBLIC later, we actually don't need a big ev_q.
         * But someone might copy/paste this and change a flag. */
        register_ev_handler(EV_ALARM, handle_user_alarm, 0);
                return;
        }
        /* Since we're doing SPAM_PUBLIC later, we actually don't need a big ev_q.
         * But someone might copy/paste this and change a flag. */
        register_ev_handler(EV_ALARM, handle_user_alarm, 0);
-       if (!(ev_q = get_big_event_q())) {
+       if (!(ev_q = get_eventq(EV_MBOX_UCQ))) {
                perror("Useralarm: Failed ev_q");
                return;
        }
        ev_q->ev_vcore = 0;
        /* We could get multiple events for a single alarm.  It's okay, since
         * __trigger can handle spurious upcalls.  If it ever is not okay, then use
                perror("Useralarm: Failed ev_q");
                return;
        }
        ev_q->ev_vcore = 0;
        /* We could get multiple events for a single alarm.  It's okay, since
         * __trigger can handle spurious upcalls.  If it ever is not okay, then use
-        * an INDIR/FALLBACK instead of SPAM_PUBLIC. */
-       ev_q->ev_flags = EVENT_IPI | EVENT_SPAM_PUBLIC;
-       ret = snprintf(path, sizeof(path), "evq %llx", ev_q);
-       ret = write(ctlfd, path, ret);
-       if (ret <= 0) {
-               perror("Useralarm: Failed to write ev_q");
+        * an INDIR (probably with SPAM_INDIR too) instead of SPAM_PUBLIC. */
+       ev_q->ev_flags = EVENT_IPI | EVENT_SPAM_PUBLIC | EVENT_WAKEUP;
+       if (devalarm_set_evq(timerfd, ev_q, alarmid)) {
+               perror("set_alarm_evq");
                return;
        }
        /* now the alarm is all set, just need to write the timer whenever we want
                return;
        }
        /* now the alarm is all set, just need to write the timer whenever we want
@@ -151,7 +179,6 @@ static void init_alarm_service(void)
 void init_awaiter(struct alarm_waiter *waiter,
                   void (*func) (struct alarm_waiter *awaiter))
 {
 void init_awaiter(struct alarm_waiter *waiter,
                   void (*func) (struct alarm_waiter *awaiter))
 {
-       run_once_racy(init_alarm_service());
        waiter->wake_up_time = ALARM_POISON_TIME;
        assert(func);
        waiter->func = func;
        waiter->wake_up_time = ALARM_POISON_TIME;
        assert(func);
        waiter->func = func;
@@ -160,17 +187,16 @@ void init_awaiter(struct alarm_waiter *waiter,
 
 /* Give this the absolute time.  For now, abs_time is the TSC time that you want
  * the alarm to go off. */
 
 /* Give this the absolute time.  For now, abs_time is the TSC time that you want
  * the alarm to go off. */
-void set_awaiter_abs(struct alarm_waiter *waiter, uint64_t abs_time)
+static void __set_awaiter_abs(struct alarm_waiter *waiter, uint64_t abs_time)
 {
        waiter->wake_up_time = abs_time;
 }
 
 /* Give this the absolute unix time (in microseconds) that you want the alarm
  * to go off. */
 {
        waiter->wake_up_time = abs_time;
 }
 
 /* Give this the absolute unix time (in microseconds) that you want the alarm
  * to go off. */
-void set_awaiter_abs_unix(struct alarm_waiter *waiter, uint64_t abs_time)
+void set_awaiter_abs_unix(struct alarm_waiter *waiter, uint64_t abs_usec)
 {
 {
-       abs_time = usec2tsc(abs_time - unixtime_offsets.tod) + unixtime_offsets.tsc;
-       set_awaiter_abs(waiter, abs_time);
+       __set_awaiter_abs(waiter, epoch_nsec_to_tsc(abs_usec * 1000));
 }
 
 /* Give this a relative time from now, in microseconds.  This might be easier to
 }
 
 /* Give this a relative time from now, in microseconds.  This might be easier to
@@ -183,7 +209,7 @@ void set_awaiter_rel(struct alarm_waiter *waiter, uint64_t usleep)
        /* This will go off if we wrap-around the TSC.  It'll never happen for legit
         * values, but this might catch some bugs with large usleeps. */
        assert(now <= then);
        /* This will go off if we wrap-around the TSC.  It'll never happen for legit
         * values, but this might catch some bugs with large usleeps. */
        assert(now <= then);
-       set_awaiter_abs(waiter, then);
+       __set_awaiter_abs(waiter, then);
 }
 
 /* Increment the timer that was already set, so that it goes off usleep usec
 }
 
 /* Increment the timer that was already set, so that it goes off usleep usec
@@ -219,13 +245,10 @@ void reset_alarm_abs(struct alarm_waiter *waiter, uint64_t abs_time)
 /* Helper, makes sure the kernel alarm is turned on at the right time. */
 static void reset_tchain_interrupt(struct timer_chain *tchain)
 {
 /* Helper, makes sure the kernel alarm is turned on at the right time. */
 static void reset_tchain_interrupt(struct timer_chain *tchain)
 {
-       int ret;
-       char buf[20];
        if (TAILQ_EMPTY(&tchain->waiters)) {
                /* Turn it off */
                printd("Turning alarm off\n");
        if (TAILQ_EMPTY(&tchain->waiters)) {
                /* Turn it off */
                printd("Turning alarm off\n");
-               ret = write(tchain->ctlfd, "cancel", sizeof("cancel"));
-               if (ret <= 0) {
+               if (devalarm_disable(tchain->timerfd)) {
                        printf("Useralarm: unable to disarm alarm!\n");
                        return;
                }
                        printf("Useralarm: unable to disarm alarm!\n");
                        return;
                }
@@ -234,9 +257,7 @@ static void reset_tchain_interrupt(struct timer_chain *tchain)
                assert(tchain->earliest_time != ALARM_POISON_TIME);
                /* TODO: check for times in the past or very close to now */
                printd("Turning alarm on for %llu\n", tchain->earliest_time);
                assert(tchain->earliest_time != ALARM_POISON_TIME);
                /* TODO: check for times in the past or very close to now */
                printd("Turning alarm on for %llu\n", tchain->earliest_time);
-               ret = snprintf(buf, sizeof(buf), "%llx", tchain->earliest_time);
-               ret = write(tchain->timerfd, buf, ret);
-               if (ret <= 0) {
+               if (devalarm_set_time(tchain->timerfd, tchain->earliest_time)) {
                        perror("Useralarm: Failed to set timer");
                        return;
                }
                        perror("Useralarm: Failed to set timer");
                        return;
                }
@@ -286,7 +307,7 @@ static void handle_user_alarm(struct event_msg *ev_msg, unsigned int ev_type,
                               void *data)
 {
        assert(ev_type == EV_ALARM);
                               void *data)
 {
        assert(ev_type == EV_ALARM);
-       if (ev_msg && (ev_msg->ev_arg2 == global_tchain.alarmid))
+       if (devalarm_get_id(ev_msg) == global_tchain.alarmid)
                __trigger_tchain(&global_tchain);
 }
 
                __trigger_tchain(&global_tchain);
 }
 
@@ -410,7 +431,7 @@ static void __tc_reset_alarm_abs(struct timer_chain *tchain,
         * on the tchain).  If it has fired, it's like a fresh insert */
        if (waiter->on_tchain)
                reset_int = __remove_awaiter(tchain, waiter);
         * on the tchain).  If it has fired, it's like a fresh insert */
        if (waiter->on_tchain)
                reset_int = __remove_awaiter(tchain, waiter);
-       set_awaiter_abs(waiter, abs_time);
+       __set_awaiter_abs(waiter, abs_time);
        /* regardless, we need to be reinserted */
        if (__insert_awaiter(tchain, waiter) || reset_int)
                reset_tchain_interrupt(tchain);
        /* regardless, we need to be reinserted */
        if (__insert_awaiter(tchain, waiter) || reset_int)
                reset_tchain_interrupt(tchain);