]> Zhao Yanbai Git Server - minix.git/commitdiff
Userspace scheduling
authorTomas Hruby <tom@minix3.org>
Mon, 29 Mar 2010 11:07:20 +0000 (11:07 +0000)
committerTomas Hruby <tom@minix3.org>
Mon, 29 Mar 2010 11:07:20 +0000 (11:07 +0000)
- cotributed by Bjorn Swift

- In this first phase, scheduling is moved from the kernel to the PM
  server. The next steps are to a) moving scheduling to its own server
  and b) include useful information in the "out of quantum" message,
  so that the scheduler can make use of this information.

- The kernel process table now keeps record of who is responsible for
  scheduling each process (p_scheduler). When this pointer is NULL,
  the process will be scheduled by the kernel. If such a process runs
  out of quantum, the kernel will simply renew its quantum an requeue
  it.

- When PM loads, it will take over scheduling of all running
  processes, except system processes, using sys_schedctl().
  Essentially, this only results in taking over init. As children
  inherit a scheduler from their parent, user space programs forked by
  init will inherit PM (for now) as their scheduler.

 - Once a process has been assigned a scheduler, and runs out of
   quantum, its RTS_NO_QUANTUM flag will be set and the process
   dequeued. The kernel will send a message to the scheduler, on the
   process' behalf, informing the scheduler that it has run out of
   quantum. The scheduler can take what ever action it pleases, based
   on its policy, and then reschedule the process using the
   sys_schedule() system call.

- Balance queues does not work as before. While the old in-kernel
  function used to renew the quantum of processes in the highest
  priority run queue, the user-space implementation only acts on
  processes that have been bumped down to a lower priority queue.
  This approach reacts slower to changes than the old one, but saves
  us sending a sys_schedule message for each process every time we
  balance the queues. Currently, when processes are moved up a
  priority queue, their quantum is also renewed, but this can be
  fiddled with.

- do_nice has been removed from kernel. PM answers to get- and
  setpriority calls, updates it's own nice variable as well as the
  max_run_queue. This will be refactored once scheduling is moved to a
  separate server. We will probably have PM update it's local nice
  value and then send a message to whoever is scheduling the process.

- changes to fix an issue in do_fork() where processes could run out
  of quantum but bypassing the code path that handles it correctly.
  The future plan is to remove the policy from do_fork() and implement
  it in userspace too.

28 files changed:
include/minix/com.h
include/minix/syslib.h
kernel/arch/i386/system.c
kernel/clock.c
kernel/config.h
kernel/debug.c
kernel/ipc.h
kernel/main.c
kernel/proc.c
kernel/proc.h
kernel/proto.h
kernel/system.c
kernel/system.h
kernel/system/Makefile
kernel/system/do_fork.c
kernel/system/do_schedctl.c [new file with mode: 0644]
kernel/system/do_schedule.c [new file with mode: 0644]
lib/libsys/Makefile
lib/libsys/sys_nice.c [deleted file]
lib/libsys/sys_schedctl.c [new file with mode: 0644]
lib/libsys/sys_schedule.c [new file with mode: 0644]
servers/is/dmp_kernel.c
servers/pm/Makefile
servers/pm/main.c
servers/pm/misc.c
servers/pm/mproc.h
servers/pm/proto.h
servers/pm/schedule.c [new file with mode: 0644]

index fb77ab070fecc2a1f7973a1dc0b2291ff4436e26..65b1481fbd351e9878b847aefd3177c394c3bf7a 100644 (file)
@@ -20,6 +20,7 @@
  *    0xC00 -  0xCFF   Virtual Memory (VM) requests
  *    0xD00 -  0xDFF   IPC server requests
  *    0xE00 -  0xEFF   Common system messages (e.g. system signals)
+ *    0xF00 -  0xFFF    Scheduling messages
  *   0x1000 - 0x10FF   Notify messages
  *
  * Zero and negative values are widely used for OK and error responses.
 #  define SYS_FORK       (KERNEL_CALL + 0)     /* sys_fork() */
 #  define SYS_EXEC       (KERNEL_CALL + 1)     /* sys_exec() */
 #  define SYS_CLEAR     (KERNEL_CALL + 2)      /* sys_clear() */
-#  define SYS_NICE       (KERNEL_CALL + 3)     /* sys_nice() */
+#  define SYS_SCHEDULE          (KERNEL_CALL + 3)      /* sys_schedule() */
 #  define SYS_PRIVCTL    (KERNEL_CALL + 4)     /* sys_privctl() */
 #  define SYS_TRACE      (KERNEL_CALL + 5)     /* sys_trace() */
 #  define SYS_KILL       (KERNEL_CALL + 6)     /* sys_kill() */
 #  define SYS_UPDATE    (KERNEL_CALL + 52)     /* sys_update() */
 #  define SYS_EXIT      (KERNEL_CALL + 53)     /* sys_exit() */
 
+#  define SYS_SCHEDCTL (KERNEL_CALL + 54)      /* sys_schedctl() */
+
 /* Total */
-#define NR_SYS_CALLS   54      /* number of system calls */ 
+#define NR_SYS_CALLS   55      /* number of system calls */
 
 #define SYS_CALL_MASK_SIZE BITMAP_CHUNKS(NR_SYS_CALLS)
 
 #      define SEMOP_OPS        m2_l1
 #      define SEMOP_SIZE       m2_i2
 
-#endif /* _MINIX_COM_H */ 
+/*===========================================================================*
+ *                Messages for Scheduling                                   *
+ *===========================================================================*/
+#define SCHEDULING_BASE        0xF00
+
+#define SCHEDULING_NO_QUANTUM  (SCHEDULING_BASE+1)
+#      define SCHEDULING_ENDPOINT      m1_i1
+#      define SCHEDULING_PRIORITY      m1_i2
+#      define SCHEDULING_QUANTUM       m1_i3
+#endif
+
+/* _MINIX_COM_H */
index 430eda042d7ec99d2eee5145dea6d81864475078..57972120bf1bfaebf56539dfec496bb1c93796c8 100644 (file)
@@ -44,6 +44,9 @@ _PROTOTYPE( int sys_clear, (endpoint_t proc_ep));
 _PROTOTYPE( int sys_exit, (void));
 _PROTOTYPE( int sys_trace, (int req, endpoint_t proc_ep, long addr, long *data_p));
 
+_PROTOTYPE( int sys_schedule, (endpoint_t proc_ep, char priority, char quantum));
+_PROTOTYPE( int sys_schedctl, (endpoint_t proc_ep));
+
 /* Shorthands for sys_runctl() system call. */
 #define sys_stop(proc_ep) sys_runctl(proc_ep, RC_STOP, 0)
 #define sys_delay_stop(proc_ep) sys_runctl(proc_ep, RC_STOP, RC_DELAY)
@@ -55,7 +58,6 @@ _PROTOTYPE( int sys_privctl, (endpoint_t proc_ep, int req, void *p));
 _PROTOTYPE( int sys_privquery_mem, (endpoint_t proc_ep,
        phys_bytes physstart, phys_bytes physlen));
 _PROTOTYPE( int sys_setgrant, (cp_grant_t *grants, int ngrants));
-_PROTOTYPE( int sys_nice, (endpoint_t proc_ep, int priority));
 
 _PROTOTYPE( int sys_int86, (struct reg86u *reg86p));
 _PROTOTYPE( int sys_vm_setbuf, (phys_bytes base, phys_bytes size,
index 5b99df26f8ed2f33d0e0e11f03dcef3aad382bc8..d8d5085091e3c0d692ee797e29f13d9844da4a33 100644 (file)
@@ -326,11 +326,12 @@ PRIVATE void printslot(struct proc *pp, const int level)
 
        COL
 
-       printf("%d: %s %d prio %d/%d time %d/%d cycles 0x%x%08x cr3 0x%lx rts %s misc %s",
+       printf("%d: %s %d prio %d time %d/%d cycles 0x%x%08x cr3 0x%lx rts %s misc %s sched %s",
                proc_nr(pp), pp->p_name, pp->p_endpoint, 
-               pp->p_priority, pp->p_max_priority, pp->p_user_time,
+               pp->p_priority, pp->p_user_time,
                pp->p_sys_time, pp->p_cycles.hi, pp->p_cycles.lo, pp->p_seg.p_cr3,
-               rtsflagstr(pp->p_rts_flags), miscflagstr(pp->p_misc_flags));
+               rtsflagstr(pp->p_rts_flags), miscflagstr(pp->p_misc_flags),
+               schedulerstr(pp->p_scheduler));
 
        if((dep = P_BLOCKEDON(pp)) != NONE) {
                printf(" blocked on: ");
index 950dadce66a13145eefa63b075cb1eebbfef7dba..b5c8539340e008f23578c825803786cd7b7ebb74 100644 (file)
@@ -32,6 +32,7 @@
 #include "kernel.h"
 #include "proc.h"
 #include <minix/endpoint.h>
+#include <assert.h>
 
 #include "clock.h"
 
@@ -57,18 +58,6 @@ PRIVATE clock_t next_timeout;        /* realtime that next timer expires */
  */
 PRIVATE clock_t realtime = 0;                /* real time clock */
 
-/*===========================================================================*
- *                             init_clock                                   *
- *===========================================================================*/
-PUBLIC void clock_init()
-{
-   
-       /* Set a watchdog timer to periodically balance the scheduling queues.
-          Side-effect sets new timer */
-
-       balance_queues(NULL);
-}
-
 /*
  * The boot processor timer interrupt handler. In addition to non-boot cpus it
  * keeps real time and notifies the clock task if need be
@@ -86,6 +75,7 @@ PUBLIC int bsp_timer_int_handler(void)
        realtime += ticks;
 
        ap_timer_int_handler();
+       assert(!proc_is_runnable(proc_ptr) || proc_ptr->p_ticks_left > 0);
 
        /* if a timer expired, notify the clock task */
        if ((next_timeout <= realtime)) {
@@ -214,7 +204,6 @@ PUBLIC int ap_timer_int_handler(void)
        }
        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
@@ -247,12 +236,8 @@ PUBLIC int ap_timer_int_handler(void)
        /* 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) {
-               /* this dequeues the process */
-               RTS_SET(p, RTS_NO_QUANTUM);
-       }
+       /* check if the processes still have some ticks left */
+       check_ticks_left(p);
 
        return 1;
 }
index 02834ea72eaa3986fb6e7060e905ce31d33216e4..e17465aa1c7e1ff4568f74674da496a302b5e680 100644 (file)
@@ -37,7 +37,6 @@
 #define USE_IRQCTL                1    /* set an interrupt policy */
 #define USE_SEGCTL                1    /* set up a remote segment */
 #define USE_PRIVCTL               1    /* system privileges control */
-#define USE_NICE          1    /* change scheduling priority */
 #define USE_UMAP                  1    /* map virtual to physical address */
 #define USE_VIRCOPY       1    /* copy using virtual addressing */ 
 #define USE_PHYSCOPY      1    /* copy using physical addressing */
index 32cc0f2ad912f05e8aabfc4eb77a7df1ce438284..ba9a33deab06df7f17093fcfce05d1a058af438b 100644 (file)
@@ -150,3 +150,14 @@ miscflagstr(const int flags)
        return str;
 }
 
+PUBLIC char *
+schedulerstr(struct proc *scheduler)
+{
+       if (scheduler != NULL)
+       {
+               return scheduler->p_name;
+       }
+
+       return "KERNEL";
+}
+
index 8000ea692857f2863f058bbd46ad41bb5f970d92..743825413c582cdf906032e8d68c6dd2b13510c7 100644 (file)
@@ -8,6 +8,7 @@
 
 /* Masks and flags for system calls. */
 #define NON_BLOCKING    0x0080  /* do not block if target not ready */
+#define FROM_KERNEL     0x0100  /* message from kernel on behalf of a process */
 
 #define WILLRECEIVE(target, source_ep) \
   ((RTS_ISSET(target, RTS_RECEIVING) && !RTS_ISSET(target, RTS_SENDING)) &&    \
index 84bd346c62d0014f5ac88d35f4f2d5e144393b8e..b2cd22c82f8d094e03284fd1c02e4dda26ea6c09 100644 (file)
@@ -79,7 +79,7 @@ PUBLIC void main()
        DEBUGMAX(("initializing %s... ", ip->proc_name));
        rp = proc_addr(ip->proc_nr);            /* get process pointer */
        ip->endpoint = rp->p_endpoint;          /* ipc endpoint */
-       rp->p_max_priority = ip->priority;      /* max scheduling priority */
+       rp->p_scheduler = NULL;                 /* no user space scheduler */
        rp->p_priority = ip->priority;          /* current priority */
        rp->p_quantum_size = ip->quantum;       /* quantum size in ticks */
        rp->p_ticks_left = ip->quantum;         /* current credit */
@@ -218,10 +218,6 @@ PUBLIC void main()
   DEBUGMAX(("system_init()... "));
   system_init();
   DEBUGMAX(("done\n"));
-  /* Initialize timers handling */
-  DEBUGMAX(("clock_init()... "));
-  clock_init();
-  DEBUGMAX(("done\n"));
 
 #if SPROFILE
   sprofiling = 0;      /* we're not profiling until instructed to */
index 361dd05ca2f305ff7c3e6a9d991b2fcacebbdc3b..78edbacd37667f843d439f8ad76ca04e66314b2c 100644 (file)
 
 /* Scheduling and message passing functions */
 FORWARD _PROTOTYPE( void idle, (void));
+/**
+ * Made public for use in clock.c (for user-space scheduling)
 FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst_e,
                message *m_ptr, int flags));
+*/
 FORWARD _PROTOTYPE( int mini_receive, (struct proc *caller_ptr, int src,
                message *m_ptr, int flags));
 FORWARD _PROTOTYPE( int mini_senda, (struct proc *caller_ptr,
@@ -54,7 +57,6 @@ FORWARD _PROTOTYPE( int deadlock, (int function,
 FORWARD _PROTOTYPE( int try_async, (struct proc *caller_ptr));
 FORWARD _PROTOTYPE( int try_one, (struct proc *src_ptr, struct proc *dst_ptr,
                int *postponed));
-FORWARD _PROTOTYPE( void sched, (struct proc *rp, int *queue, int *front));
 FORWARD _PROTOTYPE( struct proc * pick_proc, (void));
 FORWARD _PROTOTYPE( void enqueue_head, (struct proc *rp));
 
@@ -164,9 +166,16 @@ not_runnable_pick_new:
                if (proc_is_runnable(proc_ptr))
                        enqueue_head(proc_ptr);
        }
-       /* this enqueues the process again */
-       if (proc_no_quantum(proc_ptr))
+
+       /*
+        * If this process is scheduled by the kernel, we renew it's quantum
+        * and remove it's RTS_NO_QUANTUM flag.
+        */
+       if (proc_no_quantum(proc_ptr) && (proc_ptr->p_scheduler == NULL)) {
+               /* give new quantum */
+               proc_ptr->p_ticks_left = proc_ptr->p_quantum_size;
                RTS_UNSET(proc_ptr, RTS_NO_QUANTUM);
+       }
 
        /*
         * if we have no process to run, set IDLE as the current process for
@@ -187,6 +196,7 @@ check_misc_flags:
 
        assert(proc_ptr);
        assert(proc_is_runnable(proc_ptr));
+       assert(proc_ptr->p_ticks_left > 0);
        while (proc_ptr->p_misc_flags &
                (MF_KCALL_RESUME | MF_DELIVERMSG |
                 MF_SC_DEFER | MF_SC_TRACE | MF_SC_ACTIVE)) {
@@ -247,20 +257,26 @@ check_misc_flags:
                        break;
                }
 
-               /*
-                * the selected process might not be runnable anymore. We have
-                * to checkit and schedule another one
-                */
                if (!proc_is_runnable(proc_ptr))
-                       goto not_runnable_pick_new;
+                       break;
        }
+       /*
+        * After handling the misc flags the selected process might not be
+        * runnable anymore. We have to checkit and schedule another one
+        */
+       if (!proc_is_runnable(proc_ptr))
+               goto not_runnable_pick_new;
+
        TRACE(VF_SCHEDULING, printf("starting %s / %d\n",
                proc_ptr->p_name, proc_ptr->p_endpoint););
 #if DEBUG_TRACE
        proc_ptr->p_schedules++;
 #endif
 
+
        proc_ptr = arch_finish_schedcheck();
+       assert(proc_ptr->p_ticks_left > 0);
+
        cycles_accounting_stop(proc_addr(KERNEL));
 
        NOREC_RETURN(schedch, proc_ptr);
@@ -533,7 +549,7 @@ proc_nr_t src_dst;                          /* src or dst process */
 /*===========================================================================*
  *                             mini_send                                    * 
  *===========================================================================*/
-PRIVATE int mini_send(caller_ptr, dst_e, m_ptr, flags)
+PUBLIC int mini_send(caller_ptr, dst_e, m_ptr, flags)
 register struct proc *caller_ptr;      /* who is trying to send a message? */
 int dst_e;                             /* to whom is message being sent? */
 message *m_ptr;                                /* pointer to message buffer */
@@ -545,12 +561,23 @@ const int flags;
  */
   register struct proc *dst_ptr;
   register struct proc **xpp;
+  struct proc *msg_proc_ptr; /* Proc in which the message can be found */
   int dst_p;
   phys_bytes linaddr;
   vir_bytes addr;
   int r;
 
-  if(!(linaddr = umap_local(caller_ptr, D, (vir_bytes) m_ptr,
+  /* The kernel can send messages on behalf of other processes. In that case
+     we need to pass a pointer to the kernel, not the caller_ptr, to
+     umap_local as the kernel contains the message to be sent. */
+  if (flags & FROM_KERNEL) {
+       msg_proc_ptr = proc_addr(KERNEL);
+  }
+  else {
+       msg_proc_ptr = caller_ptr;
+  }
+
+  if(!(linaddr = umap_local(msg_proc_ptr, D, (vir_bytes) m_ptr,
        sizeof(message)))) {
        return EFAULT;
   }
@@ -1143,16 +1170,12 @@ PUBLIC void enqueue(
  * The mechanism is implemented here.   The actual scheduling policy is
  * defined in sched() and pick_proc().
  */
-  int q;                                       /* scheduling queue to use */
-  int front;                                   /* add to front or back */
+  int q = rp->p_priority;                      /* scheduling queue to use */
 
   NOREC_ENTER(enqueuefunc);
 
   assert(proc_is_runnable(rp));
 
-  /* Determine where to insert to process. */
-  sched(rp, &q, &front);
-
   assert(q >= 0);
 
   /* Now add the process to the queue. */
@@ -1160,10 +1183,6 @@ PUBLIC void enqueue(
       rdy_head[q] = rdy_tail[q] = rp;          /* create a new queue */
       rp->p_nextready = NULL;          /* mark new end */
   } 
-  else if (front) {                            /* add to head of queue */
-      rp->p_nextready = rdy_head[q];           /* chain head of queue */
-      rdy_head[q] = rp;                                /* set new queue head */
-  } 
   else {                                       /* add to tail of queue */
       rdy_tail[q]->p_nextready = rp;           /* chain tail of queue */       
       rdy_tail[q] = rp;                                /* set new queue tail */
@@ -1207,9 +1226,7 @@ PRIVATE void enqueue_head(struct proc *rp)
    * the process was runnable without its quantum expired when dequeued. A
    * process with no time left should vahe been handled else and differently
    */
-#if 0
-  assert(rp->p_ticks_left);
-#endif
+  assert(rp->p_ticks_left > 0);
 
   assert(q >= 0);
 
@@ -1274,39 +1291,6 @@ PUBLIC void dequeue(const struct proc *rp)
   NOREC_RETURN(dequeuefunc, );
 }
 
-/*===========================================================================*
- *                             sched                                        * 
- *===========================================================================*/
-PRIVATE void sched(rp, queue, front)
-register struct proc *rp;                      /* process to be scheduled */
-int *queue;                                    /* return: queue to use */
-int *front;                                    /* return: front or back */
-{
-/* This function determines the scheduling policy.  It is called whenever a
- * process must be added to one of the scheduling queues to decide where to
- * insert it.  As a side-effect the process' priority may be updated.  
- */
-  const int time_left = (rp->p_ticks_left > 0);        /* quantum fully consumed? */
-
-  /* Check whether the process has time left. Otherwise give a new quantum 
-   * and lower the process' priority, unless the process already is in the 
-   * lowest queue.  
-   */
-  if (! time_left) {                           /* quantum consumed ? */
-      rp->p_ticks_left = rp->p_quantum_size;   /* give new quantum */
-      if (rp->p_priority < (NR_SCHED_QUEUES-1)) {
-          rp->p_priority += 1;                 /* lower priority */
-      }
-  }
-
-  /* If there is time left, the process is added to the front of its queue, 
-   * so that it can immediately run. The queue to use simply is always the
-   * process' current priority. 
-   */
-  *queue = rp->p_priority;
-  *front = time_left;
-}
-
 /*===========================================================================*
  *                             pick_proc                                    * 
  *===========================================================================*/
@@ -1338,52 +1322,6 @@ PRIVATE struct proc * pick_proc(void)
   return NULL;
 }
 
-/*===========================================================================*
- *                             balance_queues                               *
- *===========================================================================*/
-#define Q_BALANCE_TICKS         100
-PUBLIC void balance_queues(
-  timer_t *tp                          /* watchdog timer pointer */
-)
-{
-/* Check entire process table and give all process a higher priority. This
- * effectively means giving a new quantum. If a process already is at its 
- * maximum priority, its quantum will be renewed.
- */
-  static timer_t queue_timer;                  /* timer structure to use */
-  register struct proc* rp;                    /* process table pointer  */
-  clock_t next_period;                         /* time of next period  */
-  int ticks_added = 0;                         /* total time added */
-
-  for (rp=BEG_PROC_ADDR; rp<END_PROC_ADDR; rp++) {
-      if (! isemptyp(rp)) {                            /* check slot use */
-         if (rp->p_priority > rp->p_max_priority) {    /* update priority? */
-             int paused = 0;
-             if (proc_is_runnable(rp)) {
-               RTS_SET(rp, RTS_PROC_STOP);             /* take off queue */
-               paused = 1;
-             }
-             ticks_added += rp->p_quantum_size;        /* do accounting */
-             rp->p_priority -= 1;                      /* raise priority */
-             if(paused) {
-               RTS_UNSET(rp, RTS_PROC_STOP);
-               assert(proc_is_runnable(rp));
-             }
-         }
-         else {
-             ticks_added += rp->p_quantum_size - rp->p_ticks_left;
-              rp->p_ticks_left = rp->p_quantum_size;   /* give new quantum */
-         }
-      }
-  }
-
-  /* Now schedule a new watchdog timer to balance the queues again.  The 
-   * period depends on the total amount of quantum ticks added.
-   */
-  next_period = MAX(Q_BALANCE_TICKS, ticks_added);     /* calculate next */
-  set_timer(&queue_timer, get_uptime() + next_period, balance_queues);
-}
-
 /*===========================================================================*
  *                             endpoint_lookup                              *
  *===========================================================================*/
@@ -1447,3 +1385,51 @@ const int fatalflag;
        return ok;
 }
 
+PRIVATE void notify_scheduler(struct proc *p)
+{
+       /* dequeue the process */
+       RTS_SET(p, RTS_NO_QUANTUM);
+       /*
+        * Notify the process's scheduler that it has run out of
+        * quantum. This is done by sending a message to the scheduler
+        * on the process's behalf
+        */
+       if (p->p_scheduler == p) {
+               /*
+                * If a scheduler is scheduling itself, and runs out of
+                * quantum, we don't send a message. The RTS_NO_QUANTUM
+                * flag will be removed by schedcheck in proc.c.
+                */
+       }
+       else if (p->p_scheduler != NULL) {
+               message m_no_quantum;
+               int err;
+
+               m_no_quantum.m_source = p->p_endpoint;
+               m_no_quantum.m_type   = SCHEDULING_NO_QUANTUM;
+
+               if ((err = mini_send(p, p->p_scheduler->p_endpoint,
+                                               &m_no_quantum, FROM_KERNEL))) {
+                       panic("WARNING: Scheduling: mini_send returned %d\n", err);
+               }
+       }
+}
+
+PUBLIC void check_ticks_left(struct proc * p)
+{
+       if (p->p_ticks_left <= 0) {
+               p->p_ticks_left = 0;
+               if (priv(p)->s_flags & PREEMPTIBLE) {
+                       /* this dequeues the process */
+                       notify_scheduler(p);
+               }
+               else {
+                       /*
+                        * non-preemptible processes only need their quantum to
+                        * be renewed. In fact, they by pass scheduling
+                        */
+                       p->p_ticks_left = p->p_quantum_size;
+               }
+       }
+}
+
index ee37a218ccd339f7eabd599e3bdca1ae2a13c040..3de17826916a97f12da3c28688f806241783f36f 100644 (file)
@@ -25,10 +25,10 @@ struct proc {
   short p_rts_flags;           /* process is runnable only if zero */
   short p_misc_flags;          /* flags that do not suspend the process */
 
-  char p_priority;             /* current scheduling priority */
-  char p_max_priority;         /* maximum scheduling priority */
+  char p_priority;             /* current process priority */
   char p_ticks_left;           /* number of scheduling ticks left */
   char p_quantum_size;         /* quantum size in ticks */
+  struct proc *p_scheduler;    /* who should get out of quantum msg */
 
   struct mem_map p_memmap[NR_LOCAL_SEGS];   /* memory map (T, D, S) */
   struct pagefault p_pagefault;        /* valid if PAGEFAULT in p_rts_flags set */
@@ -224,8 +224,7 @@ struct proc {
 
 /* Scheduling priorities for p_priority. Values must start at zero (highest
  * priority) and increment.  Priorities of the processes in the boot image 
- * can be set in table.c. IDLE must have a queue for itself, to prevent low 
- * priority user processes to run round-robin with IDLE. 
+ * can be set in table.c.
  */
 #define NR_SCHED_QUEUES   16   /* MUST equal minimum priority + 1 */
 #define TASK_Q            0    /* highest, used for kernel tasks */
@@ -259,6 +258,9 @@ 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, int dst_e,
+               message *m_ptr, int flags));
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* PROC_H */
index 858f5bc6f90543bf665367bba0e24d1b4955657a..12355fbd28335cb4bfe5a3ffedc9e51a421b59fb 100644 (file)
@@ -12,7 +12,6 @@ struct proc;
 struct timer;
 
 /* clock.c */
-_PROTOTYPE( void clock_init, (void) );
 _PROTOTYPE( clock_t get_uptime, (void)                                 );
 _PROTOTYPE( void set_timer, (struct timer *tp, clock_t t, tmr_func_t f)        );
 _PROTOTYPE( void reset_timer, (struct timer *tp)                       );
@@ -41,7 +40,6 @@ _PROTOTYPE( int do_ipc, (int call_nr, int src_dst,
 _PROTOTYPE( int mini_notify, (const struct proc *src, endpoint_t dst)  );
 _PROTOTYPE( void enqueue, (struct proc *rp)                            );
 _PROTOTYPE( void dequeue, (const struct proc *rp)                      );
-_PROTOTYPE( void balance_queues, (struct timer *tp)                    );
 _PROTOTYPE( struct proc * schedcheck, (void)                           );
 _PROTOTYPE( struct proc * arch_finish_schedcheck, (void)               );
 _PROTOTYPE( struct proc *endpoint_lookup, (endpoint_t ep)              );
@@ -52,6 +50,7 @@ _PROTOTYPE( int isokendpt_f, (const char *file, int line, endpoint_t e, int *p,
 _PROTOTYPE( int isokendpt_f, (endpoint_t e, int *p, int f)             );
 #define isokendpt_d(e, p, f) isokendpt_f((e), (p), (f))
 #endif
+_PROTOTYPE( void check_ticks_left, (struct proc *p));
 
 /* start.c */
 _PROTOTYPE( void cstart, (U16_t cs, U16_t ds, U16_t mds,
@@ -91,6 +90,7 @@ _PROTOTYPE( int disable_irq, (const irq_hook_t *hook)                 );
 _PROTOTYPE( int runqueues_ok, (void) );
 _PROTOTYPE( char *rtsflagstr, (int flags) );
 _PROTOTYPE( char *miscflagstr, (int flags) );
+_PROTOTYPE( char *schedulerstr, (struct proc *scheduler) );
 
 /* system/do_safemap.c */
 _PROTOTYPE( int map_invoke_vm, (struct proc * caller, int req_type,
index 2535022b4bc9c94fb22e007b54e4445d37486f4e..e2fa72d49bc9d71897882cb919f3fbee1bc1e41f 100644 (file)
@@ -179,7 +179,6 @@ PUBLIC void system_init(void)
   map(SYS_EXEC, do_exec);              /* update process after execute */
   map(SYS_CLEAR, do_clear);            /* clean up after process exit */
   map(SYS_EXIT, do_exit);              /* a system process wants to exit */
-  map(SYS_NICE, do_nice);              /* set scheduling priority */
   map(SYS_PRIVCTL, do_privctl);                /* system privileges control */
   map(SYS_TRACE, do_trace);            /* request a trace operation */
   map(SYS_SETGRANT, do_setgrant);      /* get/set own parameters */
@@ -244,8 +243,12 @@ PUBLIC void system_init(void)
   map(SYS_SETMCONTEXT, do_setmcontext); /* set machine context */
   map(SYS_GETMCONTEXT, do_getmcontext); /* get machine context */
 #endif
-}
 
+  /* Scheduling */
+  map(SYS_SCHEDULE, do_schedule);      /* reschedule a process */
+  map(SYS_SCHEDCTL, do_schedctl);      /* change process scheduler */
+
+}
 /*===========================================================================*
  *                             get_priv                                     *
  *===========================================================================*/
index 8c31c504f7490a6b515cbcc930292e39866f2c95..c85420e6cdd2d9d8d2fff9d9521f34105a5f2b6c 100644 (file)
@@ -64,11 +64,6 @@ _PROTOTYPE( int do_trace, (struct proc * caller, message *m_ptr) );
 #define do_trace do_unused
 #endif
 
-_PROTOTYPE( int do_nice, (struct proc * caller, message *m_ptr) );
-#if ! USE_NICE
-#define do_nice do_unused
-#endif
-
 _PROTOTYPE( int do_runctl, (struct proc * caller, message *m_ptr) );
 #if ! USE_RUNCTL
 #define do_runctl do_unused
@@ -210,5 +205,8 @@ _PROTOTYPE( int do_setmcontext, (struct proc * caller, message *m_ptr) );
 #define do_setmcontext do_unused
 #endif
 
+_PROTOTYPE( int do_schedule,    (struct proc * caller, message *m_ptr) );
+_PROTOTYPE( int do_schedctl, (struct proc * caller, message *m_ptr) );
+
 #endif /* SYSTEM_H */
 
index 0269471591cdda7ae0272b0c28a0e91fffba0227..a8183368cc173012a34678f1acf2236c6dd147b5 100644 (file)
@@ -30,7 +30,6 @@ OBJECTS       = \
        do_clear.o \
        do_exit.o \
        do_trace.o \
-       do_nice.o \
        do_runctl.o \
        do_update.o \
        do_times.o \
@@ -60,7 +59,9 @@ OBJECTS       = \
        do_cprofile.o \
        do_profbuf.o \
        do_vmctl.o \
-       do_mcontext.o
+       do_mcontext.o \
+       do_schedule.o \
+       do_schedctl.o
 
 build $(SYSTEM):       $(SYSTEM)($(OBJECTS))
        aal cr $@ *.o
index 0eaccf3b636201c5283a5b141468920a17de6208..1066473ddb473c48f39f66c59cf30824ed88a15d 100644 (file)
@@ -82,11 +82,24 @@ PUBLIC int do_fork(struct proc * caller, message * m_ptr)
 
   /* Parent and child have to share the quantum that the forked process had,
    * so that queued processes do not have to wait longer because of the fork.
-   * If the time left is odd, the child gets an extra tick.
    */
-  rpc->p_ticks_left = (rpc->p_ticks_left + 1) / 2;
+
+  /*
+   * we want to avoid having processes that loose their quantum without going
+   * through the standard path where the "out of quantum" is handled. We add
+   * some more time to such processes.
+   *
+   * This is a temporary solution until we are able to handle this in the
+   * userspace
+   */
+  if (rpp->p_ticks_left < 2)
+         rpp->p_ticks_left = 2;
+
+  rpc->p_ticks_left =  rpp->p_ticks_left / 2;
   rpp->p_ticks_left =  rpp->p_ticks_left / 2;  
 
+  assert(rpc->p_ticks_left > 0 && rpp->p_ticks_left > 0);
+
   /* If the parent is a privileged process, take away the privileges from the 
    * child process and inhibit it from running by setting the NO_PRIV flag.
    * The caller should explicitely set the new privileges before executing.
diff --git a/kernel/system/do_schedctl.c b/kernel/system/do_schedctl.c
new file mode 100644 (file)
index 0000000..12029f5
--- /dev/null
@@ -0,0 +1,21 @@
+#include "../system.h"
+#include <signal.h>
+#include <sys/sigcontext.h>
+#include <minix/endpoint.h>
+
+/*===========================================================================*
+ *                               do_schedctl                        *
+ *===========================================================================*/
+PUBLIC int do_schedctl(struct proc * caller, message * m_ptr)
+{
+       struct proc *p;
+
+       /* Only system processes can change process schedulers */
+       if (! (priv(caller)->s_flags & SYS_PROC))
+               return(EPERM);
+
+       p = proc_addr(_ENDPOINT_P(m_ptr->SCHEDULING_ENDPOINT));
+       p->p_scheduler = caller;
+
+       return(OK);
+}
diff --git a/kernel/system/do_schedule.c b/kernel/system/do_schedule.c
new file mode 100644 (file)
index 0000000..04421f4
--- /dev/null
@@ -0,0 +1,42 @@
+#include "../system.h"
+#include <signal.h>
+#include <sys/sigcontext.h>
+#include <minix/endpoint.h>
+
+/*===========================================================================*
+ *                             do_schedule                                  *
+ *===========================================================================*/
+PUBLIC int do_schedule(struct proc * caller, message * m_ptr)
+{
+       struct proc *p;
+
+       p = proc_addr(_ENDPOINT_P(m_ptr->SCHEDULING_ENDPOINT));
+
+       /* Make sure the priority number given is within the allowed range.*/
+       if (m_ptr->SCHEDULING_PRIORITY < TASK_Q ||
+               m_ptr->SCHEDULING_PRIORITY > NR_SCHED_QUEUES)
+               return(EINVAL);
+
+       /* Only system processes can schedule processes */
+       if (! (priv(caller)->s_flags & SYS_PROC))
+               return(EPERM);
+
+       /* Only this process' scheduler can schedule it */
+       if (caller != p->p_scheduler)
+               return(EPERM);
+
+       /* In some cases, we might be rescheduling a runnable process. In such
+        * a case (i.e. if we are updating the priority) we set the NO_QUANTUM
+        * flag before the generic unset to dequeue/enqueue the process
+        */
+       if (proc_is_runnable(p))
+               RTS_SET(p, RTS_NO_QUANTUM);
+
+       /* Clear the scheduling bit and enqueue the process */
+       p->p_priority = m_ptr->SCHEDULING_PRIORITY;
+       p->p_ticks_left = m_ptr->SCHEDULING_QUANTUM;
+
+       RTS_UNSET(p, RTS_NO_QUANTUM);
+
+       return(OK);
+}
index 0965e732a8b86661b773aa9db8b8afd75c38f52b..321d433bbd8b617ed597e509c53f5aaf16d85289 100644 (file)
@@ -50,7 +50,6 @@ SRCS=  \
        sys_kill.c \
        sys_memset.c \
        sys_newmap.c \
-       sys_nice.c \
        sys_out.c \
        sys_physcopy.c \
        sys_readbios.c \
@@ -70,6 +69,8 @@ SRCS=  \
        sys_setgrant.c \
        sys_sprof.c \
        sys_stime.c \
+        sys_schedule.c \
+        sys_schedctl.c \
        sys_times.c \
        sys_trace.c \
        sys_umap.c \
diff --git a/lib/libsys/sys_nice.c b/lib/libsys/sys_nice.c
deleted file mode 100644 (file)
index d90c53c..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "syslib.h"
-
-/*===========================================================================*
- *                                sys_nice                                  *
- *===========================================================================*/
-PUBLIC int sys_nice(endpoint_t proc_ep, int prio)
-{
-  message m;
-
-  m.PR_ENDPT = proc_ep;
-  m.PR_PRIORITY = prio;
-  return(_kernel_call(SYS_NICE, &m));
-}
diff --git a/lib/libsys/sys_schedctl.c b/lib/libsys/sys_schedctl.c
new file mode 100644 (file)
index 0000000..b60e0f9
--- /dev/null
@@ -0,0 +1,9 @@
+#include "syslib.h"
+
+PUBLIC int sys_schedctl(endpoint_t proc_ep)
+{
+       message m;
+
+       m.SCHEDULING_ENDPOINT = proc_ep;
+       return(_kernel_call(SYS_SCHEDCTL, &m));
+}
\ No newline at end of file
diff --git a/lib/libsys/sys_schedule.c b/lib/libsys/sys_schedule.c
new file mode 100644 (file)
index 0000000..c6d5dae
--- /dev/null
@@ -0,0 +1,11 @@
+#include "syslib.h"
+
+PUBLIC int sys_schedule(endpoint_t proc_ep, char priority, char quantum)
+{
+       message m;
+
+       m.SCHEDULING_ENDPOINT = proc_ep;
+       m.SCHEDULING_PRIORITY = priority;
+       m.SCHEDULING_QUANTUM  = quantum;
+       return(_kernel_call(SYS_SCHEDULE, &m));
+}
index b0abd65bc61fb94f4cc0e5ef36275abda03e8147..642747b05b3ae560486d499d56d9901ed4d65a63 100644 (file)
@@ -398,9 +398,9 @@ PUBLIC void proctab_dmp()
        size = rp->p_memmap[T].mem_len
                + ((rp->p_memmap[S].mem_phys + rp->p_memmap[S].mem_len) - data);
        printf(" %5d %10d ", _ENDPOINT_G(rp->p_endpoint), rp->p_endpoint);
-       printf("%-8.8s %02u/%02u %02d/%02u %6lu %6lu ",
+       printf("%-8.8s %02u %02d/%02u %6lu %6lu ",
               rp->p_name,
-              rp->p_priority, rp->p_max_priority,
+              rp->p_priority,
               rp->p_ticks_left, rp->p_quantum_size, 
               rp->p_user_time, rp->p_sys_time);
        PRINTRTS(rp);
index bdf1aab60ef7dfec1c9b0990aa92dc0b950beb67..33b8ba0ab89afffacc431776a1ca745b8df02fa3 100644 (file)
@@ -2,7 +2,7 @@
 PROG=  pm
 SRCS=  main.c forkexit.c break.c exec.c time.c timers.c alarm.c \
        signal.c utility.c table.c trace.c getset.c misc.c \
-       profile.c dma.c mcontext.c
+       profile.c dma.c mcontext.c schedule.c
 
 DPADD+=        ${LIBSYS} ${LIBTIMERS}
 LDADD+=        -lsys -ltimers
index a11480338ff28394f79b80b9797e90f837a40869..2f7a6c84a8397d3132a81797759d6e3020b38a5b 100644 (file)
@@ -66,6 +66,7 @@ PUBLIC int main()
 
   /* SEF local startup. */
   sef_local_startup();
+  overtake_scheduling();       /* overtake all running processes */
 
   /* This is PM's main loop-  get work and do it, forever and forever. */
   while (TRUE) {
@@ -113,6 +114,10 @@ PUBLIC int main()
                else
                        result= ENOSYS;
                break;
+       case SCHEDULING_NO_QUANTUM:
+               /* This message was sent from the kernel, don't reply */
+               do_noquantum();
+               continue;
        default:
                /* Else, if the system call number is valid, perform the
                 * call.
@@ -254,6 +259,11 @@ PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info)
                /* Get kernel endpoint identifier. */
                rmp->mp_endpoint = ip->endpoint;
 
+               /* Get scheduling info */
+               rmp->mp_max_priority = ip->priority;
+               rmp->mp_priority = ip->priority;
+               rmp->mp_time_slice = ip->quantum;
+
                /* Tell FS about this system process. */
                mess.m_type = PM_INIT;
                mess.PM_SLOT = ip->proc_nr;
index e4c023f251e006e4a551c1c9e91bba9be602537e..7a5d60ab7d38ae6de74edfcea3646c4795dadf36 100644 (file)
@@ -407,7 +407,7 @@ PUBLIC int do_reboot()
  *===========================================================================*/
 PUBLIC int do_getsetpriority()
 {
-       int r, arg_which, arg_who, arg_pri;
+       int r, arg_which, arg_who, arg_pri, new_q;
        struct mproc *rmp;
 
        arg_which = m_in.m1_i1;
@@ -439,8 +439,25 @@ PUBLIC int do_getsetpriority()
        if (rmp->mp_nice > arg_pri && mp->mp_effuid != SUPER_USER)
                return(EACCES);
        
-       /* We're SET, and it's allowed. */
-       if ((r = sys_nice(rmp->mp_endpoint, arg_pri)) != OK)
+       /* We're SET, and it's allowed.
+        *
+        * The value passed in is currently between PRIO_MIN and PRIO_MAX.
+        * We have to scale this between MIN_USER_Q and MAX_USER_Q to match
+        * the kernel's scheduling queues.
+        *
+        * TODO: This assumes that we are the scheduler, this will be changed
+        *       once the scheduler gets factored out of PM to its own server
+        */
+       if (arg_pri < PRIO_MIN || arg_pri > PRIO_MAX) return(EINVAL);
+
+       new_q = MAX_USER_Q + (arg_pri-PRIO_MIN) * (MIN_USER_Q-MAX_USER_Q+1) /
+           (PRIO_MAX-PRIO_MIN+1);
+       if (new_q < MAX_USER_Q) new_q = MAX_USER_Q;     /* shouldn't happen */
+       if (new_q > MIN_USER_Q) new_q = MIN_USER_Q;     /* shouldn't happen */
+
+       rmp->mp_max_priority = rmp->mp_priority = new_q;
+       if ((r = sys_schedule(rmp->mp_priority, rmp->mp_priority,
+               rmp->mp_time_slice)) != OK)
                return(r);
 
        rmp->mp_nice = arg_pri;
index ae614b2282d05ee1821c4d85c0f6a9de098f9948..358e93e503ed0e8734964226c8e44af390033728 100644 (file)
@@ -55,6 +55,11 @@ EXTERN struct mproc {
   /* Scheduling priority. */
   signed int mp_nice;          /* nice is PRIO_MIN..PRIO_MAX, standard 0. */
 
+  /* User space scheduling */
+  int mp_max_priority;         /* this process' highest allowed priority */
+  int mp_priority;             /* the process' current priority */
+  int mp_time_slice;           /* this process's scheduling queue */
+
   char mp_name[PROC_NAME_LEN]; /* process name */
 } mproc[NR_PROCS];
 
index 8ff58eac392483e41a099819c69dd598c3b49cf4..702f405cfa0b3e1dbc64b555b2d5a4df004e6e32 100644 (file)
@@ -60,6 +60,11 @@ _PROTOTYPE( int do_getepinfo, (void)                                 );
 _PROTOTYPE( int do_svrctl, (void)                                      );
 _PROTOTYPE( int do_getsetpriority, (void)                              );
 
+/* schedule.c */
+_PROTOTYPE( void do_noquantum, (void)                                  );
+_PROTOTYPE( void overtake_scheduling, (void)                           );
+_PROTOTYPE( void balance_queues, (struct timer *tp)                    );
+
 /* profile.c */
 _PROTOTYPE( int do_sprofile, (void)                                    );
 _PROTOTYPE( int do_cprofile, (void)                                    );
diff --git a/servers/pm/schedule.c b/servers/pm/schedule.c
new file mode 100644 (file)
index 0000000..77e84a8
--- /dev/null
@@ -0,0 +1,91 @@
+#include "pm.h"
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <minix/config.h>
+#include <minix/sysinfo.h>
+#include <minix/type.h>
+#include <machine/archtypes.h>
+#include <lib.h>
+#include "mproc.h"
+#include "../../kernel/proc.h" /* for MIN_USER_Q */
+
+PRIVATE timer_t sched_timer;
+
+/*===========================================================================*
+ *                             do_noquantum                                 *
+ *===========================================================================*/
+
+PUBLIC void do_noquantum(void)
+{
+       int rv, proc_nr_n;
+       register struct mproc *rmp;
+
+       if (pm_isokendpt(m_in.m_source, &proc_nr_n) != OK) {
+               printf("PM: WARNING: got an invalid endpoint in OOQ msg %u.\n",
+               m_in.m_source);
+               return;
+       }
+
+       rmp = &mproc[proc_nr_n];
+       if (rmp->mp_priority < MIN_USER_Q) {
+               rmp->mp_priority += 1; /* lower priority */
+       }
+
+       if ((rv = sys_schedule(rmp->mp_endpoint, rmp->mp_priority,
+               rmp->mp_time_slice))) {
+               printf("PM: An error occurred when trying to schedule %s: %d\n",
+               rmp->mp_name, rv);
+       }
+}
+
+/*===========================================================================*
+ *                             overtake_scheduling                          *
+ *===========================================================================*/
+PUBLIC void overtake_scheduling(void)
+{
+       struct mproc *trmp;
+       int proc_nr;
+
+       tmr_inittimer(&sched_timer);
+
+       for (proc_nr=0, trmp=mproc; proc_nr < NR_PROCS; proc_nr++, trmp++) {
+               /* Don't overtake system processes. When the system starts,
+                * this will typically only overtake init, from which other
+                * user space processes will inherit. */
+               if (trmp->mp_flags & IN_USE && !(trmp->mp_flags & PRIV_PROC)) {
+                       if (sys_schedctl(trmp->mp_endpoint))
+                               printf("PM: Error while overtaking scheduling for %s\n",
+                                       trmp->mp_name);
+               }
+       }
+
+       pm_set_timer(&sched_timer, 100, balance_queues, 0);
+}
+
+/*===========================================================================*
+ *                             balance_queues                               *
+ *===========================================================================*/
+
+PUBLIC void balance_queues(tp)
+struct timer *tp;
+{
+       struct mproc *rmp;
+       int proc_nr;
+       int rv;
+
+       for (proc_nr=0, rmp=mproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {
+               if (rmp->mp_flags & IN_USE) {
+                       if (rmp->mp_priority > rmp->mp_max_priority) {
+                               rmp->mp_priority -= 1; /* increase priority */
+                               if ((rv = sys_schedule(rmp->mp_endpoint,
+                                       rmp->mp_priority,
+                                       rmp->mp_time_slice))) {
+                                       printf("PM: An error occurred when balancing %s: %d\n",
+                                       rmp->mp_name, rv);
+                               }
+                       }
+               }
+       }
+
+       pm_set_timer(&sched_timer, 100, balance_queues, 0);
+}