root/kernel/sched.c

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

DEFINITIONS

This source file includes following definitions.
  1. math_state_restore
  2. schedule
  3. sys_pause
  4. wake_up
  5. wake_up_interruptible
  6. __sleep_on
  7. interruptible_sleep_on
  8. sleep_on
  9. ticks_to_floppy_on
  10. floppy_off
  11. do_floppy_timer
  12. add_timer
  13. count_active_tasks
  14. calc_load
  15. do_timer
  16. sys_alarm
  17. sys_getpid
  18. sys_getppid
  19. sys_getuid
  20. sys_geteuid
  21. sys_getgid
  22. sys_getegid
  23. sys_nice
  24. show_task
  25. show_state
  26. sched_init

   1 /*
   2  *  linux/kernel/sched.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 /*
   8  * 'sched.c' is the main kernel file. It contains scheduling primitives
   9  * (sleep_on, wakeup, schedule etc) as well as a number of simple system
  10  * call functions (type getpid(), which just extracts a field from
  11  * current-task
  12  */
  13 
  14 #define TIMER_IRQ 0
  15 
  16 #include <linux/config.h>
  17 #include <linux/signal.h>
  18 #include <linux/sched.h>
  19 #include <linux/timer.h>
  20 #include <linux/kernel.h>
  21 #include <linux/sys.h>
  22 #include <linux/fdreg.h>
  23 #include <linux/errno.h>
  24 #include <linux/time.h>
  25 #include <linux/ptrace.h>
  26 
  27 #include <asm/system.h>
  28 #include <asm/io.h>
  29 #include <asm/segment.h>
  30 
  31 int need_resched = 0;
  32 int hard_math = 0;              /* set by boot/head.S */
  33 int ignore_irq13 = 0;           /* set if exception 16 works */
  34 
  35 unsigned long * prof_buffer = NULL;
  36 unsigned long prof_len = 0;
  37 
  38 #define _S(nr) (1<<((nr)-1))
  39 
  40 #define LATCH (1193180/HZ)
  41 
  42 extern void mem_use(void);
  43 
  44 extern int timer_interrupt(void);
  45 extern int system_call(void);
  46 
  47 static unsigned long init_kernel_stack[1024];
  48 static struct task_struct init_task = INIT_TASK;
  49 
  50 unsigned long volatile jiffies=0;
  51 unsigned long startup_time=0;
  52 int jiffies_offset = 0;         /* # clock ticks to add to get "true
  53                                    time".  Should always be less than
  54                                    1 second's worth.  For time fanatics
  55                                    who like to syncronize their machines
  56                                    to WWV :-) */
  57 
  58 struct task_struct *current = &init_task;
  59 struct task_struct *last_task_used_math = NULL;
  60 
  61 struct task_struct * task[NR_TASKS] = {&init_task, };
  62 
  63 long user_stack [ PAGE_SIZE>>2 ] ;
  64 
  65 struct {
  66         long * a;
  67         short b;
  68         } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
  69 /*
  70  *  'math_state_restore()' saves the current math information in the
  71  * old math state array, and gets the new ones from the current task
  72  */
  73 void math_state_restore(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  74 {
  75         if (last_task_used_math == current)
  76                 return;
  77         if (last_task_used_math) {
  78                 __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
  79         }
  80         __asm__("fwait");
  81         last_task_used_math = current;
  82         if (current->used_math) {
  83                 __asm__("frstor %0"::"m" (current->tss.i387));
  84         } else {
  85                 __asm__("fninit"::);
  86                 current->used_math=1;
  87         }
  88 }
  89 
  90 /*
  91  *  'schedule()' is the scheduler function. It's a very simple and nice
  92  * scheduler: it's not perfect, but certainly works for most things.
  93  * The one thing you might take a look at is the signal-handler code here.
  94  *
  95  *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other
  96  * tasks can run. It can not be killed, and it cannot sleep. The 'state'
  97  * information in task[0] is never used.
  98  */
  99 void schedule(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 100 {
 101         int i,next,c;
 102         struct task_struct ** p;
 103 
 104 /* check alarm, wake up any interruptible tasks that have got a signal */
 105 
 106         need_resched = 0;
 107         for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
 108                 if (!*p || ((*p)->state != TASK_INTERRUPTIBLE))
 109                         continue;
 110                 if ((*p)->timeout && (*p)->timeout < jiffies) {
 111                         (*p)->timeout = 0;
 112                         (*p)->state = TASK_RUNNING;
 113                 } else if ((*p)->signal & ~(*p)->blocked)
 114                         (*p)->state = TASK_RUNNING;
 115         }
 116 
 117 /* this is the scheduler proper: */
 118 
 119         while (1) {
 120                 c = -1;
 121                 next = 0;
 122                 i = NR_TASKS;
 123                 p = &task[NR_TASKS];
 124                 while (--i) {
 125                         if (!*--p)
 126                                 continue;
 127                         if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
 128                                 c = (*p)->counter, next = i;
 129                 }
 130                 if (c)
 131                         break;
 132                 for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
 133                         if (*p)
 134                                 (*p)->counter = ((*p)->counter >> 1) +
 135                                                 (*p)->priority;
 136         }
 137         sti();
 138         switch_to(next);
 139 }
 140 
 141 /*
 142  * This is a little tricky because POSIX pause (3.4.2.1) should only
 143  * return for a caught signal or a signal that terminates the process.
 144  * We just block ignored signals or "harmless" default signals.
 145  * For suspending signals, we must return so that they are handled
 146  * and then pause again.  -- jrs
 147  */
 148 
 149 int sys_pause(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 150 {
 151         unsigned long old_blocked;
 152         unsigned long mask;
 153         int sig;
 154         struct sigaction * sa = current->sigaction;
 155 
 156         old_blocked = current->blocked;
 157         /* block everything we can that shouldn't interrupt pause */
 158         for (mask = 1, sig = 1; mask; sa++, mask += mask, sig++)
 159                 if (sa->sa_handler == SIG_IGN || (sa->sa_handler == SIG_DFL
 160                     && (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH)))
 161                         current->blocked |= mask;
 162         do {
 163                 current->state = TASK_INTERRUPTIBLE;
 164                 schedule();
 165         } while (!(current->signal & ~current->blocked));
 166         /* if a suspending signal interrupted us we must restart */
 167         if (!(current->signal & ~current->blocked &
 168                 ~(_S(SIGSTOP) | _S(SIGTSTP) | _S(SIGTTIN) | _S(SIGTTOU)))) {
 169                 current->blocked = old_blocked;
 170                 return -ERESTARTSYS;
 171         }
 172         current->blocked = old_blocked;
 173         return -EINTR;
 174 }
 175 
 176 /*
 177  * wake_up doesn't wake up stopped processes - they have to be awakened
 178  * with signals or similar.
 179  *
 180  * Note that this doesn't need cli-sti pairs: interrupts may not change
 181  * the wait-queue structures directly, but only call wake_up() to wake
 182  * a process. The process itself must remove the queue once it has woken.
 183  */
 184 void wake_up(struct wait_queue **q)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186         struct wait_queue *tmp;
 187         struct task_struct * p;
 188 
 189         if (!q || !(tmp = *q))
 190                 return;
 191         do {
 192                 if ((p = tmp->task) != NULL) {
 193                         if ((p->state == TASK_UNINTERRUPTIBLE) ||
 194                             (p->state == TASK_INTERRUPTIBLE)) {
 195                                 p->state = TASK_RUNNING;
 196                                 if (p->counter > current->counter)
 197                                         need_resched = 1;
 198                         }
 199                 }
 200                 if (!tmp->next) {
 201                         printk("wait_queue is bad (eip = %08x)\n",((unsigned long *) q)[-1]);
 202                         printk("        q = %08x\n",q);
 203                         printk("       *q = %08x\n",*q);
 204                         printk("      tmp = %08x\n",tmp);
 205                         break;
 206                 }
 207                 tmp = tmp->next;
 208         } while (tmp != *q);
 209 }
 210 
 211 void wake_up_interruptible(struct wait_queue **q)
     /* [previous][next][first][last][top][bottom][index][help] */
 212 {
 213         struct wait_queue *tmp;
 214         struct task_struct * p;
 215 
 216         if (!q || !(tmp = *q))
 217                 return;
 218         do {
 219                 if ((p = tmp->task) != NULL) {
 220                         if (p->state == TASK_INTERRUPTIBLE) {
 221                                 p->state = TASK_RUNNING;
 222                                 if (p->counter > current->counter)
 223                                         need_resched = 1;
 224                         }
 225                 }
 226                 if (!tmp->next) {
 227                         printk("wait_queue is bad (eip = %08x)\n",((unsigned long *) q)[-1]);
 228                         printk("        q = %08x\n",q);
 229                         printk("       *q = %08x\n",*q);
 230                         printk("      tmp = %08x\n",tmp);
 231                         break;
 232                 }
 233                 tmp = tmp->next;
 234         } while (tmp != *q);
 235 }
 236 
 237 static inline void __sleep_on(struct wait_queue **p, int state)
     /* [previous][next][first][last][top][bottom][index][help] */
 238 {
 239         unsigned long flags;
 240         struct wait_queue wait = { current, NULL };
 241 
 242         if (!p)
 243                 return;
 244         if (current == task[0])
 245                 panic("task[0] trying to sleep");
 246         current->state = state;
 247         add_wait_queue(p, &wait);
 248         save_flags(flags);
 249         sti();
 250         schedule();
 251         remove_wait_queue(p, &wait);
 252         restore_flags(flags);
 253 }
 254 
 255 void interruptible_sleep_on(struct wait_queue **p)
     /* [previous][next][first][last][top][bottom][index][help] */
 256 {
 257         __sleep_on(p,TASK_INTERRUPTIBLE);
 258 }
 259 
 260 void sleep_on(struct wait_queue **p)
     /* [previous][next][first][last][top][bottom][index][help] */
 261 {
 262         __sleep_on(p,TASK_UNINTERRUPTIBLE);
 263 }
 264 
 265 /*
 266  * OK, here are some floppy things that shouldn't be in the kernel
 267  * proper. They are here because the floppy needs a timer, and this
 268  * was the easiest way of doing it.
 269  */
 270 static struct wait_queue * wait_motor[4] = {NULL,NULL,NULL,NULL};
 271 static int  mon_timer[4]={0,0,0,0};
 272 static int moff_timer[4]={0,0,0,0};
 273 unsigned char current_DOR = 0x0C;
 274 
 275 int ticks_to_floppy_on(unsigned int nr)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277         extern unsigned char selected;
 278         unsigned char mask = 0x10 << nr;
 279 
 280         if (nr>3)
 281                 panic("floppy_on: nr>3");
 282         moff_timer[nr]=10000;           /* 100 s = very big :-) */
 283         cli();                          /* use floppy_off to turn it off */
 284         mask |= current_DOR;
 285         if (!selected) {
 286                 mask &= 0xFC;
 287                 mask |= nr;
 288         }
 289         if (mask != current_DOR) {
 290                 outb(mask,FD_DOR);
 291                 if ((mask ^ current_DOR) & 0xf0)
 292                         mon_timer[nr] = HZ/2;
 293                 else if (mon_timer[nr] < 2)
 294                         mon_timer[nr] = 2;
 295                 current_DOR = mask;
 296         }
 297         sti();
 298         return mon_timer[nr];
 299 }
 300 
 301 void floppy_off(unsigned int nr)
     /* [previous][next][first][last][top][bottom][index][help] */
 302 {
 303         moff_timer[nr]=3*HZ;
 304 }
 305 
 306 void do_floppy_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 307 {
 308         int i;
 309         unsigned char mask = 0x10;
 310 
 311         for (i=0 ; i<4 ; i++,mask <<= 1) {
 312                 if (!(mask & current_DOR))
 313                         continue;
 314                 if (mon_timer[i]) {
 315                         if (!--mon_timer[i])
 316                                 wake_up(i+wait_motor);
 317                 } else if (!moff_timer[i]) {
 318                         current_DOR &= ~mask;
 319                         outb(current_DOR,FD_DOR);
 320                 } else
 321                         moff_timer[i]--;
 322         }
 323 }
 324 
 325 #define TIME_REQUESTS 64
 326 
 327 static struct timer_list {
 328         long jiffies;
 329         void (*fn)(void);
 330         struct timer_list * next;
 331 } timer_list[TIME_REQUESTS] = { { 0, NULL, NULL }, };
 332 
 333 static struct timer_list * next_timer = NULL;
 334 
 335 void add_timer(long jiffies, void (*fn)(void))
     /* [previous][next][first][last][top][bottom][index][help] */
 336 {
 337         struct timer_list * p;
 338         unsigned long flags;
 339 
 340         if (!fn)
 341                 return;
 342         save_flags(flags);
 343         cli();
 344         if (jiffies <= 0)
 345                 (fn)();
 346         else {
 347                 for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
 348                         if (!p->fn)
 349                                 break;
 350                 if (p >= timer_list + TIME_REQUESTS)
 351                         panic("No more time requests free");
 352                 p->fn = fn;
 353                 p->jiffies = jiffies;
 354                 p->next = next_timer;
 355                 next_timer = p;
 356                 while (p->next && p->next->jiffies < p->jiffies) {
 357                         p->jiffies -= p->next->jiffies;
 358                         fn = p->fn;
 359                         p->fn = p->next->fn;
 360                         p->next->fn = fn;
 361                         jiffies = p->jiffies;
 362                         p->jiffies = p->next->jiffies;
 363                         p->next->jiffies = jiffies;
 364                         p = p->next;
 365                 }
 366                 if (p->next)
 367                         p->next->jiffies -= p->jiffies;
 368         }
 369         restore_flags(flags);
 370 }
 371 
 372 unsigned long timer_active = 0;
 373 struct timer_struct timer_table[32];
 374 
 375 /*
 376  * Hmm.. Changed this, as the GNU make sources (load.c) seems to
 377  * imply that avenrun[] is the standard name for this kind of thing.
 378  * Nothing else seems to be standardized: the fractional size etc
 379  * all seem to differ on different machines.
 380  */
 381 unsigned long avenrun[3] = { 0,0,0 };
 382 
 383 /*
 384  * Nr of active tasks - counted in fixed-point numbers
 385  */
 386 static unsigned long count_active_tasks(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 387 {
 388         struct task_struct **p;
 389         unsigned long nr = 0;
 390 
 391         for(p = &LAST_TASK; p > &FIRST_TASK; --p)
 392                 if (*p && (*p)->state == TASK_RUNNING)
 393                         nr += FIXED_1;
 394         return nr;
 395 }
 396 
 397 static inline void calc_load(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 398 {
 399         unsigned long active_tasks; /* fixed-point */
 400         static int count = LOAD_FREQ;
 401 
 402         if (count-- > 0)
 403                 return;
 404         count = LOAD_FREQ;
 405         active_tasks = count_active_tasks();
 406         CALC_LOAD(avenrun[0], EXP_1, active_tasks);
 407         CALC_LOAD(avenrun[1], EXP_5, active_tasks);
 408         CALC_LOAD(avenrun[2], EXP_15, active_tasks);
 409 }
 410 
 411 /*
 412  * The int argument is really a (struct pt_regs *), in case the
 413  * interrupt wants to know from where it was called. The timer
 414  * irq uses this to decide if it should update the user or system
 415  * times.
 416  */
 417 static void do_timer(struct pt_regs * regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 418 {
 419         unsigned long mask;
 420         struct timer_struct *tp = timer_table+0;
 421         struct task_struct ** task_p;
 422 
 423         jiffies++;
 424         calc_load();
 425         if ((VM_MASK & regs->eflags) || (3 & regs->cs)) {
 426                 current->utime++;
 427                 /* Update ITIMER_VIRT for current task if not in a system call */
 428                 if (current->it_virt_value && !(--current->it_virt_value)) {
 429                         current->it_virt_value = current->it_virt_incr;
 430                         send_sig(SIGVTALRM,current,1);
 431                 }
 432         } else {
 433                 current->stime++;
 434 #ifdef CONFIG_PROFILE
 435                 if (prof_buffer && current != task[0]) {
 436                         unsigned long eip = regs->eip;
 437                         eip >>= 2;
 438                         if (eip < prof_len)
 439                                 prof_buffer[eip]++;
 440                 }
 441 #endif
 442         }
 443         if (current == task[0] || (--current->counter)<=0) {
 444                 current->counter=0;
 445                 need_resched = 1;
 446         }
 447         /* Update ITIMER_REAL for every task */
 448         for (task_p = &LAST_TASK; task_p >= &FIRST_TASK; task_p--)
 449                 if (*task_p && (*task_p)->it_real_value
 450                         && !(--(*task_p)->it_real_value)) {
 451                         send_sig(SIGALRM,*task_p,1);
 452                         (*task_p)->it_real_value = (*task_p)->it_real_incr;
 453                         need_resched = 1;
 454                 }
 455         /* Update ITIMER_PROF for the current task */
 456         if (current->it_prof_value && !(--current->it_prof_value)) {
 457                 current->it_prof_value = current->it_prof_incr;
 458                 send_sig(SIGPROF,current,1);
 459         }
 460         for (mask = 1 ; mask ; tp++,mask += mask) {
 461                 if (mask > timer_active)
 462                         break;
 463                 if (!(mask & timer_active))
 464                         continue;
 465                 if (tp->expires > jiffies)
 466                         continue;
 467                 timer_active &= ~mask;
 468                 tp->fn();
 469                 sti();
 470         }
 471         if (next_timer) {
 472                 next_timer->jiffies--;
 473                 while (next_timer && next_timer->jiffies <= 0) {
 474                         void (*fn)(void);
 475                         
 476                         fn = next_timer->fn;
 477                         next_timer->fn = NULL;
 478                         next_timer = next_timer->next;
 479                         (fn)();
 480                 }
 481         }
 482         if (current_DOR & 0xf0)
 483                 do_floppy_timer();
 484 }
 485 
 486 int sys_alarm(long seconds)
     /* [previous][next][first][last][top][bottom][index][help] */
 487 {
 488         extern int _setitimer(int, struct itimerval *, struct itimerval *);
 489         struct itimerval new, old;
 490 
 491         new.it_interval.tv_sec = new.it_interval.tv_usec = 0;
 492         new.it_value.tv_sec = seconds;
 493         new.it_value.tv_usec = 0;
 494         _setitimer(ITIMER_REAL, &new, &old);
 495         return(old.it_value.tv_sec + (old.it_value.tv_usec / 1000000));
 496 }
 497 
 498 int sys_getpid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 499 {
 500         return current->pid;
 501 }
 502 
 503 int sys_getppid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 504 {
 505         return current->p_pptr->pid;
 506 }
 507 
 508 int sys_getuid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 509 {
 510         return current->uid;
 511 }
 512 
 513 int sys_geteuid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 514 {
 515         return current->euid;
 516 }
 517 
 518 int sys_getgid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 519 {
 520         return current->gid;
 521 }
 522 
 523 int sys_getegid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 524 {
 525         return current->egid;
 526 }
 527 
 528 int sys_nice(long increment)
     /* [previous][next][first][last][top][bottom][index][help] */
 529 {
 530         if (increment < 0 && !suser())
 531                 return -EPERM;
 532         if (increment >= current->priority)
 533                 increment = current->priority-1;
 534         current->priority -= increment;
 535         return 0;
 536 }
 537 
 538 static void show_task(int nr,struct task_struct * p)
     /* [previous][next][first][last][top][bottom][index][help] */
 539 {
 540         int i, j;
 541         unsigned char * stack;
 542 
 543         printk("%d: pid=%d, state=%d, father=%d, child=%d, ",(p == current)?-nr:nr,p->pid,
 544                 p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
 545         i = 0;
 546         j = 4096;
 547         if (!(stack = (char *) p->kernel_stack_page)) {
 548                 stack = (char *) init_kernel_stack;
 549                 j = sizeof(init_kernel_stack);
 550         }
 551         while (i<j && !*(stack++))
 552                 i++;
 553         printk("%d/%d chars free in kstack\n",i,j);
 554         printk("   PC=%08X.", *(1019 + (unsigned long *) p));
 555         if (p->p_ysptr || p->p_osptr) 
 556                 printk("   Younger sib=%d, older sib=%d\n", 
 557                         p->p_ysptr ? p->p_ysptr->pid : -1,
 558                         p->p_osptr ? p->p_osptr->pid : -1);
 559         else
 560                 printk("\n");
 561 }
 562 
 563 void show_state(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 564 {
 565         int i;
 566 
 567         printk("Task-info:\n");
 568         for (i=0 ; i<NR_TASKS ; i++)
 569                 if (task[i])
 570                         show_task(i,task[i]);
 571 }
 572 
 573 void sched_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 574 {
 575         int i;
 576         struct desc_struct * p;
 577 
 578         if (sizeof(struct sigaction) != 16)
 579                 panic("Struct sigaction MUST be 16 bytes");
 580         set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss);
 581         set_ldt_desc(gdt+FIRST_LDT_ENTRY,&init_task.ldt);
 582         set_system_gate(0x80,&system_call);
 583         p = gdt+2+FIRST_TSS_ENTRY;
 584         for(i=1 ; i<NR_TASKS ; i++) {
 585                 task[i] = NULL;
 586                 p->a=p->b=0;
 587                 p++;
 588                 p->a=p->b=0;
 589                 p++;
 590         }
 591 /* Clear NT, so that we won't have troubles with that later on */
 592         __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
 593         load_TR(0);
 594         load_ldt(0);
 595         outb_p(0x36,0x43);              /* binary, mode 3, LSB/MSB, ch 0 */
 596         outb_p(LATCH & 0xff , 0x40);    /* LSB */
 597         outb(LATCH >> 8 , 0x40);        /* MSB */
 598         request_irq(TIMER_IRQ,(void (*)(int)) do_timer);
 599 }

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