Fork me on GitHub

root/kernel/timer.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. count_active_procs
  2. calc_load
  3. get_free_callout
  4. put_free_callout
  5. do_del_callout
  6. add_callout
  7. del_callout
  8. irq_timer
  9. tv2ticks
  10. ticks2tv
  11. setitimer
  12. mktime
  13. irq_timer_bh
  14. do_callouts_bh
  15. get_system_time
  16. set_system_time
  17. timer_init

   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 }

/* [previous][next][first][last][top][bottom][index][help] */