| /* |
| * '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/kernel.h> |
| #include <signal.h> |
| #include <linux/sys.h> |
| #include <asm/system.h> |
| #include <asm/io.h> |
| #include <asm/segment.h> |
| |
| #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,}; |
| |
| long volatile jiffies=0; |
| long startup_time=0; |
| struct task_struct *current = &(init_task.task), *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) |
| __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); |
| if (current->used_math) |
| __asm__("frstor %0"::"m" (current->tss.i387)); |
| else { |
| __asm__("fninit"::); |
| current->used_math=1; |
| } |
| last_task_used_math=current; |
| } |
| |
| /* |
| * 'schedule()' is the scheduler function. This is GOOD CODE! There |
| * probably won't be any reason to change this, as it should work well |
| * in all circumstances (ie gives IO-bound processes good response etc). |
| * 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 */ |
| |
| for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) |
| if (*p) { |
| if ((*p)->alarm && (*p)->alarm < jiffies) { |
| (*p)->signal |= (1<<(SIGALRM-1)); |
| (*p)->alarm = 0; |
| } |
| if ((*p)->signal && (*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) |
| { |
| current->state = TASK_INTERRUPTIBLE; |
| schedule(); |
| return 0; |
| } |
| |
| void sleep_on(struct task_struct **p) |
| { |
| struct task_struct *tmp; |
| |
| if (!p) |
| return; |
| if (current == &(init_task.task)) |
| panic("task[0] trying to sleep"); |
| tmp = *p; |
| *p = current; |
| current->state = TASK_UNINTERRUPTIBLE; |
| schedule(); |
| if (tmp) |
| tmp->state=0; |
| } |
| |
| void interruptible_sleep_on(struct task_struct **p) |
| { |
| struct task_struct *tmp; |
| |
| if (!p) |
| return; |
| if (current == &(init_task.task)) |
| panic("task[0] trying to sleep"); |
| tmp=*p; |
| *p=current; |
| repeat: current->state = TASK_INTERRUPTIBLE; |
| schedule(); |
| if (*p && *p != current) { |
| (**p).state=0; |
| goto repeat; |
| } |
| *p=NULL; |
| if (tmp) |
| tmp->state=0; |
| } |
| |
| void wake_up(struct task_struct **p) |
| { |
| if (p && *p) { |
| (**p).state=0; |
| *p=NULL; |
| } |
| } |
| |
| void do_timer(long cpl) |
| { |
| if (cpl) |
| current->utime++; |
| else |
| current->stime++; |
| if ((--current->counter)>0) return; |
| current->counter=0; |
| if (!cpl) return; |
| schedule(); |
| } |
| |
| int sys_alarm(long seconds) |
| { |
| current->alarm = (seconds>0)?(jiffies+HZ*seconds):0; |
| return seconds; |
| } |
| |
| int sys_getpid(void) |
| { |
| return current->pid; |
| } |
| |
| int sys_getppid(void) |
| { |
| return current->father; |
| } |
| |
| 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 (current->priority-increment>0) |
| current->priority -= increment; |
| return 0; |
| } |
| |
| int sys_signal(long signal,long addr,long restorer) |
| { |
| long i; |
| |
| switch (signal) { |
| case SIGHUP: case SIGINT: case SIGQUIT: case SIGILL: |
| case SIGTRAP: case SIGABRT: case SIGFPE: case SIGUSR1: |
| case SIGSEGV: case SIGUSR2: case SIGPIPE: case SIGALRM: |
| case SIGCHLD: |
| i=(long) current->sig_fn[signal-1]; |
| current->sig_fn[signal-1] = (fn_ptr) addr; |
| current->sig_restorer = (fn_ptr) restorer; |
| return i; |
| default: return -1; |
| } |
| } |
| |
| void sched_init(void) |
| { |
| int i; |
| struct desc_struct * p; |
| |
| 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++; |
| } |
| 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); |
| } |