Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/kernel/timer.c 3 * 4 * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/asm.h> 9 #include <fiwix/kernel.h> 10 #include <fiwix/segments.h> 11 #include <fiwix/cmos.h> 12 #include <fiwix/pit.h> 13 #include <fiwix/timer.h> 14 #include <fiwix/time.h> 15 #include <fiwix/pic.h> 16 #include <fiwix/sched.h> 17 #include <fiwix/pic.h> 18 #include <fiwix/cmos.h> 19 #include <fiwix/signal.h> 20 #include <fiwix/process.h> 21 #include <fiwix/sleep.h> 22 #include <fiwix/errno.h> 23 #include <fiwix/stdio.h> 24 #include <fiwix/string.h> 25 26 /* 27 * timer.c implements a callout table using a singly linked list. 28 * 29 * head 30 * +---------+ ----------+ ... ----------+ 31 * |data|next| |data|next| ... |data|next| 32 * | | --> | | --> ... | | / | 33 * +---------+ ----------+ ... ----------+ 34 * (callout) (callout) (callout) 35 */ 36 37 struct callout callout_pool[NR_CALLOUTS]; 38 struct callout *callout_pool_head; 39 struct callout *callout_head; 40 41 static char month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 42 unsigned int avenrun[3] = { 0, 0, 0 }; 43 44 static struct bh timer_bh = { 0, &irq_timer_bh, NULL }; 45 static struct bh callouts_bh = { 0, &do_callouts_bh, NULL }; 46 static struct interrupt irq_config_timer = { 0, "timer", &irq_timer, NULL }; 47 48 static unsigned int count_active_procs(void) 49 { 50 int counter; 51 struct proc *p; 52 53 counter = 0; 54 FOR_EACH_PROCESS_RUNNING(p) { 55 counter += FIXED_1; 56 p = p->next_run; 57 } 58 return counter; 59 } 60 61 static void calc_load(void) 62 { 63 unsigned int active_procs; 64 static int count = LOAD_FREQ; 65 66 if(count-- > 0) { 67 return; 68 } 69 70 count = LOAD_FREQ; 71 active_procs = count_active_procs(); 72 CALC_LOAD(avenrun[0], EXP_1, active_procs); 73 CALC_LOAD(avenrun[1], EXP_5, active_procs); 74 CALC_LOAD(avenrun[2], EXP_15, active_procs); 75 } 76 77 static struct callout *get_free_callout(void) 78 { 79 struct callout *new; 80 81 new = NULL; 82 if(callout_pool_head) { 83 new = callout_pool_head; 84 callout_pool_head = callout_pool_head->next; 85 new->next = NULL; 86 } 87 return new; 88 } 89 90 static void put_free_callout(struct callout *old) 91 { 92 old->next = callout_pool_head; 93 callout_pool_head = old; 94 } 95 96 static void do_del_callout(struct callout *c) 97 { 98 struct callout **tmp; 99 100 if(callout_head) { 101 tmp = &callout_head; 102 while(*tmp) { 103 if((*tmp) == c) { 104 if((*tmp)->next != NULL) { 105 *tmp = (*tmp)->next; 106 (*tmp)->expires += c->expires; 107 } else { 108 *tmp = NULL; 109 } 110 put_free_callout(c); 111 break; 112 } 113 tmp = &(*tmp)->next; 114 } 115 } 116 } 117 118 void add_callout(struct callout_req *creq, unsigned int ticks) 119 { 120 unsigned long int flags; 121 struct callout *c, **tmp; 122 123 del_callout(creq); 124 SAVE_FLAGS(flags); CLI(); 125 126 if(!(c = get_free_callout())) { 127 printk("WARNING: %s(): no more callout slots!\n", __FUNCTION__); 128 RESTORE_FLAGS(flags); 129 return; 130 } 131 132 /* setup the new callout */ 133 memset_b(c, NULL, sizeof(struct callout)); 134 c->expires = ticks; 135 c->fn = creq->fn; 136 c->arg = creq->arg; 137 138 if(!callout_head) { 139 callout_head = c; 140 } else { 141 tmp = &callout_head; 142 while(*tmp) { 143 if((*tmp)->expires > c->expires) { 144 (*tmp)->expires -= c->expires; 145 c->next = *tmp; 146 break; 147 } 148 c->expires -= (*tmp)->expires; 149 tmp = &(*tmp)->next; 150 } 151 *tmp = c; 152 } 153 RESTORE_FLAGS(flags); 154 } 155 156 void del_callout(struct callout_req *creq) 157 { 158 unsigned long int flags; 159 struct callout *c; 160 161 SAVE_FLAGS(flags); CLI(); 162 c = callout_head; 163 while(c) { 164 if(c->fn == creq->fn && c->arg == creq->arg) { 165 do_del_callout(c); 166 break; 167 } 168 c = c->next; 169 } 170 RESTORE_FLAGS(flags); 171 } 172 173 void irq_timer(int num, struct sigcontext *sc) 174 { 175 if((++kstat.ticks % HZ) == 0) { 176 CURRENT_TIME++; 177 kstat.uptime++; 178 } 179 180 timer_bh.flags |= BH_ACTIVE; 181 182 if(sc->cs == KERNEL_CS) { 183 current->usage.ru_stime.tv_usec += TICK; 184 if(current->usage.ru_stime.tv_usec >= 1000000) { 185 current->usage.ru_stime.tv_sec++; 186 current->usage.ru_stime.tv_usec -= 1000000; 187 } 188 if(current->pid != IDLE) { 189 kstat.cpu_system++; 190 } 191 } else { 192 current->usage.ru_utime.tv_usec += TICK; 193 if(current->usage.ru_utime.tv_usec >= 1000000) { 194 current->usage.ru_utime.tv_sec++; 195 current->usage.ru_utime.tv_usec -= 1000000; 196 } 197 if(current->pid != IDLE) { 198 kstat.cpu_user++; 199 } 200 if(current->it_virt_value > 0) { 201 current->it_virt_value--; 202 if(!current->it_virt_value) { 203 current->it_virt_value = current->it_virt_interval; 204 send_sig(current, SIGVTALRM); 205 } 206 } 207 } 208 } 209 210 unsigned long int tv2ticks(const struct timeval *tv) 211 { 212 return((tv->tv_sec * HZ) + tv->tv_usec * HZ / 1000000); 213 } 214 215 void ticks2tv(long int ticks, struct timeval *tv) 216 { 217 tv->tv_sec = ticks / HZ; 218 tv->tv_usec = (ticks % HZ) * 1000000 / HZ; 219 } 220 221 int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) 222 { 223 switch(which) { 224 case ITIMER_REAL: 225 if((unsigned int)old_value) { 226 ticks2tv(current->it_real_interval, &old_value->it_interval); 227 ticks2tv(current->it_real_value, &old_value->it_value); 228 } 229 current->it_real_interval = tv2ticks(&new_value->it_interval); 230 current->it_real_value = tv2ticks(&new_value->it_value); 231 break; 232 case ITIMER_VIRTUAL: 233 if((unsigned int)old_value) { 234 ticks2tv(current->it_virt_interval, &old_value->it_interval); 235 ticks2tv(current->it_virt_value, &old_value->it_value); 236 } 237 current->it_virt_interval = tv2ticks(&new_value->it_interval); 238 current->it_virt_value = tv2ticks(&new_value->it_value); 239 break; 240 case ITIMER_PROF: 241 if((unsigned int)old_value) { 242 ticks2tv(current->it_prof_interval, &old_value->it_interval); 243 ticks2tv(current->it_prof_value, &old_value->it_value); 244 } 245 current->it_prof_interval = tv2ticks(&new_value->it_interval); 246 current->it_prof_value = tv2ticks(&new_value->it_value); 247 break; 248 default: 249 return -EINVAL; 250 } 251 252 return 0; 253 } 254 255 unsigned long int mktime(struct mt *mt) 256 { 257 int n, total_days; 258 unsigned long int seconds; 259 260 total_days = 0; 261 262 for(n = UNIX_EPOCH; n < mt->mt_year; n++) { 263 total_days += DAYS_PER_YEAR(n); 264 } 265 for(n = 0; n < (mt->mt_month - 1); n++) { 266 total_days += month[n]; 267 if(n == 1) { 268 total_days += LEAP_YEAR(mt->mt_year) ? 1 : 0; 269 } 270 } 271 272 total_days += (mt->mt_day - 1); 273 seconds = total_days * SECS_PER_DAY; 274 seconds += mt->mt_hour * SECS_PER_HOUR; 275 seconds += mt->mt_min * SECS_PER_MIN; 276 seconds += mt->mt_sec; 277 return seconds; 278 } 279 280 void irq_timer_bh(void) 281 { 282 struct proc *p; 283 284 if(current->usage.ru_utime.tv_sec + current->usage.ru_stime.tv_sec > current->rlim[RLIMIT_CPU].rlim_cur) { 285 send_sig(current, SIGXCPU); 286 } 287 288 if(current->it_prof_value > 0) { 289 current->it_prof_value--; 290 if(!current->it_prof_value) { 291 current->it_prof_value = current->it_prof_interval; 292 send_sig(current, SIGPROF); 293 } 294 } 295 296 calc_load(); 297 FOR_EACH_PROCESS(p) { 298 if(p->timeout > 0 && p->timeout < INFINITE_WAIT) { 299 p->timeout--; 300 if(!p->timeout) { 301 wakeup_proc(p); 302 } 303 } 304 if(p->it_real_value > 0) { 305 p->it_real_value--; 306 if(!p->it_real_value) { 307 p->it_real_value = p->it_real_interval; 308 send_sig(p, SIGALRM); 309 } 310 } 311 p = p->next; 312 } 313 314 /* callouts */ 315 if(callout_head) { 316 if(callout_head->expires > 0) { 317 callout_head->expires--; 318 if(!callout_head->expires) { 319 callouts_bh.flags |= BH_ACTIVE; 320 } 321 } else { 322 printk("%s(): callout losing ticks.\n", __FUNCTION__); 323 callouts_bh.flags |= BH_ACTIVE; 324 } 325 } 326 327 if(current->pid > IDLE && --current->cpu_count <= 0) { 328 current->cpu_count = 0; 329 need_resched = 1; 330 } 331 } 332 333 void do_callouts_bh(void) 334 { 335 struct callout *c; 336 void (*fn)(unsigned int); 337 unsigned int arg; 338 339 while(callout_head) { 340 if(callout_head->expires) { 341 break; 342 } 343 if(lock_area(AREA_CALLOUT)) { 344 continue; 345 } 346 fn = callout_head->fn; 347 arg = callout_head->arg; 348 c = callout_head; 349 callout_head = callout_head->next; 350 put_free_callout(c); 351 unlock_area(AREA_CALLOUT); 352 fn(arg); 353 } 354 } 355 356 void get_system_time(void) 357 { 358 short int cmos_century; 359 struct mt mt; 360 361 /* read date and time from CMOS */ 362 mt.mt_sec = cmos_read_date(CMOS_SEC); 363 mt.mt_min = cmos_read_date(CMOS_MIN); 364 mt.mt_hour = cmos_read_date(CMOS_HOUR); 365 mt.mt_day = cmos_read_date(CMOS_DAY); 366 mt.mt_month = cmos_read_date(CMOS_MONTH); 367 mt.mt_year = cmos_read_date(CMOS_YEAR); 368 cmos_century = cmos_read_date(CMOS_CENTURY); 369 mt.mt_year += cmos_century * 100; 370 371 kstat.boot_time = CURRENT_TIME = mktime(&mt); 372 } 373 374 void set_system_time(__time_t t) 375 { 376 int sec, spm, min, hour, d, m, y; 377 378 sec = t; 379 y = 1970; 380 while(sec >= (DAYS_PER_YEAR(y) * SECS_PER_DAY)) { 381 sec -= (DAYS_PER_YEAR(y) * SECS_PER_DAY); 382 y++; 383 } 384 385 m = 0; 386 while(sec > month[m] * SECS_PER_DAY) { 387 spm = month[m] * SECS_PER_DAY; 388 if(m == 1) { 389 spm = LEAP_YEAR(y) ? spm + SECS_PER_DAY : spm; 390 } 391 sec -= spm; 392 m++; 393 } 394 m++; 395 396 d = 1; 397 while(sec >= SECS_PER_DAY) { 398 sec -= SECS_PER_DAY; 399 d++; 400 } 401 402 hour = 0; 403 while(sec >= SECS_PER_HOUR) { 404 sec -= SECS_PER_HOUR; 405 hour++; 406 } 407 408 min = 0; 409 while(sec >= SECS_PER_MIN) { 410 sec -= SECS_PER_MIN; 411 min++; 412 } 413 414 /* write date and time to CMOS */ 415 cmos_write_date(CMOS_SEC, sec); 416 cmos_write_date(CMOS_MIN, min); 417 cmos_write_date(CMOS_HOUR, hour); 418 cmos_write_date(CMOS_DAY, d); 419 cmos_write_date(CMOS_MONTH, m); 420 cmos_write_date(CMOS_YEAR, y % 100); 421 cmos_write_date(CMOS_CENTURY, (y - (y % 100)) / 100); 422 423 CURRENT_TIME = t; 424 } 425 426 void timer_init(void) 427 { 428 int n; 429 struct callout *c; 430 431 add_bh(&timer_bh); 432 add_bh(&callouts_bh); 433 434 pit_init(HZ); 435 436 memset_b(callout_pool, NULL, sizeof(callout_pool)); 437 438 /* callout free list initialization */ 439 callout_pool_head = NULL; 440 n = NR_CALLOUTS; 441 while(n--) { 442 c = &callout_pool[n]; 443 put_free_callout(c); 444 } 445 callout_head = NULL; 446 447 printk("clock - %d type=PIT Hz=%d\n", TIMER_IRQ, HZ); 448 if(!register_irq(TIMER_IRQ, &irq_config_timer)) { 449 enable_irq(TIMER_IRQ); 450 } 451 }