1/* 2 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* Manage time triggers */ 30 31#include <mach/mach_types.h> 32#include <kern/cpu_data.h> /* current_thread() */ 33#include <kern/kalloc.h> 34#include <sys/errno.h> 35 36#include <chud/chud_xnu.h> 37 38#include <kperf/kperf.h> 39#include <kperf/buffer.h> 40#include <kperf/context.h> 41#include <kperf/action.h> 42#include <kperf/timetrigger.h> 43#include <kperf/kperf_arch.h> 44#include <kperf/pet.h> 45 46/* represents a periodic timer */ 47struct time_trigger 48{ 49 struct timer_call tcall; 50 uint64_t period; 51 unsigned actionid; 52 volatile unsigned active; 53}; 54 55/* the list of timers */ 56static unsigned timerc = 0; 57static struct time_trigger *timerv; 58static unsigned pet_timer = 999; 59 60/* maximum number of timers we can construct */ 61#define TIMER_MAX 16 62 63/* minimal interval for a timer (100usec in nsec) */ 64#define MIN_TIMER (100000) 65 66static void 67kperf_timer_schedule( struct time_trigger *trigger, uint64_t now ) 68{ 69 uint64_t deadline; 70 71 BUF_INFO1(PERF_TM_SCHED, trigger->period); 72 73 /* calculate deadline */ 74 deadline = now + trigger->period; 75 76 /* re-schedule the timer, making sure we don't apply slop */ 77 timer_call_enter( &trigger->tcall, deadline, TIMER_CALL_CRITICAL); 78} 79 80static void 81kperf_ipi_handler( void *param ) 82{ 83 int r; 84 struct kperf_sample *intbuf = NULL; 85 struct kperf_context ctx; 86 struct time_trigger *trigger = param; 87 task_t task = NULL; 88 89 BUF_INFO1(PERF_TM_HNDLR | DBG_FUNC_START, 0); 90 91 /* In an interrupt, get the interrupt buffer for this CPU */ 92 intbuf = kperf_intr_sample_buffer(); 93 94 /* On a timer, we can see the "real" current thread */ 95 ctx.cur_pid = 0; /* remove this? */ 96 ctx.cur_thread = current_thread(); 97 98 task = chudxnu_task_for_thread(ctx.cur_thread); 99 if (task) 100 ctx.cur_pid = chudxnu_pid_for_task(task); 101 102 /* who fired */ 103 ctx.trigger_type = TRIGGER_TYPE_TIMER; 104 ctx.trigger_id = (unsigned)(trigger-timerv); /* computer timer number */ 105 106 /* call the action -- kernel-only from interrupt, pend user */ 107 r = kperf_sample( intbuf, &ctx, trigger->actionid, TRUE ); 108 109 BUF_INFO1(PERF_TM_HNDLR | DBG_FUNC_END, r); 110} 111 112static void 113kperf_timer_handler( void *param0, __unused void *param1 ) 114{ 115 struct time_trigger *trigger = param0; 116 unsigned ntimer = (unsigned)(trigger - timerv); 117 118 trigger->active = 1; 119 120 /* along the lines of do not ipi if we are all shutting down */ 121 if( kperf_sampling_status() == KPERF_SAMPLING_SHUTDOWN ) 122 goto deactivate; 123 124 /* ping all CPUs */ 125 kperf_mp_broadcast( kperf_ipi_handler, trigger ); 126 127 /* release the pet thread? */ 128 if( ntimer == pet_timer ) 129 { 130 /* timer re-enabled when thread done */ 131 kperf_pet_thread_go(); 132 } 133 else 134 { 135 /* re-enable the timer 136 * FIXME: get the current time from elsewhere 137 */ 138 uint64_t now = mach_absolute_time(); 139 kperf_timer_schedule( trigger, now ); 140 } 141 142deactivate: 143 trigger->active = 0; 144} 145 146/* program the timer from the pet thread */ 147int 148kperf_timer_pet_set( unsigned timer ) 149{ 150 uint64_t now; 151 struct time_trigger *trigger = NULL; 152 153 if( timer != pet_timer ) 154 panic( "PET setting with bogus ID\n" ); 155 156 if( timer >= timerc ) 157 return EINVAL; 158 159 /* CHECKME: we probably took so damn long in the PET thread, 160 * it makes sense to take the time again. 161 */ 162 now = mach_absolute_time(); 163 trigger = &timerv[timer]; 164 165 /* reprogram */ 166 kperf_timer_schedule( trigger, now ); 167 168 return 0; 169} 170 171 172/* turn on all the timers */ 173extern int 174kperf_timer_go(void) 175{ 176 unsigned i; 177 uint64_t now = mach_absolute_time(); 178 179 for( i = 0; i < timerc; i++ ) 180 { 181 if( timerv[i].period == 0 ) 182 continue; 183 184 kperf_timer_schedule( &timerv[i], now ); 185 } 186 187 return 0; 188} 189 190 191extern int 192kperf_timer_stop(void) 193{ 194 unsigned i; 195 196 for( i = 0; i < timerc; i++ ) 197 { 198 if( timerv[i].period == 0 ) 199 continue; 200 201 while (timerv[i].active) 202 ; 203 204 timer_call_cancel( &timerv[i].tcall ); 205 } 206 207 /* wait for PET to stop, too */ 208 kperf_pet_thread_wait(); 209 210 return 0; 211} 212 213unsigned 214kperf_timer_get_petid(void) 215{ 216 return pet_timer; 217} 218 219int 220kperf_timer_set_petid(unsigned timerid) 221{ 222 struct time_trigger *trigger = NULL; 223 224 /* they can program whatever... */ 225 pet_timer = timerid; 226 227 /* clear them if it's a bogus ID */ 228 if( pet_timer >= timerc ) 229 { 230 kperf_pet_timer_config( 0, 0 ); 231 232 return 0; 233 } 234 235 /* update the values */ 236 trigger = &timerv[pet_timer]; 237 kperf_pet_timer_config( pet_timer, trigger->actionid ); 238 239 return 0; 240} 241 242int 243kperf_timer_get_period( unsigned timer, uint64_t *period ) 244{ 245 printf( "get timer %u / %u\n", timer, timerc ); 246 247 if( timer >= timerc ) 248 return EINVAL; 249 250 *period = timerv[timer].period; 251 252 return 0; 253} 254 255int 256kperf_timer_set_period( unsigned timer, uint64_t period ) 257{ 258 printf( "set timer %u\n", timer ); 259 260 if( timer >= timerc ) 261 return EINVAL; 262 263 if( period < MIN_TIMER ) 264 period = MIN_TIMER; 265 266 timerv[timer].period = period; 267 268 /* FIXME: re-program running timers? */ 269 270 return 0; 271} 272 273unsigned 274kperf_timer_get_count(void) 275{ 276 return timerc; 277} 278 279static void 280setup_timer_call( struct time_trigger *trigger ) 281{ 282 timer_call_setup( &trigger->tcall, kperf_timer_handler, trigger ); 283} 284 285extern int 286kperf_timer_set_count(unsigned count) 287{ 288 struct time_trigger *new_timerv = NULL, *old_timerv = NULL; 289 unsigned old_count, i; 290 291 /* easy no-op */ 292 if( count == timerc ) 293 { 294 printf( "already got %d timers\n", timerc ); 295 return 0; 296 } 297 298 /* TODO: allow shrinking? */ 299 if( count < timerc ) 300 return EINVAL; 301 302 /* cap it for good measure */ 303 if( count > TIMER_MAX ) 304 return EINVAL; 305 306 /* creating the action arror for the first time. create a few 307 * more things, too. 308 */ 309 if( timerc == 0 ) 310 { 311 int r; 312 313 /* main kperf */ 314 r = kperf_init(); 315 if( r ) 316 return r; 317 318 /* get the PET thread going */ 319 r = kperf_pet_init(); 320 if( r ) 321 return r; 322 } 323 324 /* create a new array */ 325 new_timerv = kalloc( count * sizeof(*new_timerv) ); 326 if( new_timerv == NULL ) 327 return ENOMEM; 328 329 old_timerv = timerv; 330 old_count = timerc; 331 332 if( old_timerv != NULL ) 333 bcopy( timerv, new_timerv, timerc * sizeof(*timerv) ); 334 335 /* zero the new entries */ 336 bzero( &new_timerv[timerc], (count - old_count) * sizeof(*new_timerv) ); 337 338 /* setup the timer call info */ 339 for( i = old_count; i < count; i++ ) 340 setup_timer_call( &new_timerv[i] ); 341 342 timerv = new_timerv; 343 timerc = count; 344 345 if( old_timerv != NULL ) 346 kfree( old_timerv, old_count * sizeof(*timerv) ); 347 348 printf( "kperf: done timer alloc, timerc %d\n", timerc ); 349 350 return 0; 351} 352