]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kperf/timetrigger.c
xnu-2050.48.11.tar.gz
[apple/xnu.git] / osfmk / kperf / timetrigger.c
CommitLineData
316670eb
A
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}