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 FORWARD
_PROTOTYPE( clock_t ticks_from_timeval
, (struct timeval
*tv
) );
23 FORWARD
_PROTOTYPE( void timeval_from_ticks
, (struct timeval
*tv
,
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
)
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
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
) {
64 ((system_hz
* (unsigned long) tv
->tv_usec
+ (US
-1)) / US
);
67 if (ticks
< 0) ticks
= LONG_MAX
;
72 /*===========================================================================*
73 * timeval_from_ticks *
74 *===========================================================================*/
75 PRIVATE
void timeval_from_ticks(tv
, ticks
)
79 tv
->tv_sec
= (long) (ticks
/ system_hz
);
80 tv
->tv_usec
= (long) ((ticks
% system_hz
) * US
/ system_hz
);
83 /*===========================================================================*
85 *===========================================================================*/
86 PRIVATE
int is_sane_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 /*===========================================================================*
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? */
103 /* Make sure 'which' is one of the defined timers. */
104 if (m_in
.which_timer
< 0 || m_in
.which_timer
>= NR_ITIMERS
)
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.
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
))
129 switch (m_in
.which_timer
) {
131 if (getval
) get_realtimer(mp
, &ovalue
);
133 if (setval
) set_realtimer(mp
, &value
);
138 case ITIMER_VIRTUAL
:
140 getset_vtimer(mp
, m_in
.which_timer
,
141 (setval
) ? &value
: NULL
,
142 (getval
) ? &ovalue
: NULL
);
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 *===========================================================================*/
160 PUBLIC
int do_alarm()
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 PRIVATE
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(__FILE__
, "invalid vtimer type", 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(__FILE__
, "sys_vtimer failed", 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 PUBLIC
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(__FILE__
, "invalid vtimer signal", 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 PRIVATE
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(__FILE__
, "get_realtimer couldn't get uptime", 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 PRIVATE
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 PUBLIC
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 pm_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 pm_cancel_timer(&rmp
->mp_timer
);
341 rmp
->mp_flags
&= ~ALARM_ON
;
345 /*===========================================================================*
347 *===========================================================================*/
348 PRIVATE
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 pm_set_timer from within this callback
368 * from the pm_expire_timers function. This is safe, but we must not use the
369 * "tp" structure below this point anymore. */
370 if (rmp
->mp_interval
[ITIMER_REAL
] > 0)
371 set_alarm(rmp
, rmp
->mp_interval
[ITIMER_REAL
]);
372 else rmp
->mp_flags
&= ~ALARM_ON
;
374 check_sig(rmp
->mp_pid
, SIGALRM
);