vm: util.S not used currently; leave it out.
[minix.git] / servers / pm / alarm.c
blob4c29103e4d5d551855c00ffa82ecbfaa0a78bc8f
1 /* This file deals with the alarm clock related system calls, eventually
2 * passing off the work to the functions in timers.c and check_sig() in
3 * signal.c to pass an alarm signal to a process.
5 * The entry points into this file are:
6 * do_itimer: perform the ITIMER system call
7 * do_alarm: perform the ALARM system call
8 * set_alarm: tell the timer interface to start or stop a process timer
9 * check_vtimer: check if one of the virtual timers needs to be restarted
12 #include "pm.h"
13 #include <signal.h>
14 #include <sys/time.h>
15 #include <string.h>
16 #include <minix/com.h>
17 #include "mproc.h"
18 #include "param.h"
20 #define US 1000000 /* shortcut for microseconds per second */
22 FORWARD _PROTOTYPE( clock_t ticks_from_timeval, (struct timeval *tv) );
23 FORWARD _PROTOTYPE( void timeval_from_ticks, (struct timeval *tv,
24 clock_t ticks) );
25 FORWARD _PROTOTYPE( int is_sane_timeval, (struct timeval *tv) );
26 FORWARD _PROTOTYPE( void getset_vtimer, (struct mproc *mp, int nwhich,
27 struct itimerval *value, struct itimerval *ovalue) );
28 FORWARD _PROTOTYPE( void get_realtimer, (struct mproc *mp,
29 struct itimerval *value) );
30 FORWARD _PROTOTYPE( void set_realtimer, (struct mproc *mp,
31 struct itimerval *value) );
32 FORWARD _PROTOTYPE( void cause_sigalrm, (struct timer *tp) );
34 /*===========================================================================*
35 * ticks_from_timeval *
36 *===========================================================================*/
37 PRIVATE clock_t ticks_from_timeval(tv)
38 struct timeval *tv;
40 clock_t ticks;
42 /* Large delays cause a lot of problems. First, the alarm system call
43 * takes an unsigned seconds count and the library has cast it to an int.
44 * That probably works, but on return the library will convert "negative"
45 * unsigneds to errors. Presumably no one checks for these errors, so
46 * force this call through. Second, If unsigned and long have the same
47 * size, converting from seconds to ticks can easily overflow. Finally,
48 * the kernel has similar overflow bugs adding ticks.
50 * Fixing this requires a lot of ugly casts to fit the wrong interface
51 * types and to avoid overflow traps. ALRM_EXP_TIME has the right type
52 * (clock_t) although it is declared as long. How can variables like
53 * this be declared properly without combinatorial explosion of message
54 * types?
57 /* In any case, the following conversion must always round up. */
59 ticks = (clock_t) (system_hz * (unsigned long) tv->tv_sec);
60 if ( (unsigned long) ticks / system_hz != (unsigned long) tv->tv_sec) {
61 ticks = LONG_MAX;
62 } else {
63 ticks += (clock_t)
64 ((system_hz * (unsigned long) tv->tv_usec + (US-1)) / US);
67 if (ticks < 0) ticks = LONG_MAX;
69 return(ticks);
72 /*===========================================================================*
73 * timeval_from_ticks *
74 *===========================================================================*/
75 PRIVATE void timeval_from_ticks(tv, ticks)
76 struct timeval *tv;
77 clock_t ticks;
79 tv->tv_sec = (long) (ticks / system_hz);
80 tv->tv_usec = (long) ((ticks % system_hz) * US / system_hz);
83 /*===========================================================================*
84 * is_sane_timeval *
85 *===========================================================================*/
86 PRIVATE int is_sane_timeval(tv)
87 struct timeval *tv;
89 /* This imposes a reasonable time value range for setitimer. */
90 return (tv->tv_sec >= 0 && tv->tv_sec <= MAX_SECS &&
91 tv->tv_usec >= 0 && tv->tv_usec < US);
94 /*===========================================================================*
95 * do_itimer *
96 *===========================================================================*/
97 PUBLIC int do_itimer()
99 struct itimerval ovalue, value; /* old and new interval timers */
100 int setval, getval; /* set and/or retrieve the values? */
101 int r;
103 /* Make sure 'which' is one of the defined timers. */
104 if (m_in.which_timer < 0 || m_in.which_timer >= NR_ITIMERS)
105 return(EINVAL);
107 /* Determine whether to set and/or return the given timer value, based on
108 * which of the new_val and old_val parameters are nonzero. At least one of
109 * them must be nonzero.
111 setval = (m_in.new_val != NULL);
112 getval = (m_in.old_val != NULL);
114 if (!setval && !getval) return(EINVAL);
116 /* If we're setting a new value, copy the new timer from user space.
117 * Also, make sure its fields have sane values.
119 if (setval) {
120 r = sys_datacopy(who_e, (vir_bytes) m_in.new_val,
121 PM_PROC_NR, (vir_bytes) &value, (phys_bytes) sizeof(value));
122 if (r != OK) return(r);
124 if (!is_sane_timeval(&value.it_value) ||
125 !is_sane_timeval(&value.it_interval))
126 return(EINVAL);
129 switch (m_in.which_timer) {
130 case ITIMER_REAL :
131 if (getval) get_realtimer(mp, &ovalue);
133 if (setval) set_realtimer(mp, &value);
135 r = OK;
136 break;
138 case ITIMER_VIRTUAL :
139 case ITIMER_PROF :
140 getset_vtimer(mp, m_in.which_timer,
141 (setval) ? &value : NULL,
142 (getval) ? &ovalue : NULL);
144 r = OK;
145 break;
147 default:
148 panic("invalid timer type: %d", m_in.which_timer);
151 /* If requested, copy the old interval timer to user space. */
152 if (r == OK && getval) {
153 r = sys_datacopy(PM_PROC_NR, (vir_bytes) &ovalue,
154 who_e, (vir_bytes) m_in.old_val, (phys_bytes) sizeof(ovalue));
157 return(r);
160 /*===========================================================================*
161 * do_alarm *
162 *===========================================================================*/
163 PUBLIC int do_alarm()
165 struct itimerval value, ovalue;
166 int remaining; /* previous time left in seconds */
168 /* retrieve the old timer value, in seconds (rounded up) */
169 get_realtimer(mp, &ovalue);
171 remaining = ovalue.it_value.tv_sec;
172 if (ovalue.it_value.tv_usec > 0) remaining++;
174 /* set the new timer value */
175 memset(&value, 0, sizeof(value));
176 value.it_value.tv_sec = m_in.seconds;
178 set_realtimer(mp, &value);
180 /* and return the old timer value */
181 return(remaining);
184 /*===========================================================================*
185 * getset_vtimer *
186 *===========================================================================*/
187 PRIVATE void getset_vtimer(rmp, which, value, ovalue)
188 struct mproc *rmp;
189 int which;
190 struct itimerval *value;
191 struct itimerval *ovalue;
193 clock_t newticks, *nptr; /* the new timer value, in ticks */
194 clock_t oldticks, *optr; /* the old ticks value, in ticks */
195 int r, num;
197 /* The default is to provide sys_vtimer with two null pointers, i.e. to do
198 * nothing at all.
200 optr = nptr = NULL;
202 /* If the old timer value is to be retrieved, have 'optr' point to the
203 * location where the old value is to be stored, and copy the interval.
205 if (ovalue != NULL) {
206 optr = &oldticks;
208 timeval_from_ticks(&ovalue->it_interval, rmp->mp_interval[which]);
211 /* If a new timer value is to be set, store the new timer value and have
212 * 'nptr' point to it. Also, store the new interval.
214 if (value != NULL) {
215 newticks = ticks_from_timeval(&value->it_value);
216 nptr = &newticks;
218 /* If no timer is set, the interval must be zero. */
219 if (newticks <= 0)
220 rmp->mp_interval[which] = 0;
221 else
222 rmp->mp_interval[which] =
223 ticks_from_timeval(&value->it_interval);
226 /* Find out which kernel timer number to use. */
227 switch (which) {
228 case ITIMER_VIRTUAL: num = VT_VIRTUAL; break;
229 case ITIMER_PROF: num = VT_PROF; break;
230 default: panic("invalid vtimer type: %d", which);
233 /* Make the kernel call. If requested, also retrieve and store
234 * the old timer value.
236 if ((r = sys_vtimer(rmp->mp_endpoint, num, nptr, optr)) != OK)
237 panic("sys_vtimer failed: %d", r);
239 if (ovalue != NULL) {
240 /* If the alarm expired already, we should take into account the
241 * interval. Return zero only if the interval is zero as well.
243 if (oldticks <= 0) oldticks = rmp->mp_interval[which];
245 timeval_from_ticks(&ovalue->it_value, oldticks);
249 /*===========================================================================*
250 * check_vtimer *
251 *===========================================================================*/
252 PUBLIC void check_vtimer(proc_nr, sig)
253 int proc_nr;
254 int sig;
256 register struct mproc *rmp;
257 int which, num;
259 rmp = &mproc[proc_nr];
261 /* Translate back the given signal to a timer type and kernel number. */
262 switch (sig) {
263 case SIGVTALRM: which = ITIMER_VIRTUAL; num = VT_VIRTUAL; break;
264 case SIGPROF: which = ITIMER_PROF; num = VT_PROF; break;
265 default: panic("invalid vtimer signal: %d", sig);
268 /* If a repetition interval was set for this virtual timer, tell the
269 * kernel to set a new timeout for the virtual timer.
271 if (rmp->mp_interval[which] > 0)
272 sys_vtimer(rmp->mp_endpoint, num, &rmp->mp_interval[which], NULL);
275 /*===========================================================================*
276 * get_realtimer *
277 *===========================================================================*/
278 PRIVATE void get_realtimer(rmp, value)
279 struct mproc *rmp;
280 struct itimerval *value;
282 clock_t exptime; /* time at which alarm will expire */
283 clock_t uptime; /* current system time */
284 clock_t remaining; /* time left on alarm */
285 int s;
287 /* First determine remaining time, in ticks, of previous alarm, if set. */
288 if (rmp->mp_flags & ALARM_ON) {
289 if ( (s = getuptime(&uptime)) != OK)
290 panic("get_realtimer couldn't get uptime: %d", s);
291 exptime = *tmr_exp_time(&rmp->mp_timer);
293 remaining = exptime - uptime;
295 /* If the alarm expired already, we should take into account the
296 * interval. Return zero only if the interval is zero as well.
298 if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL];
299 } else {
300 remaining = 0;
303 /* Convert the result to a timeval structure. */
304 timeval_from_ticks(&value->it_value, remaining);
306 /* Similarly convert and store the interval of the timer. */
307 timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]);
310 /*===========================================================================*
311 * set_realtimer *
312 *===========================================================================*/
313 PRIVATE void set_realtimer(rmp, value)
314 struct mproc *rmp;
315 struct itimerval *value;
317 clock_t ticks; /* New amount of ticks to the next alarm. */
318 clock_t interval; /* New amount of ticks for the alarm's interval. */
320 /* Convert the timeval structures in the 'value' structure to ticks. */
321 ticks = ticks_from_timeval(&value->it_value);
322 interval = ticks_from_timeval(&value->it_interval);
324 /* If no timer is set, the interval must be zero. */
325 if (ticks <= 0) interval = 0;
327 /* Apply these values. */
328 set_alarm(rmp, ticks);
329 rmp->mp_interval[ITIMER_REAL] = interval;
332 /*===========================================================================*
333 * set_alarm *
334 *===========================================================================*/
335 PUBLIC void set_alarm(rmp, ticks)
336 struct mproc *rmp; /* process that wants the alarm */
337 clock_t ticks; /* how many ticks delay before the signal */
339 if (ticks > 0) {
340 pm_set_timer(&rmp->mp_timer, ticks, cause_sigalrm, rmp->mp_endpoint);
341 rmp->mp_flags |= ALARM_ON;
342 } else if (rmp->mp_flags & ALARM_ON) {
343 pm_cancel_timer(&rmp->mp_timer);
344 rmp->mp_flags &= ~ALARM_ON;
348 /*===========================================================================*
349 * cause_sigalrm *
350 *===========================================================================*/
351 PRIVATE void cause_sigalrm(tp)
352 struct timer *tp;
354 int proc_nr_n;
355 register struct mproc *rmp;
357 /* get process from timer */
358 if(pm_isokendpt(tmr_arg(tp)->ta_int, &proc_nr_n) != OK) {
359 printf("PM: ignoring timer for invalid endpoint %d\n",
360 tmr_arg(tp)->ta_int);
361 return;
364 rmp = &mproc[proc_nr_n];
366 if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) return;
367 if ((rmp->mp_flags & ALARM_ON) == 0) return;
369 /* If an interval is set, set a new timer; otherwise clear the ALARM_ON flag.
370 * The set_alarm call will be calling pm_set_timer from within this callback
371 * from the pm_expire_timers function. This is safe, but we must not use the
372 * "tp" structure below this point anymore. */
373 if (rmp->mp_interval[ITIMER_REAL] > 0)
374 set_alarm(rmp, rmp->mp_interval[ITIMER_REAL]);
375 else rmp->mp_flags &= ~ALARM_ON;
377 check_sig(rmp->mp_pid, SIGALRM, FALSE /* ksig */);