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 <machine/machine_routines.h>
37
38#include <chud/chud_xnu.h>
39
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>
48
49/* make up for arm signal deficiencies */
50void kperf_signal_handler(void);
51
52/* represents a periodic timer */
53struct time_trigger
54{
55	struct timer_call tcall;
56	uint64_t period;
57	unsigned actionid;
58	volatile unsigned active;
59
60#ifdef USE_SIMPLE_SIGNALS
61	/* firing accounting */
62	uint64_t fire_count;
63	uint64_t last_cpu_fire[MAX_CPUS];
64#endif
65};
66
67/* the list of timers */
68static unsigned timerc = 0;
69static struct time_trigger *timerv;
70static unsigned pet_timer = 999;
71
72/* maximum number of timers we can construct */
73#define TIMER_MAX 16
74
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)
79
80static void
81kperf_timer_schedule( struct time_trigger *trigger, uint64_t now )
82{
83	uint64_t deadline;
84
85	BUF_INFO1(PERF_TM_SCHED, trigger->period);
86
87	/* if we re-programmed the timer to zero, just drop it */
88	if( !trigger->period )
89		return;
90
91	/* calculate deadline */
92	deadline = now + trigger->period;
93
94	/* re-schedule the timer, making sure we don't apply slop */
95	timer_call_enter( &trigger->tcall, deadline, TIMER_CALL_SYS_CRITICAL);
96}
97
98static void
99kperf_ipi_handler( void *param )
100{
101	int r;
102	int ncpu;
103	struct kperf_sample *intbuf = NULL;
104	struct kperf_context ctx;
105	struct time_trigger *trigger = param;
106	task_t task = NULL;
107
108	/* Always cut a tracepoint to show a sample event occurred */
109	BUF_DATA1(PERF_TM_HNDLR | DBG_FUNC_START, 0);
110
111	/* In an interrupt, get the interrupt buffer for this CPU */
112	intbuf = kperf_intr_sample_buffer();
113
114	/* On a timer, we can see the "real" current thread */
115	ctx.cur_pid = 0; /* remove this? */
116	ctx.cur_thread = current_thread();
117
118	task = chudxnu_task_for_thread(ctx.cur_thread);
119	if (task)
120		ctx.cur_pid = chudxnu_pid_for_task(task);
121
122	/* who fired */
123	ctx.trigger_type = TRIGGER_TYPE_TIMER;
124	ctx.trigger_id = (unsigned)(trigger-timerv); /* computer timer number */
125
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;
129
130	/* check samppling is on */
131	if( kperf_sampling_status() == KPERF_SAMPLING_OFF ) {
132		BUF_INFO1(PERF_TM_HNDLR | DBG_FUNC_END, SAMPLE_OFF);
133		return;
134	} else if( kperf_sampling_status() == KPERF_SAMPLING_SHUTDOWN ) {
135		BUF_INFO1(PERF_TM_HNDLR | DBG_FUNC_END, SAMPLE_SHUTDOWN);
136		return;
137	}
138
139	/* call the action -- kernel-only from interrupt, pend user */
140	r = kperf_sample( intbuf, &ctx, trigger->actionid, SAMPLE_FLAG_PEND_USER );
141
142	/* end tracepoint is informational */
143	BUF_INFO1(PERF_TM_HNDLR | DBG_FUNC_END, r);
144}
145
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.
152 */
153void
154kperf_signal_handler(void)
155{
156	int i, cpu;
157	struct time_trigger *tr = NULL;
158
159	OSMemoryBarrier();
160
161	cpu = chudxnu_cpu_number();
162	for( i = 0; i < (int) timerc; i++ )
163	{
164		tr = &timerv[i];
165		if( tr->fire_count <= tr->last_cpu_fire[cpu] )
166			continue; /* this trigger hasn't fired */
167
168		/* fire the trigger! */
169		tr->last_cpu_fire[cpu] = tr->fire_count;
170		kperf_ipi_handler( tr );
171	}
172}
173#else
174void
175kperf_signal_handler(void)
176{
177	// so we can link...
178}
179#endif
180
181static void
182kperf_timer_handler( void *param0, __unused void *param1 )
183{
184	struct time_trigger *trigger = param0;
185	unsigned ntimer = (unsigned)(trigger - timerv);
186	unsigned ncpus  = machine_info.logical_cpu_max;
187
188	trigger->active = 1;
189
190	/* along the lines of do not ipi if we are all shutting down */
191	if( kperf_sampling_status() == KPERF_SAMPLING_SHUTDOWN )
192		goto deactivate;
193
194	/* clean-up the thread-on-CPUs cache */
195	bzero(kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus));
196
197	/* ping all CPUs */
198#ifndef USE_SIMPLE_SIGNALS
199	kperf_mp_broadcast( kperf_ipi_handler, trigger );
200#else
201	trigger->fire_count++;
202	OSMemoryBarrier();
203	kperf_mp_signal();
204#endif
205
206	/* release the pet thread? */
207	if( ntimer == pet_timer )
208	{
209		/* timer re-enabled when thread done */
210		kperf_pet_thread_go();
211	}
212	else
213	{
214		/* re-enable the timer
215		 * FIXME: get the current time from elsewhere
216		 */
217		uint64_t now = mach_absolute_time();
218		kperf_timer_schedule( trigger, now );
219	}
220
221deactivate:
222	trigger->active = 0;
223}
224
225/* program the timer from the pet thread */
226int
227kperf_timer_pet_set( unsigned timer, uint64_t elapsed_ticks )
228{
229	static uint64_t pet_min_ticks = 0;
230
231	uint64_t now;
232	struct time_trigger *trigger = NULL;
233	uint64_t period = 0;
234	uint64_t deadline;
235
236	/* compute ns -> ticks */
237	if( pet_min_ticks == 0 )
238		nanoseconds_to_absolutetime(MIN_PET_TIMER_NS, &pet_min_ticks);
239
240	if( timer != pet_timer )
241		panic( "PET setting with bogus ID\n" );
242
243	if( timer >= timerc )
244		return EINVAL;
245
246	if( kperf_sampling_status() == KPERF_SAMPLING_OFF ) {
247		BUF_INFO1(PERF_PET_END, SAMPLE_OFF);
248		return 0;
249	}
250
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);
254		return 0;
255	}
256
257	/* CHECKME: we probably took so damn long in the PET thread,
258	 * it makes sense to take the time again.
259	 */
260	now = mach_absolute_time();
261	trigger = &timerv[timer];
262
263	/* if we re-programmed the timer to zero, just drop it */
264	if( !trigger->period )
265		return 0;
266
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;
270
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;
274
275	/* calculate deadline */
276	deadline = now + period;
277
278	BUF_INFO(PERF_PET_SCHED, trigger->period, period, elapsed_ticks, deadline);
279
280	/* re-schedule the timer, making sure we don't apply slop */
281	timer_call_enter( &trigger->tcall, deadline, TIMER_CALL_SYS_CRITICAL);
282
283	return 0;
284}
285
286
287/* turn on all the timers */
288extern int
289kperf_timer_go(void)
290{
291	unsigned i;
292	uint64_t now = mach_absolute_time();
293
294	for( i = 0; i < timerc; i++ )
295	{
296		if( timerv[i].period == 0 )
297			continue;
298
299		kperf_timer_schedule( &timerv[i], now );
300	}
301
302	return 0;
303}
304
305
306extern int
307kperf_timer_stop(void)
308{
309	unsigned i;
310
311	for( i = 0; i < timerc; i++ )
312	{
313		if( timerv[i].period == 0 )
314			continue;
315
316		while (timerv[i].active)
317			;
318
319		timer_call_cancel( &timerv[i].tcall );
320	}
321
322	/* wait for PET to stop, too */
323	kperf_pet_thread_wait();
324
325	return 0;
326}
327
328unsigned
329kperf_timer_get_petid(void)
330{
331	return pet_timer;
332}
333
334int
335kperf_timer_set_petid(unsigned timerid)
336{
337	struct time_trigger *trigger = NULL;
338
339	/* they can program whatever... */
340	pet_timer = timerid;
341
342	/* clear them if it's a bogus ID */
343	if( pet_timer >= timerc )
344	{
345		kperf_pet_timer_config( 0, 0 );
346
347		return 0;
348	}
349
350	/* update the values */
351	trigger = &timerv[pet_timer];
352	kperf_pet_timer_config( pet_timer, trigger->actionid );
353
354	return 0;
355}
356
357int
358kperf_timer_get_period( unsigned timer, uint64_t *period )
359{
360	if( timer >= timerc )
361		return EINVAL;
362
363	*period = timerv[timer].period;
364
365	return 0;
366}
367
368int
369kperf_timer_set_period( unsigned timer, uint64_t period )
370{
371	static uint64_t min_timer_ticks = 0;
372
373	if( timer >= timerc )
374		return EINVAL;
375
376	/* compute us -> ticks */
377	if( min_timer_ticks == 0 )
378		nanoseconds_to_absolutetime(MIN_TIMER_NS, &min_timer_ticks);
379
380	/* check actual timer */
381	if( period && (period < min_timer_ticks) )
382		period = min_timer_ticks;
383
384	timerv[timer].period = period;
385
386	/* FIXME: re-program running timers? */
387
388	return 0;
389}
390
391int
392kperf_timer_get_action( unsigned timer, uint32_t *action )
393{
394	if( timer >= timerc )
395		return EINVAL;
396
397	*action = timerv[timer].actionid;
398
399	return 0;
400}
401
402int
403kperf_timer_set_action( unsigned timer, uint32_t action )
404{
405	if( timer >= timerc )
406		return EINVAL;
407
408	timerv[timer].actionid = action;
409
410	return 0;
411}
412
413unsigned
414kperf_timer_get_count(void)
415{
416	return timerc;
417}
418
419static void
420setup_timer_call( struct time_trigger *trigger )
421{
422	timer_call_setup( &trigger->tcall, kperf_timer_handler, trigger );
423}
424
425extern int
426kperf_timer_set_count(unsigned count)
427{
428	struct time_trigger *new_timerv = NULL, *old_timerv = NULL;
429	unsigned old_count, i;
430
431	/* easy no-op */
432	if( count == timerc )
433		return 0;
434
435	/* TODO: allow shrinking? */
436	if( count < timerc )
437		return EINVAL;
438
439	/* cap it for good measure */
440	if( count > TIMER_MAX )
441		return EINVAL;
442
443	/* creating the action arror for the first time. create a few
444	 * more things, too.
445	 */
446	if( timerc == 0 )
447	{
448		int r;
449
450		/* main kperf */
451		r = kperf_init();
452		if( r )
453			return r;
454
455		/* get the PET thread going */
456		r = kperf_pet_init();
457		if( r )
458			return r;
459	}
460
461	/* first shut down any running timers since we will be messing
462	 * with the timer call structures
463	 */
464	if( kperf_timer_stop() )
465		return EBUSY;
466
467	/* create a new array */
468	new_timerv = kalloc( count * sizeof(*new_timerv) );
469	if( new_timerv == NULL )
470		return ENOMEM;
471
472	old_timerv = timerv;
473	old_count = timerc;
474
475	if( old_timerv != NULL )
476		bcopy( timerv, new_timerv, timerc * sizeof(*timerv) );
477
478	/* zero the new entries */
479	bzero( &new_timerv[timerc], (count - old_count) * sizeof(*new_timerv) );
480
481	/* (re-)setup the timer call info for all entries */
482	for( i = 0; i < count; i++ )
483		setup_timer_call( &new_timerv[i] );
484
485	timerv = new_timerv;
486	timerc = count;
487
488	if( old_timerv != NULL )
489		kfree( old_timerv, old_count * sizeof(*timerv) );
490
491	return 0;
492}
493