]> Zhao Yanbai Git Server - minix.git/commitdiff
SMP - CPU local run queues
authorTomas Hruby <tom@minix3.org>
Wed, 15 Sep 2010 14:10:18 +0000 (14:10 +0000)
committerTomas Hruby <tom@minix3.org>
Wed, 15 Sep 2010 14:10:18 +0000 (14:10 +0000)
- each CPU has its own runqueues

- processes on BSP are put on the runqueues later after a switch to
  the final stack when cpuid works to avoid special cases

- enqueue() and dequeue() use the run queues of the cpu the process is
  assigned to

- pick_proc() uses the local run queues

- printing of per-CPU run queues ('2') on serial console

kernel/arch/i386/arch_smp.c
kernel/arch/i386/arch_system.c
kernel/arch/i386/glo.h
kernel/clock.c
kernel/cpulocals.h
kernel/debug.c
kernel/main.c
kernel/proc.c
kernel/proc.h
kernel/proto.h

index 360786a33fe345914cb2aa44a2e7f2c5bee8b123..f381b7b9a4ef8cc073ce8c9245db6a9a5d912d25 100644 (file)
@@ -215,8 +215,12 @@ PRIVATE void ap_finish_booting(void)
        }
        printf("CPU %d local APIC timer is ticking\n", cpu);
 
+       /* FIXME assign CPU local idle structure */
+       get_cpulocal_var(proc_ptr) = proc_addr(IDLE);
+       get_cpulocal_var(bill_ptr) = proc_addr(IDLE);
+
        BKL_UNLOCK();
-       
+
        ap_boot_finished(cpu);
        spinlock_unlock(&boot_lock);
        for(;;);
index bf0f39934d0983068defed85b02d880b2c067b68..84fd53892929bc90cff7c8422b9ad4ab135bc1a6 100644 (file)
@@ -370,19 +370,38 @@ PUBLIC void do_ser_debug()
        ser_debug(c);
 }
 
-PRIVATE void ser_dump_queues(void)
+PRIVATE void ser_dump_queue_cpu(unsigned cpu)
 {
        int q;
+       struct proc ** rdy_head;
+       
+       rdy_head = get_cpu_var(cpu, run_q_head);
+
        for(q = 0; q < NR_SCHED_QUEUES; q++) {
                struct proc *p;
-               if(rdy_head[q]) 
+               if(rdy_head[q])  {
                        printf("%2d: ", q);
-               for(p = rdy_head[q]; p; p = p->p_nextready) {
-                       printf("%s / %d  ", p->p_name, p->p_endpoint);
+                       for(p = rdy_head[q]; p; p = p->p_nextready) {
+                               printf("%s / %d  ", p->p_name, p->p_endpoint);
+                       }
+                       printf("\n");
                }
-               printf("\n");
        }
+}
+
+PRIVATE void ser_dump_queues(void)
+{
+#ifdef CONFIG_SMP
+       unsigned cpu;
 
+       printf("--- run queues ---\n");
+       for (cpu = 0; cpu < ncpus; cpu++) {
+               printf("CPU %d :\n", cpu);
+               ser_dump_queue_cpu(cpu);
+       }
+#else
+       ser_dump_queue_cpu(0);
+#endif
 }
 
 PRIVATE void ser_dump_segs(void)
index aaa910899bcea88d4420649bcc7a8f3021495e73..27437def8ef492c2b182dfa363626188ed4dc4e3 100644 (file)
@@ -2,7 +2,7 @@
 #define __GLO_X86_H__
 
 #include "kernel/kernel.h"
-#include "proto.h"
+#include "arch_proto.h"
 
 EXTERN int cpu_has_tsc;        /* signal whether this cpu has time stamp register. This
                           feature was introduced by Pentium */
index 3f848b3cb111ed9528ea81755275c97ffc3fb8fd..e8920098b32826c61d78bc76b2803f6d9b59411f 100644 (file)
@@ -194,6 +194,7 @@ PRIVATE void load_update(void)
        u16_t slot;
        int enqueued = 0, q;
        struct proc *p;
+       struct proc **rdy_head;
 
        /* Load average data is stored as a list of numbers in a circular
         * buffer. Each slot accumulates _LOAD_UNIT_SECS of samples of
@@ -207,6 +208,7 @@ PRIVATE void load_update(void)
                kloadinfo.proc_last_slot = slot;
        }
 
+       rdy_head = get_cpulocal_var(run_q_head);
        /* Cumulation. How many processes are ready now? */
        for(q = 0; q < NR_SCHED_QUEUES; q++) {
                for(p = rdy_head[q]; p != NULL; p = p->p_nextready) {
index e16169a88c55b7221b4c162944db657d04d76e09..9c9be2db052debd30644531fcbc465cff61e1ed6 100644 (file)
@@ -5,6 +5,7 @@
 #ifndef __ASSEMBLY__
 
 #include "kernel.h"
+#include "proc.h"
 
 #ifdef CONFIG_SMP
 
@@ -68,6 +69,10 @@ DECLARE_CPULOCAL(int, pagefault_handled);
  */
 DECLARE_CPULOCAL(struct proc *, ptproc);
 
+/* CPU private run queues */
+DECLARE_CPULOCAL(struct proc *, run_q_head[NR_SCHED_QUEUES]); /* ptrs to ready list headers */
+DECLARE_CPULOCAL(struct proc *, run_q_tail[NR_SCHED_QUEUES]); /* ptrs to ready list tails */
+
 DECLARE_CPULOCAL_END
 
 #endif /* __ASSEMBLY__ */
index f6b71205f986e80e6a01a1a6cd165d3c3fff438f..e476c543953568055cee748cab8b4154ddc234a6 100644 (file)
 
 #define MAX_LOOP (NR_PROCS + NR_TASKS)
 
-PUBLIC int
-runqueues_ok(void)
+PUBLIC int runqueues_ok_cpu(unsigned cpu)
 {
   int q, l = 0;
   register struct proc *xp;
+  struct proc **rdy_head, **rdy_tail;
+
+  rdy_head = get_cpu_var(cpu, run_q_head);
+  rdy_tail = get_cpu_var(cpu, run_q_tail);
 
   for (xp = BEG_PROC_ADDR; xp < END_PROC_ADDR; ++xp) {
        xp->p_found = 0;
@@ -109,6 +112,33 @@ runqueues_ok(void)
   return 1;
 }
 
+#ifdef CONFIG_SMP
+PRIVATE int runqueues_ok_all(void)
+{
+       unsigned c;
+
+       for (c = 0 ; c < ncpus; c++) {
+               if (!runqueues_ok_cpu(c))
+                       return 0;
+       }
+       return 1;       
+}
+
+PUBLIC int runqueues_ok(void)
+{
+       return runqueues_ok_all();
+}
+
+#else
+
+PUBLIC int runqueues_ok(void)
+{
+       return runqueues_ok_cpu(0);
+}
+
+
+#endif
+
 PUBLIC char *
 rtsflagstr(const int flags)
 {
index 043926341c65bf96c7f21ba48ea052efd0fb9907..730cc30aa7c71e81c5ccfaba4ca07e90bfdd78a1 100644 (file)
 /* Prototype declarations for PRIVATE functions. */
 FORWARD _PROTOTYPE( void announce, (void));    
 
+void ser_dump_queues(void);
 PUBLIC void bsp_finish_booting(void)
 {
+  int i;
 #if SPROFILE
   sprofiling = 0;      /* we're not profiling until instructed to */
 #endif /* SPROFILE */
@@ -49,6 +51,13 @@ PUBLIC void bsp_finish_booting(void)
   get_cpulocal_var(bill_ptr) = proc_addr(IDLE);        /* it has to point somewhere */
   announce();                          /* print MINIX startup banner */
 
+  /*
+   * we have access to the cpu local run queue, only now schedule the processes.
+   * We ignore the slots for the former kernel tasks
+   */
+  for (i=0; i < NR_BOOT_PROCS - NR_TASKS; i++) {
+       RTS_UNSET(proc_addr(i), RTS_PROC_STOP);
+  }
   /*
    * enable timer interrupts and clock task on the boot CPU
    */
@@ -75,8 +84,10 @@ PUBLIC void bsp_finish_booting(void)
   cycles_accounting_init();
   DEBUGEXTRA(("done\n"));
 
-  assert(runqueues_ok());
-
+#ifdef CONFIG_SMP
+  cpu_set_flag(bsp_cpu_id, CPU_IS_READY);
+#endif
+  
   switch_to_user();
   NOT_REACHABLE;
 }
@@ -246,11 +257,10 @@ PUBLIC int main(void)
         * done this; until then, don't let it run.
         */
        if(ip->flags & PROC_FULLVM)
-               RTS_SET(rp, RTS_VMINHIBIT);
+               rp->p_rts_flags |= RTS_VMINHIBIT;
 
-       /* None of the kernel tasks run */
-       if (rp->p_nr < 0) RTS_SET(rp, RTS_PROC_STOP);
-       RTS_UNSET(rp, RTS_SLOT_FREE); /* remove RTS_SLOT_FREE and schedule */
+       rp->p_rts_flags |= RTS_PROC_STOP;
+       rp->p_rts_flags &= ~RTS_SLOT_FREE;
        alloc_segments(rp);
        DEBUGEXTRA(("done\n"));
   }
index 70938a9d9200dcdba520bacd630e08ebedaa1760..90d5d87ba1e80c9202557f6c7396b23c3518a347 100644 (file)
@@ -110,6 +110,18 @@ PUBLIC void proc_init(void)
        }
 }
 
+PRIVATE void switch_address_space_idle(void)
+{
+#ifdef CONFIG_SMP
+       /*
+        * currently we bet that VM is always alive and its pages available so
+        * when the CPU wakes up the kernel is mapped and no surprises happen.
+        * This is only a problem if more than 1 cpus are available
+        */
+       switch_address_space(proc_addr(VM_PROC_NR));
+#endif
+}
+
 /*===========================================================================*
  *                             idle                                         * 
  *===========================================================================*/
@@ -121,6 +133,8 @@ PRIVATE void idle(void)
         * the CPU utiliziation of certain workloads with high precision.
         */
 
+       switch_address_space_idle();
+
        /* start accounting for the idle time */
        context_stop(proc_addr(KERNEL));
        halt_cpu();
@@ -1209,19 +1223,21 @@ PUBLIC void enqueue(
  * responsible for inserting a process into one of the scheduling queues. 
  * The mechanism is implemented here.   The actual scheduling policy is
  * defined in sched() and pick_proc().
+ *
+ * This function can be used x-cpu as it always uses the queues of the cpu the
+ * process is assigned to.
  */
   int q = rp->p_priority;                      /* scheduling queue to use */
   struct proc * p;
-
-#if DEBUG_RACE
-  /* With DEBUG_RACE, schedule everyone at the same priority level. */
-  rp->p_priority = q = MIN_USER_Q;
-#endif
-
+  struct proc **rdy_head, **rdy_tail;
+  
   assert(proc_is_runnable(rp));
 
   assert(q >= 0);
 
+  rdy_head = get_cpu_var(rp->p_cpu, run_q_head);
+  rdy_tail = get_cpu_var(rp->p_cpu, run_q_tail);
+
   /* Now add the process to the queue. */
   if (!rdy_head[q]) {          /* add to empty queue */
       rdy_head[q] = rdy_tail[q] = rp;          /* create a new queue */
@@ -1245,7 +1261,7 @@ PUBLIC void enqueue(
      RTS_SET(p, RTS_PREEMPTED); /* calls dequeue() */
 
 #if DEBUG_SANITYCHECKS
-  assert(runqueues_ok());
+  assert(runqueues_ok_local());
 #endif
 }
 
@@ -1262,6 +1278,8 @@ PRIVATE void enqueue_head(struct proc *rp)
 {
   const int q = rp->p_priority;                        /* scheduling queue to use */
 
+  struct proc **rdy_head, **rdy_tail;
+
   assert(proc_ptr_ok(rp));
   assert(proc_is_runnable(rp));
 
@@ -1274,6 +1292,9 @@ PRIVATE void enqueue_head(struct proc *rp)
   assert(q >= 0);
 
 
+  rdy_head = get_cpu_var(rp->p_cpu, run_q_head);
+  rdy_tail = get_cpu_var(rp->p_cpu, run_q_tail);
+
   /* Now add the process to the queue. */
   if (!rdy_head[q]) {          /* add to empty queue */
       rdy_head[q] = rdy_tail[q] = rp;          /* create a new queue */
@@ -1284,7 +1305,7 @@ PRIVATE void enqueue_head(struct proc *rp)
       rdy_head[q] = rp;                                /* set new queue head */
 
 #if DEBUG_SANITYCHECKS
-  assert(runqueues_ok());
+  assert(runqueues_ok_local());
 #endif
 }
 
@@ -1297,23 +1318,31 @@ PUBLIC void dequeue(const struct proc *rp)
 /* A process must be removed from the scheduling queues, for example, because
  * it has blocked.  If the currently active process is removed, a new process
  * is picked to run by calling pick_proc().
+ *
+ * This function can operate x-cpu as it always removes the process from the
+ * queue of the cpu the process is currently assigned to.
  */
   register int q = rp->p_priority;             /* queue to use */
   register struct proc **xpp;                  /* iterate over queue */
   register struct proc *prev_xp;
 
+  struct proc **rdy_tail;
+
   assert(proc_ptr_ok(rp));
   assert(!proc_is_runnable(rp));
 
   /* Side-effect for kernel: check if the task's stack still is ok? */
   assert (!iskernelp(rp) || *priv(rp)->s_stack_guard == STACK_GUARD);
 
+  rdy_tail = get_cpu_var(rp->p_cpu, run_q_tail);
+
   /* Now make sure that the process is not in its ready queue. Remove the 
    * process if it is found. A process can be made unready even if it is not 
    * running by being sent a signal that kills it.
    */
   prev_xp = NULL;                              
-  for (xpp = &rdy_head[q]; *xpp; xpp = &(*xpp)->p_nextready) {
+  for (xpp = get_cpu_var_ptr(rp->p_cpu, run_q_head[q]); *xpp;
+                 xpp = &(*xpp)->p_nextready) {
       if (*xpp == rp) {                                /* found process to remove */
           *xpp = (*xpp)->p_nextready;          /* replace with next chain */
           if (rp == rdy_tail[q]) {             /* queue tail removed */
@@ -1326,36 +1355,10 @@ PUBLIC void dequeue(const struct proc *rp)
   }
 
 #if DEBUG_SANITYCHECKS
-  assert(runqueues_ok());
+  assert(runqueues_ok_local());
 #endif
 }
 
-#if DEBUG_RACE
-/*===========================================================================*
- *                             random_process                               * 
- *===========================================================================*/
-PRIVATE struct proc *random_process(struct proc *head)
-{
-       int i, n = 0;
-       struct proc *rp;
-       u64_t r;
-       read_tsc_64(&r);
-
-       for(rp = head; rp; rp = rp->p_nextready)
-               n++;
-
-       /* Use low-order word of TSC as pseudorandom value. */
-       i = r.lo % n;
-
-       for(rp = head; i--; rp = rp->p_nextready)
-               ;
-
-       assert(rp);
-
-       return rp;
-}
-#endif
-
 /*===========================================================================*
  *                             pick_proc                                    * 
  *===========================================================================*/
@@ -1364,26 +1367,23 @@ PRIVATE struct proc * pick_proc(void)
 /* Decide who to run now.  A new process is selected an returned.
  * When a billable process is selected, record it in 'bill_ptr', so that the 
  * clock task can tell who to bill for system time.
+ *
+ * This functions always uses the run queues of the local cpu!
  */
   register struct proc *rp;                    /* process to run */
+  struct proc **rdy_head;
   int q;                               /* iterate over queues */
 
   /* Check each of the scheduling queues for ready processes. The number of
    * queues is defined in proc.h, and priorities are set in the task table.
    * The lowest queue contains IDLE, which is always ready.
    */
+  rdy_head = get_cpulocal_var(run_q_head);
   for (q=0; q < NR_SCHED_QUEUES; q++) {        
        if(!(rp = rdy_head[q])) {
                TRACE(VF_PICKPROC, printf("queue %d empty\n", q););
                continue;
        }
-
-#if DEBUG_RACE
-       rp = random_process(rdy_head[q]);
-#endif
-
-       TRACE(VF_PICKPROC, printf("found %s / %d on queue %d\n", 
-               rp->p_name, rp->p_endpoint, q););
        assert(proc_is_runnable(rp));
        if (priv(rp)->s_flags & BILLABLE)               
                get_cpulocal_var(bill_ptr) = rp; /* bill for system time */
index 2a9b915790d86282c6f897e99afda6be52651907..e1fa0fc886f1b687a1832370a2459143f90dc8a0 100644 (file)
@@ -252,8 +252,6 @@ struct proc {
 #ifndef __ASSEMBLY__
 
 EXTERN struct proc proc[NR_TASKS + NR_PROCS];  /* process table */
-EXTERN struct proc *rdy_head[NR_SCHED_QUEUES]; /* ptrs to ready list headers */
-EXTERN struct proc *rdy_tail[NR_SCHED_QUEUES]; /* ptrs to ready list tails */
 
 _PROTOTYPE( int mini_send, (struct proc *caller_ptr, endpoint_t dst_e,
                message *m_ptr, int flags));
index f0923ee5eed703e5be2b8acf98ab3ba0fecc8e6e..0ac33a28c2c9a90ee5fc98f95cb7486978d8e64a 100644 (file)
@@ -102,6 +102,12 @@ _PROTOTYPE( int disable_irq, (const irq_hook_t *hook)                      );
 
 /* debug.c */
 _PROTOTYPE( int runqueues_ok, (void) );
+#ifndef CONFIG_SMP
+#define runqueues_ok_local runqueues_ok
+#else
+#define runqueues_ok_local() runqueues_ok_cpu(cpuid)
+_PROTOTYPE( int runqueues_ok_cpu, (unsigned cpu));
+#endif
 _PROTOTYPE( char *rtsflagstr, (int flags) );
 _PROTOTYPE( char *miscflagstr, (int flags) );
 _PROTOTYPE( char *schedulerstr, (struct proc *scheduler) );