Use FD taps for event delivery for #alarm
[akaros.git] / user / benchutil / pvcalarm.c
index 7a81279..7affaa9 100644 (file)
@@ -7,16 +7,17 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
-#include <parlib.h>
-#include <vcore.h>
-#include <event.h>
-#include <spinlock.h>
-#include <arch/atomic.h>
-#include <arch/bitmask.h>
+#include <parlib/parlib.h>
+#include <parlib/vcore.h>
+#include <parlib/event.h>
+#include <parlib/spinlock.h>
+#include <parlib/arch/atomic.h>
+#include <parlib/arch/bitmask.h>
 #include <sys/queue.h>
 #include <fcntl.h>
-#include <pvcalarm.h>
-#include <alarm_dispatch.h>
+#include <unistd.h>
+#include <benchutil/pvcalarm.h>
+#include <benchutil/alarm.h>
 
 /* Different states for enabling/disabling the per-vcore alarms. */
 enum {
@@ -32,11 +33,8 @@ struct pvcalarm_data {
        int ctlfd;
        int timerfd;
        int alarmid;
-       int state;
        uint64_t start_uptime;
-       SLIST_ENTRY(pvcalarm_data) next;
 };
-SLIST_HEAD(pvcalarm_data_list, pvcalarm_data);
 
 /* The global state of the pvcalarm service itself */
 struct pvcalarm {
@@ -45,47 +43,43 @@ struct pvcalarm {
 
        atomic_t state;
        int busy_count;
-       DECL_BITMASK(vcores, MAX_VCORES);
-       struct spin_pdr_lock list_lock;
-       struct pvcalarm_data_list list;
-       void (*handler) (struct event_msg *ev_msg, unsigned int ev_type);
+       handle_event_t handler;
+       struct pvcalarm_data *data;
 };
 
 /* The only state we need to make sure is set for the global alarm service is
  * to make sure it s in the disabled state at bootup */
 static struct pvcalarm global_pvcalarm = { .state = (void*)S_DISABLED };
-/* Thread local pointer to the pvcalarm_data.  The memory for this is allocated
- * on demand as new vcores pop up */
-static __thread struct pvcalarm_data *__pvcalarm_data; 
-/* If this function is non-null, then the per-vcore alarm service is active and
- * the function should be called early on inside vcore_entry(). */
-void (*vcore_poke_pvcalarm) (void);
-
-static void __vcore_poke_pvcalarm();
-static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type);
-static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type);
-static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type);
+
+/* Helper functions */
+static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data, int vcoreid);
+static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type,
+                            void *data);
+static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type,
+                              void *data);
+static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
+                              void *data);
 
 /* Initialize the pvcalarm service. Only call this function once */
 static int init_global_pvcalarm()
 {
-       CLR_BITMASK(global_pvcalarm.vcores, MAX_VCORES);
-       spin_pdr_init(&global_pvcalarm.list_lock);
-       SLIST_INIT(&global_pvcalarm.list);
        global_pvcalarm.interval = 0;
        global_pvcalarm.callback = NULL;
+       global_pvcalarm.busy_count = 0;
        global_pvcalarm.handler = NULL;
+
+       /* Preemptively setup timers for all possible vcores */
+       global_pvcalarm.data = malloc(max_vcores() * sizeof(struct pvcalarm_data));
+       for (int i=0; i<max_vcores(); i++) {
+               init_pvcalarm(&global_pvcalarm.data[i], i);
+       }
 }
 
 /* Run the pvc alarm associated with pvcalarm_data for the given amount of
  * time */
 static void run_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
 {
-       int ret;
-       char buf[20];
-       ret = snprintf(buf, sizeof(buf), "%llx", read_tsc() + offset);
-       ret = write(pvcalarm_data->timerfd, buf, ret);
-       if (ret <= 0) {
+       if (devalarm_set_time(pvcalarm_data->timerfd, read_tsc() + offset)) {
                perror("Useralarm: Failed to set timer");
                return;
        }
@@ -97,15 +91,13 @@ static void run_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
 static void start_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
 {
        pvcalarm_data->start_uptime = vcore_account_uptime_ticks(vcore_id());
-       run_pvcalarm(__pvcalarm_data, offset);
+       run_pvcalarm(pvcalarm_data, offset);
 }
 
 /* Stop the pvc alarm associated with pvcalarm_data */
 static void stop_pvcalarm(struct pvcalarm_data *pvcalarm_data)
 {
-       int ret;
-       ret = write(pvcalarm_data->ctlfd, "cancel", sizeof("cancel"));
-       if (ret <= 0) {
+       if (devalarm_disable(pvcalarm_data->ctlfd)) {
                printf("Useralarm: unable to disarm alarm!\n");
                return;
        }
@@ -139,12 +131,11 @@ int enable_pvcalarms(int method, uint64_t interval, void (*callback) (void))
                        global_pvcalarm.handler = handle_alarm_prof;
                        break;
        }
-       vcore_poke_pvcalarm = __vcore_poke_pvcalarm;
 
-       /* Poke all existing vcores so they run the newly initialized
-        * vcore_poke_pvcalarm function */
-       for (int i=0; i<num_vcores(); i++)
-               sys_self_notify(i, EV_NONE, 0, TRUE);
+       /* Start the timer on all vcores to go off after interval usecs */
+       for (int i=0; i<max_vcores(); i++) {
+               start_pvcalarm(&global_pvcalarm.data[i], global_pvcalarm.interval);
+       }
 
        atomic_set(&global_pvcalarm.state, S_ENABLED);
        return 0;
@@ -170,87 +161,36 @@ int disable_pvcalarms()
        global_pvcalarm.interval = 0;
        global_pvcalarm.callback = NULL;
        global_pvcalarm.handler = NULL;
-       vcore_poke_pvcalarm = NULL;
-
-       /* Loop through all allocated pvcalarm_data structs and disable any alarms
-        * associated with them */
-       struct pvcalarm_data *pvcalarm_data;
-       SLIST_FOREACH(pvcalarm_data, &global_pvcalarm.list, next) {
-               stop_pvcalarm(pvcalarm_data);
-               pvcalarm_data->state = S_DISABLED;
-       }
-
-       atomic_set(&global_pvcalarm.state, S_DISABLED);
-}
 
+       /* Stop the timer on all vcores */
+       for (int i=0; i<max_vcores(); i++)
+               stop_pvcalarm(&global_pvcalarm.data[i]);
 
-/* Allocate a new pvcalarm_data structure and add it to the global list of
- * registered pvcalarm_data structures */
-static struct pvcalarm_data *new_pvcalarm_data()
-{
-       struct pvcalarm_data *pvcalarm_data = malloc(sizeof(struct pvcalarm_data));
-       pvcalarm_data->ctlfd = 0;
-       pvcalarm_data->timerfd = 0;
-       pvcalarm_data->alarmid = 0;
-       pvcalarm_data->state = S_DISABLED;
-
-       spin_pdr_lock(&global_pvcalarm.list_lock);
-       SLIST_INSERT_HEAD(&global_pvcalarm.list, pvcalarm_data, next);
-       spin_pdr_unlock(&global_pvcalarm.list_lock);
-       return pvcalarm_data;
-}
-
-/* Free a pvcalarm_data structure allocated with new_pvcalarm_data() and remove
- * it from the global list of registered pvcalarm_data structures */
-static void delete_pvcalarm_data(struct pvcalarm_data *pvcalarm_data)
-{
-       SLIST_REMOVE(&global_pvcalarm.list, pvcalarm_data, pvcalarm_data, next);
-       free(pvcalarm_data);
+       atomic_set(&global_pvcalarm.state, S_DISABLED);
 }
 
 /* Initialize a specific pvcalarm.  This happens once per vcore as it comes
  * online and the pvcalarm service is active */
-static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data)
+static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data, int vcoreid)
 {
-       int ctlfd, timerfd, alarmid, ret;
-       char buf[20];
-       char path[32];
+       int ctlfd, timerfd, alarmid, ev_flags;
        struct event_queue *ev_q;
 
-       ctlfd = open("#A/clone", O_RDWR | O_CLOEXEC);
-       if (ctlfd < 0) {
-               perror("Pvcalarm: Can't clone an alarm");
-               return;
-       }
-       ret = read(ctlfd, buf, sizeof(buf) - 1);
-       if (ret <= 0) {
-               if (!ret)
-                       printf("Pvcalarm: Got early EOF from ctl\n");
-               else
-                       perror("Pvcalarm: 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("Pvcalarm: Can't open timer");
+       if (devalarm_get_fds(&ctlfd, &timerfd, &alarmid)) {
+               perror("Pvcalarm: alarm setup");
                return;
        }
-       alarm_dispatch_register(alarmid, handle_pvcalarm);
-       if (!(ev_q = get_big_event_q())) {
+       register_ev_handler(EV_ALARM, handle_pvcalarm, 0);
+       ev_flags = EVENT_IPI | EVENT_VCORE_PRIVATE;
+       ev_q = get_eventq_vcpd(vcoreid, ev_flags);
+       if (!ev_q) {
                perror("Pvcalarm: Failed ev_q");
                return;
        }
-       ev_q->ev_vcore = vcore_id();
-       /* We don't want a FALLBACK since we only care about getting the event on
-        * each specific vcore */
-       ev_q->ev_flags = EVENT_IPI | EVENT_INDIR;
-       ret = snprintf(path, sizeof(path), "evq %llx", ev_q);
-       ret = write(ctlfd, path, ret);
-       if (ret <= 0) {
-               perror("Pvcalarm: Failed to write ev_q");
+       ev_q->ev_vcore = vcoreid;
+       ev_q->ev_flags = ev_flags;
+       if (devalarm_set_evq(timerfd, ev_q, alarmid)) {
+               perror("Pvcalarm: Failed to set evq");
                return;
        }
        /* now the alarm is all set, just need to write the timer whenever we want
@@ -261,84 +201,75 @@ static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data)
 }
 
 /* TODO: implement a way to completely remove each per-vcore alarm and
- * deregister it from the #A device */
+ * deregister it from the #alarm device */
 
 /* A preamble function to run anytime we are about to do anything on behalf of
  * the pvcalarms while in vcore context.  This preamble is necessary to ensure
  * we maintain proper invariants when enabling and disabling the pvcalarm
  * service in a running application. */
-static void __vcore_preamble()
+static inline bool __vcore_preamble()
 {
+       int state;
        assert(in_vcore_context());
-       if (atomic_cas(&global_pvcalarm.state, S_DISABLED, S_DISABLED))
-               return;
-       if (atomic_cas(&global_pvcalarm.state, S_DISABLING, S_DISABLING))
-               return;
        __sync_fetch_and_add(&global_pvcalarm.busy_count, 1);
+       cmb();  /* order the state read after the incref.  __sync provides cpu mb */
+       state = atomic_read(&global_pvcalarm.state);
+       if (state == S_DISABLED || state == S_DISABLING)
+               goto disabled;
+       return true;
+disabled:
+       __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
+       return false;
 }
 
 /* The counterpart to the __vcore_preamble() function */
-static void __vcore_postamble()
+static inline void __vcore_postamble()
 {
        __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
 }
 
-/* The actual implementation of the vcore_poke_pvcalarm() function. When the
- * pvcalarm service is enabled vcore_poke_pvcalarm will point to this function,
- * otherwise vcore_poke_pvcalarm == NULL.  In this way, vcore_entry() can avoid
- * the overhead of always running this function by doing  a simple check for
- * NULL before running it. */
-void __vcore_poke_pvcalarm()
-{
-       __vcore_preamble();
-       uint32_t vcoreid = vcore_id();
-       if (!GET_BITMASK_BIT(global_pvcalarm.vcores, vcoreid)) {
-               SET_BITMASK_BIT(global_pvcalarm.vcores, vcoreid);
-               __pvcalarm_data = new_pvcalarm_data();
-               init_pvcalarm(__pvcalarm_data);
-       }
-       if (__pvcalarm_data->state == S_DISABLED) {
-               __pvcalarm_data->state = S_ENABLED;
-               start_pvcalarm(__pvcalarm_data, global_pvcalarm.interval);
-       }
-       __vcore_postamble();
-}
-
 /* The global handler function.  It simply calls the proper underlying handler
  * function depending on whether the service is set for the REAL or PERF
  * policy. */
-static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type)
+static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type,
+                            void *data)
 {
-       global_pvcalarm.handler(ev_msg, ev_type);
+       struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcore_id()];
+
+       if (devalarm_get_id(ev_msg) != pvcalarm_data->alarmid)
+               return;
+       if (!__vcore_preamble()) return;
+       global_pvcalarm.handler(ev_msg, ev_type, data);
+       __vcore_postamble();
 }
 
 /* The pvcalarm handler for the REAL policy.  Simply call the registered
  * callback and restart the interval alarm. */
-static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type)
+static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type,
+                              void *data)
 {
-       __vcore_preamble();
        global_pvcalarm.callback();
-       start_pvcalarm(__pvcalarm_data, global_pvcalarm.interval);
-       __vcore_postamble();
+       start_pvcalarm(&global_pvcalarm.data[vcore_id()], global_pvcalarm.interval);
 }
 
 /* The pvcalarm handler for the PROF policy.  Account for any time the vcore
  * has been offline.  Only when the uptime since the last interval is equal to
  * the interval time do we run the callback function.  Otherwise we restart the
  * alarm to make up the difference. */
-static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type)
-{
-       __vcore_preamble();
-       uint32_t uptime = vcore_account_uptime_ticks(vcore_id());
-       uint64_t diff = uptime - __pvcalarm_data->start_uptime;
+static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
+                              void *data)
+{ 
+       int vcoreid = vcore_id();
+       struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcoreid];
+       uint32_t uptime = vcore_account_uptime_ticks(vcoreid);
+       uint64_t diff = uptime - pvcalarm_data->start_uptime;
 
        if (diff < global_pvcalarm.interval) {
                uint64_t remaining = global_pvcalarm.interval - diff;
-               run_pvcalarm(__pvcalarm_data, remaining);
+               run_pvcalarm(pvcalarm_data, remaining);
        } else {
                global_pvcalarm.callback();
-               start_pvcalarm(__pvcalarm_data, global_pvcalarm.interval);
+               start_pvcalarm(pvcalarm_data, global_pvcalarm.interval);
        }
-       __vcore_postamble();
 }