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