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 * set_alarm: tell the timer interface to start or stop a process timer
8 * check_vtimer: check if one of the virtual timers needs to be restarted
14 #include <minix/com.h>
15 #include <minix/callnr.h>
19 #define US 1000000UL /* shortcut for microseconds per second */
21 static clock_t ticks_from_timeval(struct timeval
*tv
);
22 static void timeval_from_ticks(struct timeval
*tv
, clock_t ticks
);
23 static int is_sane_timeval(struct timeval
*tv
);
24 static void getset_vtimer(struct mproc
*mp
, int nwhich
, struct
25 itimerval
*value
, struct itimerval
*ovalue
);
26 static void get_realtimer(struct mproc
*mp
, struct itimerval
*value
);
27 static void set_realtimer(struct mproc
*mp
, struct itimerval
*value
);
28 static void cause_sigalrm(int arg
);
30 /*===========================================================================*
31 * ticks_from_timeval *
32 *===========================================================================*/
33 static clock_t ticks_from_timeval(tv
)
38 /* Large delays cause a lot of problems. First, the alarm system call
39 * takes an unsigned seconds count and the library has cast it to an int.
40 * That probably works, but on return the library will convert "negative"
41 * unsigneds to errors. Presumably no one checks for these errors, so
42 * force this call through. Second, If unsigned and long have the same
43 * size, converting from seconds to ticks can easily overflow. Finally,
44 * the kernel has similar overflow bugs adding ticks.
46 * Fixing this requires a lot of ugly casts to fit the wrong interface
47 * types and to avoid overflow traps. ALRM_EXP_TIME has the right type
48 * (clock_t) although it is declared as long. How can variables like
49 * this be declared properly without combinatorial explosion of message
53 /* In any case, the following conversion must always round up. */
55 ticks
= system_hz
* (unsigned long) tv
->tv_sec
;
56 if ( (ticks
/ system_hz
) != (unsigned long)tv
->tv_sec
) {
59 ticks
+= ((system_hz
* (unsigned long)tv
->tv_usec
+ (US
-1)) / US
);
62 if (ticks
> LONG_MAX
) ticks
= LONG_MAX
;
67 /*===========================================================================*
68 * timeval_from_ticks *
69 *===========================================================================*/
70 static void timeval_from_ticks(tv
, ticks
)
74 tv
->tv_sec
= (long) (ticks
/ system_hz
);
75 tv
->tv_usec
= (long) ((ticks
% system_hz
) * US
/ system_hz
);
78 /*===========================================================================*
80 *===========================================================================*/
82 is_sane_timeval(struct timeval
*tv
)
84 /* This imposes a reasonable time value range for setitimer. */
85 return (tv
->tv_sec
>= 0 && tv
->tv_sec
<= MAX_SECS
&&
86 tv
->tv_usec
>= 0 && tv
->tv_usec
< US
);
89 /*===========================================================================*
91 *===========================================================================*/
95 struct itimerval ovalue
, value
; /* old and new interval timers */
96 int setval
, getval
; /* set and/or retrieve the values? */
99 /* Make sure 'which' is one of the defined timers. */
100 which
= m_in
.m_lc_pm_itimer
.which
;
101 if (which
< 0 || which
>= NR_ITIMERS
) return(EINVAL
);
103 /* Determine whether to set and/or return the given timer value, based on
104 * which of the value and ovalue parameters are nonzero. At least one of
105 * them must be nonzero.
107 setval
= (m_in
.m_lc_pm_itimer
.value
!= 0);
108 getval
= (m_in
.m_lc_pm_itimer
.ovalue
!= 0);
110 if (!setval
&& !getval
) return(EINVAL
);
112 /* If we're setting a new value, copy the new timer from user space.
113 * Also, make sure its fields have sane values.
116 r
= sys_datacopy(who_e
, m_in
.m_lc_pm_itimer
.value
,
117 PM_PROC_NR
, (vir_bytes
)&value
, (phys_bytes
)sizeof(value
));
118 if (r
!= OK
) return(r
);
120 if (!is_sane_timeval(&value
.it_value
) ||
121 !is_sane_timeval(&value
.it_interval
))
127 if (getval
) get_realtimer(mp
, &ovalue
);
129 if (setval
) set_realtimer(mp
, &value
);
134 case ITIMER_VIRTUAL
:
136 getset_vtimer(mp
, which
, (setval
) ? &value
: NULL
,
137 (getval
) ? &ovalue
: NULL
);
143 panic("invalid timer type: %d", which
);
146 /* If requested, copy the old interval timer to user space. */
147 if (r
== OK
&& getval
) {
148 r
= sys_datacopy(PM_PROC_NR
, (vir_bytes
)&ovalue
,
149 who_e
, m_in
.m_lc_pm_itimer
.ovalue
,
150 (phys_bytes
)sizeof(ovalue
));
156 /*===========================================================================*
158 *===========================================================================*/
160 getset_vtimer(struct mproc
*rmp
, int which
, struct itimerval
*value
, struct itimerval
*ovalue
)
162 clock_t newticks
, *nptr
; /* the new timer value, in ticks */
163 clock_t oldticks
, *optr
; /* the old ticks value, in ticks */
166 /* The default is to provide sys_vtimer with two null pointers, i.e. to do
171 /* If the old timer value is to be retrieved, have 'optr' point to the
172 * location where the old value is to be stored, and copy the interval.
174 if (ovalue
!= NULL
) {
177 timeval_from_ticks(&ovalue
->it_interval
, rmp
->mp_interval
[which
]);
180 /* If a new timer value is to be set, store the new timer value and have
181 * 'nptr' point to it. Also, store the new interval.
184 newticks
= ticks_from_timeval(&value
->it_value
);
187 /* If no timer is set, the interval must be zero. */
189 rmp
->mp_interval
[which
] = 0;
191 rmp
->mp_interval
[which
] =
192 ticks_from_timeval(&value
->it_interval
);
195 /* Find out which kernel timer number to use. */
197 case ITIMER_VIRTUAL
: num
= VT_VIRTUAL
; break;
198 case ITIMER_PROF
: num
= VT_PROF
; break;
199 default: panic("invalid vtimer type: %d", which
);
202 /* Make the kernel call. If requested, also retrieve and store
203 * the old timer value.
205 if ((r
= sys_vtimer(rmp
->mp_endpoint
, num
, nptr
, optr
)) != OK
)
206 panic("sys_vtimer failed: %d", r
);
208 if (ovalue
!= NULL
) {
209 /* If the alarm expired already, we should take into account the
210 * interval. Return zero only if the interval is zero as well.
212 if (oldticks
<= 0) oldticks
= rmp
->mp_interval
[which
];
214 timeval_from_ticks(&ovalue
->it_value
, oldticks
);
218 /*===========================================================================*
220 *===========================================================================*/
222 check_vtimer(int proc_nr
, int sig
)
224 register struct mproc
*rmp
;
227 rmp
= &mproc
[proc_nr
];
229 /* Translate back the given signal to a timer type and kernel number. */
231 case SIGVTALRM
: which
= ITIMER_VIRTUAL
; num
= VT_VIRTUAL
; break;
232 case SIGPROF
: which
= ITIMER_PROF
; num
= VT_PROF
; break;
233 default: panic("invalid vtimer signal: %d", sig
);
236 /* If a repetition interval was set for this virtual timer, tell the
237 * kernel to set a new timeout for the virtual timer.
239 if (rmp
->mp_interval
[which
] > 0)
240 sys_vtimer(rmp
->mp_endpoint
, num
, &rmp
->mp_interval
[which
], NULL
);
243 /*===========================================================================*
245 *===========================================================================*/
247 get_realtimer(struct mproc
*rmp
, struct itimerval
*value
)
249 clock_t exptime
; /* time at which alarm will expire */
250 clock_t uptime
; /* current system time */
251 clock_t remaining
; /* time left on alarm */
253 /* First determine remaining time, in ticks, of previous alarm, if set. */
254 if (rmp
->mp_flags
& ALARM_ON
) {
256 exptime
= tmr_exp_time(&rmp
->mp_timer
);
258 remaining
= exptime
- uptime
;
260 /* If the alarm expired already, we should take into account the
261 * interval. Return zero only if the interval is zero as well.
263 if (remaining
<= 0) remaining
= rmp
->mp_interval
[ITIMER_REAL
];
268 /* Convert the result to a timeval structure. */
269 timeval_from_ticks(&value
->it_value
, remaining
);
271 /* Similarly convert and store the interval of the timer. */
272 timeval_from_ticks(&value
->it_interval
, rmp
->mp_interval
[ITIMER_REAL
]);
275 /*===========================================================================*
277 *===========================================================================*/
279 set_realtimer(struct mproc
*rmp
, struct itimerval
*value
)
281 clock_t ticks
; /* New amount of ticks to the next alarm. */
282 clock_t interval
; /* New amount of ticks for the alarm's interval. */
284 /* Convert the timeval structures in the 'value' structure to ticks. */
285 ticks
= ticks_from_timeval(&value
->it_value
);
286 interval
= ticks_from_timeval(&value
->it_interval
);
288 /* If no timer is set, the interval must be zero. */
289 if (ticks
<= 0) interval
= 0;
291 /* Apply these values. */
292 set_alarm(rmp
, ticks
);
293 rmp
->mp_interval
[ITIMER_REAL
] = interval
;
296 /*===========================================================================*
298 *===========================================================================*/
299 void set_alarm(rmp
, ticks
)
300 struct mproc
*rmp
; /* process that wants the alarm */
301 clock_t ticks
; /* how many ticks delay before the signal */
304 assert(ticks
<= TMRDIFF_MAX
);
305 set_timer(&rmp
->mp_timer
, ticks
, cause_sigalrm
, rmp
->mp_endpoint
);
306 rmp
->mp_flags
|= ALARM_ON
;
307 } else if (rmp
->mp_flags
& ALARM_ON
) {
308 cancel_timer(&rmp
->mp_timer
);
309 rmp
->mp_flags
&= ~ALARM_ON
;
313 /*===========================================================================*
315 *===========================================================================*/
317 cause_sigalrm(int arg
)
320 register struct mproc
*rmp
;
322 /* get process from timer */
323 if(pm_isokendpt(arg
, &proc_nr_n
) != OK
) {
324 printf("PM: ignoring timer for invalid endpoint %d\n", arg
);
328 rmp
= &mproc
[proc_nr_n
];
330 if ((rmp
->mp_flags
& (IN_USE
| EXITING
)) != IN_USE
) return;
331 if ((rmp
->mp_flags
& ALARM_ON
) == 0) return;
333 /* If an interval is set, set a new timer; otherwise clear the ALARM_ON flag.
334 * The set_alarm call will be calling set_timer from within this callback
335 * from the expire_timers function. This is safe.
337 if (rmp
->mp_interval
[ITIMER_REAL
] > 0)
338 set_alarm(rmp
, rmp
->mp_interval
[ITIMER_REAL
]);
339 else rmp
->mp_flags
&= ~ALARM_ON
;
341 mp
= &mproc
[0]; /* pretend the signal comes from PM */
343 check_sig(rmp
->mp_pid
, SIGALRM
, FALSE
/* ksig */);