xref: /minix3/minix/servers/pm/alarm.c (revision 286d266fd0c2c6853131c62e77f8edab8f6dc2bb)
1433d6423SLionel Sambuc /* This file deals with the alarm clock related system calls, eventually
2433d6423SLionel Sambuc  * passing off the work to the functions in timers.c and check_sig() in
3433d6423SLionel Sambuc  * signal.c to pass an alarm signal to a process.
4433d6423SLionel Sambuc  *
5433d6423SLionel Sambuc  * The entry points into this file are:
6433d6423SLionel Sambuc  *   do_itimer: perform the ITIMER system call
7433d6423SLionel Sambuc  *   set_alarm: tell the timer interface to start or stop a process timer
8433d6423SLionel Sambuc  *   check_vtimer: check if one of the virtual timers needs to be restarted
9433d6423SLionel Sambuc  */
10433d6423SLionel Sambuc 
11433d6423SLionel Sambuc #include "pm.h"
12433d6423SLionel Sambuc #include <signal.h>
13433d6423SLionel Sambuc #include <sys/time.h>
14433d6423SLionel Sambuc #include <minix/com.h>
15433d6423SLionel Sambuc #include <minix/callnr.h>
16cfd712b4SDavid van Moolenbroek #include <assert.h>
17433d6423SLionel Sambuc #include "mproc.h"
18433d6423SLionel Sambuc 
19433d6423SLionel Sambuc #define US 1000000UL	/* shortcut for microseconds per second */
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc static clock_t ticks_from_timeval(struct timeval *tv);
22433d6423SLionel Sambuc static void timeval_from_ticks(struct timeval *tv, clock_t ticks);
23433d6423SLionel Sambuc static int is_sane_timeval(struct timeval *tv);
24433d6423SLionel Sambuc static void getset_vtimer(struct mproc *mp, int nwhich, struct
25433d6423SLionel Sambuc 	itimerval *value, struct itimerval *ovalue);
26433d6423SLionel Sambuc static void get_realtimer(struct mproc *mp, struct itimerval *value);
27433d6423SLionel Sambuc static void set_realtimer(struct mproc *mp, struct itimerval *value);
28cfd712b4SDavid van Moolenbroek static void cause_sigalrm(int arg);
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc /*===========================================================================*
31433d6423SLionel Sambuc  *				ticks_from_timeval			     *
32433d6423SLionel Sambuc  *===========================================================================*/
ticks_from_timeval(tv)33433d6423SLionel Sambuc static clock_t ticks_from_timeval(tv)
34433d6423SLionel Sambuc struct timeval *tv;
35433d6423SLionel Sambuc {
36433d6423SLionel Sambuc   clock_t ticks;
37433d6423SLionel Sambuc 
38433d6423SLionel Sambuc   /* Large delays cause a lot of problems.  First, the alarm system call
39433d6423SLionel Sambuc    * takes an unsigned seconds count and the library has cast it to an int.
40433d6423SLionel Sambuc    * That probably works, but on return the library will convert "negative"
41433d6423SLionel Sambuc    * unsigneds to errors.  Presumably no one checks for these errors, so
42433d6423SLionel Sambuc    * force this call through.  Second, If unsigned and long have the same
43433d6423SLionel Sambuc    * size, converting from seconds to ticks can easily overflow.  Finally,
44433d6423SLionel Sambuc    * the kernel has similar overflow bugs adding ticks.
45433d6423SLionel Sambuc    *
46433d6423SLionel Sambuc    * Fixing this requires a lot of ugly casts to fit the wrong interface
47433d6423SLionel Sambuc    * types and to avoid overflow traps.  ALRM_EXP_TIME has the right type
48433d6423SLionel Sambuc    * (clock_t) although it is declared as long.  How can variables like
49433d6423SLionel Sambuc    * this be declared properly without combinatorial explosion of message
50433d6423SLionel Sambuc    * types?
51433d6423SLionel Sambuc    */
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc   /* In any case, the following conversion must always round up. */
54433d6423SLionel Sambuc 
55433d6423SLionel Sambuc   ticks = system_hz * (unsigned long) tv->tv_sec;
56433d6423SLionel Sambuc   if ( (ticks / system_hz) != (unsigned long)tv->tv_sec) {
57433d6423SLionel Sambuc 	ticks = LONG_MAX;
58433d6423SLionel Sambuc   } else {
59433d6423SLionel Sambuc 	ticks += ((system_hz * (unsigned long)tv->tv_usec + (US-1)) / US);
60433d6423SLionel Sambuc   }
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc   if (ticks > LONG_MAX) ticks = LONG_MAX;
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc   return(ticks);
65433d6423SLionel Sambuc }
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc /*===========================================================================*
68433d6423SLionel Sambuc  *				timeval_from_ticks			     *
69433d6423SLionel Sambuc  *===========================================================================*/
timeval_from_ticks(tv,ticks)70433d6423SLionel Sambuc static void timeval_from_ticks(tv, ticks)
71433d6423SLionel Sambuc struct timeval *tv;
72433d6423SLionel Sambuc clock_t ticks;
73433d6423SLionel Sambuc {
74433d6423SLionel Sambuc   tv->tv_sec = (long) (ticks / system_hz);
75433d6423SLionel Sambuc   tv->tv_usec = (long) ((ticks % system_hz) * US / system_hz);
76433d6423SLionel Sambuc }
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc /*===========================================================================*
79433d6423SLionel Sambuc  *				is_sane_timeval				     *
80433d6423SLionel Sambuc  *===========================================================================*/
81637f688fSRichard Sailer static int
is_sane_timeval(struct timeval * tv)82637f688fSRichard Sailer is_sane_timeval(struct timeval *tv)
83433d6423SLionel Sambuc {
84433d6423SLionel Sambuc   /* This imposes a reasonable time value range for setitimer. */
85433d6423SLionel Sambuc   return (tv->tv_sec >= 0 && tv->tv_sec <= MAX_SECS &&
86433d6423SLionel Sambuc  	  tv->tv_usec >= 0 && tv->tv_usec < US);
87433d6423SLionel Sambuc }
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc /*===========================================================================*
90433d6423SLionel Sambuc  *				do_itimer				     *
91433d6423SLionel Sambuc  *===========================================================================*/
92637f688fSRichard Sailer int
do_itimer(void)93637f688fSRichard Sailer do_itimer(void)
94433d6423SLionel Sambuc {
95433d6423SLionel Sambuc   struct itimerval ovalue, value;	/* old and new interval timers */
96433d6423SLionel Sambuc   int setval, getval;			/* set and/or retrieve the values? */
97433d6423SLionel Sambuc   int r, which;
98433d6423SLionel Sambuc 
99433d6423SLionel Sambuc   /* Make sure 'which' is one of the defined timers. */
100433d6423SLionel Sambuc   which = m_in.m_lc_pm_itimer.which;
101433d6423SLionel Sambuc   if (which < 0 || which >= NR_ITIMERS) return(EINVAL);
102433d6423SLionel Sambuc 
103433d6423SLionel Sambuc   /* Determine whether to set and/or return the given timer value, based on
104433d6423SLionel Sambuc    * which of the value and ovalue parameters are nonzero. At least one of
105433d6423SLionel Sambuc    * them must be nonzero.
106433d6423SLionel Sambuc    */
107433d6423SLionel Sambuc   setval = (m_in.m_lc_pm_itimer.value != 0);
108433d6423SLionel Sambuc   getval = (m_in.m_lc_pm_itimer.ovalue != 0);
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc   if (!setval && !getval) return(EINVAL);
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc   /* If we're setting a new value, copy the new timer from user space.
113433d6423SLionel Sambuc    * Also, make sure its fields have sane values.
114433d6423SLionel Sambuc    */
115433d6423SLionel Sambuc   if (setval) {
116433d6423SLionel Sambuc 	r = sys_datacopy(who_e, m_in.m_lc_pm_itimer.value,
117433d6423SLionel Sambuc 		PM_PROC_NR, (vir_bytes)&value, (phys_bytes)sizeof(value));
118433d6423SLionel Sambuc   	if (r != OK) return(r);
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc   	if (!is_sane_timeval(&value.it_value) ||
121433d6423SLionel Sambuc   	    !is_sane_timeval(&value.it_interval))
122433d6423SLionel Sambuc   		return(EINVAL);
123433d6423SLionel Sambuc   }
124433d6423SLionel Sambuc 
125433d6423SLionel Sambuc   switch (which) {
126433d6423SLionel Sambuc   	case ITIMER_REAL :
127433d6423SLionel Sambuc   		if (getval) get_realtimer(mp, &ovalue);
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc   		if (setval) set_realtimer(mp, &value);
130433d6423SLionel Sambuc 
131433d6423SLionel Sambuc   		r = OK;
132433d6423SLionel Sambuc   		break;
133433d6423SLionel Sambuc 
134433d6423SLionel Sambuc   	case ITIMER_VIRTUAL :
135433d6423SLionel Sambuc   	case ITIMER_PROF :
136433d6423SLionel Sambuc 		getset_vtimer(mp, which, (setval) ? &value : NULL,
137433d6423SLionel Sambuc 			(getval) ? &ovalue : NULL);
138433d6423SLionel Sambuc 
139433d6423SLionel Sambuc   		r = OK;
140433d6423SLionel Sambuc   		break;
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc 	default:
143433d6423SLionel Sambuc 		panic("invalid timer type: %d", which);
144433d6423SLionel Sambuc   }
145433d6423SLionel Sambuc 
146433d6423SLionel Sambuc   /* If requested, copy the old interval timer to user space. */
147433d6423SLionel Sambuc   if (r == OK && getval) {
148433d6423SLionel Sambuc 	r = sys_datacopy(PM_PROC_NR, (vir_bytes)&ovalue,
149433d6423SLionel Sambuc 		who_e, m_in.m_lc_pm_itimer.ovalue,
150433d6423SLionel Sambuc 		(phys_bytes)sizeof(ovalue));
151433d6423SLionel Sambuc   }
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc   return(r);
154433d6423SLionel Sambuc }
155433d6423SLionel Sambuc 
156433d6423SLionel Sambuc /*===========================================================================*
157433d6423SLionel Sambuc  *				getset_vtimer				     *
158433d6423SLionel Sambuc  *===========================================================================*/
159637f688fSRichard Sailer static void
getset_vtimer(struct mproc * rmp,int which,struct itimerval * value,struct itimerval * ovalue)160637f688fSRichard Sailer getset_vtimer(struct mproc *rmp, int which, struct itimerval *value, struct itimerval *ovalue)
161433d6423SLionel Sambuc {
162433d6423SLionel Sambuc   clock_t newticks, *nptr;		/* the new timer value, in ticks */
163433d6423SLionel Sambuc   clock_t oldticks, *optr;		/* the old ticks value, in ticks */
164433d6423SLionel Sambuc   int r, num;
165433d6423SLionel Sambuc 
166433d6423SLionel Sambuc   /* The default is to provide sys_vtimer with two null pointers, i.e. to do
167433d6423SLionel Sambuc    * nothing at all.
168433d6423SLionel Sambuc    */
169433d6423SLionel Sambuc   optr = nptr = NULL;
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc   /* If the old timer value is to be retrieved, have 'optr' point to the
172433d6423SLionel Sambuc    * location where the old value is to be stored, and copy the interval.
173433d6423SLionel Sambuc    */
174433d6423SLionel Sambuc   if (ovalue != NULL) {
175433d6423SLionel Sambuc   	optr = &oldticks;
176433d6423SLionel Sambuc 
177433d6423SLionel Sambuc   	timeval_from_ticks(&ovalue->it_interval, rmp->mp_interval[which]);
178433d6423SLionel Sambuc   }
179433d6423SLionel Sambuc 
180433d6423SLionel Sambuc   /* If a new timer value is to be set, store the new timer value and have
181433d6423SLionel Sambuc    * 'nptr' point to it. Also, store the new interval.
182433d6423SLionel Sambuc    */
183433d6423SLionel Sambuc   if (value != NULL) {
184433d6423SLionel Sambuc   	newticks = ticks_from_timeval(&value->it_value);
185433d6423SLionel Sambuc   	nptr = &newticks;
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc   	/* If no timer is set, the interval must be zero. */
188433d6423SLionel Sambuc   	if (newticks <= 0)
189433d6423SLionel Sambuc   		rmp->mp_interval[which] = 0;
190433d6423SLionel Sambuc 	else
191433d6423SLionel Sambuc 		rmp->mp_interval[which] =
192433d6423SLionel Sambuc 			ticks_from_timeval(&value->it_interval);
193433d6423SLionel Sambuc   }
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc   /* Find out which kernel timer number to use. */
196433d6423SLionel Sambuc   switch (which) {
197433d6423SLionel Sambuc   case ITIMER_VIRTUAL: num = VT_VIRTUAL; break;
198433d6423SLionel Sambuc   case ITIMER_PROF:    num = VT_PROF;    break;
199433d6423SLionel Sambuc   default:             panic("invalid vtimer type: %d", which);
200433d6423SLionel Sambuc   }
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc   /* Make the kernel call. If requested, also retrieve and store
203433d6423SLionel Sambuc    * the old timer value.
204433d6423SLionel Sambuc    */
205433d6423SLionel Sambuc   if ((r = sys_vtimer(rmp->mp_endpoint, num, nptr, optr)) != OK)
206433d6423SLionel Sambuc   	panic("sys_vtimer failed: %d", r);
207433d6423SLionel Sambuc 
208433d6423SLionel Sambuc   if (ovalue != NULL) {
209433d6423SLionel Sambuc   	/* If the alarm expired already, we should take into account the
210433d6423SLionel Sambuc   	 * interval. Return zero only if the interval is zero as well.
211433d6423SLionel Sambuc   	 */
212433d6423SLionel Sambuc   	if (oldticks <= 0) oldticks = rmp->mp_interval[which];
213433d6423SLionel Sambuc 
214433d6423SLionel Sambuc 	timeval_from_ticks(&ovalue->it_value, oldticks);
215433d6423SLionel Sambuc   }
216433d6423SLionel Sambuc }
217433d6423SLionel Sambuc 
218433d6423SLionel Sambuc /*===========================================================================*
219433d6423SLionel Sambuc  *				check_vtimer				     *
220433d6423SLionel Sambuc  *===========================================================================*/
221637f688fSRichard Sailer void
check_vtimer(int proc_nr,int sig)222637f688fSRichard Sailer check_vtimer(int proc_nr, int sig)
223433d6423SLionel Sambuc {
224433d6423SLionel Sambuc   register struct mproc *rmp;
225433d6423SLionel Sambuc   int which, num;
226433d6423SLionel Sambuc 
227433d6423SLionel Sambuc   rmp = &mproc[proc_nr];
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc   /* Translate back the given signal to a timer type and kernel number. */
230433d6423SLionel Sambuc   switch (sig) {
231433d6423SLionel Sambuc   case SIGVTALRM: which = ITIMER_VIRTUAL; num = VT_VIRTUAL; break;
232433d6423SLionel Sambuc   case SIGPROF:   which = ITIMER_PROF;    num = VT_PROF;    break;
233433d6423SLionel Sambuc   default: panic("invalid vtimer signal: %d", sig);
234433d6423SLionel Sambuc   }
235433d6423SLionel Sambuc 
236433d6423SLionel Sambuc   /* If a repetition interval was set for this virtual timer, tell the
237433d6423SLionel Sambuc    * kernel to set a new timeout for the virtual timer.
238433d6423SLionel Sambuc    */
239433d6423SLionel Sambuc   if (rmp->mp_interval[which] > 0)
240433d6423SLionel Sambuc   	sys_vtimer(rmp->mp_endpoint, num, &rmp->mp_interval[which], NULL);
241433d6423SLionel Sambuc }
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc /*===========================================================================*
244433d6423SLionel Sambuc  *				get_realtimer				     *
245433d6423SLionel Sambuc  *===========================================================================*/
246637f688fSRichard Sailer static void
get_realtimer(struct mproc * rmp,struct itimerval * value)247637f688fSRichard Sailer get_realtimer(struct mproc *rmp, struct itimerval *value)
248433d6423SLionel Sambuc {
249433d6423SLionel Sambuc   clock_t exptime;	/* time at which alarm will expire */
250433d6423SLionel Sambuc   clock_t uptime;	/* current system time */
251433d6423SLionel Sambuc   clock_t remaining;	/* time left on alarm */
252433d6423SLionel Sambuc 
253433d6423SLionel Sambuc   /* First determine remaining time, in ticks, of previous alarm, if set. */
254433d6423SLionel Sambuc   if (rmp->mp_flags & ALARM_ON) {
255d91f738bSDavid van Moolenbroek 	uptime = getticks();
256cfd712b4SDavid van Moolenbroek 	exptime = tmr_exp_time(&rmp->mp_timer);
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc   	remaining = exptime - uptime;
259433d6423SLionel Sambuc 
260433d6423SLionel Sambuc   	/* If the alarm expired already, we should take into account the
261433d6423SLionel Sambuc   	 * interval. Return zero only if the interval is zero as well.
262433d6423SLionel Sambuc   	 */
263433d6423SLionel Sambuc   	if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL];
264433d6423SLionel Sambuc   } else {
265433d6423SLionel Sambuc   	remaining = 0;
266433d6423SLionel Sambuc   }
267433d6423SLionel Sambuc 
268433d6423SLionel Sambuc   /* Convert the result to a timeval structure. */
269433d6423SLionel Sambuc   timeval_from_ticks(&value->it_value, remaining);
270433d6423SLionel Sambuc 
271433d6423SLionel Sambuc   /* Similarly convert and store the interval of the timer. */
272433d6423SLionel Sambuc   timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]);
273433d6423SLionel Sambuc }
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc /*===========================================================================*
276433d6423SLionel Sambuc  *				set_realtimer				     *
277433d6423SLionel Sambuc  *===========================================================================*/
278637f688fSRichard Sailer static void
set_realtimer(struct mproc * rmp,struct itimerval * value)279637f688fSRichard Sailer set_realtimer(struct mproc *rmp, struct itimerval *value)
280433d6423SLionel Sambuc {
281433d6423SLionel Sambuc   clock_t ticks;	/* New amount of ticks to the next alarm. */
282433d6423SLionel Sambuc   clock_t interval;	/* New amount of ticks for the alarm's interval. */
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc   /* Convert the timeval structures in the 'value' structure to ticks. */
285433d6423SLionel Sambuc   ticks = ticks_from_timeval(&value->it_value);
286433d6423SLionel Sambuc   interval = ticks_from_timeval(&value->it_interval);
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc   /* If no timer is set, the interval must be zero. */
289433d6423SLionel Sambuc   if (ticks <= 0) interval = 0;
290433d6423SLionel Sambuc 
291433d6423SLionel Sambuc   /* Apply these values. */
292433d6423SLionel Sambuc   set_alarm(rmp, ticks);
293433d6423SLionel Sambuc   rmp->mp_interval[ITIMER_REAL] = interval;
294433d6423SLionel Sambuc }
295433d6423SLionel Sambuc 
296433d6423SLionel Sambuc /*===========================================================================*
297433d6423SLionel Sambuc  *				set_alarm				     *
298433d6423SLionel Sambuc  *===========================================================================*/
set_alarm(rmp,ticks)299433d6423SLionel Sambuc void set_alarm(rmp, ticks)
300433d6423SLionel Sambuc struct mproc *rmp;		/* process that wants the alarm */
301433d6423SLionel Sambuc clock_t ticks;			/* how many ticks delay before the signal */
302433d6423SLionel Sambuc {
303433d6423SLionel Sambuc   if (ticks > 0) {
304cfd712b4SDavid van Moolenbroek 	assert(ticks <= TMRDIFF_MAX);
305433d6423SLionel Sambuc   	set_timer(&rmp->mp_timer, ticks, cause_sigalrm, rmp->mp_endpoint);
306433d6423SLionel Sambuc 	rmp->mp_flags |=  ALARM_ON;
307433d6423SLionel Sambuc   } else if (rmp->mp_flags & ALARM_ON) {
308433d6423SLionel Sambuc   	cancel_timer(&rmp->mp_timer);
309433d6423SLionel Sambuc   	rmp->mp_flags &= ~ALARM_ON;
310433d6423SLionel Sambuc   }
311433d6423SLionel Sambuc }
312433d6423SLionel Sambuc 
313433d6423SLionel Sambuc /*===========================================================================*
314433d6423SLionel Sambuc  *				cause_sigalrm				     *
315433d6423SLionel Sambuc  *===========================================================================*/
316cfd712b4SDavid van Moolenbroek static void
cause_sigalrm(int arg)317cfd712b4SDavid van Moolenbroek cause_sigalrm(int arg)
318433d6423SLionel Sambuc {
319433d6423SLionel Sambuc   int proc_nr_n;
320433d6423SLionel Sambuc   register struct mproc *rmp;
321433d6423SLionel Sambuc 
322433d6423SLionel Sambuc   /* get process from timer */
323cfd712b4SDavid van Moolenbroek   if(pm_isokendpt(arg, &proc_nr_n) != OK) {
324cfd712b4SDavid van Moolenbroek 	printf("PM: ignoring timer for invalid endpoint %d\n", arg);
325433d6423SLionel Sambuc   	return;
326433d6423SLionel Sambuc   }
327433d6423SLionel Sambuc 
328433d6423SLionel Sambuc   rmp = &mproc[proc_nr_n];
329433d6423SLionel Sambuc 
330433d6423SLionel Sambuc   if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) return;
331433d6423SLionel Sambuc   if ((rmp->mp_flags & ALARM_ON) == 0) return;
332433d6423SLionel Sambuc 
333433d6423SLionel Sambuc   /* If an interval is set, set a new timer; otherwise clear the ALARM_ON flag.
334433d6423SLionel Sambuc    * The set_alarm call will be calling set_timer from within this callback
335cfd712b4SDavid van Moolenbroek    * from the expire_timers function. This is safe.
336433d6423SLionel Sambuc    */
337433d6423SLionel Sambuc   if (rmp->mp_interval[ITIMER_REAL] > 0)
338433d6423SLionel Sambuc 	set_alarm(rmp, rmp->mp_interval[ITIMER_REAL]);
339433d6423SLionel Sambuc   else rmp->mp_flags &= ~ALARM_ON;
340433d6423SLionel Sambuc 
341*286d266fSDavid van Moolenbroek   mp = &mproc[0];		/* pretend the signal comes from PM */
342*286d266fSDavid van Moolenbroek 
343433d6423SLionel Sambuc   check_sig(rmp->mp_pid, SIGALRM, FALSE /* ksig */);
344433d6423SLionel Sambuc }
345