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 "mproc.h" 17 18 #define US 1000000UL /* shortcut for microseconds per second */ 19 20 static clock_t ticks_from_timeval(struct timeval *tv); 21 static void timeval_from_ticks(struct timeval *tv, clock_t ticks); 22 static int is_sane_timeval(struct timeval *tv); 23 static void getset_vtimer(struct mproc *mp, int nwhich, struct 24 itimerval *value, struct itimerval *ovalue); 25 static void get_realtimer(struct mproc *mp, struct itimerval *value); 26 static void set_realtimer(struct mproc *mp, struct itimerval *value); 27 static void cause_sigalrm(minix_timer_t *tp); 28 29 /*===========================================================================* 30 * ticks_from_timeval * 31 *===========================================================================*/ 32 static clock_t ticks_from_timeval(tv) 33 struct timeval *tv; 34 { 35 clock_t ticks; 36 37 /* Large delays cause a lot of problems. First, the alarm system call 38 * takes an unsigned seconds count and the library has cast it to an int. 39 * That probably works, but on return the library will convert "negative" 40 * unsigneds to errors. Presumably no one checks for these errors, so 41 * force this call through. Second, If unsigned and long have the same 42 * size, converting from seconds to ticks can easily overflow. Finally, 43 * the kernel has similar overflow bugs adding ticks. 44 * 45 * Fixing this requires a lot of ugly casts to fit the wrong interface 46 * types and to avoid overflow traps. ALRM_EXP_TIME has the right type 47 * (clock_t) although it is declared as long. How can variables like 48 * this be declared properly without combinatorial explosion of message 49 * types? 50 */ 51 52 /* In any case, the following conversion must always round up. */ 53 54 ticks = system_hz * (unsigned long) tv->tv_sec; 55 if ( (ticks / system_hz) != (unsigned long)tv->tv_sec) { 56 ticks = LONG_MAX; 57 } else { 58 ticks += ((system_hz * (unsigned long)tv->tv_usec + (US-1)) / US); 59 } 60 61 if (ticks > LONG_MAX) ticks = LONG_MAX; 62 63 return(ticks); 64 } 65 66 /*===========================================================================* 67 * timeval_from_ticks * 68 *===========================================================================*/ 69 static void timeval_from_ticks(tv, ticks) 70 struct timeval *tv; 71 clock_t ticks; 72 { 73 tv->tv_sec = (long) (ticks / system_hz); 74 tv->tv_usec = (long) ((ticks % system_hz) * US / system_hz); 75 } 76 77 /*===========================================================================* 78 * is_sane_timeval * 79 *===========================================================================*/ 80 static int is_sane_timeval(tv) 81 struct timeval *tv; 82 { 83 /* This imposes a reasonable time value range for setitimer. */ 84 return (tv->tv_sec >= 0 && tv->tv_sec <= MAX_SECS && 85 tv->tv_usec >= 0 && tv->tv_usec < US); 86 } 87 88 /*===========================================================================* 89 * do_itimer * 90 *===========================================================================*/ 91 int do_itimer() 92 { 93 struct itimerval ovalue, value; /* old and new interval timers */ 94 int setval, getval; /* set and/or retrieve the values? */ 95 int r, which; 96 97 /* Make sure 'which' is one of the defined timers. */ 98 which = m_in.m_lc_pm_itimer.which; 99 if (which < 0 || which >= NR_ITIMERS) return(EINVAL); 100 101 /* Determine whether to set and/or return the given timer value, based on 102 * which of the value and ovalue parameters are nonzero. At least one of 103 * them must be nonzero. 104 */ 105 setval = (m_in.m_lc_pm_itimer.value != 0); 106 getval = (m_in.m_lc_pm_itimer.ovalue != 0); 107 108 if (!setval && !getval) return(EINVAL); 109 110 /* If we're setting a new value, copy the new timer from user space. 111 * Also, make sure its fields have sane values. 112 */ 113 if (setval) { 114 r = sys_datacopy(who_e, m_in.m_lc_pm_itimer.value, 115 PM_PROC_NR, (vir_bytes)&value, (phys_bytes)sizeof(value)); 116 if (r != OK) return(r); 117 118 if (!is_sane_timeval(&value.it_value) || 119 !is_sane_timeval(&value.it_interval)) 120 return(EINVAL); 121 } 122 123 switch (which) { 124 case ITIMER_REAL : 125 if (getval) get_realtimer(mp, &ovalue); 126 127 if (setval) set_realtimer(mp, &value); 128 129 r = OK; 130 break; 131 132 case ITIMER_VIRTUAL : 133 case ITIMER_PROF : 134 getset_vtimer(mp, which, (setval) ? &value : NULL, 135 (getval) ? &ovalue : NULL); 136 137 r = OK; 138 break; 139 140 default: 141 panic("invalid timer type: %d", which); 142 } 143 144 /* If requested, copy the old interval timer to user space. */ 145 if (r == OK && getval) { 146 r = sys_datacopy(PM_PROC_NR, (vir_bytes)&ovalue, 147 who_e, m_in.m_lc_pm_itimer.ovalue, 148 (phys_bytes)sizeof(ovalue)); 149 } 150 151 return(r); 152 } 153 154 /*===========================================================================* 155 * getset_vtimer * 156 *===========================================================================*/ 157 static void getset_vtimer(rmp, which, value, ovalue) 158 struct mproc *rmp; 159 int which; 160 struct itimerval *value; 161 struct itimerval *ovalue; 162 { 163 clock_t newticks, *nptr; /* the new timer value, in ticks */ 164 clock_t oldticks, *optr; /* the old ticks value, in ticks */ 165 int r, num; 166 167 /* The default is to provide sys_vtimer with two null pointers, i.e. to do 168 * nothing at all. 169 */ 170 optr = nptr = NULL; 171 172 /* If the old timer value is to be retrieved, have 'optr' point to the 173 * location where the old value is to be stored, and copy the interval. 174 */ 175 if (ovalue != NULL) { 176 optr = &oldticks; 177 178 timeval_from_ticks(&ovalue->it_interval, rmp->mp_interval[which]); 179 } 180 181 /* If a new timer value is to be set, store the new timer value and have 182 * 'nptr' point to it. Also, store the new interval. 183 */ 184 if (value != NULL) { 185 newticks = ticks_from_timeval(&value->it_value); 186 nptr = &newticks; 187 188 /* If no timer is set, the interval must be zero. */ 189 if (newticks <= 0) 190 rmp->mp_interval[which] = 0; 191 else 192 rmp->mp_interval[which] = 193 ticks_from_timeval(&value->it_interval); 194 } 195 196 /* Find out which kernel timer number to use. */ 197 switch (which) { 198 case ITIMER_VIRTUAL: num = VT_VIRTUAL; break; 199 case ITIMER_PROF: num = VT_PROF; break; 200 default: panic("invalid vtimer type: %d", which); 201 } 202 203 /* Make the kernel call. If requested, also retrieve and store 204 * the old timer value. 205 */ 206 if ((r = sys_vtimer(rmp->mp_endpoint, num, nptr, optr)) != OK) 207 panic("sys_vtimer failed: %d", r); 208 209 if (ovalue != NULL) { 210 /* If the alarm expired already, we should take into account the 211 * interval. Return zero only if the interval is zero as well. 212 */ 213 if (oldticks <= 0) oldticks = rmp->mp_interval[which]; 214 215 timeval_from_ticks(&ovalue->it_value, oldticks); 216 } 217 } 218 219 /*===========================================================================* 220 * check_vtimer * 221 *===========================================================================*/ 222 void check_vtimer(proc_nr, sig) 223 int proc_nr; 224 int sig; 225 { 226 register struct mproc *rmp; 227 int which, num; 228 229 rmp = &mproc[proc_nr]; 230 231 /* Translate back the given signal to a timer type and kernel number. */ 232 switch (sig) { 233 case SIGVTALRM: which = ITIMER_VIRTUAL; num = VT_VIRTUAL; break; 234 case SIGPROF: which = ITIMER_PROF; num = VT_PROF; break; 235 default: panic("invalid vtimer signal: %d", sig); 236 } 237 238 /* If a repetition interval was set for this virtual timer, tell the 239 * kernel to set a new timeout for the virtual timer. 240 */ 241 if (rmp->mp_interval[which] > 0) 242 sys_vtimer(rmp->mp_endpoint, num, &rmp->mp_interval[which], NULL); 243 } 244 245 /*===========================================================================* 246 * get_realtimer * 247 *===========================================================================*/ 248 static void get_realtimer(rmp, value) 249 struct mproc *rmp; 250 struct itimerval *value; 251 { 252 clock_t exptime; /* time at which alarm will expire */ 253 clock_t uptime; /* current system time */ 254 clock_t remaining; /* time left on alarm */ 255 int s; 256 257 /* First determine remaining time, in ticks, of previous alarm, if set. */ 258 if (rmp->mp_flags & ALARM_ON) { 259 if ( (s = getticks(&uptime)) != OK) 260 panic("get_realtimer couldn't get uptime: %d", s); 261 exptime = *tmr_exp_time(&rmp->mp_timer); 262 263 remaining = exptime - uptime; 264 265 /* If the alarm expired already, we should take into account the 266 * interval. Return zero only if the interval is zero as well. 267 */ 268 if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL]; 269 } else { 270 remaining = 0; 271 } 272 273 /* Convert the result to a timeval structure. */ 274 timeval_from_ticks(&value->it_value, remaining); 275 276 /* Similarly convert and store the interval of the timer. */ 277 timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]); 278 } 279 280 /*===========================================================================* 281 * set_realtimer * 282 *===========================================================================*/ 283 static void set_realtimer(rmp, value) 284 struct mproc *rmp; 285 struct itimerval *value; 286 { 287 clock_t ticks; /* New amount of ticks to the next alarm. */ 288 clock_t interval; /* New amount of ticks for the alarm's interval. */ 289 290 /* Convert the timeval structures in the 'value' structure to ticks. */ 291 ticks = ticks_from_timeval(&value->it_value); 292 interval = ticks_from_timeval(&value->it_interval); 293 294 /* If no timer is set, the interval must be zero. */ 295 if (ticks <= 0) interval = 0; 296 297 /* Apply these values. */ 298 set_alarm(rmp, ticks); 299 rmp->mp_interval[ITIMER_REAL] = interval; 300 } 301 302 /*===========================================================================* 303 * set_alarm * 304 *===========================================================================*/ 305 void set_alarm(rmp, ticks) 306 struct mproc *rmp; /* process that wants the alarm */ 307 clock_t ticks; /* how many ticks delay before the signal */ 308 { 309 if (ticks > 0) { 310 set_timer(&rmp->mp_timer, ticks, cause_sigalrm, rmp->mp_endpoint); 311 rmp->mp_flags |= ALARM_ON; 312 } else if (rmp->mp_flags & ALARM_ON) { 313 cancel_timer(&rmp->mp_timer); 314 rmp->mp_flags &= ~ALARM_ON; 315 } 316 } 317 318 /*===========================================================================* 319 * cause_sigalrm * 320 *===========================================================================*/ 321 static void cause_sigalrm(tp) 322 minix_timer_t *tp; 323 { 324 int proc_nr_n; 325 register struct mproc *rmp; 326 327 /* get process from timer */ 328 if(pm_isokendpt(tmr_arg(tp)->ta_int, &proc_nr_n) != OK) { 329 printf("PM: ignoring timer for invalid endpoint %d\n", 330 tmr_arg(tp)->ta_int); 331 return; 332 } 333 334 rmp = &mproc[proc_nr_n]; 335 336 if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) return; 337 if ((rmp->mp_flags & ALARM_ON) == 0) return; 338 339 /* If an interval is set, set a new timer; otherwise clear the ALARM_ON flag. 340 * The set_alarm call will be calling set_timer from within this callback 341 * from the expire_timers function. This is safe, but we must not use the 342 * "tp" structure below this point anymore. 343 */ 344 if (rmp->mp_interval[ITIMER_REAL] > 0) 345 set_alarm(rmp, rmp->mp_interval[ITIMER_REAL]); 346 else rmp->mp_flags &= ~ALARM_ON; 347 348 check_sig(rmp->mp_pid, SIGALRM, FALSE /* ksig */); 349 } 350