From: Tomas Hruby Date: Fri, 6 Nov 2009 09:04:15 +0000 (+0000) Subject: Clock task split X-Git-Tag: v3.1.6~218 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/openssl_button.gif?a=commitdiff_plain;h=f2a1f21a396f6f891ab81196d20fed03b09b9abc;p=minix.git Clock task split - preemption handled in the clock timer interrupt handler, not in the clock task - more achitecture independent clock timer handling code - smp ready as each CPU can have its own timer --- diff --git a/kernel/arch/i386/clock.c b/kernel/arch/i386/clock.c index 530236ff3..f5e456dc2 100755 --- a/kernel/arch/i386/clock.c +++ b/kernel/arch/i386/clock.c @@ -6,6 +6,8 @@ #include "../../kernel.h" +#include "../../clock.h" + #define CLOCK_ACK_BIT 0x80 /* PS/2 clock interrupt acknowledge bit */ /* Clock parameters. */ @@ -14,28 +16,30 @@ #define SQUARE_WAVE 0x36 /* ccaammmb, a = access, m = mode, b = BCD */ /* 11x11, 11 = LSB then MSB, x11 = sq wave */ #define TIMER_FREQ 1193182 /* clock frequency for timer in PC and AT */ -#define TIMER_COUNT (TIMER_FREQ/system_hz) /* initial value for counter*/ +#define TIMER_COUNT(freq) (TIMER_FREQ/(freq)) /* initial value for counter*/ + +PRIVATE irq_hook_t pic_timer_hook; /* interrupt handler hook */ /*===========================================================================* - * arch_init_clock * + * init_8235A_timer * *===========================================================================*/ -PUBLIC int arch_init_clock(void) +PUBLIC int init_8253A_timer(unsigned freq) { /* Initialize channel 0 of the 8253A timer to, e.g., 60 Hz, * and register the CLOCK task's interrupt handler to be run * on every clock tick. */ outb(TIMER_MODE, SQUARE_WAVE); /* run continuously */ - outb(TIMER0, (TIMER_COUNT & 0xff)); /* timer low byte */ - outb(TIMER0, TIMER_COUNT >> 8); /* timer high byte */ + outb(TIMER0, (TIMER_COUNT(freq) & 0xff)); /* timer low byte */ + outb(TIMER0, TIMER_COUNT(freq) >> 8); /* timer high byte */ return OK; } /*===========================================================================* - * clock_stop * + * stop_8235A_timer * *===========================================================================*/ -PUBLIC void clock_stop(void) +PUBLIC void stop_8253A_timer(void) { /* Reset the clock to the BIOS rate. (For rebooting.) */ outb(TIMER_MODE, 0x36); @@ -44,9 +48,9 @@ PUBLIC void clock_stop(void) } /*===========================================================================* - * read_clock * + * read_8235A_timer * *===========================================================================*/ -PUBLIC clock_t read_clock(void) +PUBLIC clock_t read_8253A_timer(void) { /* Read the counter of channel 0 of the 8253A timer. This counter * counts down at a rate of TIMER_FREQ and restarts at @@ -63,3 +67,26 @@ PUBLIC clock_t read_clock(void) return count; } +PUBLIC int arch_init_local_timer(unsigned freq) +{ + init_8253A_timer(freq); + + return 0; +} + +PUBLIC void arch_stop_local_timer(void) +{ + stop_8253A_timer(); +} + +PUBLIC int arch_register_local_timer_handler(irq_handler_t handler) +{ + /* Using PIC, Initialize the CLOCK's interrupt hook. */ + pic_timer_hook.proc_nr_e = NONE; + + put_irq_handler(&pic_timer_hook, CLOCK_IRQ, handler); + put_irq_handler(&pic_timer_hook, CLOCK_IRQ, + (irq_handler_t)bsp_timer_int_handler); + + return 0; +} diff --git a/kernel/arch/i386/clock.h b/kernel/arch/i386/clock.h new file mode 100644 index 000000000..4b83158f2 --- /dev/null +++ b/kernel/arch/i386/clock.h @@ -0,0 +1,8 @@ +#ifndef __CLOCK_X86_H__ +#define __CLOCK_X86_H__ + +_PROTOTYPE(int init_8253A_timer, (unsigned freq)); +_PROTOTYPE(void stop_8253A_timer, (void)); +_PROTOTYPE(clock_t read_8253A_timer, (void)); + +#endif /* __CLOCK_X86_H__ */ diff --git a/kernel/clock.c b/kernel/clock.c index d9aa3ba2a..514c5b639 100755 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -12,8 +12,8 @@ * Sep 30, 2004 source code documentation updated (Jorrit N. Herder) * Sep 24, 2004 redesigned alarm timers (Jorrit N. Herder) * - * The function do_clocktick() is triggered by the clock's interrupt - * handler when a watchdog timer has expired or a process must be scheduled. + * Clock task is notified by the clock's interrupt handler when a timer + * has expired. * * In addition to the main clock_task() entry point, which starts the main * loop, there are several other minor entry points: @@ -24,7 +24,6 @@ * read_clock: read the counter of channel 0 of the 8253A timer * * (+) The CLOCK task keeps tracks of watchdog timers for the entire kernel. - * The watchdog functions of expired timers are executed in do_clocktick(). * It is crucial that watchdog functions not block, or the CLOCK task may * be blocked. Do not send() a message when the receiver is not expecting it. * Instead, notify(), which always returns, should be used. @@ -37,11 +36,11 @@ #include #include +#include "clock.h" + /* Function prototype for PRIVATE functions. */ FORWARD _PROTOTYPE( void init_clock, (void) ); -FORWARD _PROTOTYPE( int clock_handler, (irq_hook_t *hook) ); -FORWARD _PROTOTYPE( void do_clocktick, (message *m_ptr) ); FORWARD _PROTOTYPE( void load_update, (void)); /* The CLOCK's timers queue. The functions in operate on this. @@ -57,7 +56,6 @@ PRIVATE clock_t next_timeout; /* realtime that next timer expires */ /* The time is incremented by the interrupt handler on each clock tick. */ PRIVATE clock_t realtime = 0; /* real time clock */ -PRIVATE irq_hook_t clock_hook; /* interrupt handler hook */ /*===========================================================================* * clock_task * @@ -83,7 +81,9 @@ PUBLIC void clock_task() if (is_notify(m.m_type)) { switch (_ENDPOINT_P(m.m_source)) { case HARDWARE: - do_clocktick(&m); /* handle clock tick */ + tmrs_exptimers(&clock_timers, realtime, NULL); + next_timeout = (clock_timers == NULL) ? + TMR_NEVER : clock_timers->tmr_exp_time; break; default: /* illegal request type */ kprintf("CLOCK: illegal notify %d from %d.\n", @@ -98,169 +98,45 @@ PUBLIC void clock_task() } } -/*===========================================================================* - * do_clocktick * - *===========================================================================*/ -PRIVATE void do_clocktick(m_ptr) -message *m_ptr; /* pointer to request message */ -{ - register struct proc *bill_copy = bill_ptr; - -/* Despite its name, this routine is not called on every clock tick. It - * is called on those clock ticks when a lot of work needs to be done. - */ - - /* A process used up a full quantum. The interrupt handler stored this - * process in 'prev_ptr'. First make sure that the process is not on the - * scheduling queues. Then announce the process ready again. Since it has - * no more time left, it gets a new quantum and is inserted at the right - * place in the queues. As a side-effect a new process will be scheduled. - */ - if (prev_ptr->p_ticks_left <= 0 && priv(prev_ptr)->s_flags & PREEMPTIBLE) { - if(prev_ptr->p_rts_flags == 0) { /* if it was runnable .. */ - lock; - { - dequeue(prev_ptr); /* take it off the queues */ - enqueue(prev_ptr); /* and reinsert it again */ - } - unlock; - } else { - kprintf("CLOCK: %d not runnable; flags: %x\n", - prev_ptr->p_endpoint, prev_ptr->p_rts_flags); - } - } - - /* Check if a process-virtual timer expired. Check prev_ptr, but also - * bill_ptr - one process's user time is another's system time, and the - * profile timer decreases for both! Do this before the queue operations - * below, which may alter bill_ptr. Note the use a copy of bill_ptr, because - * bill_ptr may have been changed above, and this code can't be put higher - * up because otherwise cause_sig() may dequeue prev_ptr before we do. - */ - vtimer_check(prev_ptr); - - if (prev_ptr != bill_copy) - vtimer_check(bill_copy); - - /* Check if a clock timer expired and run its watchdog function. */ - if (next_timeout <= realtime) { - tmrs_exptimers(&clock_timers, realtime, NULL); - next_timeout = (clock_timers == NULL) ? - TMR_NEVER : clock_timers->tmr_exp_time; - } - - return; -} - /*===========================================================================* * init_clock * *===========================================================================*/ PRIVATE void init_clock() { - /* First of all init the clock system. - * - * Here the (a) clock is set to produce a interrupt at - * every 1/60 second (ea. 60Hz). - * - * Running right away. - */ - arch_init_clock(); /* architecture-dependent initialization. */ - /* Initialize the CLOCK's interrupt hook. */ - clock_hook.proc_nr_e = CLOCK; - - put_irq_handler(&clock_hook, CLOCK_IRQ, clock_handler); - enable_irq(&clock_hook); /* ready for clock interrupts */ - - /* Set a watchdog timer to periodically balance the scheduling queues. */ - balance_queues(NULL); /* side-effect sets new timer */ + /* Set a watchdog timer to periodically balance the scheduling queues. + Side-effect sets new timer */ + + balance_queues(NULL); } -/*===========================================================================* - * clock_handler * - *===========================================================================*/ -PRIVATE int clock_handler(hook) -irq_hook_t *hook; -{ -/* This executes on each clock tick (i.e., every time the timer chip generates - * an interrupt). It does a little bit of work so the clock task does not have - * to be called on every tick. The clock task is called when: - * - * (1) the scheduling quantum of the running process has expired, or - * (2) a timer has expired and the watchdog function should be run. - * - * Many global global and static variables are accessed here. The safety of - * this must be justified. All scheduling and message passing code acquires a - * lock by temporarily disabling interrupts, so no conflicts with calls from - * the task level can occur. Furthermore, interrupts are not reentrant, the - * interrupt handler cannot be bothered by other interrupts. - * - * Variables that are updated in the clock's interrupt handler: - * lost_ticks: - * Clock ticks counted outside the clock task. This for example - * is used when the boot monitor processes a real mode interrupt. - * realtime: - * The current uptime is incremented with all outstanding ticks. - * proc_ptr, bill_ptr: - * These are used for accounting and virtual timers. It does not - * matter if proc.c is changing them, provided they are always - * valid pointers, since at worst the previous process would be - * billed. +/* + * The boot processor timer interrupt handler. In addition to non-boot cpus it + * keeps real time and notifies the clock task if need be */ - register unsigned ticks; - register int expired; - - if(minix_panicing) return; - - /* Get number of ticks and update realtime. */ - ticks = lost_ticks + 1; - lost_ticks = 0; - realtime += ticks; - - /* Update user and system accounting times. Charge the current process for - * user time. If the current process is not billable, that is, if a non-user - * process is running, charge the billable process for system time as well. - * Thus the unbillable process' user time is the billable user's system time. - */ - - proc_ptr->p_user_time += ticks; - if (priv(proc_ptr)->s_flags & PREEMPTIBLE) { - proc_ptr->p_ticks_left -= ticks; - } - if (! (priv(proc_ptr)->s_flags & BILLABLE)) { - bill_ptr->p_sys_time += ticks; - bill_ptr->p_ticks_left -= ticks; - } +PUBLIC int bsp_timer_int_handler(void) +{ + unsigned ticks; + + if(minix_panicing) + return 0; + + /* Get number of ticks and update realtime. */ + ticks = lost_ticks + 1; + lost_ticks = 0; + realtime += ticks; + + ap_timer_int_handler(); - /* Decrement virtual timers, if applicable. We decrement both the virtual - * and the profile timer of the current process, and if the current process - * is not billable, the timer of the billed process as well. - * If any of the timers expire, do_clocktick() will send out signals. - */ - expired = 0; - if ((proc_ptr->p_misc_flags & MF_VIRT_TIMER) && - (proc_ptr->p_virt_left -= ticks) <= 0) expired = 1; - if ((proc_ptr->p_misc_flags & MF_PROF_TIMER) && - (proc_ptr->p_prof_left -= ticks) <= 0) expired = 1; - if (! (priv(proc_ptr)->s_flags & BILLABLE) && - (bill_ptr->p_misc_flags & MF_PROF_TIMER) && - (bill_ptr->p_prof_left -= ticks) <= 0) expired = 1; - - /* Update load average. */ - load_update(); - - /* Check if do_clocktick() must be called. Done for alarms and scheduling. - * Some processes, such as the kernel tasks, cannot be preempted. - */ - if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0) || expired) { - prev_ptr = proc_ptr; /* store running process */ - mini_notify(proc_addr(HARDWARE), CLOCK); /* send notification */ - } - - if (do_serial_debug) - do_ser_debug(); - - return(1); /* reenable interrupts */ + /* if a timer expired, notify the clock task */ + if ((next_timeout <= realtime)) { + mini_notify(proc_addr(HARDWARE), CLOCK); /* send notification */ + } + + if (do_serial_debug) + do_ser_debug(); + + return(1); /* reenable interrupts */ } /*===========================================================================* @@ -334,3 +210,92 @@ PRIVATE void load_update(void) kloadinfo.last_clock = realtime; } +/* + * Timer interupt handler. This is the only think executed on non boot + * processors. It is called by bsp_timer_int_handler() on the boot processor + */ +PUBLIC int ap_timer_int_handler(void) +{ + + /* Update user and system accounting times. Charge the current process + * for user time. If the current process is not billable, that is, if a + * non-user process is running, charge the billable process for system + * time as well. Thus the unbillable process' user time is the billable + * user's system time. + */ + + unsigned ticks = 1; + int expired = 0; + struct proc * p, * billp; + + /* Update user and system accounting times. Charge the current process + * for user time. If the current process is not billable, that is, if a + * non-user process is running, charge the billable process for system + * time as well. Thus the unbillable process' user time is the billable + * user's system time. + */ + + /* FIXME prepared for get_cpu_local_var() */ + p = proc_ptr; + billp = bill_ptr; + + p->p_user_time += ticks; + if (priv(p)->s_flags & PREEMPTIBLE) { + p->p_ticks_left -= ticks; + } + if (! (priv(p)->s_flags & BILLABLE)) { + billp->p_sys_time += ticks; + billp->p_ticks_left -= ticks; + } + + /* Decrement virtual timers, if applicable. We decrement both the + * virtual and the profile timer of the current process, and if the + * current process is not billable, the timer of the billed process as + * well. If any of the timers expire, do_clocktick() will send out + * signals. + */ + if ((p->p_misc_flags & MF_VIRT_TIMER) && + (p->p_virt_left -= ticks) <= 0) expired = 1; + if ((p->p_misc_flags & MF_PROF_TIMER) && + (p->p_prof_left -= ticks) <= 0) expired = 1; + if (! (priv(p)->s_flags & BILLABLE) && + (billp->p_misc_flags & MF_PROF_TIMER) && + (billp->p_prof_left -= ticks) <= 0) expired = 1; + + /* + * Check if a process-virtual timer expired. Check current process, but + * also bill_ptr - one process's user time is another's system time, and + * the profile timer decreases for both! + */ + vtimer_check(p); + + if (p != billp) + vtimer_check(billp); + + /* Update load average. */ + load_update(); + + /* check if the process is still runnable after checking the vtimer */ + if (p->p_rts_flags == 0 && p->p_ticks_left <= 0 && + priv(p)->s_flags & PREEMPTIBLE) { + proc_ptr = NULL; /* no process is scheduled for dequeue and + enqueue */ + dequeue(p); /* take it off the queues */ + enqueue(p); /* and reinsert it again */ + proc_ptr = p; /* restore some consitent state */ + } + + return 1; +} + +PUBLIC int boot_cpu_init_timer(unsigned freq) +{ + if (arch_init_local_timer(freq)) + return -1; + + if (arch_register_local_timer_handler( + (irq_handler_t) bsp_timer_int_handler)) + return -1; + + return 0; +} diff --git a/kernel/clock.h b/kernel/clock.h new file mode 100644 index 000000000..b5936d315 --- /dev/null +++ b/kernel/clock.h @@ -0,0 +1,16 @@ +#ifndef __CLOCK_H__ +#define __CLOCK_H__ + +#include "kernel.h" +#include "arch/i386/clock.h" + +_PROTOTYPE(int boot_cpu_init_timer, (unsigned freq)); + +_PROTOTYPE(int bsp_timer_int_handler, (void)); +_PROTOTYPE(int ap_timer_int_handler, (void)); + +_PROTOTYPE(int arch_init_local_timer, (unsigned freq)); +_PROTOTYPE(void arch_stop_local_timer, (void)); +_PROTOTYPE(int arch_register_local_timer_handler, (irq_handler_t handler)); + +#endif /* __CLOCK_H__ */ diff --git a/kernel/glo.h b/kernel/glo.h index b6f289392..25ecf45af 100755 --- a/kernel/glo.h +++ b/kernel/glo.h @@ -32,7 +32,6 @@ EXTERN struct loadinfo kloadinfo; /* status of load average */ /* Process scheduling information and the kernel reentry count. */ EXTERN struct proc *proc_ptr; /* pointer to currently running process */ EXTERN struct proc *next_ptr; /* next process to run after restart() */ -EXTERN struct proc *prev_ptr; EXTERN struct proc *bill_ptr; /* process to bill for clock ticks */ EXTERN struct proc *vmrestart; /* first process on vmrestart queue */ EXTERN struct proc *vmrequest; /* first process on vmrequest queue */ diff --git a/kernel/main.c b/kernel/main.c index c867544ec..dda583819 100755 --- a/kernel/main.c +++ b/kernel/main.c @@ -18,6 +18,7 @@ #include #include "proc.h" #include "debug.h" +#include "clock.h" /* Prototype declarations for PRIVATE functions. */ FORWARD _PROTOTYPE( void announce, (void)); @@ -195,6 +196,16 @@ PUBLIC void main() */ bill_ptr = proc_addr(IDLE); /* it has to point somewhere */ announce(); /* print MINIX startup banner */ + + /* + * enable timer interrupts and clock task on the boot CPU + */ + if (boot_cpu_init_timer(system_hz)) { + minix_panic("FATAL : failed to initialize timer interrupts, " + "cannot continue without any clock source!", + NO_NUM); + } + /* Warnings for sanity checks that take time. These warnings are printed * so it's a clear warning no full release should be done with them * enabled. @@ -208,6 +219,7 @@ PUBLIC void main() #if DEBUG_PROC_CHECK FIXME("PROC check enabled"); #endif + restart(); } @@ -257,7 +269,7 @@ timer_t *tp; * monitor), RBT_MONITOR (execute given code), RBT_RESET (hard reset). */ intr_init(INTS_ORIG); - clock_stop(); + arch_stop_local_timer(); arch_shutdown(tp ? tmr_arg(tp)->ta_int : RBT_PANIC); } diff --git a/kernel/proc.c b/kernel/proc.c index 5d6afee47..e3bdc05ae 100755 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -1190,9 +1190,8 @@ register struct proc *rp; /* this process is now runnable */ * process yet or current process isn't ready any more, or * it's PREEMPTIBLE. */ - vmassert(proc_ptr); - if((proc_ptr->p_priority > rp->p_priority) && - (priv(proc_ptr)->s_flags & PREEMPTIBLE)) + if(!proc_ptr || (proc_ptr->p_priority > rp->p_priority) || + (priv(proc_ptr)->s_flags & PREEMPTIBLE)) pick_proc(); #if DEBUG_SCHED_CHECK diff --git a/kernel/proto.h b/kernel/proto.h index 60ffb85cc..c44b8d31f 100755 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -132,9 +132,6 @@ _PROTOTYPE( int vm_phys_memset, (phys_bytes source, u8_t pattern, phys_bytes count) ); _PROTOTYPE( vir_bytes alloc_remote_segment, (u32_t *, segframe_t *, int, phys_bytes, vir_bytes, int)); -_PROTOTYPE( int arch_init_clock, (void) ); -_PROTOTYPE( clock_t read_clock, (void) ); -_PROTOTYPE( void clock_stop, (void) ); _PROTOTYPE( int intr_init, (int) ); _PROTOTYPE( int intr_disabled, (void) ); _PROTOTYPE( void idle_task, (void) );