xref: /minix3/minix/kernel/clock.c (revision 9624407e7addfd8b88486acfe3a0e056e2b92ee3)
1d91f738bSDavid van Moolenbroek /* This file contains the architecture-independent clock functionality, which
2d91f738bSDavid van Moolenbroek  * handles time related functions.  Important events that are handled here
3d91f738bSDavid van Moolenbroek  * include setting and monitoring alarm timers and deciding when to
4d91f738bSDavid van Moolenbroek  * (re)schedule processes.  System services can access its services through
5d91f738bSDavid van Moolenbroek  * system calls, such as sys_setalarm().
6433d6423SLionel Sambuc  *
7433d6423SLionel Sambuc  * Changes:
8433d6423SLionel Sambuc  *   Aug 18, 2006   removed direct hardware access etc, MinixPPC (Ingmar Alting)
9433d6423SLionel Sambuc  *   Oct 08, 2005   reordering and comment editing (A. S. Woodhull)
10433d6423SLionel Sambuc  *   Mar 18, 2004   clock interface moved to SYSTEM task (Jorrit N. Herder)
11433d6423SLionel Sambuc  *   Sep 30, 2004   source code documentation updated  (Jorrit N. Herder)
12433d6423SLionel Sambuc  *   Sep 24, 2004   redesigned alarm timers  (Jorrit N. Herder)
13433d6423SLionel Sambuc  */
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc #include <minix/endpoint.h>
16d91f738bSDavid van Moolenbroek #include <stdlib.h>
17d91f738bSDavid van Moolenbroek #include <string.h>
18433d6423SLionel Sambuc #include <assert.h>
19433d6423SLionel Sambuc 
20433d6423SLionel Sambuc #include "clock.h"
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc #ifdef USE_WATCHDOG
23433d6423SLionel Sambuc #include "watchdog.h"
24433d6423SLionel Sambuc #endif
25433d6423SLionel Sambuc 
26433d6423SLionel Sambuc /* Function prototype for PRIVATE functions.
27433d6423SLionel Sambuc  */
28433d6423SLionel Sambuc static void load_update(void);
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc /* The CLOCK's timers queue. The functions in <minix/timers.h> operate on this.
31433d6423SLionel Sambuc  * Each system process possesses a single synchronous alarm timer. If other
32433d6423SLionel Sambuc  * kernel parts want to use additional timers, they must declare their own
33433d6423SLionel Sambuc  * persistent (static) timer structure, which can be passed to the clock
34433d6423SLionel Sambuc  * via (re)set_kernel_timer().
35433d6423SLionel Sambuc  * When a timer expires its watchdog function is run by the CLOCK task.
36433d6423SLionel Sambuc  */
37433d6423SLionel Sambuc static minix_timer_t *clock_timers;	/* queue of CLOCK timers */
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc /* Number of ticks to adjust realtime by. A negative value implies slowing
40433d6423SLionel Sambuc  * down realtime, a positive value implies speeding it up.
41433d6423SLionel Sambuc  */
42433d6423SLionel Sambuc static int32_t adjtime_delta = 0;
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc /*
45d91f738bSDavid van Moolenbroek  * Initialize the clock variables.
46d91f738bSDavid van Moolenbroek  */
47d91f738bSDavid van Moolenbroek void
init_clock(void)48d91f738bSDavid van Moolenbroek init_clock(void)
49d91f738bSDavid van Moolenbroek {
50d91f738bSDavid van Moolenbroek 	char *value;
51d91f738bSDavid van Moolenbroek 
52d91f738bSDavid van Moolenbroek 	/* Initialize clock information structure. */
53d91f738bSDavid van Moolenbroek 	memset(&kclockinfo, 0, sizeof(kclockinfo));
54d91f738bSDavid van Moolenbroek 
55d91f738bSDavid van Moolenbroek 	/* Get clock tick frequency. */
56d91f738bSDavid van Moolenbroek 	value = env_get("hz");
57d91f738bSDavid van Moolenbroek 	if (value != NULL)
58d91f738bSDavid van Moolenbroek 		kclockinfo.hz = atoi(value);
59d91f738bSDavid van Moolenbroek 	if (value == NULL || kclockinfo.hz < 2 || kclockinfo.hz > 50000)
60d91f738bSDavid van Moolenbroek 		kclockinfo.hz = DEFAULT_HZ;
61d91f738bSDavid van Moolenbroek 
62d91f738bSDavid van Moolenbroek 	/* Load average data initialization. */
63d91f738bSDavid van Moolenbroek 	memset(&kloadinfo, 0, sizeof(kloadinfo));
64d91f738bSDavid van Moolenbroek }
65d91f738bSDavid van Moolenbroek 
66d91f738bSDavid van Moolenbroek /*
67433d6423SLionel Sambuc  * The boot processor's timer interrupt handler. In addition to non-boot cpus
68433d6423SLionel Sambuc  * it keeps real time and notifies the clock task if need be.
69433d6423SLionel Sambuc  */
timer_int_handler(void)70433d6423SLionel Sambuc int timer_int_handler(void)
71433d6423SLionel Sambuc {
72433d6423SLionel Sambuc 	/* Update user and system accounting times. Charge the current process
73433d6423SLionel Sambuc 	 * for user time. If the current process is not billable, that is, if a
74433d6423SLionel Sambuc 	 * non-user process is running, charge the billable process for system
75433d6423SLionel Sambuc 	 * time as well.  Thus the unbillable process' user time is the billable
76433d6423SLionel Sambuc 	 * user's system time.
77433d6423SLionel Sambuc 	 */
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc 	struct proc * p, * billp;
80433d6423SLionel Sambuc 
81433d6423SLionel Sambuc 	/* FIXME watchdog for slave cpus! */
82433d6423SLionel Sambuc #ifdef USE_WATCHDOG
83433d6423SLionel Sambuc 	/*
84433d6423SLionel Sambuc 	 * we need to know whether local timer ticks are happening or whether
85433d6423SLionel Sambuc 	 * the kernel is locked up. We don't care about overflows as we only
86433d6423SLionel Sambuc 	 * need to know that it's still ticking or not
87433d6423SLionel Sambuc 	 */
88433d6423SLionel Sambuc 	watchdog_local_timer_ticks++;
89433d6423SLionel Sambuc #endif
90433d6423SLionel Sambuc 
91433d6423SLionel Sambuc 	if (cpu_is_bsp(cpuid)) {
92d91f738bSDavid van Moolenbroek 		kclockinfo.uptime++;
93433d6423SLionel Sambuc 
94433d6423SLionel Sambuc 		/* if adjtime_delta has ticks remaining, apply one to realtime.
95433d6423SLionel Sambuc 		 * limit changes to every other interrupt.
96433d6423SLionel Sambuc 		 */
97d91f738bSDavid van Moolenbroek 		if (adjtime_delta != 0 && kclockinfo.uptime & 0x1) {
98433d6423SLionel Sambuc 			/* go forward or stay behind */
99d91f738bSDavid van Moolenbroek 			kclockinfo.realtime += (adjtime_delta > 0) ? 2 : 0;
100433d6423SLionel Sambuc 			adjtime_delta += (adjtime_delta > 0) ? -1 : +1;
101433d6423SLionel Sambuc 		} else {
102d91f738bSDavid van Moolenbroek 			kclockinfo.realtime++;
103433d6423SLionel Sambuc 		}
104433d6423SLionel Sambuc 	}
105433d6423SLionel Sambuc 
106433d6423SLionel Sambuc 	/* Update user and system accounting times. Charge the current process
107433d6423SLionel Sambuc 	 * for user time. If the current process is not billable, that is, if a
108433d6423SLionel Sambuc 	 * non-user process is running, charge the billable process for system
109433d6423SLionel Sambuc 	 * time as well.  Thus the unbillable process' user time is the billable
110433d6423SLionel Sambuc 	 * user's system time.
111433d6423SLionel Sambuc 	 */
112433d6423SLionel Sambuc 
113433d6423SLionel Sambuc 	p = get_cpulocal_var(proc_ptr);
114433d6423SLionel Sambuc 	billp = get_cpulocal_var(bill_ptr);
115433d6423SLionel Sambuc 
116433d6423SLionel Sambuc 	p->p_user_time++;
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc 	if (! (priv(p)->s_flags & BILLABLE)) {
119433d6423SLionel Sambuc 		billp->p_sys_time++;
120433d6423SLionel Sambuc 	}
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc 	/* Decrement virtual timers, if applicable. We decrement both the
123433d6423SLionel Sambuc 	 * virtual and the profile timer of the current process, and if the
124433d6423SLionel Sambuc 	 * current process is not billable, the timer of the billed process as
125433d6423SLionel Sambuc 	 * well.  If any of the timers expire, do_clocktick() will send out
126433d6423SLionel Sambuc 	 * signals.
127433d6423SLionel Sambuc 	 */
128433d6423SLionel Sambuc 	if ((p->p_misc_flags & MF_VIRT_TIMER) && (p->p_virt_left > 0)) {
129433d6423SLionel Sambuc 		p->p_virt_left--;
130433d6423SLionel Sambuc 	}
131433d6423SLionel Sambuc 	if ((p->p_misc_flags & MF_PROF_TIMER) && (p->p_prof_left > 0)) {
132433d6423SLionel Sambuc 		p->p_prof_left--;
133433d6423SLionel Sambuc 	}
134433d6423SLionel Sambuc 	if (! (priv(p)->s_flags & BILLABLE) &&
135433d6423SLionel Sambuc 			(billp->p_misc_flags & MF_PROF_TIMER) &&
136433d6423SLionel Sambuc 			(billp->p_prof_left > 0)) {
137433d6423SLionel Sambuc 		billp->p_prof_left--;
138433d6423SLionel Sambuc 	}
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc 	/*
141433d6423SLionel Sambuc 	 * Check if a process-virtual timer expired. Check current process, but
142433d6423SLionel Sambuc 	 * also bill_ptr - one process's user time is another's system time, and
143433d6423SLionel Sambuc 	 * the profile timer decreases for both!
144433d6423SLionel Sambuc 	 */
145433d6423SLionel Sambuc 	vtimer_check(p);
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc 	if (p != billp)
148433d6423SLionel Sambuc 		vtimer_check(billp);
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc 	/* Update load average. */
151433d6423SLionel Sambuc 	load_update();
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc 	if (cpu_is_bsp(cpuid)) {
154*cfd712b4SDavid van Moolenbroek 		/*
155*cfd712b4SDavid van Moolenbroek 		 * If a timer expired, notify the clock task.  Keep in mind
156*cfd712b4SDavid van Moolenbroek 		 * that clock tick values may overflow, so we must only look at
157*cfd712b4SDavid van Moolenbroek 		 * relative differences, and only if there are timers at all.
158*cfd712b4SDavid van Moolenbroek 		 */
159*cfd712b4SDavid van Moolenbroek 		if (clock_timers != NULL &&
160*cfd712b4SDavid van Moolenbroek 		    tmr_has_expired(clock_timers, kclockinfo.uptime))
161d91f738bSDavid van Moolenbroek 			tmrs_exptimers(&clock_timers, kclockinfo.uptime, NULL);
162433d6423SLionel Sambuc 
163433d6423SLionel Sambuc #ifdef DEBUG_SERIAL
164433d6423SLionel Sambuc 		if (kinfo.do_serial_debug)
165433d6423SLionel Sambuc 			do_ser_debug();
166433d6423SLionel Sambuc #endif
167433d6423SLionel Sambuc 
168433d6423SLionel Sambuc 	}
169433d6423SLionel Sambuc 
170433d6423SLionel Sambuc 	arch_timer_int_handler();
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc 	return(1);					/* reenable interrupts */
173433d6423SLionel Sambuc }
174433d6423SLionel Sambuc 
175433d6423SLionel Sambuc /*===========================================================================*
176433d6423SLionel Sambuc  *				get_realtime				     *
177433d6423SLionel Sambuc  *===========================================================================*/
get_realtime(void)178433d6423SLionel Sambuc clock_t get_realtime(void)
179433d6423SLionel Sambuc {
180433d6423SLionel Sambuc   /* Get and return the current wall time in ticks since boot. */
181d91f738bSDavid van Moolenbroek   return(kclockinfo.realtime);
182433d6423SLionel Sambuc }
183433d6423SLionel Sambuc 
184433d6423SLionel Sambuc /*===========================================================================*
185433d6423SLionel Sambuc  *				set_realtime				     *
186433d6423SLionel Sambuc  *===========================================================================*/
set_realtime(clock_t newrealtime)187433d6423SLionel Sambuc void set_realtime(clock_t newrealtime)
188433d6423SLionel Sambuc {
189d91f738bSDavid van Moolenbroek   kclockinfo.realtime = newrealtime;
190433d6423SLionel Sambuc }
191433d6423SLionel Sambuc 
192433d6423SLionel Sambuc /*===========================================================================*
193433d6423SLionel Sambuc  *				set_adjtime_delta			     *
194433d6423SLionel Sambuc  *===========================================================================*/
set_adjtime_delta(int32_t ticks)195433d6423SLionel Sambuc void set_adjtime_delta(int32_t ticks)
196433d6423SLionel Sambuc {
197433d6423SLionel Sambuc   adjtime_delta = ticks;
198433d6423SLionel Sambuc }
199433d6423SLionel Sambuc 
200433d6423SLionel Sambuc /*===========================================================================*
201433d6423SLionel Sambuc  *				get_monotonic				     *
202433d6423SLionel Sambuc  *===========================================================================*/
get_monotonic(void)203433d6423SLionel Sambuc clock_t get_monotonic(void)
204433d6423SLionel Sambuc {
205433d6423SLionel Sambuc   /* Get and return the number of ticks since boot. */
206d91f738bSDavid van Moolenbroek   return(kclockinfo.uptime);
207d91f738bSDavid van Moolenbroek }
208d91f738bSDavid van Moolenbroek 
209d91f738bSDavid van Moolenbroek /*===========================================================================*
210d91f738bSDavid van Moolenbroek  *				set_boottime				     *
211d91f738bSDavid van Moolenbroek  *===========================================================================*/
set_boottime(time_t newboottime)212d91f738bSDavid van Moolenbroek void set_boottime(time_t newboottime)
213d91f738bSDavid van Moolenbroek {
214d91f738bSDavid van Moolenbroek   kclockinfo.boottime = newboottime;
215d91f738bSDavid van Moolenbroek }
216d91f738bSDavid van Moolenbroek 
217d91f738bSDavid van Moolenbroek /*===========================================================================*
218d91f738bSDavid van Moolenbroek  *				get_boottime				     *
219d91f738bSDavid van Moolenbroek  *===========================================================================*/
get_boottime(void)220d91f738bSDavid van Moolenbroek time_t get_boottime(void)
221d91f738bSDavid van Moolenbroek {
222d91f738bSDavid van Moolenbroek   /* Get and return the number of seconds since the UNIX epoch. */
223d91f738bSDavid van Moolenbroek   return(kclockinfo.boottime);
224433d6423SLionel Sambuc }
225433d6423SLionel Sambuc 
226433d6423SLionel Sambuc /*===========================================================================*
227433d6423SLionel Sambuc  *				set_kernel_timer			     *
228433d6423SLionel Sambuc  *===========================================================================*/
set_kernel_timer(minix_timer_t * tp,clock_t exp_time,tmr_func_t watchdog,int arg)2296077d1adSDr. Florian Grätz void set_kernel_timer(
2306077d1adSDr. Florian Grätz   minix_timer_t *tp,			/* pointer to timer structure */
2316077d1adSDr. Florian Grätz   clock_t exp_time,			/* expiration monotonic time */
232*cfd712b4SDavid van Moolenbroek   tmr_func_t watchdog,			/* watchdog to be called */
233*cfd712b4SDavid van Moolenbroek   int arg				/* argument for watchdog function */
2346077d1adSDr. Florian Grätz )
235433d6423SLionel Sambuc {
236433d6423SLionel Sambuc /* Insert the new timer in the active timers list. Always update the
237433d6423SLionel Sambuc  * next timeout time by setting it to the front of the active list.
238433d6423SLionel Sambuc  */
239*cfd712b4SDavid van Moolenbroek   (void)tmrs_settimer(&clock_timers, tp, exp_time, watchdog, arg, NULL, NULL);
240433d6423SLionel Sambuc }
241433d6423SLionel Sambuc 
242433d6423SLionel Sambuc /*===========================================================================*
243433d6423SLionel Sambuc  *				reset_kernel_timer			     *
244433d6423SLionel Sambuc  *===========================================================================*/
reset_kernel_timer(minix_timer_t * tp)2456077d1adSDr. Florian Grätz void reset_kernel_timer(
2466077d1adSDr. Florian Grätz   minix_timer_t *tp			/* pointer to timer structure */
2476077d1adSDr. Florian Grätz )
248433d6423SLionel Sambuc {
249433d6423SLionel Sambuc /* The timer pointed to by 'tp' is no longer needed. Remove it from both the
250433d6423SLionel Sambuc  * active and expired lists. Always update the next timeout time by setting
251433d6423SLionel Sambuc  * it to the front of the active list.
252433d6423SLionel Sambuc  */
253*cfd712b4SDavid van Moolenbroek   if (tmr_is_set(tp))
254*cfd712b4SDavid van Moolenbroek 	(void)tmrs_clrtimer(&clock_timers, tp, NULL, NULL);
255433d6423SLionel Sambuc }
256433d6423SLionel Sambuc 
257433d6423SLionel Sambuc /*===========================================================================*
258433d6423SLionel Sambuc  *				load_update				     *
259433d6423SLionel Sambuc  *===========================================================================*/
load_update(void)260433d6423SLionel Sambuc static void load_update(void)
261433d6423SLionel Sambuc {
262433d6423SLionel Sambuc 	u16_t slot;
263433d6423SLionel Sambuc 	int enqueued = 0, q;
264433d6423SLionel Sambuc 	struct proc *p;
265433d6423SLionel Sambuc 	struct proc **rdy_head;
266433d6423SLionel Sambuc 
267433d6423SLionel Sambuc 	/* Load average data is stored as a list of numbers in a circular
268433d6423SLionel Sambuc 	 * buffer. Each slot accumulates _LOAD_UNIT_SECS of samples of
269433d6423SLionel Sambuc 	 * the number of runnable processes. Computations can then
270433d6423SLionel Sambuc 	 * be made of the load average over variable periods, in the
271433d6423SLionel Sambuc 	 * user library (see getloadavg(3)).
272433d6423SLionel Sambuc 	 */
273d91f738bSDavid van Moolenbroek 	slot = (kclockinfo.uptime / system_hz / _LOAD_UNIT_SECS) %
274d91f738bSDavid van Moolenbroek 	    _LOAD_HISTORY;
275433d6423SLionel Sambuc 	if(slot != kloadinfo.proc_last_slot) {
276433d6423SLionel Sambuc 		kloadinfo.proc_load_history[slot] = 0;
277433d6423SLionel Sambuc 		kloadinfo.proc_last_slot = slot;
278433d6423SLionel Sambuc 	}
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc 	rdy_head = get_cpulocal_var(run_q_head);
281433d6423SLionel Sambuc 	/* Cumulation. How many processes are ready now? */
282433d6423SLionel Sambuc 	for(q = 0; q < NR_SCHED_QUEUES; q++) {
283433d6423SLionel Sambuc 		for(p = rdy_head[q]; p != NULL; p = p->p_nextready) {
284433d6423SLionel Sambuc 			enqueued++;
285433d6423SLionel Sambuc 		}
286433d6423SLionel Sambuc 	}
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 	kloadinfo.proc_load_history[slot] += enqueued;
289433d6423SLionel Sambuc 
290433d6423SLionel Sambuc 	/* Up-to-dateness. */
291d91f738bSDavid van Moolenbroek 	kloadinfo.last_clock = kclockinfo.uptime;
292433d6423SLionel Sambuc }
293433d6423SLionel Sambuc 
boot_cpu_init_timer(unsigned freq)294433d6423SLionel Sambuc int boot_cpu_init_timer(unsigned freq)
295433d6423SLionel Sambuc {
296433d6423SLionel Sambuc 	if (init_local_timer(freq))
297433d6423SLionel Sambuc 		return -1;
298433d6423SLionel Sambuc 
299433d6423SLionel Sambuc 	if (register_local_timer_handler(
300433d6423SLionel Sambuc 				(irq_handler_t) timer_int_handler))
301433d6423SLionel Sambuc 		return -1;
302433d6423SLionel Sambuc 
303433d6423SLionel Sambuc 	return 0;
304433d6423SLionel Sambuc }
305433d6423SLionel Sambuc 
app_cpu_init_timer(unsigned freq)306433d6423SLionel Sambuc int app_cpu_init_timer(unsigned freq)
307433d6423SLionel Sambuc {
308433d6423SLionel Sambuc 	if (init_local_timer(freq))
309433d6423SLionel Sambuc 		return -1;
310433d6423SLionel Sambuc 
311433d6423SLionel Sambuc 	return 0;
312433d6423SLionel Sambuc }
313