* 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 */
_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)
_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,
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: ");
#include "kernel.h"
#include "proc.h"
#include <minix/endpoint.h>
+#include <assert.h>
#include "clock.h"
*/
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
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)) {
}
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
/* 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;
}
#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 */
return str;
}
+PUBLIC char *
+schedulerstr(struct proc *scheduler)
+{
+ if (scheduler != NULL)
+ {
+ return scheduler->p_name;
+ }
+
+ return "KERNEL";
+}
+
/* 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)) && \
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 */
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 */
/* 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,
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));
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
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)) {
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);
/*===========================================================================*
* 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 */
*/
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;
}
* 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. */
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 */
* 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);
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 *
*===========================================================================*/
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 *
*===========================================================================*/
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;
+ }
+ }
+}
+
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 */
/* 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 */
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 */
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) );
_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) );
_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,
_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,
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 */
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 *
*===========================================================================*/
#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
#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 */
do_clear.o \
do_exit.o \
do_trace.o \
- do_nice.o \
do_runctl.o \
do_update.o \
do_times.o \
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
/* 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.
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
sys_kill.c \
sys_memset.c \
sys_newmap.c \
- sys_nice.c \
sys_out.c \
sys_physcopy.c \
sys_readbios.c \
sys_setgrant.c \
sys_sprof.c \
sys_stime.c \
+ sys_schedule.c \
+ sys_schedctl.c \
sys_times.c \
sys_trace.c \
sys_umap.c \
+++ /dev/null
-#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));
-}
--- /dev/null
+#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
--- /dev/null
+#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));
+}
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);
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
/* 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) {
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.
/* 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;
*===========================================================================*/
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;
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;
/* 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];
_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) );
--- /dev/null
+#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);
+}