#include <mach/mach_types.h>
#include <kern/cpu_data.h>
#include <kern/kalloc.h>
#include <sys/errno.h>
#include <chud/chud_xnu.h>
#include <kperf/kperf.h>
#include <kperf/buffer.h>
#include <kperf/context.h>
#include <kperf/action.h>
#include <kperf/timetrigger.h>
#include <kperf/kperf_arch.h>
#include <kperf/pet.h>
struct time_trigger
{
struct timer_call tcall;
uint64_t period;
unsigned actionid;
volatile unsigned active;
};
static unsigned timerc = 0;
static struct time_trigger *timerv;
static unsigned pet_timer = 999;
#define TIMER_MAX 16
#define MIN_TIMER (100000)
static void
kperf_timer_schedule( struct time_trigger *trigger, uint64_t now )
{
uint64_t deadline;
BUF_INFO1(PERF_TM_SCHED, trigger->period);
deadline = now + trigger->period;
timer_call_enter( &trigger->tcall, deadline, TIMER_CALL_CRITICAL);
}
static void
kperf_ipi_handler( void *param )
{
int r;
struct kperf_sample *intbuf = NULL;
struct kperf_context ctx;
struct time_trigger *trigger = param;
task_t task = NULL;
BUF_INFO1(PERF_TM_HNDLR | DBG_FUNC_START, 0);
intbuf = kperf_intr_sample_buffer();
ctx.cur_pid = 0;
ctx.cur_thread = current_thread();
task = chudxnu_task_for_thread(ctx.cur_thread);
if (task)
ctx.cur_pid = chudxnu_pid_for_task(task);
ctx.trigger_type = TRIGGER_TYPE_TIMER;
ctx.trigger_id = (unsigned)(trigger-timerv);
r = kperf_sample( intbuf, &ctx, trigger->actionid, TRUE );
BUF_INFO1(PERF_TM_HNDLR | DBG_FUNC_END, r);
}
static void
kperf_timer_handler( void *param0, __unused void *param1 )
{
struct time_trigger *trigger = param0;
unsigned ntimer = (unsigned)(trigger - timerv);
trigger->active = 1;
if( kperf_sampling_status() == KPERF_SAMPLING_SHUTDOWN )
goto deactivate;
kperf_mp_broadcast( kperf_ipi_handler, trigger );
if( ntimer == pet_timer )
{
kperf_pet_thread_go();
}
else
{
uint64_t now = mach_absolute_time();
kperf_timer_schedule( trigger, now );
}
deactivate:
trigger->active = 0;
}
int
kperf_timer_pet_set( unsigned timer )
{
uint64_t now;
struct time_trigger *trigger = NULL;
if( timer != pet_timer )
panic( "PET setting with bogus ID\n" );
if( timer >= timerc )
return EINVAL;
now = mach_absolute_time();
trigger = &timerv[timer];
kperf_timer_schedule( trigger, now );
return 0;
}
extern int
kperf_timer_go(void)
{
unsigned i;
uint64_t now = mach_absolute_time();
for( i = 0; i < timerc; i++ )
{
if( timerv[i].period == 0 )
continue;
kperf_timer_schedule( &timerv[i], now );
}
return 0;
}
extern int
kperf_timer_stop(void)
{
unsigned i;
for( i = 0; i < timerc; i++ )
{
if( timerv[i].period == 0 )
continue;
while (timerv[i].active)
;
timer_call_cancel( &timerv[i].tcall );
}
kperf_pet_thread_wait();
return 0;
}
unsigned
kperf_timer_get_petid(void)
{
return pet_timer;
}
int
kperf_timer_set_petid(unsigned timerid)
{
struct time_trigger *trigger = NULL;
pet_timer = timerid;
if( pet_timer >= timerc )
{
kperf_pet_timer_config( 0, 0 );
return 0;
}
trigger = &timerv[pet_timer];
kperf_pet_timer_config( pet_timer, trigger->actionid );
return 0;
}
int
kperf_timer_get_period( unsigned timer, uint64_t *period )
{
printf( "get timer %u / %u\n", timer, timerc );
if( timer >= timerc )
return EINVAL;
*period = timerv[timer].period;
return 0;
}
int
kperf_timer_set_period( unsigned timer, uint64_t period )
{
printf( "set timer %u\n", timer );
if( timer >= timerc )
return EINVAL;
if( period < MIN_TIMER )
period = MIN_TIMER;
timerv[timer].period = period;
return 0;
}
unsigned
kperf_timer_get_count(void)
{
return timerc;
}
static void
setup_timer_call( struct time_trigger *trigger )
{
timer_call_setup( &trigger->tcall, kperf_timer_handler, trigger );
}
extern int
kperf_timer_set_count(unsigned count)
{
struct time_trigger *new_timerv = NULL, *old_timerv = NULL;
unsigned old_count, i;
if( count == timerc )
{
printf( "already got %d timers\n", timerc );
return 0;
}
if( count < timerc )
return EINVAL;
if( count > TIMER_MAX )
return EINVAL;
if( timerc == 0 )
{
int r;
r = kperf_init();
if( r )
return r;
r = kperf_pet_init();
if( r )
return r;
}
new_timerv = kalloc( count * sizeof(*new_timerv) );
if( new_timerv == NULL )
return ENOMEM;
old_timerv = timerv;
old_count = timerc;
if( old_timerv != NULL )
bcopy( timerv, new_timerv, timerc * sizeof(*timerv) );
bzero( &new_timerv[timerc], (count - old_count) * sizeof(*new_timerv) );
for( i = old_count; i < count; i++ )
setup_timer_call( &new_timerv[i] );
timerv = new_timerv;
timerc = count;
if( old_timerv != NULL )
kfree( old_timerv, old_count * sizeof(*timerv) );
printf( "kperf: done timer alloc, timerc %d\n", timerc );
return 0;
}