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.
4 *
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
9 */
10
11 #include "pm.h"
12 #include <signal.h>
13 #include <sys/time.h>
14 #include <minix/com.h>
15 #include <minix/callnr.h>
16 #include <assert.h>
17 #include "mproc.h"
18
19 #define US 1000000UL /* shortcut for microseconds per second */
20
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);
29
30 /*===========================================================================*
31 * ticks_from_timeval *
32 *===========================================================================*/
ticks_from_timeval(tv)33 static clock_t ticks_from_timeval(tv)
34 struct timeval *tv;
35 {
36 clock_t ticks;
37
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.
45 *
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
50 * types?
51 */
52
53 /* In any case, the following conversion must always round up. */
54
55 ticks = system_hz * (unsigned long) tv->tv_sec;
56 if ( (ticks / system_hz) != (unsigned long)tv->tv_sec) {
57 ticks = LONG_MAX;
58 } else {
59 ticks += ((system_hz * (unsigned long)tv->tv_usec + (US-1)) / US);
60 }
61
62 if (ticks > LONG_MAX) ticks = LONG_MAX;
63
64 return(ticks);
65 }
66
67 /*===========================================================================*
68 * timeval_from_ticks *
69 *===========================================================================*/
timeval_from_ticks(tv,ticks)70 static void timeval_from_ticks(tv, ticks)
71 struct timeval *tv;
72 clock_t ticks;
73 {
74 tv->tv_sec = (long) (ticks / system_hz);
75 tv->tv_usec = (long) ((ticks % system_hz) * US / system_hz);
76 }
77
78 /*===========================================================================*
79 * is_sane_timeval *
80 *===========================================================================*/
81 static int
is_sane_timeval(struct timeval * tv)82 is_sane_timeval(struct timeval *tv)
83 {
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);
87 }
88
89 /*===========================================================================*
90 * do_itimer *
91 *===========================================================================*/
92 int
do_itimer(void)93 do_itimer(void)
94 {
95 struct itimerval ovalue, value; /* old and new interval timers */
96 int setval, getval; /* set and/or retrieve the values? */
97 int r, which;
98
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);
102
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.
106 */
107 setval = (m_in.m_lc_pm_itimer.value != 0);
108 getval = (m_in.m_lc_pm_itimer.ovalue != 0);
109
110 if (!setval && !getval) return(EINVAL);
111
112 /* If we're setting a new value, copy the new timer from user space.
113 * Also, make sure its fields have sane values.
114 */
115 if (setval) {
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);
119
120 if (!is_sane_timeval(&value.it_value) ||
121 !is_sane_timeval(&value.it_interval))
122 return(EINVAL);
123 }
124
125 switch (which) {
126 case ITIMER_REAL :
127 if (getval) get_realtimer(mp, &ovalue);
128
129 if (setval) set_realtimer(mp, &value);
130
131 r = OK;
132 break;
133
134 case ITIMER_VIRTUAL :
135 case ITIMER_PROF :
136 getset_vtimer(mp, which, (setval) ? &value : NULL,
137 (getval) ? &ovalue : NULL);
138
139 r = OK;
140 break;
141
142 default:
143 panic("invalid timer type: %d", which);
144 }
145
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));
151 }
152
153 return(r);
154 }
155
156 /*===========================================================================*
157 * getset_vtimer *
158 *===========================================================================*/
159 static void
getset_vtimer(struct mproc * rmp,int which,struct itimerval * value,struct itimerval * ovalue)160 getset_vtimer(struct mproc *rmp, int which, struct itimerval *value, struct itimerval *ovalue)
161 {
162 clock_t newticks, *nptr; /* the new timer value, in ticks */
163 clock_t oldticks, *optr; /* the old ticks value, in ticks */
164 int r, num;
165
166 /* The default is to provide sys_vtimer with two null pointers, i.e. to do
167 * nothing at all.
168 */
169 optr = nptr = NULL;
170
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.
173 */
174 if (ovalue != NULL) {
175 optr = &oldticks;
176
177 timeval_from_ticks(&ovalue->it_interval, rmp->mp_interval[which]);
178 }
179
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.
182 */
183 if (value != NULL) {
184 newticks = ticks_from_timeval(&value->it_value);
185 nptr = &newticks;
186
187 /* If no timer is set, the interval must be zero. */
188 if (newticks <= 0)
189 rmp->mp_interval[which] = 0;
190 else
191 rmp->mp_interval[which] =
192 ticks_from_timeval(&value->it_interval);
193 }
194
195 /* Find out which kernel timer number to use. */
196 switch (which) {
197 case ITIMER_VIRTUAL: num = VT_VIRTUAL; break;
198 case ITIMER_PROF: num = VT_PROF; break;
199 default: panic("invalid vtimer type: %d", which);
200 }
201
202 /* Make the kernel call. If requested, also retrieve and store
203 * the old timer value.
204 */
205 if ((r = sys_vtimer(rmp->mp_endpoint, num, nptr, optr)) != OK)
206 panic("sys_vtimer failed: %d", r);
207
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.
211 */
212 if (oldticks <= 0) oldticks = rmp->mp_interval[which];
213
214 timeval_from_ticks(&ovalue->it_value, oldticks);
215 }
216 }
217
218 /*===========================================================================*
219 * check_vtimer *
220 *===========================================================================*/
221 void
check_vtimer(int proc_nr,int sig)222 check_vtimer(int proc_nr, int sig)
223 {
224 register struct mproc *rmp;
225 int which, num;
226
227 rmp = &mproc[proc_nr];
228
229 /* Translate back the given signal to a timer type and kernel number. */
230 switch (sig) {
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);
234 }
235
236 /* If a repetition interval was set for this virtual timer, tell the
237 * kernel to set a new timeout for the virtual timer.
238 */
239 if (rmp->mp_interval[which] > 0)
240 sys_vtimer(rmp->mp_endpoint, num, &rmp->mp_interval[which], NULL);
241 }
242
243 /*===========================================================================*
244 * get_realtimer *
245 *===========================================================================*/
246 static void
get_realtimer(struct mproc * rmp,struct itimerval * value)247 get_realtimer(struct mproc *rmp, struct itimerval *value)
248 {
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 */
252
253 /* First determine remaining time, in ticks, of previous alarm, if set. */
254 if (rmp->mp_flags & ALARM_ON) {
255 uptime = getticks();
256 exptime = tmr_exp_time(&rmp->mp_timer);
257
258 remaining = exptime - uptime;
259
260 /* If the alarm expired already, we should take into account the
261 * interval. Return zero only if the interval is zero as well.
262 */
263 if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL];
264 } else {
265 remaining = 0;
266 }
267
268 /* Convert the result to a timeval structure. */
269 timeval_from_ticks(&value->it_value, remaining);
270
271 /* Similarly convert and store the interval of the timer. */
272 timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]);
273 }
274
275 /*===========================================================================*
276 * set_realtimer *
277 *===========================================================================*/
278 static void
set_realtimer(struct mproc * rmp,struct itimerval * value)279 set_realtimer(struct mproc *rmp, struct itimerval *value)
280 {
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. */
283
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);
287
288 /* If no timer is set, the interval must be zero. */
289 if (ticks <= 0) interval = 0;
290
291 /* Apply these values. */
292 set_alarm(rmp, ticks);
293 rmp->mp_interval[ITIMER_REAL] = interval;
294 }
295
296 /*===========================================================================*
297 * set_alarm *
298 *===========================================================================*/
set_alarm(rmp,ticks)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 */
302 {
303 if (ticks > 0) {
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;
310 }
311 }
312
313 /*===========================================================================*
314 * cause_sigalrm *
315 *===========================================================================*/
316 static void
cause_sigalrm(int arg)317 cause_sigalrm(int arg)
318 {
319 int proc_nr_n;
320 register struct mproc *rmp;
321
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);
325 return;
326 }
327
328 rmp = &mproc[proc_nr_n];
329
330 if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) return;
331 if ((rmp->mp_flags & ALARM_ON) == 0) return;
332
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.
336 */
337 if (rmp->mp_interval[ITIMER_REAL] > 0)
338 set_alarm(rmp, rmp->mp_interval[ITIMER_REAL]);
339 else rmp->mp_flags &= ~ALARM_ON;
340
341 mp = &mproc[0]; /* pretend the signal comes from PM */
342
343 check_sig(rmp->mp_pid, SIGALRM, FALSE /* ksig */);
344 }
345