process: move all the optimizations of switchout() into the C code
authorAlan Cox <alan@linux.intel.com>
Thu, 29 Mar 2018 14:42:24 +0000 (15:42 +0100)
committerAlan Cox <alan@linux.intel.com>
Thu, 29 Mar 2018 14:42:24 +0000 (15:42 +0100)
This fixes a ton of duplication in the asm code, and in addition makes the
pre-emption cases faster as we know in those cases that the optimizations
never apply.

switchout() becomes a C fnction

platform_switchout() is now the required platform specific stub which shouldn't
need to do any optimizing beyond udata copy elimiations.

Merge a z80fixedbank example of the changes.

Kernel/lib/z80fixedbank.s
Kernel/process.c

index 0ba4763..0c3d693 100644 (file)
@@ -14,7 +14,7 @@
         .globl _getproc
         .globl _trap_monitor
         .globl trap_illegal
-        .globl _switchout
+        .globl _platform_switchout
         .globl _switchin
         .globl _doexec
         .globl _dofork
 
         .area _COMMONMEM
 
-; Switchout switches out the current process, finds another that is READY,
+; __switchout switches out the current process, finds another that is READY,
 ; possibly the same process, and switches it in.  When a process is
 ; restarted after calling switchout, it thinks it has just returned
 ; from switchout().
 ; 
 ; This function can have no arguments or auto variables.
 ;
-;      FIXME: can we optimise all the pushes and move them into slow_path
-;
-_switchout:
-        di
-        call _chksigs
-        ; save machine state
-       ld a,l
-       or a
-       jr nz, switchsig
-
+_platform_switchout:
         ld hl, #0 ; return code set here is ignored, but _switchin can 
         ; return from either _switchout OR _dofork, so they must both write 
         ; U_DATA__U_SP with the following on the stack:
@@ -63,41 +54,6 @@ _switchout:
         push iy
         ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin
 
-       ld a, (_nready)
-       or a
-       jr nz, slow_path
-
-idling:
-       ei
-       call _platform_idle
-       di
-       ld a, (_nready)
-       or a
-       jr z, idling
-       cp #1
-       jr nz, slow_path
-       ld hl, (U_DATA__U_PTAB)
-       ld a, (hl)              ; Process table status is first byte
-       ; Are we the one process ?
-       cp #P_READY
-       jr nz, slow_path
-       ; We are - fast path return from switchout
-       ld (hl), #P_RUNNING
-       pop iy
-       pop ix
-       pop hl
-       ei
-       ret
-
-; As we tried to sleep we raced a signal so got the boot back out
-; into the land of the living.
-switchsig:
-       ld hl, (U_DATA__U_PTAB)
-       ld (hl), #P_RUNNING
-       ei
-       ret
-
-slow_path:
        ; Stash the uarea back into process memory
        call map_process_always
        ld hl, #U_DATA
index 4d9e630..e102f54 100644 (file)
@@ -2,6 +2,7 @@
 #undef DEBUGHARDER             /* report calls to wakeup() that lead nowhere */
 #undef DEBUGREALLYHARD         /* turn on getproc dumping */
 #undef DEBUG_PREEMPT           /* debug pre-emption */
+#define DEBUG_NREADY           /* debug nready counting */
 
 #include <kernel.h>
 #include <tty.h>
@@ -16,7 +17,6 @@
  * event equal to the process's own ptab address is a wait().
  */
 
-
 static void do_psleep(void *event, uint8_t state)
 {
        irqflags_t irq = di();
@@ -40,7 +40,6 @@ static void do_psleep(void *event, uint8_t state)
        udata.u_ptab->p_status = state;
        udata.u_ptab->p_wait = event;
        udata.u_ptab->p_waitno = ++waitno;
-       nready--;
 
        /* Invalidate signal cache if in IOWAIT */
        if (state == P_IOWAIT)
@@ -86,6 +85,11 @@ void wakeup(void *event)
 }
 
 
+/*
+ * When we wake a process in Fuzix we don't force any kind of reschedule
+ * even if the priority is higher. It's too expensive on many systems to
+ * be that fair.
+ */
 void pwake(ptptr p)
 {
        if (p->p_status > P_RUNNING && p->p_status < P_STOPPED) {
@@ -97,6 +101,66 @@ void pwake(ptptr p)
        }
 }
 
+/* This used to be an assembly function in older FUZIX but it turns out we
+   have to do a lot of common optimizations so it is now a C helper fronting
+   platform_switchout(). For speed and sanity reasons not every platform goes
+   via this path when pre-empting, but instead implements a subset of the checks
+   in the platform code.
+
+   switchout() is called when a process is giving up the processor for some
+   reason
+ */
+
+void switchout(void)
+{
+       di();
+
+       /* We do the accounting in switchout as it's cheaper and easier to
+          do it once. Useful trick borrowed from Linux */
+       if (udata.u_ptab->p_status > P_READY)
+               nready--;
+
+#ifdef DEBUG_NREADY
+       {
+               ptptr p;
+               uint8_t n = 0;
+               for (p = ptab; p < ptab_end; ++p) {
+                       if (p->p_status == P_RUNNING ||
+                               p->p_status == P_READY)
+                                       n++;
+               }
+               if (n != nready)
+                       panic("nready");
+       }
+#endif
+       /* If we have a signal we need to get to processing them we keep
+          running until it happens */
+       if (chksigs()) {
+               if (udata.u_ptab->p_status > P_READY)
+                       nready++;
+               udata.u_ptab->p_status = P_RUNNING;
+               ei();
+               return;
+       }
+       /* When we are idle we widdle our thumbs here until a polled event
+          in platform_idle or an interrupt wakes someone up */
+       while (nready == 0) {
+               ei();
+               platform_idle();
+               di();
+       }
+       /* If only one process is ready to run and it's us then just
+          return. This is the normal path in most Fuzix use cases as we
+          are waiting for input while mostly system idle */
+       if (nready == 1 && udata.u_ptab->p_status == P_READY) {
+               udata.u_ptab->p_status = P_RUNNING;
+               ei();
+               return;
+       }
+       /* We probably need to run somehting else */
+       platform_switchout();
+}
+
 /* Getproc returns the process table pointer of a runnable process.
  * It is actually the scheduler.  If there are none, it loops.
  * This is the only time-wasting loop in the system.
@@ -433,7 +497,10 @@ void unix_syscall(void)
                /* Time to switch out? - we may have overstayed our welcome inside
                   a syscall so switch straight afterwards */
                udata.u_ptab->p_status = P_READY;
-               switchout();
+               /* We know there will be a switch if we hit this point so
+                  don't look for optimizations. Likewise we know a signal
+                  process will stay running/ready */
+               platform_switchout();
        }
        ei();
        chksigs();
@@ -517,11 +584,9 @@ static uint8_t chksigset(struct sigbits *sb, uint8_t b)
                                /* FIXME: can we ever end up here not in READY/RUNNING ? */
                                /* Yes: we could be in P_SLEEP on a close race
                                   with do_psleep() */
-                               nready--;
                                udata.u_ptab->p_status = P_STOPPED;
                                udata.u_ptab->p_event = j;
                                sb->s_pending &= ~m;    // unset the bit
-                               irqrestore(irq);
                                switchout();
                                /* Other things may have happened */
                                return 0xFF;
@@ -670,6 +735,8 @@ void acctexit(ptptr p)
 
 /* Perform the terminal process signalling */
 /* FIXME: why return a value - we don't use it */
+/* FIXME: pass the process id not parent in then can reuse in doexit
+   special cases */
 static int signal_parent(ptptr p)
 {
         if (p->p_sig[1].s_ignored & (1UL << SIGCHLD)) {