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