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 *===========================================================================*/ 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 *===========================================================================*/ 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 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 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 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 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 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 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 *===========================================================================*/ 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 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