]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/pmc/pmc.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / osfmk / pmc / pmc.c
diff --git a/osfmk/pmc/pmc.c b/osfmk/pmc/pmc.c
deleted file mode 100644 (file)
index a637a1b..0000000
+++ /dev/null
@@ -1,2953 +0,0 @@
-/*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <kern/kalloc.h>
-#include <kern/kern_types.h>
-#include <kern/locks.h>
-#include <kern/misc_protos.h>
-#include <kern/task.h>
-#include <kern/thread.h>
-#include <kern/zalloc.h>
-#include <machine/machine_cpu.h>
-
-#include <pmc/pmc.h>
-
-#include <libkern/OSAtomic.h>
-
-#if defined(__i386__) || defined(__x86_64__)
-#include <i386/mp.h>
-#endif
-
-#if CONFIG_COUNTERS
-
-/* various debug logging enable */
-#undef DEBUG_COUNTERS
-
-typedef uint8_t pmc_state_event_t;
-
-#define PMC_STATE_EVENT_START                          0
-#define PMC_STATE_EVENT_STOP                           1
-#define PMC_STATE_EVENT_FREE                           2
-#define PMC_STATE_EVENT_INTERRUPT                      3
-#define PMC_STATE_EVENT_END_OF_INTERRUPT       4
-#define PMC_STATE_EVENT_CONTEXT_IN                     5
-#define PMC_STATE_EVENT_CONTEXT_OUT                    6
-#define PMC_STATE_EVENT_LOAD_FINISHED          7
-#define PMC_STATE_EVENT_STORE_FINISHED         8
-
-/* PMC spin timeouts */
-#define PMC_SPIN_THRESHOLD     10      /* Number of spins to allow before checking mach_absolute_time() */
-#define PMC_SPIN_TIMEOUT_US    10      /* Time in microseconds before the spin causes an assert */
-
-uint64_t pmc_spin_timeout_count = 0;   /* Number of times where a PMC spin loop causes a timeout */
-
-#ifdef DEBUG_COUNTERS
-#      include <pexpert/pexpert.h>
-#      define COUNTER_DEBUG(...) \
-       do { \
-               kprintf("[%s:%s][%u] ", __FILE__, __PRETTY_FUNCTION__, cpu_number()); \
-               kprintf(__VA_ARGS__); \
-       } while(0)
-
-#      define PRINT_PERF_MON(x)        \
-       do { \
-               kprintf("perfmon: %p (obj: %p refCt: %u switchable: %u)\n", \
-                       x, x->object, x->useCount, \
-                       (x->methods.flags & PERFMON_FLAG_SUPPORTS_CONTEXT_SWITCHING) ? \
-                       1 : 0); \
-       } while(0)
-
-static const char const * pmc_state_state_name(pmc_state_t state) {
-       switch (PMC_STATE_STATE(state)) {
-               case PMC_STATE_STATE_INVALID:
-                       return "INVALID";
-               case PMC_STATE_STATE_STOP:
-                       return "STOP";
-               case PMC_STATE_STATE_CAN_RUN:
-                       return "CAN_RUN";
-               case PMC_STATE_STATE_LOAD:
-                       return "LOAD";
-               case PMC_STATE_STATE_RUN:
-                       return "RUN";
-               case PMC_STATE_STATE_STORE:
-                       return "STORE";
-               case PMC_STATE_STATE_INTERRUPT:
-                       return "INTERRUPT";
-               case PMC_STATE_STATE_DEALLOC:
-                       return "DEALLOC";
-               default:
-                       return "UNKNOWN";
-       }
-}
-
-static const char const * pmc_state_event_name(pmc_state_event_t event) {
-       switch (event) {
-               case PMC_STATE_EVENT_START:
-                       return "START";
-               case PMC_STATE_EVENT_STOP:
-                       return "STOP";
-               case PMC_STATE_EVENT_FREE:
-                       return "FREE";
-               case PMC_STATE_EVENT_INTERRUPT:
-                       return "INTERRUPT";
-               case PMC_STATE_EVENT_END_OF_INTERRUPT:
-                       return "END OF INTERRUPT";
-               case PMC_STATE_EVENT_CONTEXT_IN:
-                       return "CONTEXT IN";
-               case PMC_STATE_EVENT_CONTEXT_OUT:
-                       return "CONTEXT OUT";
-               case PMC_STATE_EVENT_LOAD_FINISHED:
-                       return "LOAD_FINISHED";
-               case PMC_STATE_EVENT_STORE_FINISHED:
-                       return "STORE_FINISHED";
-               default:
-                       return "UNKNOWN";
-       }
-}
-
-#      define PMC_STATE_FORMAT "<%s, %u, %s%s%s>"
-#      define PMC_STATE_ARGS(x)        pmc_state_state_name(x), PMC_STATE_CONTEXT_COUNT(x), ((PMC_STATE_FLAGS(x) & PMC_STATE_FLAGS_INTERRUPTING) ? "I" : ""), \
-                                       ((PMC_STATE_FLAGS(x) & PMC_STATE_FLAGS_STOPPING) ? "S" : ""), ((PMC_STATE_FLAGS(x) & PMC_STATE_FLAGS_DEALLOCING) ? "D" : "")
-#else
-#      define COUNTER_DEBUG(...)
-#      define PRINT_PERF_MON(x)
-#      define PMC_STATE_FORMAT
-#      define PMC_STATE_ARGS(x)
-#endif
-
-/*!struct
- * pmc_config is the data behind a pmc_config_t.
- * @member object A pointer to an instance of IOPerformanceCounterConfiguration
- * @member method A pointer to a method to call to handle PMI.
- * @member interrupt_after_value Cause a PMI after the counter counts this many
- * events.
- * @member refCon Passed to the @method method as the refCon argument.
- */
-struct pmc_config {
-       pmc_config_object_t object;
-       volatile pmc_interrupt_method_t method;
-       uint64_t interrupt_after_value;
-       void *refCon;
-};
-
-/*
- * Allocation Zones
- * 
- * Two allocation zones - Perf zone small and Perf zone big.
- * Each zone has associated maximums, defined below.
- * The small zone is the max of the smallest allocation objects (all sizes on
- * K64):
- *     perf_monitor_t - 48 bytes
- *             perf_monitor_methods_t - 28 bytes
- *     pmc_reservation_t - 48 bytes
- *  pmc_config_t - 32 bytes
- * perf_small_zone unit size is (on K64) 48 bytes
- * perf_small_zone max count must be max number of perf monitors, plus (max
- * number of reservations * 2). The "*2" is because each reservation has a
- * pmc_config_t within.
- *
- * Big zone is max of the larger allocation units
- *     pmc_t - 144 bytes
- *             pmc_methods_t - 116 bytes
- * perf_big_zone unit size is (on K64) 144 bytes
- * perf_big_zone max count is the max number of PMCs we support.
- */
-
-static zone_t perf_small_zone = NULL;
-#define MAX_PERF_SMALLS                (256 + 8196 + 8196)
-#define PERF_SMALL_UNIT_SZ     (MAX(MAX(sizeof(struct perf_monitor), \
-       sizeof(struct pmc_reservation)), sizeof(struct pmc_config))) 
-
-static zone_t perf_big_zone = NULL;
-#define MAX_PERF_BIGS          (1024)
-#define PERF_BIG_UNIT_SZ       (sizeof(struct pmc))
-
-/*
- * Locks and Lock groups
- */
-static lck_grp_t *pmc_lock_grp = LCK_GRP_NULL;
-static lck_grp_attr_t *pmc_lock_grp_attr;
-static lck_attr_t *pmc_lock_attr;
-
-/* PMC tracking queue locks */
-
-static lck_mtx_t  cpu_monitor_queue_mutex;   /* protects per-cpu queues at initialisation time */
-static lck_spin_t perf_monitor_queue_spin;   /* protects adding and removing from queue */
-static lck_spin_t perf_counters_queue_spin;  /* protects adding and removing from queue */
-
-/* Reservation tracking queues lock */
-static lck_spin_t reservations_spin;
-
-/*
- * Tracking queues
- *
- * Keeps track of registered perf monitors and perf counters
- */
-
-static queue_head_t **cpu_monitor_queues = NULL;
-
-static queue_head_t *perf_monitors_queue = NULL;
-static volatile uint32_t perf_monitors_count = 0U;
-
-static queue_head_t *perf_counters_queue = NULL;
-static volatile uint32_t perf_counters_count = 0U;
-
-/* 
- * Reservation queues
- *
- * Keeps track of all system, task, and thread-level reservations (both active and
- * inactive).
- *
- * We track them all here (rather than in their respective task or thread only)
- * so that we can inspect our tracking data directly (rather than peeking at
- * every task and thread) to determine if/when a new reservation would
- * constitute a conflict.
- */
-static queue_head_t *system_reservations = NULL;
-static volatile uint32_t system_reservation_count = 0U;
-
-static queue_head_t *task_reservations = NULL;
-static volatile uint32_t task_reservation_count = 0U;
-
-static queue_head_t *thread_reservations = NULL;
-static volatile uint32_t thread_reservation_count = 0U;
-
-#if XNU_KERNEL_PRIVATE
-
-/*
- * init_pmc_locks creates and initializes all the locks and lock groups and lock
- * attributes required for the pmc sub-system.
- */
-static void init_pmc_locks(void) {
-       pmc_lock_attr = lck_attr_alloc_init();
-       assert(pmc_lock_attr);
-
-       pmc_lock_grp_attr = lck_grp_attr_alloc_init();
-       assert(pmc_lock_grp_attr);
-
-       pmc_lock_grp = lck_grp_alloc_init("pmc", pmc_lock_grp_attr);
-       assert(pmc_lock_grp);
-
-       lck_spin_init(&perf_monitor_queue_spin, pmc_lock_grp, pmc_lock_attr);
-       lck_spin_init(&perf_counters_queue_spin, pmc_lock_grp, pmc_lock_attr);
-
-       lck_spin_init(&reservations_spin, pmc_lock_grp, pmc_lock_attr);
-
-       lck_mtx_init(&cpu_monitor_queue_mutex, pmc_lock_grp, pmc_lock_attr);
-}
-
-/*
- * init_pmc_zones initializes the allocation zones used by the pmc subsystem
- */
-static void init_pmc_zones(void) {
-       perf_small_zone = zinit(PERF_SMALL_UNIT_SZ, 
-               MAX_PERF_SMALLS * PERF_SMALL_UNIT_SZ, MAX_PERF_SMALLS, 
-               "pmc.small zone");
-
-       assert(perf_small_zone);
-
-       perf_big_zone = zinit(PERF_BIG_UNIT_SZ,
-               MAX_PERF_BIGS * PERF_BIG_UNIT_SZ, MAX_PERF_BIGS, 
-               "pmc.big zone");
-
-       assert(perf_big_zone);
-}
-
-/*
- * init_pmc_queues allocates and initializes the tracking queues for
- * registering and reserving individual pmcs and perf monitors.
- */
-static void init_pmc_queues(void) {
-    
-       perf_monitors_queue = (queue_head_t*)kalloc(sizeof(queue_head_t));
-       assert(perf_monitors_queue);
-
-       queue_init(perf_monitors_queue);
-
-       perf_counters_queue = (queue_head_t*)kalloc(sizeof(queue_head_t));
-       assert(perf_counters_queue);
-
-       queue_init(perf_counters_queue);
-
-       system_reservations = (queue_head_t*)kalloc(sizeof(queue_t));
-       assert(system_reservations);
-
-       queue_init(system_reservations);
-
-       task_reservations = (queue_head_t*)kalloc(sizeof(queue_head_t));
-       assert(task_reservations);
-
-       queue_init(task_reservations);
-
-       thread_reservations = (queue_head_t*)kalloc(sizeof(queue_head_t));
-       assert(thread_reservations);
-
-       queue_init(thread_reservations);
-}
-
-/*
- * pmc_bootstrap brings up all the necessary infrastructure required to use the
- * pmc sub-system.
- */
-__private_extern__
-void pmc_bootstrap(void) {
-       /* build our alloc zones */
-       init_pmc_zones();
-
-       /* build the locks */
-       init_pmc_locks();
-
-       /* build our tracking queues */
-       init_pmc_queues();
-}
-
-#endif /* XNU_KERNEL_PRIVATE */
-
-/*
- * Perf Monitor Internals
- */
-
-static perf_monitor_t perf_monitor_alloc(void) {
-       /* perf monitors come from the perf small zone */
-       return (perf_monitor_t)zalloc(perf_small_zone);
-}
-
-static void perf_monitor_free(void *pm) {
-       zfree(perf_small_zone, pm);
-}
-
-static void perf_monitor_init(perf_monitor_t pm, int cpu) {
-       assert(pm);
-
-       pm->object = NULL;
-
-       bzero(&(pm->methods), sizeof(perf_monitor_methods_t));
-
-       pm->useCount = 1;       /* initial retain count of 1, for caller */
-       
-       pm->reservedCounters = 0;
-    
-       pm->cpu = cpu;
-
-       pm->link.next = pm->link.prev = (queue_entry_t)NULL;
-       pm->cpu_link.next = pm->cpu_link.prev = (queue_entry_t)NULL;
-}
-
-/*
- * perf_monitor_dequeue removes the given perf_monitor_t from the
- * perf_monitor_queue, thereby unregistering it with the system.
- */
-static void perf_monitor_dequeue(perf_monitor_t pm) {
-       lck_spin_lock(&perf_monitor_queue_spin);
-       
-       if (pm->methods.flags & PERFMON_FLAG_REQUIRES_IDLE_NOTIFICATIONS) {
-               /* If this flag is set, the monitor is already validated to be 
-                * accessible from a single cpu only.
-                */
-               queue_remove(cpu_monitor_queues[pm->cpu], pm, perf_monitor_t, cpu_link); 
-       }
-       
-       /* 
-        * remove the @pm object from the @perf_monitor_queue queue (it is of type
-        * <perf_monitor_t> and has a field called @link that is the queue_link_t
-        */
-       queue_remove(perf_monitors_queue, pm, perf_monitor_t, link);
-
-       perf_monitors_count--;
-
-       lck_spin_unlock(&perf_monitor_queue_spin);
-}
-
-/*
- * perf_monitor_enqueue adds the given perf_monitor_t to the perf_monitor_queue,
- * thereby registering it for use with the system.
- */
-static void perf_monitor_enqueue(perf_monitor_t pm) {
-    
-       lck_mtx_lock(&cpu_monitor_queue_mutex);
-       lck_spin_lock(&perf_monitor_queue_spin);
-
-       if (pm->cpu >= 0) {
-               /* Deferred initialisation; saves memory and permits ml_get_max_cpus()
-                * to block until cpu initialisation is complete.
-                */
-               if (!cpu_monitor_queues) {
-                       uint32_t max_cpus;
-                       queue_head_t **queues;
-                       uint32_t i;
-               
-                       lck_spin_unlock(&perf_monitor_queue_spin);
-               
-                       max_cpus = ml_get_max_cpus();
-
-                       queues = (queue_head_t**)kalloc(sizeof(queue_head_t*) * max_cpus);
-                       assert(queues);
-                       for (i = 0; i < max_cpus; i++) {
-                               queue_head_t *queue = (queue_head_t*)kalloc(sizeof(queue_head_t));
-                               assert(queue);
-                               queue_init(queue);
-                               queues[i] = queue;
-                       }
-               
-                       lck_spin_lock(&perf_monitor_queue_spin);
-               
-                       cpu_monitor_queues = queues;
-               }
-           
-               queue_enter(cpu_monitor_queues[pm->cpu], pm, perf_monitor_t, cpu_link);
-       }
-       
-       queue_enter(perf_monitors_queue, pm, perf_monitor_t, link);
-       perf_monitors_count++;
-       
-       lck_spin_unlock(&perf_monitor_queue_spin);
-       lck_mtx_unlock(&cpu_monitor_queue_mutex);
-}
-
-/*
- * perf_monitor_reference increments the reference count for the given
- * perf_monitor_t.
- */
-static void perf_monitor_reference(perf_monitor_t pm) {
-       assert(pm);
-
-       OSIncrementAtomic(&(pm->useCount));
-}
-
-/*
- * perf_monitor_deallocate decrements the reference count for the given
- * perf_monitor_t.  If the reference count hits 0, the object is released back
- * to the perf_small_zone via a call to perf_monitor_free().
- */
-static void perf_monitor_deallocate(perf_monitor_t pm) {
-       assert(pm);
-
-       /* If we just removed the last reference count */
-       if(1 == OSDecrementAtomic(&(pm->useCount))) {
-               /* Free the object */
-               perf_monitor_free(pm);
-       }
-}
-
-/*
- * perf_monitor_find attempts to find a perf_monitor_t that corresponds to the
- * given C++ object pointer that was used when registering with the subsystem.
- *
- * If found, the method returns the perf_monitor_t with an extra reference 
- * placed on the object (or NULL if not
- * found).
- *
- * NOTE: Caller must use perf_monitor_deallocate to remove the extra reference after
- * calling perf_monitor_find.
- */
-static perf_monitor_t perf_monitor_find(perf_monitor_object_t monitor) {
-       assert(monitor);
-       perf_monitor_t element = NULL;
-       perf_monitor_t found = NULL;
-
-       lck_spin_lock(&perf_monitor_queue_spin);
-       
-       queue_iterate(perf_monitors_queue, element, perf_monitor_t, link) {
-               if(element->object == monitor) {
-                       perf_monitor_reference(element);
-                       found = element;
-                       break;
-               }
-       }
-
-       lck_spin_unlock(&perf_monitor_queue_spin);
-
-       return found;
-}
-
-/*
- * perf_monitor_add_pmc adds a newly registered PMC to the perf monitor it is
- * associated with.
- */
-
-static void perf_monitor_add_pmc(perf_monitor_t pm, pmc_t pmc __unused) {
-       assert(pm);
-       assert(pmc);
-
-       /* Today, we merely add a reference count now that a new pmc is attached */
-       perf_monitor_reference(pm);
-}
-
-/*
- * perf_monitor_remove_pmc removes a newly *un*registered PMC from the perf
- * monitor it is associated with.
- */
-static void perf_monitor_remove_pmc(perf_monitor_t pm, pmc_t pmc __unused) {
-       assert(pm);
-       assert(pmc);
-
-       /* Today, we merely remove a reference count now that the pmc is detached */
-       perf_monitor_deallocate(pm);
-}
-
-/*
- * Perf Counter internals
- */
-
-static pmc_t pmc_alloc(void) {
-       return (pmc_t)zalloc(perf_big_zone);
-}
-
-static void pmc_free(void *pmc) {
-       zfree(perf_big_zone, pmc);
-}
-
-/*
- * pmc_init initializes a newly allocated pmc_t
- */
-static void pmc_init(pmc_t pmc) {
-       assert(pmc);
-
-       pmc->object = NULL;
-       pmc->monitor = NULL;
-
-       bzero(&pmc->methods, sizeof(pmc_methods_t));
-
-       /* One reference for the caller */
-       pmc->useCount = 1;
-}
-
-/*
- * pmc_reference increments the reference count of the given pmc_t
- */
-static void pmc_reference(pmc_t pmc) {
-       assert(pmc);
-
-       OSIncrementAtomic(&(pmc->useCount));
-}
-
-/*
- * pmc_deallocate decrements the reference count of the given pmc_t. If the
- * reference count hits zero, the given pmc_t is deallocated and released back
- * to the allocation zone.
- */
-static void pmc_deallocate(pmc_t pmc) {
-       assert(pmc);
-
-       /* If we just removed the last reference count */
-       if(1 == OSDecrementAtomic(&(pmc->useCount))) {
-               /* Free the pmc */
-               pmc_free(pmc);
-       }
-}
-
-/*
- * pmc_dequeue removes the given, newly *un*registered pmc from the
- * perf_counters_queue.
- */
-static void pmc_dequeue(pmc_t pmc) {
-       lck_spin_lock(&perf_counters_queue_spin);
-
-       queue_remove(perf_counters_queue, pmc, pmc_t, link);
-
-       perf_counters_count--;
-
-       lck_spin_unlock(&perf_counters_queue_spin);
-}
-
-/*
- * pmc_enqueue adds the given, newly registered pmc to the perf_counters_queue
- */
-static void pmc_enqueue(pmc_t pmc) {
-       lck_spin_lock(&perf_counters_queue_spin);
-
-       queue_enter(perf_counters_queue, pmc, pmc_t, link);
-
-       perf_counters_count++;
-
-       lck_spin_unlock(&perf_counters_queue_spin);
-}
-
-/*
- * pmc_find attempts to locate a pmc_t that was registered with the given
- * pmc_object_t pointer.  If found, it returns the pmc_t with an extra reference
- * which must be dropped by the caller by calling pmc_deallocate().
- */
-static pmc_t pmc_find(pmc_object_t object) {
-       assert(object);
-
-       lck_spin_lock(&perf_counters_queue_spin);
-       
-       pmc_t element = NULL;
-       pmc_t found = NULL;
-
-       queue_iterate(perf_counters_queue, element, pmc_t, link) {
-               if(element->object == object) {
-                       pmc_reference(element);
-                       found = element;
-                       break;
-               }
-       }
-
-       lck_spin_unlock(&perf_counters_queue_spin);
-
-       return found;
-}
-
-/*
- * Config internals
- */
-
-/* Allocate a pmc_config_t */
-static pmc_config_t pmc_config_alloc(pmc_t pmc __unused) {
-       return (pmc_config_t)zalloc(perf_small_zone);
-}
-
-/* Free a pmc_config_t, and underlying pmc_config_object_t (if needed) */
-static void pmc_config_free(pmc_t pmc, pmc_config_t config) {
-       assert(pmc);
-       assert(config);
-
-       if(config->object) {
-               pmc->methods.free_config(pmc->object, config->object);
-               config->object = NULL;
-       }
-
-       zfree(perf_small_zone, config);
-}
-
-static kern_return_t pmc_open(pmc_t pmc) {
-       assert(pmc);
-       assert(pmc->object);
-       assert(pmc->open_object);
-
-       return pmc->methods.open(pmc->object, pmc->open_object);
-}
-
-static kern_return_t pmc_close(pmc_t pmc) {
-       assert(pmc);
-       assert(pmc->object);
-       assert(pmc->open_object);
-
-       return pmc->methods.close(pmc->object, pmc->open_object);
-}
-
-/*
- * Reservation Internals
- */
-
-static kern_return_t pmc_internal_reservation_set_pmc(pmc_reservation_t resv, pmc_t pmc);
-static void pmc_internal_reservation_store(pmc_reservation_t reservation);
-static void pmc_internal_reservation_load(pmc_reservation_t reservation);
-
-static pmc_reservation_t reservation_alloc(void) {
-       /* pmc reservations come from the perf small zone */
-       return (pmc_reservation_t)zalloc(perf_small_zone);
-}
-
-/*
- * reservation_free deallocates and releases all resources associated with the
- * given pmc_reservation_t.  This includes freeing the config used to create the
- * reservation, decrementing the reference count for the pmc used to create the
- * reservation, and deallocating the reservation's memory.
- */
-static void reservation_free(pmc_reservation_t resv) {
-       /* Free config */
-       if(resv->config) {
-               assert(resv->pmc);
-
-               pmc_free_config(resv->pmc, resv->config);
-
-               resv->config = NULL;
-       }
-
-       /* release PMC */
-       (void)pmc_internal_reservation_set_pmc(resv, NULL);
-
-       /* Free reservation */
-       zfree(perf_small_zone, resv);
-}
-
-/*
- * reservation_init initializes a newly created reservation.
- */
-static void reservation_init(pmc_reservation_t resv) {
-       assert(resv);
-
-       resv->pmc = NULL;
-       resv->config = NULL;
-       resv->value = 0ULL;
-
-       resv->flags = 0U;
-       resv->state = PMC_STATE(PMC_STATE_STATE_STOP, 0, 0);
-       resv->active_last_context_in = 0U;
-
-       /*
-        * Since this member is a union, we only need to set either the task 
-        * or thread to NULL.
-        */
-       resv->task = TASK_NULL;
-}
-
-/*
- * pmc_internal_reservation_set_pmc sets the pmc associated with the reservation object. If
- * there was one set already, it is deallocated (reference is dropped) before
- * the new one is set.  This methods increases the reference count of the given
- * pmc_t.
- *
- * NOTE: It is okay to pass NULL as the pmc_t - this will have the effect of
- * dropping the reference on any previously set pmc, and setting the reservation
- * to having no pmc set.
- */
-static kern_return_t pmc_internal_reservation_set_pmc(pmc_reservation_t resv, pmc_t pmc) {
-       assert(resv);
-
-       if(resv->pmc) {
-               (void)pmc_close(resv->pmc);
-               pmc_deallocate(resv->pmc);
-               resv->pmc = NULL;
-       }
-
-       resv->pmc = pmc;
-
-       if(resv->pmc) {
-               pmc_reference(resv->pmc);
-               if(KERN_SUCCESS != pmc_open(resv->pmc)) {
-                       pmc_deallocate(resv->pmc);
-                       resv->pmc = NULL;
-
-                       return KERN_FAILURE;
-               }
-       }
-
-       return KERN_SUCCESS;
-}
-
-/* 
- * Used to place reservation into one of the system, task, and thread queues
- * Assumes the queue's spin lock is already held.
- */
-static void pmc_internal_reservation_enqueue(queue_t queue, pmc_reservation_t resv) {
-       assert(queue);
-       assert(resv);
-
-       queue_enter(queue, resv, pmc_reservation_t, link);
-}
-
-static void pmc_internal_reservation_dequeue(queue_t queue, pmc_reservation_t resv) {
-       assert(queue);
-       assert(resv);
-
-       queue_remove(queue, resv, pmc_reservation_t, link);
-}
-
-/* Returns TRUE if the reservation applies to the current execution context */
-static boolean_t pmc_internal_reservation_matches_context(pmc_reservation_t resv) {
-       boolean_t ret = FALSE;
-       assert(resv);
-
-       if(PMC_FLAG_IS_SYSTEM_SCOPE(resv->flags)) {
-               ret = TRUE;
-       } else if(PMC_FLAG_IS_TASK_SCOPE(resv->flags)) {
-               if(current_task() == resv->task) {
-                       ret = TRUE;
-               }
-       } else if(PMC_FLAG_IS_THREAD_SCOPE(resv->flags)) {
-               if(current_thread() == resv->thread) {
-                       ret = TRUE;
-               }
-       }
-
-       return ret;
-}
-
-/*
- * pmc_accessible_core_count returns the number of logical cores that can access
- * a given @pmc.  0 means every core in the system.
- */
-static uint32_t pmc_accessible_core_count(pmc_t pmc) {
-       assert(pmc);
-
-       uint32_t *cores = NULL;
-       size_t coreCt = 0UL;
-
-       if(KERN_SUCCESS != pmc->methods.accessible_cores(pmc->object,
-               &cores, &coreCt)) {
-               coreCt = 0U;
-       }
-
-       return (uint32_t)coreCt;
-}
-
-/* spin lock for the queue must already be held */
-/*
- * This method will inspect the task/thread of the reservation to see if it
- * matches the new incoming one (for thread/task reservations only).  Will only
- * return TRUE if the task/thread matches.
- */
-static boolean_t pmc_internal_reservation_queue_contains_pmc(queue_t queue, pmc_reservation_t resv) {
-       assert(queue);
-       assert(resv);
-
-       boolean_t ret = FALSE;
-       pmc_reservation_t tmp = NULL;
-
-       queue_iterate(queue, tmp, pmc_reservation_t, link) {
-               if(tmp->pmc == resv->pmc) {
-                       /* PMC matches - make sure scope matches first */
-                       switch(PMC_FLAG_SCOPE(tmp->flags)) {
-                               case PMC_FLAG_SCOPE_SYSTEM:
-                                       /*
-                                        * Found a reservation in system queue with same pmc - always a
-                                        * conflict.
-                                        */
-                                       ret = TRUE;
-                                       break;
-                               case PMC_FLAG_SCOPE_THREAD:
-                                       /*
-                                        * Found one in thread queue with the same PMC as the
-                                        * argument. Only a conflict if argument scope isn't
-                                        * thread or system, or the threads match.
-                                        */
-                                       ret = (PMC_FLAG_SCOPE(resv->flags) != PMC_FLAG_SCOPE_THREAD) || 
-                                               (tmp->thread == resv->thread);
-
-                                       if(!ret) {
-                                               /*
-                                                * so far, no conflict - check that the pmc that is
-                                                * being reserved isn't accessible from more than
-                                                * one core, if it is, we need to say it's already
-                                                * taken.
-                                                */
-                                               if(1 != pmc_accessible_core_count(tmp->pmc)) {
-                                                       ret = TRUE;
-                                               }
-                                       }
-                                       break;
-                               case PMC_FLAG_SCOPE_TASK:
-                                       /* 
-                                        * Follow similar semantics for task scope.
-                                        */
-
-                                       ret = (PMC_FLAG_SCOPE(resv->flags) != PMC_FLAG_SCOPE_TASK) ||
-                                               (tmp->task == resv->task);
-                                       if(!ret) {
-                                               /*
-                                                * so far, no conflict - check that the pmc that is
-                                                * being reserved isn't accessible from more than
-                                                * one core, if it is, we need to say it's already
-                                                * taken.
-                                                */
-                                               if(1 != pmc_accessible_core_count(tmp->pmc)) {
-                                                       ret = TRUE;
-                                               }
-                                       }
-
-                                       break;
-                       }
-
-                       if(ret) break;
-               }
-       }
-
-       return ret;
-}
-
-/*
- * pmc_internal_reservation_validate_for_pmc returns TRUE if the given reservation can be 
- * added to its target queue without creating conflicts (target queue is 
- * determined by the reservation's scope flags). Further, this method returns
- * FALSE if any level contains a reservation for a PMC that can be accessed from
- * more than just 1 core, and the given reservation also wants the same PMC.
- */
-static boolean_t pmc_internal_reservation_validate_for_pmc(pmc_reservation_t resv) {
-       assert(resv);
-       boolean_t ret = TRUE;
-
-       if(pmc_internal_reservation_queue_contains_pmc(system_reservations, resv) ||
-               pmc_internal_reservation_queue_contains_pmc(task_reservations, resv) ||
-               pmc_internal_reservation_queue_contains_pmc(thread_reservations, resv)) {
-               ret = FALSE;
-       }
-
-       return ret;
-}
-
-static void pmc_internal_update_thread_flag(thread_t thread, boolean_t newFlag) {
-       assert(thread);
-
-       /* See if this thread needs it's PMC flag set */
-       pmc_reservation_t tmp = NULL;
-
-       if(!newFlag) {
-               /*
-                * If the parent task just dropped its reservation, iterate the thread
-                * reservations to see if we need to keep the pmc flag set for the given
-                * thread or not.
-                */
-               lck_spin_lock(&reservations_spin);
-       
-               queue_iterate(thread_reservations, tmp, pmc_reservation_t, link) {
-                       if(tmp->thread == thread) {
-                               newFlag = TRUE;
-                               break;
-                       }
-               }
-
-               lck_spin_unlock(&reservations_spin);
-       }
-
-       if(newFlag) {
-               OSBitOrAtomic(THREAD_PMC_FLAG, &thread->t_chud);
-       } else {
-               OSBitAndAtomic(~(THREAD_PMC_FLAG), &thread->t_chud);
-       }
-}
-
-/* 
- * This operation is (worst case) O(N*M) where N is number of threads in the
- * given task, and M is the number of thread reservations in our system.
- */
-static void pmc_internal_update_task_flag(task_t task, boolean_t newFlag) {
-       assert(task);
-       thread_t thread = NULL;
-
-       if(newFlag) {
-               OSBitOrAtomic(TASK_PMC_FLAG, &task->t_chud);
-       } else {
-               OSBitAndAtomic(~(TASK_PMC_FLAG), &task->t_chud);
-       }
-
-       task_lock(task);
-
-       queue_iterate(&task->threads, thread, thread_t, task_threads) {
-               /* propagate the task's mask down to each thread  */
-               pmc_internal_update_thread_flag(thread, newFlag);
-       }
-
-       task_unlock(task);
-}
-
-/*
- * pmc_internal_reservation_add adds a reservation to the global tracking queues after
- * ensuring there are no reservation conflicts.  To do this, it takes all the
- * spin locks for all the queue (to ensure no other core goes and adds a
- * reservation for the same pmc to a queue that has already been checked).
- */
-static boolean_t pmc_internal_reservation_add(pmc_reservation_t resv) {
-       assert(resv);
-
-       boolean_t ret = FALSE;
-
-       /* always lock all three in the same order */
-       lck_spin_lock(&reservations_spin);
-
-       /* Check if the reservation can be added without conflicts */
-       if(pmc_internal_reservation_validate_for_pmc(resv)) {
-           
-               /* add reservation to appropriate scope */
-               switch(PMC_FLAG_SCOPE(resv->flags)) {
-               case PMC_FLAG_SCOPE_SYSTEM:
-                       /* Simply add it to the system queue */
-                       pmc_internal_reservation_enqueue(system_reservations, resv);
-                       system_reservation_count++;
-                       
-                       lck_spin_unlock(&reservations_spin);
-
-                       break;
-
-               case PMC_FLAG_SCOPE_TASK:
-                       assert(resv->task);
-
-                       /* Not only do we enqueue it in our local queue for tracking */
-                       pmc_internal_reservation_enqueue(task_reservations, resv);
-                       task_reservation_count++;
-
-                       lck_spin_unlock(&reservations_spin);
-
-                       /* update the task mask, and propagate it to existing threads */
-                       pmc_internal_update_task_flag(resv->task, TRUE);
-                       break;
-
-               /* Thread-switched counter */
-               case PMC_FLAG_SCOPE_THREAD:
-                       assert(resv->thread);
-
-                       /*
-                        * Works the same as a task-switched counter, only at
-                        * thread-scope
-                        */
-
-                       pmc_internal_reservation_enqueue(thread_reservations, resv);
-                       thread_reservation_count++;
-
-                       lck_spin_unlock(&reservations_spin);
-                       
-                       pmc_internal_update_thread_flag(resv->thread, TRUE);
-                       break;
-               }
-               
-               ret = TRUE;
-       } else {
-               lck_spin_unlock(&reservations_spin);
-       }                       
-       
-       return ret;
-}
-
-static void pmc_internal_reservation_broadcast(pmc_reservation_t reservation, void (*action_func)(void *)) {
-       uint32_t * cores;
-       size_t core_cnt;
-       
-       /* Get the list of accessible cores */
-       if (KERN_SUCCESS == pmc_get_accessible_core_list(reservation->pmc, &cores, &core_cnt)) {
-               boolean_t intrs_enabled = ml_set_interrupts_enabled(FALSE);
-
-               /* Fast case: the PMC is only accessible from one core and we happen to be on it */
-               if (core_cnt == 1 && cores[0] == (uint32_t)cpu_number()) {
-                       action_func(reservation);
-               } else {
-                       /* Call action_func on every accessible core */
-#if defined(__i386__) || defined(__x86_64__)
-                       size_t ii;
-                       cpumask_t mask = 0;
-                       
-                       /* Build a mask for the accessible cores */
-                       if (core_cnt > 0) {
-                               for (ii = 0; ii < core_cnt; ii++) {
-                                       mask |= cpu_to_cpumask(cores[ii]);
-                               }
-                       } else {
-                               /* core_cnt = 0 really means all cpus */
-                               mask = CPUMASK_ALL;
-                       }
-                       mp_cpus_call(mask, ASYNC, action_func, reservation);
-#else
-#error pmc_reservation_interrupt needs an inter-processor method invocation mechanism for this architecture
-#endif
-               }
-
-               ml_set_interrupts_enabled(intrs_enabled);
-       }
-       
-}
-
-/*
- * pmc_internal_reservation_remove removes the given reservation from the appropriate
- * reservation queue according to its scope. 
- *
- * NOTE: The scope flag must have been set for this method to function.
- */
-static void pmc_internal_reservation_remove(pmc_reservation_t resv) {
-       assert(resv);
-
-       /*
-        * Due to the way the macros are written, we can't just blindly queue-remove
-        * the reservation without knowing which queue it's in. We figure this out
-        * using the reservation's scope flags.
-        */
-
-       /* Lock the global spin lock */
-       lck_spin_lock(&reservations_spin);
-
-       switch(PMC_FLAG_SCOPE(resv->flags)) {
-
-               case PMC_FLAG_SCOPE_SYSTEM:
-                       pmc_internal_reservation_dequeue(system_reservations, resv);
-                       system_reservation_count--;
-                       
-                       lck_spin_unlock(&reservations_spin);
-                       
-                       break;
-
-               case PMC_FLAG_SCOPE_TASK:
-                       /* remove from the global queue */
-                       pmc_internal_reservation_dequeue(task_reservations, resv);
-                       task_reservation_count--;
-
-                       /* unlock the global */
-                       lck_spin_unlock(&reservations_spin);
-
-                       /* Recalculate task's counter mask */
-                       pmc_internal_update_task_flag(resv->task, FALSE);
-                       
-                       break;
-
-               case PMC_FLAG_SCOPE_THREAD:
-                       pmc_internal_reservation_dequeue(thread_reservations, resv);
-                       thread_reservation_count--;
-
-                       lck_spin_unlock(&reservations_spin);
-
-                       /* recalculate the thread's counter mask */
-                       pmc_internal_update_thread_flag(resv->thread, FALSE);
-
-                       break;
-       }
-}
-
-/* Reservation State Machine
- *
- * The PMC subsystem uses a 3-tuple of state information packed into a 32-bit quantity and a 
- * set of 9 events to provide MP-safe bookkeeping and control flow.  The 3-tuple is comprised 
- * of a state, a count of active contexts, and a set of modifier flags.  A state machine defines
- * the possible transitions at each event point given the current 3-tuple.  Atomicity is handled
- * by reading the current 3-tuple, applying the transformations indicated by the state machine
- * and then attempting to OSCompareAndSwap the transformed value.  If the OSCompareAndSwap fails,
- * the process is repeated until either the OSCompareAndSwap succeeds or not valid transitions are
- * available.
- *
- * The state machine is described using tuple notation for the current state and a related notation
- * for describing the transformations.  For concisness, the flag and state names are abbreviated as
- * follows:
- * 
- * states:
- * S = STOP
- * CR = CAN_RUN
- * L = LOAD
- * R = RUN
- * ST = STORE
- * I = INTERRUPT
- * D = DEALLOC
- *
- * flags:
- *
- * S = STOPPING
- * D = DEALLOCING
- * I = INTERRUPTING
- *
- * The tuple notation is formed from the following pattern:
- *
- * tuple = < state, active-context-count, flags >
- * state = S | CR | L | R | ST | I | D
- * active-context-count = 0 | >0 | 1 | >1
- * flags = flags flag | blank
- * flag = S | D | I
- *
- * The transform notation is similar, but only describes the modifications made to the current state.
- * The notation is formed from the following pattern:
- * 
- * transform = < state, active-context-count, flags >
- * state = S | CR | L | R | ST | I | D
- * active-context-count = + | - | blank
- * flags = flags flag | flags !flag | blank
- * flag = S | D | I
- *
- * And now for the state machine:
- * State               Start           Stop            Free            Interrupt               End Interrupt           Context In              Context Out     Load Finished           Store Finished
- * <CR, 0, >                           <S, , >         <D, , >                 <L, +, >
- * <D, 0, >
- * <D, 1, D>                                                                   < , -, !D>
- * <D, >1, D>                                                                  < , -, >
- * <I, 0, D>                                                                   <D, , !D>
- * <I, 0, S>   < , , !S>                               < , , !SD>              <S, , !S>
- * <I, 0, >                                    < , , S>        < , , D>        <CR, , >
- * <L, 1, D>                                                                   <ST, -, >
- * <L, 1, ID>                                                                  <ST, -, >
- * <L, 1, IS>                                                  < , , !SD>      <ST, -, >
- * <L, 1, S>   < , , !S>                               < , , !SD>              <ST, -, >
- * <L, 1, >                                    < , , S>        < , , D>        < , , IS>                                                       < , +, >        <R, , >
- * <L, >1, D>                                                                  < , -, >                <R, -, >
- * <L, >1, ID>                                                                 < , -, >                <R, -, >
- * <L, >1, IS>                                                 < , , !SD>      < , -, >                <R, -, >
- * <L, >1, S>  < , , !S>                               < , , !SD>              < , -, >                <R, -, >
- * <L, >1, >                           < , , S>        < , , D>        < , , IS>                                                       < , +, >                < , -, >                <R, , >
- * <R, 1, D>                                                                   <ST, -, >
- * <R, 1, ID>                                                                  <ST, -, >
- * <R, 1, IS>                                                  < , , !SD>      <ST, -, >
- * <R, 1, S>   < , , !S>                               < , , !SD>              <ST, -, >
- * <R, 1, >                                    < , , S>        < , , D>        < , , IS>                                                       < , +, >        <ST, -, >
- * <R, >1, D>                                                                  < , -, >
- * <R, >1, ID>                                                                 < , -, >
- * <R, >1, IS>                                                 < , , !SD>      < , -, >
- * <R, >1, S>  < , , !S>                               < , , !SD>              < , -, >
- * <R, >1, >                           < , , S>        < , , D>        < , , IS>                                                       < , +, >                < , -, >
- * <S, 0, >            <CR, , >                                <D, , >
- * <S, 1, ID>                                                                  <I, -, !I>
- * <S, 1, IS>                                                  < , , !SD>      <I, -, !I>
- * <S, 1, S>   < , , !S>                               <D, , !SD>              < , -, !S>
- * <S, 1, >                                    < , , S>        <D, , D>        <L, +, >                <CR, -, >
- * <S, >1, ID>                                                                 < , -, >
- * <S, >1, IS>                                                 < , , !SD>      < , -, >
- * <S, >1, S>  < , , !S>                               <D, , !SD>              < , -, >
- * <S, >1, >                           < , , S>        <D, , D>                <L, +, >                < , -, >
- * <ST, 0, D>                                                                  <D, , !D>
- * <ST, 0, ID>                                                                 <I, , !I>
- * <ST, 0, IS>                                                 < , , !SD>      <I, , !I>
- * <ST, 0, S>  < , , !S>                               < , , !SD>              <S, , !S>
- * <ST, 0, >                           < , , S>        < , , D>        < , , IS>                                                       < , +, >                <CR, , >
- * <ST, >0, D>                                                                 < , -, >                                                        <D, , >
- * <ST, >0, ID>                                                                < , -, >                                                        <S, , >
- * <ST, >0, IS>                                                        < , , !SD>                                                                              < , -, >                        <S, , >
- * <ST, >0, S> < , , !S>                               < , , !SD>              < , -, >                                                        <S, , >
- * <ST, >0, >                          < , , S>        < , , D>        < , , IS>                                                       < , +, >                < , -, >                        <L, , >
- */
-
-static uint32_t pmc_internal_reservation_next_state(uint32_t current_state, pmc_state_event_t event) {
-       uint32_t new_state = PMC_STATE(PMC_STATE_STATE_INVALID, 0, 0);
-       
-       switch (event) {
-               case PMC_STATE_EVENT_START:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_INTERRUPT, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_STOPPING):
-                                       new_state = PMC_STATE_MODIFY(current_state, 0, 0, PMC_STATE_FLAGS_STOPPING);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 0) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_CAN_RUN, 0, 0, 0);
-                                       }
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_STOP:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_CAN_RUN, 0, 0):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_STOP, 0, 0, 0);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_INTERRUPT, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, 0):
-                                       new_state = PMC_STATE_MODIFY(current_state, 0, PMC_STATE_FLAGS_STOPPING, 0);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 0) {
-                                               new_state = PMC_STATE_MODIFY(current_state, 0, PMC_STATE_FLAGS_STOPPING, 0);
-                                       }
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_FREE:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_CAN_RUN, 0, 0):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_DEALLOC, 0, 0, 0);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_INTERRUPT, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_STOPPING):
-                                       new_state = PMC_STATE_MODIFY(current_state, 0, PMC_STATE_FLAGS_DEALLOCING, PMC_STATE_FLAGS_STOPPING);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_INTERRUPT, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, 0):
-                                       new_state = PMC_STATE_MODIFY(current_state, 0, PMC_STATE_FLAGS_DEALLOCING, 0);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, PMC_STATE_FLAGS_STOPPING):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_DEALLOC, 0, PMC_STATE_FLAGS_DEALLOCING, PMC_STATE_FLAGS_STOPPING);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 0) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_DEALLOC, 0, PMC_STATE_FLAGS_DEALLOCING, 0);
-                                       } else {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_DEALLOC, 0, 0, 0);
-                                       }
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_INTERRUPT:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, 0):
-                                       new_state = PMC_STATE_MODIFY(current_state, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING, 0);
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_END_OF_INTERRUPT:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_INTERRUPT, 0, PMC_STATE_FLAGS_DEALLOCING):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_DEALLOC, 0, 0, PMC_STATE_FLAGS_DEALLOCING);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_INTERRUPT, 0, PMC_STATE_FLAGS_STOPPING):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_STOP, 0, 0, PMC_STATE_FLAGS_STOPPING);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_INTERRUPT, 0, 0):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_CAN_RUN, 0, 0, 0);
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_CONTEXT_IN:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_CAN_RUN, 0, 0):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_LOAD, 1, 0, 0);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, 0):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, 0):
-                                       new_state = PMC_STATE_MODIFY(current_state, 1, 0, 0);
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 0) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_LOAD, 1, 0, 0);
-                                       }
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_CONTEXT_OUT:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_DEALLOC, 0, PMC_STATE_FLAGS_DEALLOCING):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 1) {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, PMC_STATE_FLAGS_DEALLOCING);
-                                       } else {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, 0);
-                                       }                                       
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 1) {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_RUN, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 1) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_STORE, -1, 0, 0);
-                                       } else {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 1) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_INTERRUPT, -1, 0, PMC_STATE_FLAGS_INTERRUPTING);
-                                       } else {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, PMC_STATE_FLAGS_STOPPING):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 1) {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, PMC_STATE_FLAGS_STOPPING);
-                                       } else {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STOP, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 0) {
-                                               if (PMC_STATE_CONTEXT_COUNT(current_state) == 1) {
-                                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_CAN_RUN, -1, 0, 0);
-                                               } else {
-                                                       new_state = PMC_STATE_MODIFY(current_state, -1, 0, 0);
-                                               }
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 0) {
-                                               new_state = PMC_STATE_MODIFY(current_state, -1, 0, 0);
-                                       }
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_LOAD_FINISHED:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, PMC_STATE_FLAGS_STOPPING):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) > 1) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_RUN, -1, 0, 0);
-                                       } else {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_STORE, -1, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_LOAD, 0, 0):
-                                       new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_RUN, 0, 0, 0);
-                                       break;
-                       }
-                       break;
-               case PMC_STATE_EVENT_STORE_FINISHED:
-                       switch (current_state & ~(PMC_STATE_CONTEXT_COUNT_MASK)) {
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_DEALLOCING):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 0) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_DEALLOC, 0, 0, PMC_STATE_FLAGS_DEALLOCING);
-                                       } else {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_DEALLOC, 0, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_DEALLOCING):
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_INTERRUPTING | PMC_STATE_FLAGS_STOPPING):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 0) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_INTERRUPT, 0, 0, PMC_STATE_FLAGS_INTERRUPTING);
-                                       } else {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_STOP, 0, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, PMC_STATE_FLAGS_STOPPING):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 0) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_STOP, 0, 0, PMC_STATE_FLAGS_STOPPING);
-                                       } else {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_STOP, 0, 0, 0);
-                                       }
-                                       break;
-                               case PMC_STATE(PMC_STATE_STATE_STORE, 0, 0):
-                                       if (PMC_STATE_CONTEXT_COUNT(current_state) == 0) {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_CAN_RUN, 0, 0, 0);
-                                       } else {
-                                               new_state = PMC_STATE_MOVE(current_state, PMC_STATE_STATE_LOAD, 0, 0, 0);
-                                       }
-                                       break;
-                       }
-                       break;
-       }
-       
-       return new_state;
-}
-
-static uint32_t pmc_internal_reservation_move_for_event(pmc_reservation_t reservation, pmc_state_event_t event, pmc_state_t *old_state_out) {
-       pmc_state_t oldState;
-       pmc_state_t newState;
-
-       assert(reservation);
-       
-       /* Determine what state change, if any, we need to do.  Keep trying until either we succeed doing a transition
-        * or the there is no valid move.
-        */     
-       do {
-               oldState = reservation->state;
-               newState = pmc_internal_reservation_next_state(oldState, event);
-       } while (newState != PMC_STATE_INVALID && !OSCompareAndSwap(oldState, newState, &(reservation->state)));
-       
-       if (newState != PMC_STATE_INVALID) {
-               COUNTER_DEBUG("Moved reservation %p from state "PMC_STATE_FORMAT" to state "PMC_STATE_FORMAT" for event %s\n", reservation, PMC_STATE_ARGS(oldState), PMC_STATE_ARGS(newState), pmc_state_event_name(event));
-       } else {
-               COUNTER_DEBUG("No valid moves for reservation %p in state "PMC_STATE_FORMAT" for event %s\n", reservation, PMC_STATE_ARGS(oldState), pmc_state_event_name(event));
-       }
-       
-       if (old_state_out != NULL) {
-               *old_state_out = oldState;
-       }
-       
-       return newState;
-}
-                                       
-static void pmc_internal_reservation_context_out(pmc_reservation_t reservation) {
-       assert(reservation);
-       pmc_state_t newState;
-       pmc_state_t oldState;
-
-       /* Clear that the this reservation was active when this cpu did its last context in */
-       OSBitAndAtomic(~(1U << cpu_number()), &(reservation->active_last_context_in));
-       
-       /* Move the state machine */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_CONTEXT_OUT, &oldState))) {
-               return;
-       }
-       
-       /* Do any actions required based on the state change */
-       if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_STORE && PMC_STATE_STATE(oldState) != PMC_STATE_STATE_STORE) {
-               /* Just moved into STORE, so store the reservation. */
-               pmc_internal_reservation_store(reservation);
-       } else if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_DEALLOC && PMC_STATE_CONTEXT_COUNT(newState) == 0 && PMC_STATE_FLAGS(newState) == 0) {
-               /* Wakeup any thread blocking for this reservation to hit <DEALLOC, 0, > */
-               thread_wakeup((event_t)reservation);
-       }
-       
-}
-
-static void pmc_internal_reservation_context_in(pmc_reservation_t reservation) {
-       assert(reservation);
-       pmc_state_t oldState;
-       pmc_state_t newState;
-       
-       /* Move the state machine */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_CONTEXT_IN, &oldState))) {
-               return;
-       }
-
-       /* Mark that the reservation was active when this cpu did its last context in */
-       OSBitOrAtomic(1U << cpu_number(), &(reservation->active_last_context_in));
-               
-       /* Do any actions required based on the state change */
-       if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_LOAD && PMC_STATE_STATE(oldState) != PMC_STATE_STATE_LOAD) {
-               /* Just moved into LOAD, so load the reservation. */
-               pmc_internal_reservation_load(reservation);
-       }
-       
-}
-
-static void pmc_internal_reservation_store(pmc_reservation_t reservation) {
-       assert(reservation);
-       assert(PMC_STATE_STATE(reservation->state) == PMC_STATE_STATE_STORE);
-       
-       assert(reservation->pmc);
-       assert(reservation->config);
-
-       pmc_state_t newState;
-       kern_return_t ret = KERN_SUCCESS;
-       
-       pmc_t store_pmc = reservation->pmc;
-       pmc_object_t store_pmc_obj = store_pmc->object;
-       perf_monitor_t store_pm = store_pmc->monitor;
-
-       /* 
-        * Instruct the Perf Monitor that contains this counter to turn 
-        * off the global disable for this counter.
-        */
-       ret = store_pm->methods.disable_counters(store_pm->object, &store_pmc_obj, 1);
-       if(KERN_SUCCESS != ret) {
-               COUNTER_DEBUG(" [error] disable_counters: 0x%x\n", ret);
-               return;
-       }
-
-       /* Instruct the counter to disable itself */
-       ret = store_pmc->methods.disable(store_pmc_obj);
-       if(KERN_SUCCESS != ret) {
-               COUNTER_DEBUG("  [error] disable: 0x%x\n", ret);
-       }
-
-       /* store the counter value into the reservation's stored count */
-       ret = store_pmc->methods.get_count(store_pmc_obj, &reservation->value);
-       if(KERN_SUCCESS != ret) {
-               COUNTER_DEBUG("  [error] get_count: 0x%x\n", ret);
-               return;
-       }
-               
-       /* Advance the state machine now that the STORE is finished */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_STORE_FINISHED, NULL))) {
-               return;
-       }
-
-       /* Do any actions required based on the state change */
-       if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_LOAD) {
-               /* Just moved into LOAD, so load the reservation. */
-               pmc_internal_reservation_load(reservation);
-       } else if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_DEALLOC && PMC_STATE_CONTEXT_COUNT(newState) == 0 && PMC_STATE_FLAGS(newState) == 0) {
-               /* Wakeup any thread blocking for this reservation to hit <DEALLOC, 0, > */
-               thread_wakeup((event_t)reservation);
-       }
-       
-}
-
-static void pmc_internal_reservation_load(pmc_reservation_t reservation) {
-       assert(reservation);
-       assert(PMC_STATE_STATE(reservation->state) == PMC_STATE_STATE_LOAD);
-
-       pmc_state_t newState;
-       kern_return_t ret = KERN_SUCCESS;
-
-       assert(reservation->pmc);
-       assert(reservation->config);
-       
-       pmc_t load_pmc = reservation->pmc;
-       pmc_object_t load_pmc_obj = load_pmc->object;
-       perf_monitor_t load_pm = load_pmc->monitor;
-
-       /* Set the control register up with the stored configuration */
-       ret = load_pmc->methods.set_config(load_pmc_obj, reservation->config->object);
-       if(KERN_SUCCESS != ret) {
-               COUNTER_DEBUG("  [error] set_config: 0x%x\n", ret);
-               return;
-       }
-
-       /* load the counter value */
-       ret = load_pmc->methods.set_count(load_pmc_obj, reservation->value);
-       if(KERN_SUCCESS != ret) {
-               COUNTER_DEBUG("  [error] set_count: 0x%x\n", ret);
-               return;
-       }
-
-       /* Locally enable the counter */
-       ret = load_pmc->methods.enable(load_pmc_obj);
-       if(KERN_SUCCESS != ret) {
-               COUNTER_DEBUG("  [error] enable: 0x%x\n", ret);
-               return;
-       }
-
-       /*
-        * Instruct the Perf Monitor containing the pmc to enable the
-        * counter.
-        */
-       ret = load_pm->methods.enable_counters(load_pm->object, &load_pmc_obj, 1);
-       if(KERN_SUCCESS != ret) {
-               COUNTER_DEBUG("  [error] enable_counters: 0x%x\n", ret);
-               /* not on the hardware. */
-               return;
-       }
-       
-       /* Advance the state machine now that the STORE is finished */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_LOAD_FINISHED, NULL))) {
-               return;
-       }
-
-       /* Do any actions required based on the state change */
-       if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_STORE) {
-               /* Just moved into STORE, so store the reservation. */
-               pmc_internal_reservation_store(reservation);
-       }
-       
-}
-
-/*
- * pmc_accessible_from_core will return TRUE if the given @pmc is directly
- * (e.g., hardware) readable from the given logical core.
- *
- * NOTE: This method is interrupt safe.
- */
-static inline boolean_t pmc_accessible_from_core(pmc_t pmc, uint32_t logicalCore) {
-       boolean_t ret = FALSE;
-
-       assert(pmc);
-
-       ret = pmc->methods.accessible_from_core(pmc->object, logicalCore);
-
-       return ret;
-}
-
-static void pmc_internal_reservation_start_cpu(void * arg) {
-       pmc_reservation_t reservation = (pmc_reservation_t)arg;
-       
-       assert(reservation);
-       
-
-       if (pmc_internal_reservation_matches_context(reservation)) {
-               /* We are in context, but the reservation may have already had the context_in method run.  Attempt
-                * to set this cpu's bit in the active_last_context_in mask.  If we set it, call context_in.
-                */
-               uint32_t oldMask = OSBitOrAtomic(1U << cpu_number(), &(reservation->active_last_context_in));
-               
-               if ((oldMask & (1U << cpu_number())) == 0) {
-                       COUNTER_DEBUG("Starting already in-context reservation %p for cpu %d\n", reservation, cpu_number());
-                       
-                       pmc_internal_reservation_context_in(reservation);
-               }
-       }
-}
-
-static void pmc_internal_reservation_stop_cpu(void * arg) {
-       pmc_reservation_t reservation = (pmc_reservation_t)arg;
-       
-       assert(reservation);
-       
-       
-       if (pmc_internal_reservation_matches_context(reservation)) {
-               COUNTER_DEBUG("Stopping in-context reservation %p for cpu %d\n", reservation, cpu_number());
-
-               pmc_internal_reservation_context_out(reservation);
-       }
-}      
-
-/*!fn
- * pmc_reservation_interrupt is called when a PMC reservation which was setup
- * with an interrupt threshold counts the requested number of events. When the
- * underlying counter hits the threshold, an interrupt is generated, and this
- * method is called. This method marks the reservation as stopped, and passes
- * control off to the user-registered callback method, along with the
- * reservation (so that the user can, for example, write a 0 to the counter, and
- * restart the reservation).
- * This method assumes the reservation has a valid pmc_config_t within.
- *
- * @param target The pmc_reservation_t that caused the interrupt.
- * @param refCon User specified reference constant.
- */
-static void pmc_reservation_interrupt(void *target, void *refCon) {
-       pmc_reservation_t reservation = (pmc_reservation_t)target;
-       pmc_state_t newState;
-       uint64_t timeout;
-       uint32_t spins;
-
-       assert(reservation);
-
-       /* Move the state machine */
-       if (PMC_STATE_INVALID == pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_INTERRUPT, NULL)) {
-               return;
-       }
-
-       /* A valid state move has been made, but won't be picked up until a context switch occurs.  To cause matching
-        * contexts that are currently running to update, we do an inter-processor message to run pmc_internal_reservation_stop_cpu
-        * on every cpu that can access the PMC.
-        */
-       pmc_internal_reservation_broadcast(reservation, pmc_internal_reservation_stop_cpu);
-                       
-       /* Spin waiting for the state to turn to INTERRUPT */
-       nanoseconds_to_absolutetime(PMC_SPIN_TIMEOUT_US * 1000, &timeout);
-       timeout += mach_absolute_time();
-       spins = 0;
-       while (PMC_STATE_STATE(reservation->state) != PMC_STATE_STATE_INTERRUPT) {
-               /* Assert if this takes longer than PMC_SPIN_TIMEOUT_US */
-               if (++spins > PMC_SPIN_THRESHOLD) {
-                       if (mach_absolute_time() > timeout) {
-                               pmc_spin_timeout_count++;
-                               assert(0);
-                       }
-               }
-
-               cpu_pause();
-       }
-                       
-       assert(reservation->config);
-       assert(reservation->config->method);                    
-               
-       /* Call the registered callback handler */
-#if DEBUG_COUNTERS
-       uint64_t start = mach_absolute_time();
-#endif /* DEBUG */
-       
-       (void)reservation->config->method(reservation, refCon);
-       
-#if DEBUG_COUNTERS
-       uint64_t end = mach_absolute_time();
-       if((end - start) > 5000ULL) {
-               kprintf("%s - user method %p took %llu ns\n", __FUNCTION__, 
-                               reservation->config->method, (end - start));
-       }
-#endif
-       
-       /* Move the state machine */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_END_OF_INTERRUPT, NULL))) {
-               return;
-       }
-       
-       /* Do any post-move actions necessary */
-       if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_CAN_RUN) {
-               pmc_internal_reservation_broadcast(reservation, pmc_internal_reservation_start_cpu);
-       } else if (PMC_STATE_STATE(newState) == PMC_STATE_STATE_DEALLOC && PMC_STATE_CONTEXT_COUNT(newState) == 0 && PMC_STATE_FLAGS(newState) == 0) {
-               /* Wakeup any thread blocking for this reservation to hit <DEALLOC, 0, > */
-               thread_wakeup((event_t)reservation);
-       }
-}      
-
-/*
- * Apple-private KPI for Apple kext's (IOProfileFamily) only
- */
-
-#if 0
-#pragma mark -
-#pragma mark IOProfileFamily private KPI
-#endif
-
-/*
- * perf_monitor_register registers a new Performance Monitor, and its associated
- * callback methods.  The given perf_monitor_object_t is the first argument to
- * each callback when they are called.
- */
-kern_return_t perf_monitor_register(perf_monitor_object_t monitor,
-       perf_monitor_methods_t *methods) {
-       int cpu = -1;
-
-       COUNTER_DEBUG("registering perf monitor %p\n", monitor);
-
-       if(!monitor || !methods) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* Protect against out-of-date driver kexts */
-       if(MACH_PERFMON_METHODS_VERSION != methods->perf_monitor_methods_version) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* If the monitor requires idle notifications, ensure that it is 
-        * accessible from a single core only.
-        */
-       if (methods->flags & PERFMON_FLAG_REQUIRES_IDLE_NOTIFICATIONS) {
-               uint32_t *cores;
-               size_t core_cnt;
-           
-               if (KERN_SUCCESS == methods->accessible_cores(monitor, &cores, &core_cnt)) {
-                       /* 
-                        * Guard against disabled cores - monitors will always match and
-                        * attempt registration, irrespective of 'cpus=x' boot-arg.
-                        */
-                       if ((core_cnt == 1) && (cores[0] < (uint32_t)ml_get_max_cpus())) {
-                               cpu = cores[0];
-                       } else {
-                               return KERN_INVALID_ARGUMENT;
-                       }
-               }           
-       }
-
-       /* All methods are required */
-       if(!methods->accessible_cores |
-          !methods->enable_counters || !methods->disable_counters ||
-          !methods->on_idle || !methods->on_idle_exit) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* prevent dupes. */
-       perf_monitor_t dupe = perf_monitor_find(monitor);
-       if(dupe) {
-               COUNTER_DEBUG("Duplicate registration for %p\n", monitor);
-               perf_monitor_deallocate(dupe);
-               return KERN_FAILURE;
-       }
-
-       perf_monitor_t pm = perf_monitor_alloc();
-       if(!pm) {
-               return KERN_RESOURCE_SHORTAGE;
-       }
-
-       /* initialize the object */
-       perf_monitor_init(pm, cpu);
-
-       /* copy in the registration info */
-       pm->object = monitor;
-       memcpy(&(pm->methods), methods, sizeof(perf_monitor_methods_t));
-
-       /* place it in the tracking queues */
-       perf_monitor_enqueue(pm);
-
-       /* debug it */
-       PRINT_PERF_MON(pm);
-
-       return KERN_SUCCESS;
-}
-
-/*
- * perf_monitor_unregister unregisters a previously registered Perf Monitor,
- * looking it up by reference pointer (the same that was used in
- * perf_monitor_register()).
- */
-kern_return_t perf_monitor_unregister(perf_monitor_object_t monitor) {
-       kern_return_t ret = KERN_FAILURE;
-
-       COUNTER_DEBUG("unregistering perf monitor %p\n", monitor);
-
-       if(!monitor) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       perf_monitor_t pm = perf_monitor_find(monitor);
-       if(pm) {
-               /* Remove it from the queues. */
-               perf_monitor_dequeue(pm);
-
-               /* drop extra retain from find */
-               perf_monitor_deallocate(pm);
-
-               /* and release the object */
-               perf_monitor_deallocate(pm);
-
-               ret = KERN_SUCCESS;
-       } else {
-               COUNTER_DEBUG("could not find a registered pm that matches!\n");
-       }
-
-       return ret;
-}
-
-/*
- * pmc_register registers a new PMC for use with the pmc subsystem. Each PMC is
- * associated with a Perf Monitor.  Perf Monitors are looked up by the reference
- * pointer that was used to previously register them. 
- *
- * PMCs are registered with a reference pointer (@pmc_object), and a set of
- * callback methods.  When the given callback methods are called from xnu, the
- * first argument will always be the reference pointer used to register the PMC.
- *
- * NOTE: @monitor must have been successfully registered via
- * perf_monitor_register before this method will succeed.
- */
-kern_return_t pmc_register(perf_monitor_object_t monitor, pmc_object_t pmc_object,
-       pmc_methods_t *methods, void *object) {
-
-       COUNTER_DEBUG("%p %p\n", monitor, pmc_object);
-
-       if(!monitor || !pmc_object || !methods || !object) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* Prevent version mismatches */
-       if(MACH_PMC_METHODS_VERSION != methods->pmc_methods_version) {
-               COUNTER_DEBUG("version mismatch\n");
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* All methods are required. */
-       if(!methods->create_config || 
-               !methods->free_config ||
-               !methods->config_set_value || 
-               !methods->config_set_threshold || 
-               !methods->config_set_handler ||
-               !methods->set_config || 
-               !methods->get_monitor || 
-               !methods->get_name ||
-               !methods->accessible_from_core || 
-               !methods->accessible_cores ||
-               !methods->get_count || 
-               !methods->set_count ||
-               !methods->disable ||
-               !methods->enable ||
-               !methods->open || 
-               !methods->close) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* make sure this perf monitor object is already registered */
-       /*
-        * NOTE: this adds a reference to the parent, so we'll have to drop it in
-        * any failure code paths from here on out.
-        */
-       perf_monitor_t pm = perf_monitor_find(monitor);
-       if(!pm) {
-               COUNTER_DEBUG("Could not find perf monitor for %p\n", monitor);
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* make a new pmc */
-       pmc_t pmc = pmc_alloc();
-       if(!pmc) {
-               /* drop the extra reference from perf_monitor_find() */
-               perf_monitor_deallocate(pm);
-               return KERN_RESOURCE_SHORTAGE;
-       }
-
-       /* init it */
-       pmc_init(pmc);
-
-       pmc->object = pmc_object;
-       pmc->open_object = object;
-
-       /* copy the callbacks in */
-       memcpy(&(pmc->methods), methods, sizeof(pmc_methods_t));
-
-       pmc->monitor = pm;
-
-       perf_monitor_add_pmc(pmc->monitor, pmc);
-
-       /* enqueue it in our tracking queue */
-       pmc_enqueue(pmc);
-
-       /* drop extra reference from perf_monitor_find() */
-       perf_monitor_deallocate(pm);
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_unregister unregisters a previously registered PMC, looking it up by
- * reference point to *both* the Perf Monitor it was created with, and the PMC's
- * reference pointer itself.
- */
-kern_return_t pmc_unregister(perf_monitor_object_t monitor, pmc_object_t pmc_object) {
-       COUNTER_DEBUG("%p %p\n", monitor, pmc_object);
-
-       if(!monitor || !pmc_object) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       pmc_t pmc = pmc_find(pmc_object);
-       if(!pmc) {
-               COUNTER_DEBUG("Could not find a matching pmc.\n");
-               return KERN_FAILURE;
-       }
-
-       /* remove it from the global queue */
-       pmc_dequeue(pmc);
-
-       perf_monitor_remove_pmc(pmc->monitor, pmc);
-
-       /* remove extra reference count from pmc_find() */
-       pmc_deallocate(pmc);
-
-       /* dealloc the pmc */
-       pmc_deallocate(pmc);
-
-       return KERN_SUCCESS;
-}
-
-static void perf_monitor_reservation_add(perf_monitor_t monitor) {
-    assert(monitor);
-    OSIncrementAtomic(&(monitor->reservedCounters));
-}
-
-static void perf_monitor_reservation_remove(perf_monitor_t monitor) {
-    assert(monitor);
-    OSDecrementAtomic(&(monitor->reservedCounters));    
-}
-
-#if 0
-#pragma mark -
-#pragma mark KPI
-#endif
-
-/*
- * Begin in-kernel and in-kext KPI methods
- */
-
-/*
- * pmc_create_config creates a new configuration area from a given @pmc.
- *
- * NOTE: This method is not interrupt safe.
- */
-kern_return_t pmc_create_config(pmc_t pmc, pmc_config_t *config) {
-       pmc_config_t tmp = NULL;
-
-       if(!pmc || !config) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       pmc_reference(pmc);
-
-       tmp = pmc_config_alloc(pmc);
-       if(tmp) {
-               tmp->object = pmc->methods.create_config(pmc->object);
-
-               if(!tmp->object) {
-                       pmc_config_free(pmc, tmp);
-                       tmp = NULL;
-               } else {
-                       tmp->interrupt_after_value = 0ULL;
-                       tmp->method = NULL;
-                       tmp->refCon = NULL;
-               }
-       }
-
-       pmc_deallocate(pmc);
-
-       if(!tmp) {
-               return KERN_RESOURCE_SHORTAGE;
-       }
-
-       *config = tmp;
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_free_config frees a configuration area created from a given @pmc
- *
- * NOTE: This method is not interrupt safe.
- */
-void pmc_free_config(pmc_t pmc, pmc_config_t config) {
-       assert(pmc);
-       assert(config);
-
-       pmc_reference(pmc);
-
-       pmc_config_free(pmc, config);
-
-       pmc_deallocate(pmc);
-}
-
-/*
- * pmc_config_set_value sets up configuration area key-value pairs.  These pairs
- * are to be either pre-known, or looked up via CoreProfile.framework.
- *
- * NOTE: This method is not interrupt safe.
- */
-kern_return_t pmc_config_set_value(pmc_t pmc, pmc_config_t config,
-       uint8_t id, uint64_t value) {
-
-       kern_return_t ret = KERN_INVALID_ARGUMENT;
-       
-       if(!pmc || !config) {
-               return ret;
-       }
-
-       pmc_reference(pmc);
-
-       ret = pmc->methods.config_set_value(config->object, id, value);
-
-       pmc_deallocate(pmc);
-
-       return ret;
-}
-
-/*
- * pmc_config_set_interrupt_threshold modifies a config object, instructing
- * the pmc that it should generate a call to the given pmc_interrupt_method_t
- * after the counter counts @threshold events.
- *
- * PMC Threshold handler methods will have the pmc_reservation_t that generated the interrupt
- * as the first argument when the interrupt handler is invoked, and the given
- * @refCon (which may be NULL) as the second.
- *
- * See pmc_interrupt_method_t.
- *
- * NOTE: This method is not interrupt safe.
- */
-kern_return_t pmc_config_set_interrupt_threshold(pmc_t pmc, pmc_config_t config, 
-       uint64_t threshold, pmc_interrupt_method_t method, void *refCon) {
-       kern_return_t ret = KERN_INVALID_ARGUMENT;
-
-       if(!config || !pmc) {
-               return ret;
-       }
-       
-       assert(config);
-       assert(pmc);
-
-       pmc_reference(pmc);
-
-       do {
-               /*
-                * We have a minor annoyance to side-step here. The driver layer expects
-                * the config to never change once a reservation has been taken out with
-                * it.  However, in order to have the PMI method have the reservation as
-                * the first argument (in order to allow the user-method to, for
-                * example, write a 0 to it, and restart it), we need to create the
-                * pmc_reservation_t before setting it up in the config object.
-                * We overcome this by caching the method in the pmc_config_t stand-in,
-                * and mutating the pmc_config_object_t just before returning a
-                * reservation (in pmc_reserve() and friends, below).
-                */
-
-               /* might as well stash this away too. */
-               config->interrupt_after_value = threshold;
-               config->method = method;
-               config->refCon = refCon;
-
-               ret = KERN_SUCCESS;
-
-       }while(0);
-
-       pmc_deallocate(pmc);
-
-       return ret;
-}
-
-/*
- * pmc_get_pmc_list returns an allocated list of pmc_t's, as well as the number
- * of pmc_t's returned. Callers should free this list with a call to
- * pmc_free_pmc_list().
- *
- * NOTE: This method is not interrupt safe.
- */
-kern_return_t pmc_get_pmc_list(pmc_t **pmcs, size_t *pmcCount) {
-       pmc_t *array = NULL;
-       pmc_t pmc = NULL;
-       size_t count = 0UL;
-       
-       do {
-               /* Copy down (to the stack) the count of perf counters */
-               vm_size_t size = perf_counters_count;
-
-               /* Allocate that sized chunk */
-               array = (pmc_t *)kalloc(sizeof(pmc_t) * size);
-               if(!array) {
-                       return KERN_RESOURCE_SHORTAGE;
-               }
-
-               /* Take the spin lock */
-               lck_spin_lock(&perf_counters_queue_spin);
-
-               /* verify the size didn't change while we were allocating */
-               if(size != perf_counters_count) {
-                       /*
-                        * queue size has changed between alloc and now - go back and
-                        * make another pass.
-                        */
-
-                       /* drop the lock */
-                       lck_spin_unlock(&perf_counters_queue_spin);
-
-                       /* free the block */
-                       kfree(array, sizeof(pmc_t) * size);
-                       array = NULL;
-               }
-
-               /* if we get here, and array is NULL, we try again. */
-       }while(!array);
-
-       /* copy the bits out */
-       queue_iterate(perf_counters_queue, pmc, pmc_t, link) {
-               /* copy out the pointer */
-               array[count++] = pmc;
-       }
-
-       lck_spin_unlock(&perf_counters_queue_spin);
-
-       /* return the list and the size */
-       *pmcs = array;
-       *pmcCount = count;
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_free_pmc_list frees an array of pmc_t that has been returned from
- * pmc_get_pmc_list.
- * 
- * NOTE: This method is not interrupt safe.
- */
-void pmc_free_pmc_list(pmc_t *pmcs, size_t pmcCount) {
-       if(pmcs && pmcCount) {
-               COUNTER_DEBUG("pmcs: %p pmcCount: %lu\n", pmcs, pmcCount);
-
-               kfree(pmcs, pmcCount * sizeof(pmc_t));
-       }
-}
-
-kern_return_t pmc_find_by_name(const char *name, pmc_t **pmcs, size_t *pmcCount) {
-       kern_return_t ret = KERN_INVALID_ARGUMENT;
-
-       if(!name || !pmcs || !pmcCount) {
-               return ret;
-       }
-
-       pmc_t *list = NULL;
-       size_t count = 0UL;
-
-       if(KERN_SUCCESS == (ret = pmc_get_pmc_list(&list, &count))) {
-               size_t matchCount = 0UL, ii = 0UL, swapPtr = 0UL;
-               size_t len = strlen(name);
-
-               for(ii = 0UL; ii < count; ii++) {
-                       const char *pmcName = pmc_get_name(list[ii]);
-
-                       if(strlen(pmcName) < len) {
-                               /*
-                                * If the pmc name is shorter than the requested match, it's no 
-                                * match, as we're looking for the most specific match(es).
-                                */
-                               continue;
-                       }
-
-                       if(0 == strncmp(name, pmcName, len)) {
-                               pmc_t temp = list[ii];
-                               
-                               // move matches to the head of the array.
-                               list[ii] = list[swapPtr];
-                               list[swapPtr] = temp;
-                               swapPtr++;
-
-                               // keep a count of the matches
-                               matchCount++;
-                       }
-               }
-
-               if(matchCount) {
-                       /*
-                        * If we have matches, they are all at the head of the array, so
-                        * just allocate enough space for @matchCount pmc_t's, and copy the
-                        * head of the array to the new allocation.  Then free the old
-                        * allocation.
-                        */
-
-                       pmc_t *result = (pmc_t *)kalloc(sizeof(pmc_t) * matchCount);
-                       if(result) {
-                               // copy the matches
-                               memcpy(result, list, sizeof(pmc_t) * matchCount);
-
-                               ret = KERN_SUCCESS;
-                       }
-
-                       pmc_free_pmc_list(list, count);
-
-                       if(!result) {
-                               *pmcs = NULL;
-                               *pmcCount = 0UL;
-                               return KERN_RESOURCE_SHORTAGE;
-                       }
-
-                       *pmcs = result;
-                       *pmcCount = matchCount;
-               } else {
-                       *pmcs = NULL;
-                       *pmcCount = 0UL;
-               }
-       }
-
-       return ret;
-}
-
-/*
- * pmc_get_name returns a pointer (not copied) to the human-readable name of the
- * given pmc.
- *
- * NOTE: Driver authors must take care to not allocate during this method, as
- * this method *IS* interrupt safe.
- */
-const char *pmc_get_name(pmc_t pmc) {
-       assert(pmc);
-
-       const char *name = pmc->methods.get_name(pmc->object);
-
-       return name;
-}
-
-/*
- * pmc_get_accessible_core_list returns a pointer to an array of logical core
- * numbers (as well as the size of that array) that represent the local cores
- * (hardware threads) from which the given @pmc can be accessed directly.
- *
- * NOTE: This method is interrupt safe.
- */
-kern_return_t pmc_get_accessible_core_list(pmc_t pmc, uint32_t **logicalCores,
-       size_t *logicalCoreCt) {
-
-       kern_return_t ret = KERN_INVALID_ARGUMENT;
-
-       if(!pmc || !logicalCores || !logicalCoreCt) {
-               return ret;
-       }
-
-       ret = pmc->methods.accessible_cores(pmc->object, logicalCores, logicalCoreCt);
-
-       return ret;
-}
-
-static boolean_t pmc_reservation_setup_pmi(pmc_reservation_t resv, pmc_config_t config) {
-       assert(resv);
-       assert(resv->pmc);
-       assert(config);
-       assert(config->object);
-
-       /* If there's no PMI to setup, return success */
-       if(config->interrupt_after_value && config->method) {
-
-               /* set the threshold */
-               kern_return_t ret = resv->pmc->methods.config_set_threshold(config->object,
-                       config->interrupt_after_value);
-
-               if(KERN_SUCCESS != ret) {
-                       /*
-                        * This is the most useful error message here, as this only happens
-                        * as a result of pmc_reserve*()
-                        */
-                       COUNTER_DEBUG("Failed to set threshold for pmc %p\n", resv->pmc);
-                       return FALSE;
-               }
-
-               if(KERN_SUCCESS != resv->pmc->methods.config_set_handler(config->object, 
-                       (void *)resv, &pmc_reservation_interrupt, config->refCon)) {
-
-                       COUNTER_DEBUG("Failed to set handler for pmc %p\n", resv->pmc);
-                       return FALSE;
-               }
-       }
-
-       return TRUE;
-}
-
-/*
- * pmc_reserve will attempt to reserve the given @pmc, with a given
- * configuration object, for counting system-wide. This method will fail with
- * KERN_FAILURE if the given pmc is already reserved at any scope.
- *
- * This method consumes the given configuration object if it returns
- * KERN_SUCCESS. Any other return value indicates the caller
- * must free the config object via pmc_free_config().
- *
- * NOTE: This method is NOT interrupt safe.
- */
-kern_return_t pmc_reserve(pmc_t pmc, pmc_config_t config,
-       pmc_reservation_t *reservation) {
-
-       if(!pmc || !config || !reservation) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       pmc_reservation_t resv = reservation_alloc();
-       if(!resv) {
-               return KERN_RESOURCE_SHORTAGE;
-       }
-
-       reservation_init(resv);
-
-       resv->flags |= PMC_FLAG_SCOPE_SYSTEM;
-       resv->config = config;
-
-       if(KERN_SUCCESS != pmc_internal_reservation_set_pmc(resv, pmc)) {
-               resv->config = NULL;
-               return KERN_FAILURE;
-       }
-       
-       /* enqueue reservation in proper place */
-       if(!pmc_internal_reservation_add(resv) || !pmc_reservation_setup_pmi(resv, config)) {
-               /* Prevent free of config object */
-               resv->config = NULL;
-               
-               reservation_free(resv);
-               return KERN_FAILURE;
-       }
-
-       perf_monitor_reservation_add(pmc->monitor);
-       
-       *reservation = resv;
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_reserve_task will attempt to reserve the given @pmc with a given
- * configuration object, for counting when the given @task is running on any
- * logical core that can directly access the given @pmc.  This method will fail
- * with KERN_FAILURE if the given pmc is already reserved at either system or
- * thread scope.  
- *
- * This method consumes the given configuration object if it returns
- * KERN_SUCCESS. Any other return value indicates the caller
- * must free the config object via pmc_free_config().
- *
- * NOTE: You can reserve the same pmc for N different tasks concurrently.
- * NOTE: This method is NOT interrupt safe.
- */
-kern_return_t pmc_reserve_task(pmc_t pmc, pmc_config_t config, 
-       task_t task, pmc_reservation_t *reservation) {
-
-       if(!pmc || !config || !reservation || !task) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       if (!(pmc->monitor->methods.flags & PERFMON_FLAG_SUPPORTS_CONTEXT_SWITCHING)) {
-               COUNTER_DEBUG("pmc %p cannot be context switched!\n", pmc);
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       pmc_reservation_t resv = reservation_alloc();
-       if(!resv) {
-               return KERN_RESOURCE_SHORTAGE;
-       }
-
-       reservation_init(resv);
-
-       resv->flags |= PMC_FLAG_SCOPE_TASK;
-       resv->task = task;
-
-       resv->config = config;
-
-       if(KERN_SUCCESS != pmc_internal_reservation_set_pmc(resv, pmc)) {
-               resv->config = NULL;
-               return KERN_FAILURE;
-       }
-       
-       /* enqueue reservation in proper place */
-       if(!pmc_internal_reservation_add(resv) || !pmc_reservation_setup_pmi(resv, config)) {
-               /* Prevent free of config object */
-               resv->config = NULL;
-
-               reservation_free(resv);
-               return KERN_FAILURE;
-       }
-
-       perf_monitor_reservation_add(pmc->monitor);
-
-       *reservation = resv;
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_reserve_thread will attempt to reserve the given @pmc with a given
- * configuration object, for counting when the given @thread is running on any
- * logical core that can directly access the given @pmc.  This method will fail
- * with KERN_FAILURE if the given pmc is already reserved at either system or
- * task scope.  
- *
- * This method consumes the given configuration object if it returns
- * KERN_SUCCESS. Any other return value indicates the caller
- * must free the config object via pmc_free_config().
- *
- * NOTE: You can reserve the same pmc for N different threads concurrently.
- * NOTE: This method is NOT interrupt safe.
- */
-kern_return_t pmc_reserve_thread(pmc_t pmc, pmc_config_t config, 
-       thread_t thread, pmc_reservation_t *reservation) {
-       if(!pmc || !config || !reservation || !thread) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       if (!(pmc->monitor->methods.flags & PERFMON_FLAG_SUPPORTS_CONTEXT_SWITCHING)) {
-               COUNTER_DEBUG("pmc %p cannot be context switched!\n", pmc);
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       pmc_reservation_t resv = reservation_alloc();
-       if(!resv) {
-               return KERN_RESOURCE_SHORTAGE;
-       }
-
-       reservation_init(resv);
-
-       resv->flags |= PMC_FLAG_SCOPE_THREAD;
-       resv->thread = thread;
-
-       resv->config = config;
-
-       if(KERN_SUCCESS != pmc_internal_reservation_set_pmc(resv, pmc)) {
-               resv->config = NULL;
-               return KERN_FAILURE;
-       }
-       
-       /* enqueue reservation in proper place */
-       if(!pmc_internal_reservation_add(resv) || !pmc_reservation_setup_pmi(resv, config)) {
-               /* Prevent free of config object */
-               resv->config = NULL;
-
-               reservation_free(resv);
-               return KERN_FAILURE;
-       }
-
-       perf_monitor_reservation_add(pmc->monitor);
-
-       *reservation = resv;
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_reservation_start instructs the given reservation to start counting as
- * soon as possible. 
- *
- * NOTE: This method is interrupt safe.
- */
-kern_return_t pmc_reservation_start(pmc_reservation_t reservation) {
-       pmc_state_t newState;
-
-       if(!reservation) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       /* Move the state machine */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_START, NULL))) {
-               return KERN_FAILURE;
-       }
-       
-       /* If we are currently in an interrupt, don't bother to broadcast since it won't do anything now and the interrupt will
-        * broadcast right before it leaves
-        */
-       if (PMC_STATE_STATE(newState) != PMC_STATE_STATE_INTERRUPT) {   
-               /* A valid state move has been made, but won't be picked up until a context switch occurs.  To cause matching
-                * contexts that are currently running to update, we do an inter-processor message to run pmc_internal_reservation_start_cpu
-                * on every cpu that can access the PMC.
-                */
-               pmc_internal_reservation_broadcast(reservation, pmc_internal_reservation_start_cpu);
-       }
-       
-       return KERN_SUCCESS;                     
-}
-
-/*
- * pmc_reservation_stop instructs the given reservation to stop counting as
- * soon as possible.  When this method returns, the pmc will be marked as stopping
- * and subsequent calls to pmc_reservation_start will succeed.  This does not mean
- * that the pmc hardware has _actually_ stopped running.  Assuming no other changes
- * to the reservation state, the pmc hardware _will_ stop shortly.
- *
- */
-kern_return_t pmc_reservation_stop(pmc_reservation_t reservation) {
-       pmc_state_t newState;
-
-       if(!reservation) {
-               return KERN_INVALID_ARGUMENT;
-       }
-       
-       /* Move the state machine */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_STOP, NULL))) {
-               return KERN_FAILURE;
-       }
-       
-       /* If we are currently in an interrupt, don't bother to broadcast since it won't do anything now and the interrupt will
-        * broadcast right before it leaves.  Similarly, if we just moved directly to STOP, don't bother broadcasting.
-        */
-       if (PMC_STATE_STATE(newState) != PMC_STATE_STATE_INTERRUPT && PMC_STATE_STATE(newState) != PMC_STATE_STATE_STOP) {      
-               /* A valid state move has been made, but won't be picked up until a context switch occurs.  To cause matching
-                        * contexts that are currently running to update, we do an inter-processor message to run pmc_internal_reservation_stop_cpu
-                * on every cpu that can access the PMC.
-                */
-               
-               pmc_internal_reservation_broadcast(reservation, pmc_internal_reservation_stop_cpu);
-       }
-       
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_reservation_read will read the event count associated with a reservation.
- * If the caller is current executing in a context that both a) matches the
- * reservation's context, and b) can access the reservation's pmc directly, the
- * value will be read from hardware.  Otherwise, this returns the reservation's
- * stored value.
- *
- * NOTE: This method is interrupt safe.
- * NOTE: When not on the interrupt stack, this method may block.
- */
-kern_return_t pmc_reservation_read(pmc_reservation_t reservation, uint64_t *value) {
-       kern_return_t ret = KERN_FAILURE;
-       uint64_t timeout;
-       uint32_t spins;
-
-       if(!reservation || !value) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       nanoseconds_to_absolutetime(PMC_SPIN_TIMEOUT_US * 1000, &timeout);
-       timeout += mach_absolute_time();
-       spins = 0;
-       do {
-               uint32_t state = reservation->state;
-               
-               if((PMC_STATE_STATE(state) == PMC_STATE_STATE_RUN)) {
-                       /* Attempt read from hardware via drivers. */
-
-                       assert(reservation->pmc);
-
-                       ret = reservation->pmc->methods.get_count(reservation->pmc->object, value);
-                       
-                       break;
-               } else if ((PMC_STATE_STATE(state) == PMC_STATE_STATE_STORE) ||
-                                  (PMC_STATE_STATE(state) == PMC_STATE_STATE_LOAD)) {
-                       /* Spin */
-                       /* Assert if this takes longer than PMC_SPIN_TIMEOUT_US */
-                       if (++spins > PMC_SPIN_THRESHOLD) {
-                               if (mach_absolute_time() > timeout) {
-                                       pmc_spin_timeout_count++;
-                                       assert(0);
-                               }
-                       }
-
-                       cpu_pause();
-               } else {
-                       break;
-               }
-       } while (1);
-
-       /* If the direct hardware read failed (for whatever reason) */
-       if(KERN_SUCCESS != ret) {
-               /* Read stored value */
-               *value = reservation->value;
-       }
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_reservation_write will write the event count associated with a reservation.
- * If the caller is current executing in a context that both a) matches the
- * reservation's context, and b) can access the reservation's pmc directly, the
- * value will be written to hardware.  Otherwise, this writes the reservation's
- * stored value.
- *
- * NOTE: This method is interrupt safe.
- * NOTE: When not on the interrupt stack, this method may block.
- */
-kern_return_t pmc_reservation_write(pmc_reservation_t reservation, uint64_t value) {
-       kern_return_t ret = KERN_FAILURE;
-       uint64_t timeout;
-       uint32_t spins;
-
-       if(!reservation) {
-               return KERN_INVALID_ARGUMENT;
-       }
-
-       nanoseconds_to_absolutetime(PMC_SPIN_TIMEOUT_US * 1000, &timeout);
-       timeout += mach_absolute_time();
-       spins = 0;
-       do {
-               uint32_t state = reservation->state;
-               
-               if((PMC_STATE_STATE(state) == PMC_STATE_STATE_RUN)) {
-                               /* Write to hardware via drivers. */
-                       assert(reservation->pmc);
-
-                       ret = reservation->pmc->methods.set_count(reservation->pmc->object, value);
-                       break;
-               } else if ((PMC_STATE_STATE(state) == PMC_STATE_STATE_STORE) ||
-                                  (PMC_STATE_STATE(state) == PMC_STATE_STATE_LOAD)) {
-                       /* Spin */
-                       /* Assert if this takes longer than PMC_SPIN_TIMEOUT_US */
-                       if (++spins > PMC_SPIN_THRESHOLD) {
-                               if (mach_absolute_time() > timeout) {
-                                       pmc_spin_timeout_count++;
-                                       assert(0);
-                               }
-                       }
-
-                       cpu_pause();
-               } else {
-                       break;
-               }
-       } while (1);
-       
-       if(KERN_SUCCESS != ret) {
-               /* Write stored value */
-               reservation->value = value;
-       }
-
-       return KERN_SUCCESS;
-}
-
-/* 
- * pmc_reservation_free releases a reservation and all associated resources.
- *
- * NOTE: This method is NOT interrupt safe.
- */
-kern_return_t pmc_reservation_free(pmc_reservation_t reservation) {
-       pmc_state_t newState;
-       
-       if(!reservation) {
-               return KERN_INVALID_ARGUMENT;
-       }
-       
-       perf_monitor_reservation_remove(reservation->pmc->monitor);
-       
-       /* Move the state machine */
-       if (PMC_STATE_INVALID == (newState = pmc_internal_reservation_move_for_event(reservation, PMC_STATE_EVENT_FREE, NULL))) {
-               return KERN_FAILURE;
-       }
-
-       /* If we didn't move directly to DEALLOC, help things along */  
-       if (PMC_STATE_STATE(newState) != PMC_STATE_STATE_DEALLOC) {     
-               /* A valid state move has been made, but won't be picked up until a context switch occurs.  To cause matching
-                * contexts that are currently running to update, we do an inter-processor message to run pmc_internal_reservation_stop_cpu
-                * on every cpu that can access the PMC.
-                */
-               pmc_internal_reservation_broadcast(reservation, pmc_internal_reservation_stop_cpu);
-       }
-
-       /* Block until the reservation hits the <DEALLOC, 0, > state */
-       while (!(PMC_STATE_STATE(reservation->state) == PMC_STATE_STATE_DEALLOC && PMC_STATE_CONTEXT_COUNT(reservation->state) == 0 && PMC_STATE_FLAGS(reservation->state) == 0)) {
-               assert_wait((event_t)reservation, THREAD_UNINT);
-               thread_block(THREAD_CONTINUE_NULL);
-       }
-
-       /* remove from queues */
-       pmc_internal_reservation_remove(reservation);
-               
-       /* free reservation */
-       reservation_free(reservation);
-
-       return KERN_SUCCESS;
-}
-
-/*
- * pmc_idle notifies eligible monitors of impending per-CPU idle, and can be used to save state.
- */
-boolean_t pmc_idle(void) {
-       perf_monitor_t monitor = NULL;
-       queue_head_t *cpu_queue;
-
-       lck_spin_lock(&perf_monitor_queue_spin);
-       
-       if (cpu_monitor_queues) {
-               cpu_queue = cpu_monitor_queues[cpu_number()];
-       
-               queue_iterate(cpu_queue, monitor, perf_monitor_t, cpu_link) {
-                       perf_monitor_methods_t *methods = &(monitor->methods);
-                       if ((methods->flags & PERFMON_FLAG_ALWAYS_ACTIVE) || (monitor->reservedCounters)) {                 
-                               methods->on_idle(monitor->object);
-                       }
-               }
-       }
-
-       lck_spin_unlock(&perf_monitor_queue_spin);
-
-       return TRUE;
-}
-
-/*
- * pmc_idle_exit notifies eligible monitors of wake from idle; it can be used to restore state.
- */
-boolean_t pmc_idle_exit(void) {
-       perf_monitor_t monitor = NULL;
-       queue_head_t *cpu_queue;
-
-       lck_spin_lock(&perf_monitor_queue_spin);
-       
-       if (cpu_monitor_queues) {
-               cpu_queue = cpu_monitor_queues[cpu_number()];
-       
-               queue_iterate(cpu_queue, monitor, perf_monitor_t, cpu_link) {
-                       perf_monitor_methods_t *methods = &(monitor->methods);
-                       if ((methods->flags & PERFMON_FLAG_ALWAYS_ACTIVE) || (monitor->reservedCounters)) {                 
-                               methods->on_idle_exit(monitor->object);
-                       }
-               }
-       }
-
-       lck_spin_unlock(&perf_monitor_queue_spin);
-
-       return TRUE;
-}
-
-/*
- * pmc_context_switch performs all context switching necessary to save all pmc
- * state associated with @oldThread (and the task to which @oldThread belongs),
- * as well as to restore all pmc state associated with @newThread (and the task
- * to which @newThread belongs).
- *
- * NOTE: This method IS interrupt safe.
- */
-boolean_t pmc_context_switch(thread_t oldThread, thread_t newThread) {
-       pmc_reservation_t resv = NULL;
-       uint32_t cpuNum = cpu_number();
-
-       lck_spin_lock(&reservations_spin);
-
-       /* Save pmc states */
-       if (thread_reservation_count) {
-               queue_iterate(thread_reservations, resv, pmc_reservation_t, link) {
-                       if ((oldThread == resv->thread) && pmc_accessible_from_core(resv->pmc, cpuNum)) {
-                               (void)pmc_internal_reservation_context_out(resv);
-                       }
-               }
-       }
-       
-       if (task_reservation_count) {
-               queue_iterate(task_reservations, resv, pmc_reservation_t, link) {
-                       if ((resv->task == oldThread->task) && pmc_accessible_from_core(resv->pmc, cpuNum)) {
-                       (void)pmc_internal_reservation_context_out(resv);
-                       }
-               }
-       }
-       
-       /* Restore */
-       if (thread_reservation_count) {
-               queue_iterate(thread_reservations, resv, pmc_reservation_t, link) {
-                       if ((resv->thread == newThread) && pmc_accessible_from_core(resv->pmc, cpuNum)) {
-                               (void)pmc_internal_reservation_context_in(resv);
-                       }
-               }
-       }
-
-       if (task_reservation_count) {
-               queue_iterate(task_reservations, resv, pmc_reservation_t, link) {
-                       if ((resv->task == newThread->task) && pmc_accessible_from_core(resv->pmc, cpuNum)) {
-                               (void)pmc_internal_reservation_context_in(resv);
-                       }
-               }
-       }
-       
-       lck_spin_unlock(&reservations_spin);
-
-       return TRUE;
-}
-
-#else /* !CONFIG_COUNTERS */
-
-#if 0
-#pragma mark -
-#pragma mark Dummy functions
-#endif
-
-/*
- * In the case that someone has chosen not to include the PMC KPI in some
- * configuration, we still have exports for kexts, so we'll need to define stub
- * methods that return failures.
- */
-kern_return_t perf_monitor_register(perf_monitor_object_t monitor __unused,
-       perf_monitor_methods_t *methods __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t perf_monitor_unregister(perf_monitor_object_t monitor __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_register(perf_monitor_object_t monitor __unused, 
-       pmc_object_t pmc __unused, pmc_methods_t *methods __unused, void *object __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_unregister(perf_monitor_object_t monitor __unused,
-       pmc_object_t pmc __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_create_config(pmc_t pmc __unused, 
-       pmc_config_t *config __unused) {
-       return KERN_FAILURE;
-}
-
-void pmc_free_config(pmc_t pmc __unused, pmc_config_t config __unused) {
-}
-
-kern_return_t pmc_config_set_value(pmc_t pmc __unused, 
-       pmc_config_t config __unused, uint8_t id __unused, 
-       uint64_t value __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_config_set_interrupt_threshold(pmc_t pmc __unused, 
-       pmc_config_t config __unused, uint64_t threshold __unused, 
-       pmc_interrupt_method_t method __unused, void *refCon __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_get_pmc_list(pmc_t **pmcs __unused, size_t *pmcCount __unused) {
-       return KERN_FAILURE;
-}
-
-void pmc_free_pmc_list(pmc_t *pmcs __unused, size_t pmcCount __unused) {
-}
-
-kern_return_t pmc_find_by_name(const char *name __unused, pmc_t **pmcs __unused, 
-       size_t *pmcCount __unused) {
-       return KERN_FAILURE;
-}
-
-const char *pmc_get_name(pmc_t pmc __unused) {
-       return "";
-}
-
-kern_return_t pmc_get_accessible_core_list(pmc_t pmc __unused, 
-       uint32_t **logicalCores __unused, size_t *logicalCoreCt __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reserve(pmc_t pmc __unused, 
-       pmc_config_t config __unused, pmc_reservation_t *reservation __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reserve_task(pmc_t pmc __unused, 
-       pmc_config_t config __unused, task_t task __unused, 
-       pmc_reservation_t *reservation __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reserve_thread(pmc_t pmc __unused, 
-       pmc_config_t config __unused, thread_t thread __unused, 
-       pmc_reservation_t *reservation __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reservation_start(pmc_reservation_t reservation __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reservation_stop(pmc_reservation_t reservation __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reservation_read(pmc_reservation_t reservation __unused, 
-       uint64_t *value __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reservation_write(pmc_reservation_t reservation __unused, 
-       uint64_t value __unused) {
-       return KERN_FAILURE;
-}
-
-kern_return_t pmc_reservation_free(pmc_reservation_t reservation __unused) {
-       return KERN_FAILURE;
-}
-
-
-#endif /* !CONFIG_COUNTERS */