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
16 #include <minix/com.h>
20 #define US 1000000 /* shortcut for microseconds per second */
22 static clock_t ticks_from_timeval(struct timeval
*tv
);
23 static void timeval_from_ticks(struct timeval
*tv
, clock_t ticks
);
24 static int is_sane_timeval(struct timeval
*tv
);
25 static void getset_vtimer(struct mproc
*mp
, int nwhich
, struct
26 itimerval
*value
, struct itimerval
*ovalue
);
27 static void get_realtimer(struct mproc
*mp
, struct itimerval
*value
);
28 static void set_realtimer(struct mproc
*mp
, struct itimerval
*value
);
29 static void cause_sigalrm(struct timer
*tp
);
31 /*===========================================================================*
32 * ticks_from_timeval *
33 *===========================================================================*/
34 static clock_t ticks_from_timeval(tv
)
39 /* Large delays cause a lot of problems. First, the alarm system call
40 * takes an unsigned seconds count and the library has cast it to an int.
41 * That probably works, but on return the library will convert "negative"
42 * unsigneds to errors. Presumably no one checks for these errors, so
43 * force this call through. Second, If unsigned and long have the same
44 * size, converting from seconds to ticks can easily overflow. Finally,
45 * the kernel has similar overflow bugs adding ticks.
47 * Fixing this requires a lot of ugly casts to fit the wrong interface
48 * types and to avoid overflow traps. ALRM_EXP_TIME has the right type
49 * (clock_t) although it is declared as long. How can variables like
50 * this be declared properly without combinatorial explosion of message
54 /* In any case, the following conversion must always round up. */
56 ticks
= (clock_t) (system_hz
* (unsigned long) tv
->tv_sec
);
57 if ( (unsigned long) ticks
/ system_hz
!= (unsigned long) tv
->tv_sec
) {
61 ((system_hz
* (unsigned long) tv
->tv_usec
+ (US
-1)) / US
);
64 if (ticks
< 0) ticks
= LONG_MAX
;
69 /*===========================================================================*
70 * timeval_from_ticks *
71 *===========================================================================*/
72 static void timeval_from_ticks(tv
, ticks
)
76 tv
->tv_sec
= (long) (ticks
/ system_hz
);
77 tv
->tv_usec
= (long) ((ticks
% system_hz
) * US
/ system_hz
);
80 /*===========================================================================*
82 *===========================================================================*/
83 static int is_sane_timeval(tv
)
86 /* This imposes a reasonable time value range for setitimer. */
87 return (tv
->tv_sec
>= 0 && tv
->tv_sec
<= MAX_SECS
&&
88 tv
->tv_usec
>= 0 && tv
->tv_usec
< US
);
91 /*===========================================================================*
93 *===========================================================================*/
96 struct itimerval ovalue
, value
; /* old and new interval timers */
97 int setval
, getval
; /* set and/or retrieve the values? */
100 /* Make sure 'which' is one of the defined timers. */
101 if (m_in
.which_timer
< 0 || m_in
.which_timer
>= NR_ITIMERS
)
104 /* Determine whether to set and/or return the given timer value, based on
105 * which of the new_val and old_val parameters are nonzero. At least one of
106 * them must be nonzero.
108 setval
= (m_in
.new_val
!= NULL
);
109 getval
= (m_in
.old_val
!= NULL
);
111 if (!setval
&& !getval
) return(EINVAL
);
113 /* If we're setting a new value, copy the new timer from user space.
114 * Also, make sure its fields have sane values.
117 r
= sys_datacopy(who_e
, (vir_bytes
) m_in
.new_val
,
118 PM_PROC_NR
, (vir_bytes
) &value
, (phys_bytes
) sizeof(value
));
119 if (r
!= OK
) return(r
);
121 if (!is_sane_timeval(&value
.it_value
) ||
122 !is_sane_timeval(&value
.it_interval
))
126 switch (m_in
.which_timer
) {
128 if (getval
) get_realtimer(mp
, &ovalue
);
130 if (setval
) set_realtimer(mp
, &value
);
135 case ITIMER_VIRTUAL
:
137 getset_vtimer(mp
, m_in
.which_timer
,
138 (setval
) ? &value
: NULL
,
139 (getval
) ? &ovalue
: NULL
);
145 panic("invalid timer type: %d", m_in
.which_timer
);
148 /* If requested, copy the old interval timer to user space. */
149 if (r
== OK
&& getval
) {
150 r
= sys_datacopy(PM_PROC_NR
, (vir_bytes
) &ovalue
,
151 who_e
, (vir_bytes
) m_in
.old_val
, (phys_bytes
) sizeof(ovalue
));
157 /*===========================================================================*
159 *===========================================================================*/
162 struct itimerval value
, ovalue
;
163 int remaining
; /* previous time left in seconds */
165 /* retrieve the old timer value, in seconds (rounded up) */
166 get_realtimer(mp
, &ovalue
);
168 remaining
= ovalue
.it_value
.tv_sec
;
169 if (ovalue
.it_value
.tv_usec
> 0) remaining
++;
171 /* set the new timer value */
172 memset(&value
, 0, sizeof(value
));
173 value
.it_value
.tv_sec
= m_in
.seconds
;
175 set_realtimer(mp
, &value
);
177 /* and return the old timer value */
181 /*===========================================================================*
183 *===========================================================================*/
184 static void getset_vtimer(rmp
, which
, value
, ovalue
)
187 struct itimerval
*value
;
188 struct itimerval
*ovalue
;
190 clock_t newticks
, *nptr
; /* the new timer value, in ticks */
191 clock_t oldticks
, *optr
; /* the old ticks value, in ticks */
194 /* The default is to provide sys_vtimer with two null pointers, i.e. to do
199 /* If the old timer value is to be retrieved, have 'optr' point to the
200 * location where the old value is to be stored, and copy the interval.
202 if (ovalue
!= NULL
) {
205 timeval_from_ticks(&ovalue
->it_interval
, rmp
->mp_interval
[which
]);
208 /* If a new timer value is to be set, store the new timer value and have
209 * 'nptr' point to it. Also, store the new interval.
212 newticks
= ticks_from_timeval(&value
->it_value
);
215 /* If no timer is set, the interval must be zero. */
217 rmp
->mp_interval
[which
] = 0;
219 rmp
->mp_interval
[which
] =
220 ticks_from_timeval(&value
->it_interval
);
223 /* Find out which kernel timer number to use. */
225 case ITIMER_VIRTUAL
: num
= VT_VIRTUAL
; break;
226 case ITIMER_PROF
: num
= VT_PROF
; break;
227 default: panic("invalid vtimer type: %d", which
);
230 /* Make the kernel call. If requested, also retrieve and store
231 * the old timer value.
233 if ((r
= sys_vtimer(rmp
->mp_endpoint
, num
, nptr
, optr
)) != OK
)
234 panic("sys_vtimer failed: %d", r
);
236 if (ovalue
!= NULL
) {
237 /* If the alarm expired already, we should take into account the
238 * interval. Return zero only if the interval is zero as well.
240 if (oldticks
<= 0) oldticks
= rmp
->mp_interval
[which
];
242 timeval_from_ticks(&ovalue
->it_value
, oldticks
);
246 /*===========================================================================*
248 *===========================================================================*/
249 void check_vtimer(proc_nr
, sig
)
253 register struct mproc
*rmp
;
256 rmp
= &mproc
[proc_nr
];
258 /* Translate back the given signal to a timer type and kernel number. */
260 case SIGVTALRM
: which
= ITIMER_VIRTUAL
; num
= VT_VIRTUAL
; break;
261 case SIGPROF
: which
= ITIMER_PROF
; num
= VT_PROF
; break;
262 default: panic("invalid vtimer signal: %d", sig
);
265 /* If a repetition interval was set for this virtual timer, tell the
266 * kernel to set a new timeout for the virtual timer.
268 if (rmp
->mp_interval
[which
] > 0)
269 sys_vtimer(rmp
->mp_endpoint
, num
, &rmp
->mp_interval
[which
], NULL
);
272 /*===========================================================================*
274 *===========================================================================*/
275 static void get_realtimer(rmp
, value
)
277 struct itimerval
*value
;
279 clock_t exptime
; /* time at which alarm will expire */
280 clock_t uptime
; /* current system time */
281 clock_t remaining
; /* time left on alarm */
284 /* First determine remaining time, in ticks, of previous alarm, if set. */
285 if (rmp
->mp_flags
& ALARM_ON
) {
286 if ( (s
= getuptime(&uptime
)) != OK
)
287 panic("get_realtimer couldn't get uptime: %d", s
);
288 exptime
= *tmr_exp_time(&rmp
->mp_timer
);
290 remaining
= exptime
- uptime
;
292 /* If the alarm expired already, we should take into account the
293 * interval. Return zero only if the interval is zero as well.
295 if (remaining
<= 0) remaining
= rmp
->mp_interval
[ITIMER_REAL
];
300 /* Convert the result to a timeval structure. */
301 timeval_from_ticks(&value
->it_value
, remaining
);
303 /* Similarly convert and store the interval of the timer. */
304 timeval_from_ticks(&value
->it_interval
, rmp
->mp_interval
[ITIMER_REAL
]);
307 /*===========================================================================*
309 *===========================================================================*/
310 static void set_realtimer(rmp
, value
)
312 struct itimerval
*value
;
314 clock_t ticks
; /* New amount of ticks to the next alarm. */
315 clock_t interval
; /* New amount of ticks for the alarm's interval. */
317 /* Convert the timeval structures in the 'value' structure to ticks. */
318 ticks
= ticks_from_timeval(&value
->it_value
);
319 interval
= ticks_from_timeval(&value
->it_interval
);
321 /* If no timer is set, the interval must be zero. */
322 if (ticks
<= 0) interval
= 0;
324 /* Apply these values. */
325 set_alarm(rmp
, ticks
);
326 rmp
->mp_interval
[ITIMER_REAL
] = interval
;
329 /*===========================================================================*
331 *===========================================================================*/
332 void set_alarm(rmp
, ticks
)
333 struct mproc
*rmp
; /* process that wants the alarm */
334 clock_t ticks
; /* how many ticks delay before the signal */
337 set_timer(&rmp
->mp_timer
, ticks
, cause_sigalrm
, rmp
->mp_endpoint
);
338 rmp
->mp_flags
|= ALARM_ON
;
339 } else if (rmp
->mp_flags
& ALARM_ON
) {
340 cancel_timer(&rmp
->mp_timer
);
341 rmp
->mp_flags
&= ~ALARM_ON
;
345 /*===========================================================================*
347 *===========================================================================*/
348 static void cause_sigalrm(tp
)
352 register struct mproc
*rmp
;
354 /* get process from timer */
355 if(pm_isokendpt(tmr_arg(tp
)->ta_int
, &proc_nr_n
) != OK
) {
356 printf("PM: ignoring timer for invalid endpoint %d\n",
357 tmr_arg(tp
)->ta_int
);
361 rmp
= &mproc
[proc_nr_n
];
363 if ((rmp
->mp_flags
& (IN_USE
| EXITING
)) != IN_USE
) return;
364 if ((rmp
->mp_flags
& ALARM_ON
) == 0) return;
366 /* If an interval is set, set a new timer; otherwise clear the ALARM_ON flag.
367 * The set_alarm call will be calling set_timer from within this callback
368 * from the expire_timers function. This is safe, but we must not use the
369 * "tp" structure below this point anymore.
371 if (rmp
->mp_interval
[ITIMER_REAL
] > 0)
372 set_alarm(rmp
, rmp
->mp_interval
[ITIMER_REAL
]);
373 else rmp
->mp_flags
&= ~ALARM_ON
;
375 check_sig(rmp
->mp_pid
, SIGALRM
, FALSE
/* ksig */);