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

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