]>
git.saurik.com Git - apple/xnu.git/blob - osfmk/kperf/timetrigger.c
2 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 /* Manage time triggers */
31 #include <mach/mach_types.h>
32 #include <kern/cpu_data.h> /* current_thread() */
33 #include <kern/kalloc.h>
34 #include <sys/errno.h>
36 #include <machine/machine_routines.h>
38 #include <chud/chud_xnu.h>
40 #include <kperf/kperf.h>
41 #include <kperf/buffer.h>
42 #include <kperf/context.h>
43 #include <kperf/action.h>
44 #include <kperf/timetrigger.h>
45 #include <kperf/kperf_arch.h>
46 #include <kperf/pet.h>
47 #include <kperf/sample.h>
49 /* make up for arm signal deficiencies */
50 void kperf_signal_handler(void);
52 /* represents a periodic timer */
55 struct timer_call tcall
;
58 volatile unsigned active
;
60 #ifdef USE_SIMPLE_SIGNALS
61 /* firing accounting */
63 uint64_t last_cpu_fire
[MAX_CPUS
];
67 /* the list of timers */
68 static unsigned timerc
= 0;
69 static struct time_trigger
*timerv
;
70 static unsigned pet_timer
= 999;
72 /* maximum number of timers we can construct */
75 /* minimal interval for a timer (10usec in nsec) */
76 #define MIN_TIMER_NS (10000)
77 /* minimal interval for pet timer (2msec in nsec) */
78 #define MIN_PET_TIMER_NS (2000000)
81 kperf_timer_schedule( struct time_trigger
*trigger
, uint64_t now
)
85 BUF_INFO1(PERF_TM_SCHED
, trigger
->period
);
87 /* if we re-programmed the timer to zero, just drop it */
88 if( !trigger
->period
)
91 /* calculate deadline */
92 deadline
= now
+ trigger
->period
;
94 /* re-schedule the timer, making sure we don't apply slop */
95 timer_call_enter( &trigger
->tcall
, deadline
, TIMER_CALL_SYS_CRITICAL
);
99 kperf_ipi_handler( void *param
)
103 struct kperf_sample
*intbuf
= NULL
;
104 struct kperf_context ctx
;
105 struct time_trigger
*trigger
= param
;
108 /* Always cut a tracepoint to show a sample event occurred */
109 BUF_DATA1(PERF_TM_HNDLR
| DBG_FUNC_START
, 0);
111 /* In an interrupt, get the interrupt buffer for this CPU */
112 intbuf
= kperf_intr_sample_buffer();
114 /* On a timer, we can see the "real" current thread */
115 ctx
.cur_pid
= 0; /* remove this? */
116 ctx
.cur_thread
= current_thread();
118 task
= chudxnu_task_for_thread(ctx
.cur_thread
);
120 ctx
.cur_pid
= chudxnu_pid_for_task(task
);
123 ctx
.trigger_type
= TRIGGER_TYPE_TIMER
;
124 ctx
.trigger_id
= (unsigned)(trigger
-timerv
); /* computer timer number */
126 ncpu
= chudxnu_cpu_number();
127 if (ctx
.trigger_id
== pet_timer
&& ncpu
< machine_info
.logical_cpu_max
)
128 kperf_thread_on_cpus
[ncpu
] = ctx
.cur_thread
;
130 /* check samppling is on */
131 if( kperf_sampling_status() == KPERF_SAMPLING_OFF
) {
132 BUF_INFO1(PERF_TM_HNDLR
| DBG_FUNC_END
, SAMPLE_OFF
);
134 } else if( kperf_sampling_status() == KPERF_SAMPLING_SHUTDOWN
) {
135 BUF_INFO1(PERF_TM_HNDLR
| DBG_FUNC_END
, SAMPLE_SHUTDOWN
);
139 /* call the action -- kernel-only from interrupt, pend user */
140 r
= kperf_sample( intbuf
, &ctx
, trigger
->actionid
, SAMPLE_FLAG_PEND_USER
);
142 /* end tracepoint is informational */
143 BUF_INFO1(PERF_TM_HNDLR
| DBG_FUNC_END
, r
);
146 #ifdef USE_SIMPLE_SIGNALS
147 /* if we can't pass a (function, arg) pair through a signal properly,
148 * we do it the simple way. When a timer fires, we increment a counter
149 * in the time trigger and broadcast a generic signal to all cores. Cores
150 * search the time trigger list for any triggers for which their last seen
151 * firing counter is lower than the current one.
154 kperf_signal_handler(void)
157 struct time_trigger
*tr
= NULL
;
161 cpu
= chudxnu_cpu_number();
162 for( i
= 0; i
< (int) timerc
; i
++ )
165 if( tr
->fire_count
<= tr
->last_cpu_fire
[cpu
] )
166 continue; /* this trigger hasn't fired */
168 /* fire the trigger! */
169 tr
->last_cpu_fire
[cpu
] = tr
->fire_count
;
170 kperf_ipi_handler( tr
);
175 kperf_signal_handler(void)
182 kperf_timer_handler( void *param0
, __unused
void *param1
)
184 struct time_trigger
*trigger
= param0
;
185 unsigned ntimer
= (unsigned)(trigger
- timerv
);
186 unsigned ncpus
= machine_info
.logical_cpu_max
;
190 /* along the lines of do not ipi if we are all shutting down */
191 if( kperf_sampling_status() == KPERF_SAMPLING_SHUTDOWN
)
194 /* clean-up the thread-on-CPUs cache */
195 bzero(kperf_thread_on_cpus
, ncpus
* sizeof(*kperf_thread_on_cpus
));
198 #ifndef USE_SIMPLE_SIGNALS
199 kperf_mp_broadcast( kperf_ipi_handler
, trigger
);
201 trigger
->fire_count
++;
206 /* release the pet thread? */
207 if( ntimer
== pet_timer
)
209 /* timer re-enabled when thread done */
210 kperf_pet_thread_go();
214 /* re-enable the timer
215 * FIXME: get the current time from elsewhere
217 uint64_t now
= mach_absolute_time();
218 kperf_timer_schedule( trigger
, now
);
225 /* program the timer from the pet thread */
227 kperf_timer_pet_set( unsigned timer
, uint64_t elapsed_ticks
)
229 static uint64_t pet_min_ticks
= 0;
232 struct time_trigger
*trigger
= NULL
;
236 /* compute ns -> ticks */
237 if( pet_min_ticks
== 0 )
238 nanoseconds_to_absolutetime(MIN_PET_TIMER_NS
, &pet_min_ticks
);
240 if( timer
!= pet_timer
)
241 panic( "PET setting with bogus ID\n" );
243 if( timer
>= timerc
)
246 if( kperf_sampling_status() == KPERF_SAMPLING_OFF
) {
247 BUF_INFO1(PERF_PET_END
, SAMPLE_OFF
);
251 // don't repgram the timer if it's been shutdown
252 if( kperf_sampling_status() == KPERF_SAMPLING_SHUTDOWN
) {
253 BUF_INFO1(PERF_PET_END
, SAMPLE_SHUTDOWN
);
257 /* CHECKME: we probably took so damn long in the PET thread,
258 * it makes sense to take the time again.
260 now
= mach_absolute_time();
261 trigger
= &timerv
[timer
];
263 /* if we re-programmed the timer to zero, just drop it */
264 if( !trigger
->period
)
267 /* subtract the time the pet sample took being careful not to underflow */
268 if ( trigger
->period
> elapsed_ticks
)
269 period
= trigger
->period
- elapsed_ticks
;
271 /* make sure we don't set the next PET sample to happen too soon */
272 if ( period
< pet_min_ticks
)
273 period
= pet_min_ticks
;
275 /* calculate deadline */
276 deadline
= now
+ period
;
278 BUF_INFO(PERF_PET_SCHED
, trigger
->period
, period
, elapsed_ticks
, deadline
);
280 /* re-schedule the timer, making sure we don't apply slop */
281 timer_call_enter( &trigger
->tcall
, deadline
, TIMER_CALL_SYS_CRITICAL
);
287 /* turn on all the timers */
292 uint64_t now
= mach_absolute_time();
294 for( i
= 0; i
< timerc
; i
++ )
296 if( timerv
[i
].period
== 0 )
299 kperf_timer_schedule( &timerv
[i
], now
);
307 kperf_timer_stop(void)
311 for( i
= 0; i
< timerc
; i
++ )
313 if( timerv
[i
].period
== 0 )
316 while (timerv
[i
].active
)
319 timer_call_cancel( &timerv
[i
].tcall
);
322 /* wait for PET to stop, too */
323 kperf_pet_thread_wait();
329 kperf_timer_get_petid(void)
335 kperf_timer_set_petid(unsigned timerid
)
337 struct time_trigger
*trigger
= NULL
;
339 /* they can program whatever... */
342 /* clear them if it's a bogus ID */
343 if( pet_timer
>= timerc
)
345 kperf_pet_timer_config( 0, 0 );
350 /* update the values */
351 trigger
= &timerv
[pet_timer
];
352 kperf_pet_timer_config( pet_timer
, trigger
->actionid
);
358 kperf_timer_get_period( unsigned timer
, uint64_t *period
)
360 if( timer
>= timerc
)
363 *period
= timerv
[timer
].period
;
369 kperf_timer_set_period( unsigned timer
, uint64_t period
)
371 static uint64_t min_timer_ticks
= 0;
373 if( timer
>= timerc
)
376 /* compute us -> ticks */
377 if( min_timer_ticks
== 0 )
378 nanoseconds_to_absolutetime(MIN_TIMER_NS
, &min_timer_ticks
);
380 /* check actual timer */
381 if( period
&& (period
< min_timer_ticks
) )
382 period
= min_timer_ticks
;
384 timerv
[timer
].period
= period
;
386 /* FIXME: re-program running timers? */
392 kperf_timer_get_action( unsigned timer
, uint32_t *action
)
394 if( timer
>= timerc
)
397 *action
= timerv
[timer
].actionid
;
403 kperf_timer_set_action( unsigned timer
, uint32_t action
)
405 if( timer
>= timerc
)
408 timerv
[timer
].actionid
= action
;
414 kperf_timer_get_count(void)
420 setup_timer_call( struct time_trigger
*trigger
)
422 timer_call_setup( &trigger
->tcall
, kperf_timer_handler
, trigger
);
426 kperf_timer_set_count(unsigned count
)
428 struct time_trigger
*new_timerv
= NULL
, *old_timerv
= NULL
;
429 unsigned old_count
, i
;
432 if( count
== timerc
)
435 /* TODO: allow shrinking? */
439 /* cap it for good measure */
440 if( count
> TIMER_MAX
)
443 /* creating the action arror for the first time. create a few
455 /* get the PET thread going */
456 r
= kperf_pet_init();
461 /* first shut down any running timers since we will be messing
462 * with the timer call structures
464 if( kperf_timer_stop() )
467 /* create a new array */
468 new_timerv
= kalloc( count
* sizeof(*new_timerv
) );
469 if( new_timerv
== NULL
)
475 if( old_timerv
!= NULL
)
476 bcopy( timerv
, new_timerv
, timerc
* sizeof(*timerv
) );
478 /* zero the new entries */
479 bzero( &new_timerv
[timerc
], (count
- old_count
) * sizeof(*new_timerv
) );
481 /* (re-)setup the timer call info for all entries */
482 for( i
= 0; i
< count
; i
++ )
483 setup_timer_call( &new_timerv
[i
] );
488 if( old_timerv
!= NULL
)
489 kfree( old_timerv
, old_count
* sizeof(*timerv
) );