]> Zhao Yanbai Git Server - minix.git/commitdiff
Clock task split
authorTomas Hruby <tom@minix3.org>
Fri, 6 Nov 2009 09:04:15 +0000 (09:04 +0000)
committerTomas Hruby <tom@minix3.org>
Fri, 6 Nov 2009 09:04:15 +0000 (09:04 +0000)
- 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

kernel/arch/i386/clock.c
kernel/arch/i386/clock.h [new file with mode: 0644]
kernel/clock.c
kernel/clock.h [new file with mode: 0644]
kernel/glo.h
kernel/main.c
kernel/proc.c
kernel/proto.h

index 530236ff38047551d516fce0c0647d5bd5840816..f5e456dc2e40ad772e5add8081d5b63a55745e5c 100755 (executable)
@@ -6,6 +6,8 @@
 
 #include "../../kernel.h"
 
+#include "../../clock.h"
+
 #define CLOCK_ACK_BIT   0x80    /* PS/2 clock interrupt acknowledge bit */
 
 /* Clock parameters. */
 #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 (file)
index 0000000..4b83158
--- /dev/null
@@ -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__ */
index d9aa3ba2aaba1c6fb316c27bb3953b664a8bd068..514c5b6391e7bc17df86298c600934b02ddd4ecc 100755 (executable)
@@ -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. 
 #include <minix/endpoint.h>
 #include <minix/portio.h>
 
+#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 <timers.h> 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 (file)
index 0000000..b5936d3
--- /dev/null
@@ -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__ */
index b6f289392ec11aa62b135fdc1901dd73915246a4..25ecf45af093e9d8068a81a4d3d6975c85b18f46 100755 (executable)
@@ -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 */
index c867544ecf2597ef9c80f123f5c25bf2f0109cb7..dda583819e60c231ef454d3d90f0953716fc88c5 100755 (executable)
@@ -18,6 +18,7 @@
 #include <minix/endpoint.h>
 #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);
 }
 
index 5d6afee47441b430c6d710dfcff1c5bd3f21e3d3..e3bdc05ae3befb819793f1054e3862fa89e216b8 100755 (executable)
@@ -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
index 60ffb85cc1c941ea22d03d3232b39b39a555596d..c44b8d31f0f189239b5fc2e08ed442e00415786d 100755 (executable)
@@ -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)                                     );