proc.c

Go to the documentation of this file.
00001 /* This file contains essentially all of the process and message handling.
00002  * Together with "mpx.s" it forms the lowest layer of the MINIX kernel.
00003  * There is one entry point from the outside:
00004  *
00005  *   sys_call:        a system call, i.e., the kernel is trapped with an INT
00006  *
00007  * As well as several entry points used from the interrupt and task level:
00008  *
00009  *   lock_notify:     notify a process of a system event
00010  *   lock_send:       send a message to a process
00011  *   lock_enqueue:    put a process on one of the scheduling queues 
00012  *   lock_dequeue:    remove a process from the scheduling queues
00013  *
00014  * Changes:
00015  *   Aug 19, 2005     rewrote scheduling code  (Jorrit N. Herder)
00016  *   Jul 25, 2005     rewrote system call handling  (Jorrit N. Herder)
00017  *   May 26, 2005     rewrote message passing functions  (Jorrit N. Herder)
00018  *   May 24, 2005     new notification system call  (Jorrit N. Herder)
00019  *   Oct 28, 2004     nonblocking send and receive calls  (Jorrit N. Herder)
00020  *
00021  * The code here is critical to make everything work and is important for the
00022  * overall performance of the system. A large fraction of the code deals with
00023  * list manipulation. To make this both easy to understand and fast to execute 
00024  * pointer pointers are used throughout the code. Pointer pointers prevent
00025  * exceptions for the head or tail of a linked list. 
00026  *
00027  *  node_t *queue, *new_node;   // assume these as global variables
00028  *  node_t **xpp = &queue;      // get pointer pointer to head of queue 
00029  *  while (*xpp != NULL)        // find last pointer of the linked list
00030  *      xpp = &(*xpp)->next;    // get pointer to next pointer 
00031  *  *xpp = new_node;            // now replace the end (the NULL pointer) 
00032  *  new_node->next = NULL;      // and mark the new end of the list
00033  * 
00034  * For example, when adding a new node to the end of the list, one normally 
00035  * makes an exception for an empty list and looks up the end of the list for 
00036  * nonempty lists. As shown above, this is not required with pointer pointers.
00037  */
00038 
00039 #include <minix/com.h>
00040 #include <minix/callnr.h>
00041 #include <minix/endpoint.h>
00042 #include "debug.h"
00043 #include "kernel.h"
00044 #include "proc.h"
00045 #include <signal.h>
00046 
00047 /* Scheduling and message passing functions. The functions are available to 
00048  * other parts of the kernel through lock_...(). The lock temporarily disables 
00049  * interrupts to prevent race conditions. 
00050  */
00051 FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst_e,
00052                 message *m_ptr, unsigned flags));
00053 FORWARD _PROTOTYPE( int mini_receive, (struct proc *caller_ptr, int src,
00054                 message *m_ptr, unsigned flags));
00055 FORWARD _PROTOTYPE( int mini_notify, (struct proc *caller_ptr, int dst));
00056 FORWARD _PROTOTYPE( int deadlock, (int function,
00057                 register struct proc *caller, int src_dst));
00058 FORWARD _PROTOTYPE( void enqueue, (struct proc *rp));
00059 FORWARD _PROTOTYPE( void dequeue, (struct proc *rp));
00060 FORWARD _PROTOTYPE( void sched, (struct proc *rp, int *queue, int *front));
00061 FORWARD _PROTOTYPE( void pick_proc, (void));
00062 
00063 #define BuildMess(m_ptr, src, dst_ptr) \
00064         (m_ptr)->m_source = proc_addr(src)->p_endpoint;         \
00065         (m_ptr)->m_type = NOTIFY_FROM(src);                             \
00066         (m_ptr)->NOTIFY_TIMESTAMP = get_uptime();                       \
00067         switch (src) {                                                  \
00068         case HARDWARE:                                                  \
00069                 (m_ptr)->NOTIFY_ARG = priv(dst_ptr)->s_int_pending;     \
00070                 priv(dst_ptr)->s_int_pending = 0;                       \
00071                 break;                                                  \
00072         case SYSTEM:                                                    \
00073                 (m_ptr)->NOTIFY_ARG = priv(dst_ptr)->s_sig_pending;     \
00074                 priv(dst_ptr)->s_sig_pending = 0;                       \
00075                 break;                                                  \
00076         }
00077 
00078 #if (CHIP == INTEL)
00079 #define CopyMess(s,sp,sm,dp,dm) \
00080         cp_mess(proc_addr(s)->p_endpoint, \
00081                 (sp)->p_memmap[D].mem_phys,     \
00082                 (vir_bytes)sm, (dp)->p_memmap[D].mem_phys, (vir_bytes)dm)
00083 #endif /* (CHIP == INTEL) */
00084 
00085 #if (CHIP == M68000)
00086 /* M68000 does not have cp_mess() in assembly like INTEL. Declare prototype
00087  * for cp_mess() here and define the function below. Also define CopyMess. 
00088  */
00089 #endif /* (CHIP == M68000) */
00090 
00091 /*===========================================================================*
00092  *                              sys_call                                     * 
00093  *===========================================================================*/
00094 PUBLIC int sys_call(call_nr, src_dst_e, m_ptr, bit_map)
00095 int call_nr;                    /* system call number and flags */
00096 int src_dst_e;                  /* src to receive from or dst to send to */
00097 message *m_ptr;                 /* pointer to message in the caller's space */
00098 long bit_map;                   /* notification event set or flags */
00099 {
00100 /* System calls are done by trapping to the kernel with an INT instruction.
00101  * The trap is caught and sys_call() is called to send or receive a message
00102  * (or both). The caller is always given by 'proc_ptr'.
00103  */
00104   register struct proc *caller_ptr = proc_ptr;  /* get pointer to caller */
00105   int function = call_nr & SYSCALL_FUNC;        /* get system call function */
00106   unsigned flags = call_nr & SYSCALL_FLAGS;     /* get flags */
00107   int mask_entry;                               /* bit to check in send mask */
00108   int group_size;                               /* used for deadlock check */
00109   int result;                                   /* the system call's result */
00110   int src_dst;
00111   vir_clicks vlo, vhi;          /* virtual clicks containing message to send */
00112 
00113 #if 0
00114   if (caller_ptr->p_rts_flags & SLOT_FREE)
00115   {
00116         kprintf("called by the dead?!?\n");
00117         return EINVAL;
00118   }
00119 #endif
00120   
00121   /* Require a valid source and/ or destination process, unless echoing. */
00122   if (src_dst_e != ANY && function != ECHO) {
00123       if(!isokendpt(src_dst_e, &src_dst)) {
00124 #if DEBUG_ENABLE_IPC_WARNINGS
00125           kprintf("sys_call: trap %d by %d with bad endpoint %d\n", 
00126               function, proc_nr(caller_ptr), src_dst_e);
00127 #endif
00128           return EDEADSRCDST;
00129       }
00130   } else src_dst = src_dst_e;
00131 
00132   /* Check if the process has privileges for the requested call. Calls to the 
00133    * kernel may only be SENDREC, because tasks always reply and may not block 
00134    * if the caller doesn't do receive(). 
00135    */
00136   if (! (priv(caller_ptr)->s_trap_mask & (1 << function)) || 
00137           (iskerneln(src_dst) && function != SENDREC
00138            && function != RECEIVE)) {
00139 #if DEBUG_ENABLE_IPC_WARNINGS
00140       kprintf("sys_call: trap %d not allowed, caller %d, src_dst %d\n", 
00141           function, proc_nr(caller_ptr), src_dst);
00142 #endif
00143       return(ETRAPDENIED);              /* trap denied by mask or kernel */
00144   }
00145 
00146   /* If the call involves a message buffer, i.e., for SEND, RECEIVE, SENDREC, 
00147    * or ECHO, check the message pointer. This check allows a message to be 
00148    * anywhere in data or stack or gap. It will have to be made more elaborate 
00149    * for machines which don't have the gap mapped. 
00150    */
00151   if (function & CHECK_PTR) {
00152       vlo = (vir_bytes) m_ptr >> CLICK_SHIFT;           
00153       vhi = ((vir_bytes) m_ptr + MESS_SIZE - 1) >> CLICK_SHIFT;
00154       if (vlo < caller_ptr->p_memmap[D].mem_vir || vlo > vhi ||
00155               vhi >= caller_ptr->p_memmap[S].mem_vir + 
00156               caller_ptr->p_memmap[S].mem_len) {
00157 #if DEBUG_ENABLE_IPC_WARNINGS
00158           kprintf("sys_call: invalid message pointer, trap %d, caller %d\n",
00159                 function, proc_nr(caller_ptr));
00160 #endif
00161           return(EFAULT);               /* invalid message pointer */
00162       }
00163   }
00164 
00165   /* If the call is to send to a process, i.e., for SEND, SENDREC or NOTIFY,
00166    * verify that the caller is allowed to send to the given destination. 
00167    */
00168   if (function & CHECK_DST) {
00169       if (! get_sys_bit(priv(caller_ptr)->s_ipc_to, nr_to_id(src_dst))) {
00170 #if DEBUG_ENABLE_IPC_WARNINGS
00171           kprintf("sys_call: ipc mask denied trap %d from %d to %d\n",
00172                 function, proc_nr(caller_ptr), src_dst);
00173 #endif
00174           return(ECALLDENIED);          /* call denied by ipc mask */
00175       }
00176   }
00177 
00178   /* Check for a possible deadlock for blocking SEND(REC) and RECEIVE. */
00179   if (function & CHECK_DEADLOCK) {
00180       if (group_size = deadlock(function, caller_ptr, src_dst)) {
00181 #if DEBUG_ENABLE_IPC_WARNINGS
00182           kprintf("sys_call: trap %d from %d to %d deadlocked, group size %d\n",
00183               function, proc_nr(caller_ptr), src_dst, group_size);
00184 #endif
00185           return(ELOCKED);
00186       }
00187   }
00188 
00189   /* Now check if the call is known and try to perform the request. The only
00190    * system calls that exist in MINIX are sending and receiving messages.
00191    *   - SENDREC: combines SEND and RECEIVE in a single system call
00192    *   - SEND:    sender blocks until its message has been delivered
00193    *   - RECEIVE: receiver blocks until an acceptable message has arrived
00194    *   - NOTIFY:  nonblocking call; deliver notification or mark pending
00195    *   - ECHO:    nonblocking call; directly echo back the message 
00196    */
00197   switch(function) {
00198   case SENDREC:
00199       /* A flag is set so that notifications cannot interrupt SENDREC. */
00200       caller_ptr->p_misc_flags |= REPLY_PENDING;
00201       /* fall through */
00202   case SEND:                    
00203       result = mini_send(caller_ptr, src_dst_e, m_ptr, flags);
00204       if (function == SEND || result != OK) {   
00205           break;                                /* done, or SEND failed */
00206       }                                         /* fall through for SENDREC */
00207   case RECEIVE:                 
00208       if (function == RECEIVE)
00209           caller_ptr->p_misc_flags &= ~REPLY_PENDING;
00210       result = mini_receive(caller_ptr, src_dst_e, m_ptr, flags);
00211       break;
00212   case NOTIFY:
00213       result = mini_notify(caller_ptr, src_dst);
00214       break;
00215   case ECHO:
00216       CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, caller_ptr, m_ptr);
00217       result = OK;
00218       break;
00219   default:
00220       result = EBADCALL;                        /* illegal system call */
00221   }
00222 
00223   /* Now, return the result of the system call to the caller. */
00224   return(result);
00225 }
00226 
00227 /*===========================================================================*
00228  *                              deadlock                                     * 
00229  *===========================================================================*/
00230 PRIVATE int deadlock(function, cp, src_dst) 
00231 int function;                                   /* trap number */
00232 register struct proc *cp;                       /* pointer to caller */
00233 int src_dst;                                    /* src or dst process */
00234 {
00235 /* Check for deadlock. This can happen if 'caller_ptr' and 'src_dst' have
00236  * a cyclic dependency of blocking send and receive calls. The only cyclic 
00237  * depency that is not fatal is if the caller and target directly SEND(REC)
00238  * and RECEIVE to each other. If a deadlock is found, the group size is 
00239  * returned. Otherwise zero is returned. 
00240  */
00241   register struct proc *xp;                     /* process pointer */
00242   int group_size = 1;                           /* start with only caller */
00243   int trap_flags;
00244 
00245   while (src_dst != ANY) {                      /* check while process nr */
00246       int src_dst_e;
00247       xp = proc_addr(src_dst);                  /* follow chain of processes */
00248       group_size ++;                            /* extra process in group */
00249 
00250       /* Check whether the last process in the chain has a dependency. If it 
00251        * has not, the cycle cannot be closed and we are done.
00252        */
00253       if (xp->p_rts_flags & RECEIVING) {        /* xp has dependency */
00254           if(xp->p_getfrom_e == ANY) src_dst = ANY;
00255           else okendpt(xp->p_getfrom_e, &src_dst);
00256       } else if (xp->p_rts_flags & SENDING) {   /* xp has dependency */
00257           okendpt(xp->p_sendto_e, &src_dst);
00258       } else {
00259           return(0);                            /* not a deadlock */
00260       }
00261 
00262       /* Now check if there is a cyclic dependency. For group sizes of two,  
00263        * a combination of SEND(REC) and RECEIVE is not fatal. Larger groups
00264        * or other combinations indicate a deadlock.  
00265        */
00266       if (src_dst == proc_nr(cp)) {             /* possible deadlock */
00267           if (group_size == 2) {                /* caller and src_dst */
00268               /* The function number is magically converted to flags. */
00269               if ((xp->p_rts_flags ^ (function << 2)) & SENDING) { 
00270                   return(0);                    /* not a deadlock */
00271               }
00272           }
00273           return(group_size);                   /* deadlock found */
00274       }
00275   }
00276   return(0);                                    /* not a deadlock */
00277 }
00278 
00279 /*===========================================================================*
00280  *                              mini_send                                    * 
00281  *===========================================================================*/
00282 PRIVATE int mini_send(caller_ptr, dst_e, m_ptr, flags)
00283 register struct proc *caller_ptr;       /* who is trying to send a message? */
00284 int dst_e;                              /* to whom is message being sent? */
00285 message *m_ptr;                         /* pointer to message buffer */
00286 unsigned flags;                         /* system call flags */
00287 {
00288 /* Send a message from 'caller_ptr' to 'dst'. If 'dst' is blocked waiting
00289  * for this message, copy the message to it and unblock 'dst'. If 'dst' is
00290  * not waiting at all, or is waiting for another source, queue 'caller_ptr'.
00291  */
00292   register struct proc *dst_ptr;
00293   register struct proc **xpp;
00294   int dst_p;
00295 
00296   dst_p = _ENDPOINT_P(dst_e);
00297   dst_ptr = proc_addr(dst_p);
00298 
00299   if (dst_ptr->p_rts_flags & NO_ENDPOINT) return EDSTDIED;
00300 
00301   /* Check if 'dst' is blocked waiting for this message. The destination's 
00302    * SENDING flag may be set when its SENDREC call blocked while sending.  
00303    */
00304   if ( (dst_ptr->p_rts_flags & (RECEIVING | SENDING)) == RECEIVING &&
00305        (dst_ptr->p_getfrom_e == ANY
00306          || dst_ptr->p_getfrom_e == caller_ptr->p_endpoint)) {
00307         /* Destination is indeed waiting for this message. */
00308         CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, dst_ptr,
00309                  dst_ptr->p_messbuf);
00310         if ((dst_ptr->p_rts_flags &= ~RECEIVING) == 0) enqueue(dst_ptr);
00311   } else if ( ! (flags & NON_BLOCKING)) {
00312         /* Destination is not waiting.  Block and dequeue caller. */
00313         caller_ptr->p_messbuf = m_ptr;
00314         if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr);
00315         caller_ptr->p_rts_flags |= SENDING;
00316         caller_ptr->p_sendto_e = dst_e;
00317 
00318         /* Process is now blocked.  Put in on the destination's queue. */
00319         xpp = &dst_ptr->p_caller_q;             /* find end of list */
00320         while (*xpp != NIL_PROC) xpp = &(*xpp)->p_q_link;       
00321         *xpp = caller_ptr;                      /* add caller to end */
00322         caller_ptr->p_q_link = NIL_PROC;        /* mark new end of list */
00323   } else {
00324         return(ENOTREADY);
00325   }
00326   return(OK);
00327 }
00328 
00329 /*===========================================================================*
00330  *                              mini_receive                                 * 
00331  *===========================================================================*/
00332 PRIVATE int mini_receive(caller_ptr, src_e, m_ptr, flags)
00333 register struct proc *caller_ptr;       /* process trying to get message */
00334 int src_e;                              /* which message source is wanted */
00335 message *m_ptr;                         /* pointer to message buffer */
00336 unsigned flags;                         /* system call flags */
00337 {
00338 /* A process or task wants to get a message.  If a message is already queued,
00339  * acquire it and deblock the sender.  If no message from the desired source
00340  * is available block the caller, unless the flags don't allow blocking.  
00341  */
00342   register struct proc **xpp;
00343   register struct notification **ntf_q_pp;
00344   message m;
00345   int bit_nr;
00346   sys_map_t *map;
00347   bitchunk_t *chunk;
00348   int i, src_id, src_proc_nr, src_p;
00349 
00350   if(src_e == ANY) src_p = ANY;
00351   else
00352   {
00353         okendpt(src_e, &src_p);
00354         if (proc_addr(src_p)->p_rts_flags & NO_ENDPOINT) return ESRCDIED;
00355   }
00356 
00357 
00358   /* Check to see if a message from desired source is already available.
00359    * The caller's SENDING flag may be set if SENDREC couldn't send. If it is
00360    * set, the process should be blocked.
00361    */
00362   if (!(caller_ptr->p_rts_flags & SENDING)) {
00363 
00364     /* Check if there are pending notifications, except for SENDREC. */
00365     if (! (caller_ptr->p_misc_flags & REPLY_PENDING)) {
00366 
00367         map = &priv(caller_ptr)->s_notify_pending;
00368         for (chunk=&map->chunk[0]; chunk<&map->chunk[NR_SYS_CHUNKS]; chunk++) {
00369 
00370             /* Find a pending notification from the requested source. */ 
00371             if (! *chunk) continue;                     /* no bits in chunk */
00372             for (i=0; ! (*chunk & (1<<i)); ++i) {}      /* look up the bit */
00373             src_id = (chunk - &map->chunk[0]) * BITCHUNK_BITS + i;
00374             if (src_id >= NR_SYS_PROCS) break;          /* out of range */
00375             src_proc_nr = id_to_nr(src_id);             /* get source proc */
00376 #if DEBUG_ENABLE_IPC_WARNINGS
00377             if(src_proc_nr == NONE) {
00378                 kprintf("mini_receive: sending notify from NONE\n");
00379             }
00380 #endif
00381             if (src_e!=ANY && src_p != src_proc_nr) continue;/* source not ok */
00382             *chunk &= ~(1 << i);                        /* no longer pending */
00383 
00384             /* Found a suitable source, deliver the notification message. */
00385             BuildMess(&m, src_proc_nr, caller_ptr);     /* assemble message */
00386             CopyMess(src_proc_nr, proc_addr(HARDWARE), &m, caller_ptr, m_ptr);
00387             return(OK);                                 /* report success */
00388         }
00389     }
00390 
00391     /* Check caller queue. Use pointer pointers to keep code simple. */
00392     xpp = &caller_ptr->p_caller_q;
00393     while (*xpp != NIL_PROC) {
00394         if (src_e == ANY || src_p == proc_nr(*xpp)) {
00395 #if 0
00396             if ((*xpp)->p_rts_flags & SLOT_FREE)
00397             {
00398                 kprintf("listening to the dead?!?\n");
00399                 return EINVAL;
00400             }
00401 #endif
00402 
00403             /* Found acceptable message. Copy it and update status. */
00404             CopyMess((*xpp)->p_nr, *xpp, (*xpp)->p_messbuf, caller_ptr, m_ptr);
00405             if (((*xpp)->p_rts_flags &= ~SENDING) == 0) enqueue(*xpp);
00406             *xpp = (*xpp)->p_q_link;            /* remove from queue */
00407             return(OK);                         /* report success */
00408         }
00409         xpp = &(*xpp)->p_q_link;                /* proceed to next */
00410     }
00411   }
00412 
00413   /* No suitable message is available or the caller couldn't send in SENDREC. 
00414    * Block the process trying to receive, unless the flags tell otherwise.
00415    */
00416   if ( ! (flags & NON_BLOCKING)) {
00417       caller_ptr->p_getfrom_e = src_e;          
00418       caller_ptr->p_messbuf = m_ptr;
00419       if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr);
00420       caller_ptr->p_rts_flags |= RECEIVING;             
00421       return(OK);
00422   } else {
00423       return(ENOTREADY);
00424   }
00425 }
00426 
00427 /*===========================================================================*
00428  *                              mini_notify                                  * 
00429  *===========================================================================*/
00430 PRIVATE int mini_notify(caller_ptr, dst)
00431 register struct proc *caller_ptr;       /* sender of the notification */
00432 int dst;                                /* which process to notify */
00433 {
00434   register struct proc *dst_ptr = proc_addr(dst);
00435   int src_id;                           /* source id for late delivery */
00436   message m;                            /* the notification message */
00437 
00438   /* Check to see if target is blocked waiting for this message. A process 
00439    * can be both sending and receiving during a SENDREC system call.
00440    */
00441   if ((dst_ptr->p_rts_flags & (RECEIVING|SENDING)) == RECEIVING &&
00442       ! (dst_ptr->p_misc_flags & REPLY_PENDING) &&
00443       (dst_ptr->p_getfrom_e == ANY || 
00444       dst_ptr->p_getfrom_e == caller_ptr->p_endpoint)) {
00445 
00446       /* Destination is indeed waiting for a message. Assemble a notification 
00447        * message and deliver it. Copy from pseudo-source HARDWARE, since the
00448        * message is in the kernel's address space.
00449        */ 
00450       BuildMess(&m, proc_nr(caller_ptr), dst_ptr);
00451       CopyMess(proc_nr(caller_ptr), proc_addr(HARDWARE), &m, 
00452           dst_ptr, dst_ptr->p_messbuf);
00453       dst_ptr->p_rts_flags &= ~RECEIVING;       /* deblock destination */
00454       if (dst_ptr->p_rts_flags == 0) enqueue(dst_ptr);
00455       return(OK);
00456   } 
00457 
00458   /* Destination is not ready to receive the notification. Add it to the 
00459    * bit map with pending notifications. Note the indirectness: the system id 
00460    * instead of the process number is used in the pending bit map.
00461    */ 
00462   src_id = priv(caller_ptr)->s_id;
00463   set_sys_bit(priv(dst_ptr)->s_notify_pending, src_id); 
00464   return(OK);
00465 }
00466 
00467 /*===========================================================================*
00468  *                              lock_notify                                  *
00469  *===========================================================================*/
00470 PUBLIC int lock_notify(src_e, dst_e)
00471 int src_e;                      /* (endpoint) sender of the notification */
00472 int dst_e;                      /* (endpoint) who is to be notified */
00473 {
00474 /* Safe gateway to mini_notify() for tasks and interrupt handlers. The sender
00475  * is explicitely given to prevent confusion where the call comes from. MINIX 
00476  * kernel is not reentrant, which means to interrupts are disabled after 
00477  * the first kernel entry (hardware interrupt, trap, or exception). Locking
00478  * is done by temporarily disabling interrupts. 
00479  */
00480   int result, src, dst;
00481 
00482   if(!isokendpt(src_e, &src) || !isokendpt(dst_e, &dst))
00483         return EDEADSRCDST;
00484 
00485   /* Exception or interrupt occurred, thus already locked. */
00486   if (k_reenter >= 0) {
00487       result = mini_notify(proc_addr(src), dst); 
00488   }
00489 
00490   /* Call from task level, locking is required. */
00491   else {
00492       lock(0, "notify");
00493       result = mini_notify(proc_addr(src), dst); 
00494       unlock(0);
00495   }
00496   return(result);
00497 }
00498 
00499 /*===========================================================================*
00500  *                              enqueue                                      * 
00501  *===========================================================================*/
00502 PRIVATE void enqueue(rp)
00503 register struct proc *rp;       /* this process is now runnable */
00504 {
00505 /* Add 'rp' to one of the queues of runnable processes.  This function is 
00506  * responsible for inserting a process into one of the scheduling queues. 
00507  * The mechanism is implemented here.   The actual scheduling policy is
00508  * defined in sched() and pick_proc().
00509  */
00510   int q;                                        /* scheduling queue to use */
00511   int front;                                    /* add to front or back */
00512 
00513 #if DEBUG_SCHED_CHECK
00514   check_runqueues("enqueue");
00515   if (rp->p_ready) kprintf("enqueue() already ready process\n");
00516 #endif
00517 
00518   /* Determine where to insert to process. */
00519   sched(rp, &q, &front);
00520 
00521   /* Now add the process to the queue. */
00522   if (rdy_head[q] == NIL_PROC) {                /* add to empty queue */
00523       rdy_head[q] = rdy_tail[q] = rp;           /* create a new queue */
00524       rp->p_nextready = NIL_PROC;               /* mark new end */
00525   } 
00526   else if (front) {                             /* add to head of queue */
00527       rp->p_nextready = rdy_head[q];            /* chain head of queue */
00528       rdy_head[q] = rp;                         /* set new queue head */
00529   } 
00530   else {                                        /* add to tail of queue */
00531       rdy_tail[q]->p_nextready = rp;            /* chain tail of queue */       
00532       rdy_tail[q] = rp;                         /* set new queue tail */
00533       rp->p_nextready = NIL_PROC;               /* mark new end */
00534   }
00535 
00536   /* Now select the next process to run. */
00537   pick_proc();                  
00538 
00539 #if DEBUG_SCHED_CHECK
00540   rp->p_ready = 1;
00541   check_runqueues("enqueue");
00542 #endif
00543 }
00544 
00545 /*===========================================================================*
00546  *                              dequeue                                      * 
00547  *===========================================================================*/
00548 PRIVATE void dequeue(rp)
00549 register struct proc *rp;       /* this process is no longer runnable */
00550 {
00551 /* A process must be removed from the scheduling queues, for example, because
00552  * it has blocked.  If the currently active process is removed, a new process
00553  * is picked to run by calling pick_proc().
00554  */
00555   register int q = rp->p_priority;              /* queue to use */
00556   register struct proc **xpp;                   /* iterate over queue */
00557   register struct proc *prev_xp;
00558 
00559   /* Side-effect for kernel: check if the task's stack still is ok? */
00560   if (iskernelp(rp)) {                          
00561         if (*priv(rp)->s_stack_guard != STACK_GUARD)
00562                 panic("stack overrun by task", proc_nr(rp));
00563   }
00564 
00565 #if DEBUG_SCHED_CHECK
00566   check_runqueues("dequeue");
00567   if (! rp->p_ready) kprintf("dequeue() already unready process\n");
00568 #endif
00569 
00570   /* Now make sure that the process is not in its ready queue. Remove the 
00571    * process if it is found. A process can be made unready even if it is not 
00572    * running by being sent a signal that kills it.
00573    */
00574   prev_xp = NIL_PROC;                           
00575   for (xpp = &rdy_head[q]; *xpp != NIL_PROC; xpp = &(*xpp)->p_nextready) {
00576 
00577       if (*xpp == rp) {                         /* found process to remove */
00578           *xpp = (*xpp)->p_nextready;           /* replace with next chain */
00579           if (rp == rdy_tail[q])                /* queue tail removed */
00580               rdy_tail[q] = prev_xp;            /* set new tail */
00581           if (rp == proc_ptr || rp == next_ptr) /* active process removed */
00582               pick_proc();                      /* pick new process to run */
00583           break;
00584       }
00585       prev_xp = *xpp;                           /* save previous in chain */
00586   }
00587 
00588 #if DEBUG_SCHED_CHECK
00589   rp->p_ready = 0;
00590   check_runqueues("dequeue");
00591 #endif
00592 }
00593 
00594 /*===========================================================================*
00595  *                              sched                                        * 
00596  *===========================================================================*/
00597 PRIVATE void sched(rp, queue, front)
00598 register struct proc *rp;                       /* process to be scheduled */
00599 int *queue;                                     /* return: queue to use */
00600 int *front;                                     /* return: front or back */
00601 {
00602 /* This function determines the scheduling policy.  It is called whenever a
00603  * process must be added to one of the scheduling queues to decide where to
00604  * insert it.  As a side-effect the process' priority may be updated.  
00605  */
00606   int time_left = (rp->p_ticks_left > 0);       /* quantum fully consumed */
00607 
00608   /* Check whether the process has time left. Otherwise give a new quantum 
00609    * and lower the process' priority, unless the process already is in the 
00610    * lowest queue.  
00611    */
00612   if (! time_left) {                            /* quantum consumed ? */
00613       rp->p_ticks_left = rp->p_quantum_size;    /* give new quantum */
00614       if (rp->p_priority < (IDLE_Q-1)) {         
00615           rp->p_priority += 1;                  /* lower priority */
00616       }
00617   }
00618 
00619   /* If there is time left, the process is added to the front of its queue, 
00620    * so that it can immediately run. The queue to use simply is always the
00621    * process' current priority. 
00622    */
00623   *queue = rp->p_priority;
00624   *front = time_left;
00625 }
00626 
00627 /*===========================================================================*
00628  *                              pick_proc                                    * 
00629  *===========================================================================*/
00630 PRIVATE void pick_proc()
00631 {
00632 /* Decide who to run now.  A new process is selected by setting 'next_ptr'.
00633  * When a billable process is selected, record it in 'bill_ptr', so that the 
00634  * clock task can tell who to bill for system time.
00635  */
00636   register struct proc *rp;                     /* process to run */
00637   int q;                                        /* iterate over queues */
00638 
00639   /* Check each of the scheduling queues for ready processes. The number of
00640    * queues is defined in proc.h, and priorities are set in the task table.
00641    * The lowest queue contains IDLE, which is always ready.
00642    */
00643   for (q=0; q < NR_SCHED_QUEUES; q++) { 
00644       if ( (rp = rdy_head[q]) != NIL_PROC) {
00645           next_ptr = rp;                        /* run process 'rp' next */
00646           if (priv(rp)->s_flags & BILLABLE)             
00647               bill_ptr = rp;                    /* bill for system time */
00648           return;                                
00649       }
00650   }
00651 }
00652 
00653 /*===========================================================================*
00654  *                              balance_queues                               *
00655  *===========================================================================*/
00656 #define Q_BALANCE_TICKS  100
00657 PUBLIC void balance_queues(tp)
00658 timer_t *tp;                                    /* watchdog timer pointer */
00659 {
00660 /* Check entire process table and give all process a higher priority. This
00661  * effectively means giving a new quantum. If a process already is at its 
00662  * maximum priority, its quantum will be renewed.
00663  */
00664   static timer_t queue_timer;                   /* timer structure to use */
00665   register struct proc* rp;                     /* process table pointer  */
00666   clock_t next_period;                          /* time of next period  */
00667   int ticks_added = 0;                          /* total time added */
00668 
00669   for (rp=BEG_PROC_ADDR; rp<END_PROC_ADDR; rp++) {
00670       if (! isemptyp(rp)) {                             /* check slot use */
00671           lock(5,"balance_queues");
00672           if (rp->p_priority > rp->p_max_priority) {    /* update priority? */
00673               if (rp->p_rts_flags == 0) dequeue(rp);    /* take off queue */
00674               ticks_added += rp->p_quantum_size;        /* do accounting */
00675               rp->p_priority -= 1;                      /* raise priority */
00676               if (rp->p_rts_flags == 0) enqueue(rp);    /* put on queue */
00677           }
00678           else {
00679               ticks_added += rp->p_quantum_size - rp->p_ticks_left;
00680               rp->p_ticks_left = rp->p_quantum_size;    /* give new quantum */
00681           }
00682           unlock(5);
00683       }
00684   }
00685 #if DEBUG
00686   kprintf("ticks_added: %d\n", ticks_added);
00687 #endif
00688 
00689   /* Now schedule a new watchdog timer to balance the queues again.  The 
00690    * period depends on the total amount of quantum ticks added.
00691    */
00692   next_period = MAX(Q_BALANCE_TICKS, ticks_added);      /* calculate next */
00693   set_timer(&queue_timer, get_uptime() + next_period, balance_queues);
00694 }
00695 
00696 /*===========================================================================*
00697  *                              lock_send                                    *
00698  *===========================================================================*/
00699 PUBLIC int lock_send(dst_e, m_ptr)
00700 int dst_e;                      /* to whom is message being sent? */
00701 message *m_ptr;                 /* pointer to message buffer */
00702 {
00703 /* Safe gateway to mini_send() for tasks. */
00704   int result;
00705   lock(2, "send");
00706   result = mini_send(proc_ptr, dst_e, m_ptr, NON_BLOCKING);
00707   unlock(2);
00708   return(result);
00709 }
00710 
00711 /*===========================================================================*
00712  *                              lock_enqueue                                 *
00713  *===========================================================================*/
00714 PUBLIC void lock_enqueue(rp)
00715 struct proc *rp;                /* this process is now runnable */
00716 {
00717 /* Safe gateway to enqueue() for tasks. */
00718   lock(3, "enqueue");
00719   enqueue(rp);
00720   unlock(3);
00721 }
00722 
00723 /*===========================================================================*
00724  *                              lock_dequeue                                 *
00725  *===========================================================================*/
00726 PUBLIC void lock_dequeue(rp)
00727 struct proc *rp;                /* this process is no longer runnable */
00728 {
00729 /* Safe gateway to dequeue() for tasks. */
00730   if (k_reenter >= 0) {
00731         /* We're in an exception or interrupt, so don't lock (and ... 
00732          * don't unlock).
00733          */
00734         dequeue(rp);
00735   } else {
00736         lock(4, "dequeue");
00737         dequeue(rp);
00738         unlock(4);
00739   }
00740 }
00741 
00742 /*===========================================================================*
00743  *                              isokendpt_f                                  *
00744  *===========================================================================*/
00745 #if DEBUG_ENABLE_IPC_WARNINGS
00746 PUBLIC int isokendpt_f(file, line, e, p, fatalflag)
00747 char *file;
00748 int line;
00749 #else
00750 PUBLIC int isokendpt_f(e, p, fatalflag)
00751 #endif
00752 int e, *p, fatalflag;
00753 {
00754         int ok = 0;
00755         /* Convert an endpoint number into a process number.
00756          * Return nonzero if the process is alive with the corresponding
00757          * generation number, zero otherwise.
00758          *
00759          * This function is called with file and line number by the
00760          * isokendpt_d macro if DEBUG_ENABLE_IPC_WARNINGS is defined,
00761          * otherwise without. This allows us to print the where the
00762          * conversion was attempted, making the errors verbose without
00763          * adding code for that at every call.
00764          * 
00765          * If fatalflag is nonzero, we must panic if the conversion doesn't
00766          * succeed.
00767          */
00768         *p = _ENDPOINT_P(e);
00769         if(!isokprocn(*p)) {
00770 #if DEBUG_ENABLE_IPC_WARNINGS
00771                 kprintf("kernel:%s:%d: bad endpoint %d: proc %d out of range\n",
00772                 file, line, e, *p);
00773 #endif
00774         } else if(isemptyn(*p)) {
00775 #if DEBUG_ENABLE_IPC_WARNINGS
00776         kprintf("kernel:%s:%d: bad endpoint %d: proc %d empty\n", file, line, e, *p);
00777 #endif
00778         } else if(proc_addr(*p)->p_endpoint != e) {
00779 #if DEBUG_ENABLE_IPC_WARNINGS
00780                 kprintf("kernel:%s:%d: bad endpoint %d: proc %d has ept %d (generation %d vs. %d)\n", file, line,
00781                 e, *p, proc_addr(*p)->p_endpoint,
00782                 _ENDPOINT_G(e), _ENDPOINT_G(proc_addr(*p)->p_endpoint));
00783 #endif
00784         } else ok = 1;
00785         if(!ok && fatalflag) {
00786                 panic("invalid endpoint ", e);
00787         }
00788         return ok;
00789 }
00790 

Generated on Fri Apr 14 22:57:24 2006 for minix by  doxygen 1.4.6