x86: Upgrade backtrace
[akaros.git] / kern / arch / x86 / perfmon.c
index 2bb7e28..c313c91 100644 (file)
@@ -42,7 +42,6 @@
 #include <smp.h>
 #include <atomic.h>
 #include <core_set.h>
 #include <smp.h>
 #include <atomic.h>
 #include <core_set.h>
-#include <kref.h>
 #include <percpu.h>
 #include <kmalloc.h>
 #include <err.h>
 #include <percpu.h>
 #include <kmalloc.h>
 #include <err.h>
@@ -198,6 +197,29 @@ static void perfmon_set_unfixed_trigger(unsigned int idx, uint64_t count)
        write_msr(MSR_IA32_PERFCTR0 + idx, write_val);
 }
 
        write_msr(MSR_IA32_PERFCTR0 + idx, write_val);
 }
 
+/* Helper: sets errno/errstr based on the error code returned from the core.  We
+ * don't have a great way to get errors back from smp_do_in_cores() commands.
+ * We use negative counter values (e.g. i = -EBUSY) to signal an error of a
+ * certain type.  This converts that to something useful for userspace. */
+static void perfmon_convert_error(int err_code, int core_id)
+{
+       switch (err_code) {
+       case EBUSY:
+               set_error(err_code, "Fixed perf counter is busy on core %d", core_id);
+               break;
+       case ENOSPC:
+               set_error(err_code, "Perf counter idx out of range on core %d",
+                         core_id);
+               break;
+       case ENOENT:
+               set_error(err_code, "Perf counter not set on core %d", core_id);
+               break;
+       default:
+               set_error(err_code, "Unknown perf counter error on core %d", core_id);
+               break;
+       };
+}
+
 static void perfmon_do_cores_alloc(void *opaque)
 {
        struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
 static void perfmon_do_cores_alloc(void *opaque)
 {
        struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
@@ -211,7 +233,7 @@ static void perfmon_do_cores_alloc(void *opaque)
 
                i = PMEV_GET_EVENT(pa->ev.event);
                if (i >= (int) cpu_caps.fix_counters_x_proc) {
 
                i = PMEV_GET_EVENT(pa->ev.event);
                if (i >= (int) cpu_caps.fix_counters_x_proc) {
-                       i = -EINVAL;
+                       i = -ENOSPC;
                } else if (!perfmon_fix_event_available(i, fxctrl_value)) {
                        i = -EBUSY;
                } else {
                } else if (!perfmon_fix_event_available(i, fxctrl_value)) {
                        i = -EBUSY;
                } else {
@@ -284,6 +306,30 @@ static void perfmon_do_cores_free(void *opaque)
        pa->cores_counters[coreno] = (counter_t) err;
 }
 
        pa->cores_counters[coreno] = (counter_t) err;
 }
 
+/* Helper: Reads a fixed counter's value.  Returns the max amount possible if
+ * the counter overflowed. */
+static uint64_t perfmon_read_fixed_counter(int ccno)
+{
+       uint64_t overflow_status = read_msr(MSR_CORE_PERF_GLOBAL_STATUS);
+
+       if (overflow_status & (1ULL << (32 + ccno)))
+               return (1ULL << cpu_caps.bits_x_fix_counter) - 1;
+       else
+               return read_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno);
+}
+
+/* Helper: Reads an unfixed counter's value.  Returns the max amount possible if
+ * the counter overflowed. */
+static uint64_t perfmon_read_unfixed_counter(int ccno)
+{
+       uint64_t overflow_status = read_msr(MSR_CORE_PERF_GLOBAL_STATUS);
+
+       if (overflow_status & (1ULL << ccno))
+               return (1ULL << cpu_caps.bits_x_counter) - 1;
+       else
+               return read_msr(MSR_IA32_PERFCTR0 + ccno);
+}
+
 static void perfmon_do_cores_status(void *opaque)
 {
        struct perfmon_status_env *env = (struct perfmon_status_env *) opaque;
 static void perfmon_do_cores_status(void *opaque)
 {
        struct perfmon_status_env *env = (struct perfmon_status_env *) opaque;
@@ -293,11 +339,9 @@ static void perfmon_do_cores_status(void *opaque)
 
        spin_lock_irqsave(&cctx->lock);
        if (perfmon_is_fixed_event(&env->pa->ev))
 
        spin_lock_irqsave(&cctx->lock);
        if (perfmon_is_fixed_event(&env->pa->ev))
-               env->pef->cores_values[coreno] =
-                   read_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno);
+               env->pef->cores_values[coreno] = perfmon_read_fixed_counter(ccno);
        else
        else
-               env->pef->cores_values[coreno] =
-                   read_msr(MSR_IA32_PERFCTR0 + ccno);
+               env->pef->cores_values[coreno] = perfmon_read_unfixed_counter(ccno);
        spin_unlock_irqsave(&cctx->lock);
 }
 
        spin_unlock_irqsave(&cctx->lock);
 }
 
@@ -485,7 +529,8 @@ int perfmon_open_event(const struct core_set *cset, struct perfmon_session *ps,
 
                        if (unlikely(ccno < 0)) {
                                perfmon_destroy_alloc(pa);
 
                        if (unlikely(ccno < 0)) {
                                perfmon_destroy_alloc(pa);
-                               return (int) ccno;
+                               perfmon_convert_error(-(int)ccno, i);
+                               return -1;
                        }
                }
        }
                        }
                }
        }
@@ -565,36 +610,23 @@ void perfmon_free_event_status(struct perfmon_status *pef)
        kfree(pef);
 }
 
        kfree(pef);
 }
 
-static void perfmon_release_session(struct kref *kref)
-{
-       struct perfmon_session *ps =
-           container_of(kref, struct perfmon_session, ref);
-
-       for (int i = 0; i < ARRAY_SIZE(ps->allocs); i++) {
-               struct perfmon_alloc *pa = ps->allocs[i];
-
-               if (pa)
-                       perfmon_destroy_alloc(pa);
-       }
-       kfree(ps);
-}
-
 struct perfmon_session *perfmon_create_session(void)
 {
        struct perfmon_session *ps = kzmalloc(sizeof(struct perfmon_session),
                                              MEM_WAIT);
 
 struct perfmon_session *perfmon_create_session(void)
 {
        struct perfmon_session *ps = kzmalloc(sizeof(struct perfmon_session),
                                              MEM_WAIT);
 
-       kref_init(&ps->ref, perfmon_release_session, 1);
        qlock_init(&ps->qlock);
        return ps;
 }
 
        qlock_init(&ps->qlock);
        return ps;
 }
 
-void perfmon_get_session(struct perfmon_session *ps)
-{
-       kref_get(&ps->ref, 1);
-}
-
 void perfmon_close_session(struct perfmon_session *ps)
 {
 void perfmon_close_session(struct perfmon_session *ps)
 {
-       kref_put(&ps->ref);
+       struct perfmon_alloc *pa;
+
+       for (int i = 0; i < ARRAY_SIZE(ps->allocs); i++) {
+               pa = ps->allocs[i];
+               if (pa)
+                       perfmon_destroy_alloc(pa);
+       }
+       kfree(ps);
 }
 }