/* * linux/kernel/sched.c * * (C) 1991 Linus Torvalds */ /* * 'sched.c' is the main kernel file. It contains scheduling primitives * (sleep_on, wakeup, schedule etc) as well as a number of simple system * call functions (type getpid(), which just extracts a field from * current-task */ #include <linux/sched.h> #include <linux/timer.h> #include <linux/kernel.h> #include <linux/sys.h> #include <linux/fdreg.h> #include <asm/system.h> #include <asm/io.h> #include <asm/segment.h> #include <sys/time.h> #include <signal.h> #include <errno.h> int need_resched = 0; #define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) static void show_task(int nr,struct task_struct * p) { int i,j = 4096-sizeof(struct task_struct); printk("%d: pid=%d, state=%d, father=%d, child=%d, ",nr,p->pid, p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1); i=0; while (i<j && !((char *)(p+1))[i]) i++; printk("%d/%d chars free in kstack\n\r",i,j); printk(" PC=%08X.", *(1019 + (unsigned long *) p)); if (p->p_ysptr || p->p_osptr) printk(" Younger sib=%d, older sib=%d\n\r", p->p_ysptr ? p->p_ysptr->pid : -1, p->p_osptr ? p->p_osptr->pid : -1); else printk("\n\r"); } void show_state(void) { int i; printk("\rTask-info:\n\r"); for (i=0 ; i<NR_TASKS ; i++) if (task[i]) show_task(i,task[i]); } #define LATCH (1193180/HZ) extern void mem_use(void); extern int timer_interrupt(void); extern int system_call(void); union task_union { struct task_struct task; char stack[PAGE_SIZE]; }; static union task_union init_task = {INIT_TASK, }; unsigned long volatile jiffies=0; unsigned long startup_time=0; int jiffies_offset = 0; /* # clock ticks to add to get "true time". Should always be less than 1 second's worth. For time fanatics who like to syncronize their machines to WWV :-) */ struct task_struct *current = &(init_task.task); struct task_struct *last_task_used_math = NULL; struct task_struct * task[NR_TASKS] = {&(init_task.task), }; long user_stack [ PAGE_SIZE>>2 ] ; struct { long * a; short b; } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 }; /* * 'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task */ void math_state_restore() { if (last_task_used_math == current) return; __asm__("fwait"); if (last_task_used_math) { __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); } last_task_used_math=current; if (current->used_math) { __asm__("frstor %0"::"m" (current->tss.i387)); } else { __asm__("fninit"::); current->used_math=1; } } /* * 'schedule()' is the scheduler function. It's a very simple and nice * scheduler: it's not perfect, but certainly works for most things. * The one thing you might take a look at is the signal-handler code here. * * NOTE!! Task 0 is the 'idle' task, which gets called when no other * tasks can run. It can not be killed, and it cannot sleep. The 'state' * information in task[0] is never used. */ void schedule(void) { int i,next,c; struct task_struct ** p; /* check alarm, wake up any interruptible tasks that have got a signal */ need_resched = 0; for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) if (*p) { if ((*p)->timeout && (*p)->timeout < jiffies) if ((*p)->state == TASK_INTERRUPTIBLE) { (*p)->timeout = 0; (*p)->state = TASK_RUNNING; } if (((*p)->signal & ~(*p)->blocked) && (*p)->state==TASK_INTERRUPTIBLE) (*p)->state=TASK_RUNNING; } /* this is the scheduler proper: */ while (1) { c = -1; next = 0; i = NR_TASKS; p = &task[NR_TASKS]; while (--i) { if (!*--p) continue; if ((*p)->state == TASK_RUNNING && (*p)->counter > c) c = (*p)->counter, next = i; } if (c) break; for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) if (*p) (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; } switch_to(next); } int sys_pause(void) { unsigned long old_blocked; unsigned long mask; struct sigaction * sa = current->sigaction; old_blocked = current->blocked; for (mask=1 ; mask ; sa++,mask += mask) if (sa->sa_handler == SIG_IGN) current->blocked |= mask; current->state = TASK_INTERRUPTIBLE; schedule(); current->blocked = old_blocked; return -EINTR; } /* * wake_up doesn't wake up stopped processes - they have to be awakened * with signals or similar. */ void wake_up(struct task_struct **p) { struct task_struct * wakeup_ptr, * tmp; if (p && *p) { wakeup_ptr = *p; *p = NULL; while (wakeup_ptr && wakeup_ptr != task[0]) { if (wakeup_ptr->state == TASK_ZOMBIE) printk("wake_up: TASK_ZOMBIE\n"); else if (wakeup_ptr->state != TASK_STOPPED) { wakeup_ptr->state = TASK_RUNNING; if (wakeup_ptr->counter > current->counter) need_resched = 1; } tmp = wakeup_ptr->next_wait; wakeup_ptr->next_wait = task[0]; wakeup_ptr = tmp; } } } static inline void __sleep_on(struct task_struct **p, int state) { unsigned int flags; if (!p) return; if (current == task[0]) panic("task[0] trying to sleep"); __asm__("pushfl ; popl %0":"=r" (flags)); current->next_wait = *p; task[0]->next_wait = NULL; *p = current; current->state = state; sti(); schedule(); if (current->next_wait != task[0]) wake_up(p); current->next_wait = NULL; __asm__("pushl %0 ; popfl"::"r" (flags)); } void interruptible_sleep_on(struct task_struct **p) { __sleep_on(p,TASK_INTERRUPTIBLE); } void sleep_on(struct task_struct **p) { __sleep_on(p,TASK_UNINTERRUPTIBLE); } /* * OK, here are some floppy things that shouldn't be in the kernel * proper. They are here because the floppy needs a timer, and this * was the easiest way of doing it. */ static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL}; static int mon_timer[4]={0,0,0,0}; static int moff_timer[4]={0,0,0,0}; unsigned char current_DOR = 0x0C; int ticks_to_floppy_on(unsigned int nr) { extern unsigned char selected; unsigned char mask = 0x10 << nr; if (nr>3) panic("floppy_on: nr>3"); moff_timer[nr]=10000; /* 100 s = very big :-) */ cli(); /* use floppy_off to turn it off */ mask |= current_DOR; if (!selected) { mask &= 0xFC; mask |= nr; } if (mask != current_DOR) { outb(mask,FD_DOR); if ((mask ^ current_DOR) & 0xf0) mon_timer[nr] = HZ/2; else if (mon_timer[nr] < 2) mon_timer[nr] = 2; current_DOR = mask; } sti(); return mon_timer[nr]; } void floppy_off(unsigned int nr) { moff_timer[nr]=3*HZ; } void do_floppy_timer(void) { int i; unsigned char mask = 0x10; for (i=0 ; i<4 ; i++,mask <<= 1) { if (!(mask & current_DOR)) continue; if (mon_timer[i]) { if (!--mon_timer[i]) wake_up(i+wait_motor); } else if (!moff_timer[i]) { current_DOR &= ~mask; outb(current_DOR,FD_DOR); } else moff_timer[i]--; } } #define TIME_REQUESTS 64 static struct timer_list { long jiffies; void (*fn)(); struct timer_list * next; } timer_list[TIME_REQUESTS] = { { 0, NULL, NULL }, }; static struct timer_list * next_timer = NULL; void add_timer(long jiffies, void (*fn)(void)) { struct timer_list * p; if (!fn) return; cli(); if (jiffies <= 0) (fn)(); else { for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++) if (!p->fn) break; if (p >= timer_list + TIME_REQUESTS) panic("No more time requests free"); p->fn = fn; p->jiffies = jiffies; p->next = next_timer; next_timer = p; while (p->next && p->next->jiffies < p->jiffies) { p->jiffies -= p->next->jiffies; fn = p->fn; p->fn = p->next->fn; p->next->fn = fn; jiffies = p->jiffies; p->jiffies = p->next->jiffies; p->next->jiffies = jiffies; p = p->next; } } sti(); } #define FSHIFT 11 #define FSCALE (1<<FSHIFT) /* * Constants for averages over 1, 5, and 15 minutes * when sampling at 5 second intervals. */ static unsigned long cexp[3] = { 1884, /* 0.9200444146293232 * FSCALE, exp(-1/12) */ 2014, /* 0.9834714538216174 * FSCALE, exp(-1/60) */ 2037, /* 0.9944598480048967 * FSCALE, exp(-1/180) */ }; unsigned long averunnable[3] = { 0, }; /* fixed point numbers */ void update_avg(void) { int i, n=0; struct task_struct **p; for(p = &LAST_TASK; p > &FIRST_TASK; --p) if (*p && ((*p)->state == TASK_RUNNING || (*p)->state == TASK_UNINTERRUPTIBLE)) ++n; for (i = 0; i < 3; ++i) averunnable[i] = (cexp[i] * averunnable[i] + n * FSCALE * (FSCALE - cexp[i])) >> FSHIFT; } unsigned long timer_active = 0; struct timer_struct timer_table[32]; void do_timer(long cpl) { unsigned long mask; struct timer_struct *tp = timer_table+0; struct task_struct ** task_p; static int avg_cnt = 0; for (mask = 1 ; mask ; tp++,mask += mask) { if (mask > timer_active) break; if (!(mask & timer_active)) continue; if (tp->expires > jiffies) continue; timer_active &= ~mask; tp->fn(); sti(); } /* Update ITIMER_REAL for every task */ for (task_p = &LAST_TASK; task_p >= &FIRST_TASK; task_p--) if (*task_p && (*task_p)->it_real_value && !(--(*task_p)->it_real_value)) { send_sig(SIGALRM,*task_p,1); (*task_p)->it_real_value = (*task_p)->it_real_incr; need_resched = 1; } /* Update ITIMER_PROF for the current task */ if (current->it_prof_value && !(--current->it_prof_value)) { current->it_prof_value = current->it_prof_incr; send_sig(SIGPROF,current,1); } /* Update ITIMER_VIRT for current task if not in a system call */ if (cpl && current->it_virt_value && !(--current->it_virt_value)) { current->it_virt_value = current->it_virt_incr; send_sig(SIGVTALRM,current,1); } if (cpl) current->utime++; else current->stime++; if (next_timer) { next_timer->jiffies--; while (next_timer && next_timer->jiffies <= 0) { void (*fn)(void); fn = next_timer->fn; next_timer->fn = NULL; next_timer = next_timer->next; (fn)(); } } if (current_DOR & 0xf0) do_floppy_timer(); if (--avg_cnt < 0) { avg_cnt = 500; update_avg(); } if ((--current->counter)<=0) { current->counter=0; need_resched = 1; } } int sys_alarm(long seconds) { extern int _setitimer(int, struct itimerval *, struct itimerval *); struct itimerval new, old; new.it_interval.tv_sec = new.it_interval.tv_usec = 0; new.it_value.tv_sec = seconds; new.it_value.tv_usec = 0; _setitimer(ITIMER_REAL, &new, &old); return(old.it_value.tv_sec + (old.it_value.tv_usec / 1000000)); } int sys_getpid(void) { return current->pid; } int sys_getppid(void) { return current->p_pptr->pid; } int sys_getuid(void) { return current->uid; } int sys_geteuid(void) { return current->euid; } int sys_getgid(void) { return current->gid; } int sys_getegid(void) { return current->egid; } int sys_nice(long increment) { if (increment < 0 && !suser()) return -EPERM; if (increment >= current->priority) increment = current->priority-1; current->priority -= increment; return 0; } void sched_init(void) { int i; struct desc_struct * p; if (sizeof(struct sigaction) != 16) panic("Struct sigaction MUST be 16 bytes"); set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); p = gdt+2+FIRST_TSS_ENTRY; for(i=1 ; i<NR_TASKS ; i++) { task[i] = NULL; p->a=p->b=0; p++; p->a=p->b=0; p++; } /* Clear NT, so that we won't have troubles with that later on */ __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); ltr(0); lldt(0); outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */ outb_p(LATCH & 0xff , 0x40); /* LSB */ outb(LATCH >> 8 , 0x40); /* MSB */ set_intr_gate(0x20,&timer_interrupt); outb(inb_p(0x21)&~0x01,0x21); set_system_gate(0x80,&system_call); }