From b8b0d51fa7882db68a63f943c86f83510e70a3d5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 29 Mar 2018 15:42:24 +0100 Subject: [PATCH] process: move all the optimizations of switchout() into the C code 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 | 50 ++----------------------- Kernel/process.c | 77 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/Kernel/lib/z80fixedbank.s b/Kernel/lib/z80fixedbank.s index 0ba47638..0c3d693e 100644 --- a/Kernel/lib/z80fixedbank.s +++ b/Kernel/lib/z80fixedbank.s @@ -14,7 +14,7 @@ .globl _getproc .globl _trap_monitor .globl trap_illegal - .globl _switchout + .globl _platform_switchout .globl _switchin .globl _doexec .globl _dofork @@ -38,23 +38,14 @@ .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 diff --git a/Kernel/process.c b/Kernel/process.c index 4d9e6309..e102f549 100644 --- a/Kernel/process.c +++ b/Kernel/process.c @@ -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 #include @@ -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)) { -- 2.34.1