kernel: Major rework of the single process in memory model
authorAlan Cox <alan@linux.intel.com>
Sat, 8 Dec 2018 00:09:28 +0000 (00:09 +0000)
committerAlan Cox <alan@linux.intel.com>
Sat, 8 Dec 2018 00:09:28 +0000 (00:09 +0000)
- Support 'parent runs first' in fork. We will also need this for flat memory
  models like 68000 to work nicely. In our case we write the child to swap but
  for flat models we'd duplicate the update and write a short stub stack to
  the child

- Introduce a makeproc to replace newproc. Different arguments so deliberately
  break all the old code

- Fix some interestingly bad scheduling corner cases where we have a process
  running for a long time with nothing else that then causes fork to thrash

- Make tmpfree always a function. Useful for debug and also avoids icky asm
  dependencies

- Fix the Z80 single process model to use the new features

- Fix the Z80 banked process model to pass the new arguments

- Also adjust the switching rate on the Plus 3 as our test platform for the
  single process in memory tunings

Takes our boot time down about 33% !

Kernel/devio.c
Kernel/include/kernel.h
Kernel/lib/z80fixedbank-banked.s
Kernel/lib/z80fixedbank-core.s
Kernel/lib/z80single.s
Kernel/platform-zx+3/README
Kernel/platform-zx+3/config.h
Kernel/process.c
Kernel/simple.c
Kernel/start.c
Kernel/syscall_proc.c

index e8a2697..284f981 100644 (file)
@@ -183,6 +183,11 @@ void *tmpbuf(void)
        bp->bf_time = ++bufclock;       /* Time stamp it */
        return bp->__bf_data;
 }
+
+void tmpfree(void *p)
+{
+       brelse(p);
+}
 #endif
 
 /*
index eef3c44..614a601 100644 (file)
@@ -212,7 +212,6 @@ typedef struct blkbuf {
     uget((uaddr),(buf)->__bf_data + (off), (len))
 #define blkptr(buf, off, len)  ((void *)((buf)->__bf_data + (off)))
 #define blkzero(buf)           memset(buf->__bf_data, 0, BLKSIZE)
-#define tmpfree(x)     brelse((void *)x)
 #else
 extern void blktok(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len);
 extern void blkfromk(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len);
@@ -221,7 +220,6 @@ extern void blkfromu(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len
 /* Worst case is needing to copy over about 64 bytes */
 extern void *blkptr(struct blkbuf *buf, uint16_t offset, uint16_t len);
 extern void blkzero(struct blkbuf *buf);
-extern void tmpfree(void *p);
 #endif
 
 /* TODO: consider smaller inodes or clever caching. 2BSD uses small
@@ -862,6 +860,7 @@ extern void brelse(bufptr);
 extern void bawrite(bufptr);
 extern int bfree(bufptr bp, uint8_t dirty); /* dirty: 0=clean, 1=dirty (write back), 2=dirty+immediate write */
 extern void *tmpbuf(void);
+extern void tmpfree(void *p);
 extern bufptr zerobuf(void);
 extern void bufsync(void);
 extern bufptr bfind(uint16_t dev, blkno_t blk);
@@ -957,7 +956,7 @@ extern void psleep_nosig(void *event);
 extern void wakeup(void *event);
 extern void pwake(ptptr p);
 extern ptptr getproc(void);
-extern void newproc(ptptr p);
+extern void makeproc(ptptr p, u_data *u);
 extern ptptr ptab_alloc(void);
 extern void ssig(ptptr proc, uint8_t sig);
 extern void recalc_cursig(void);
index 6a6a839..f859831 100644 (file)
@@ -9,7 +9,7 @@
         .module z80fixedbank
 
         .globl _ptab_alloc
-        .globl _newproc
+        .globl _makeproc
         .globl _chksigs
         .globl _getproc
         .globl _platform_monitor
@@ -24,6 +24,7 @@
        .globl _need_resched
        .globl _nready
        .globl _platform_idle
+       .globl _udata
 
        .globl map_kernel_restore
        .globl map_process_a
@@ -306,12 +307,14 @@ _dofork:
        pop bc
 
         ; Make a new process table entry, etc.
-        ld  hl, (fork_proc_ptr)
+       ld hl, #_udata
+        ld hl, (fork_proc_ptr)
         push hl
        push af
-        call _newproc
+        call _makeproc
        pop af
         pop bc 
+       pop bc
 
         ; runticks = 0;
         ld hl, #0
index 3b69222..90483db 100644 (file)
@@ -9,7 +9,7 @@
         .module z80fixedbank
 
         .globl _ptab_alloc
-        .globl _newproc
+        .globl _makeproc
         .globl _chksigs
         .globl _getproc
         .globl _platform_monitor
@@ -25,6 +25,7 @@
        .globl _nready
        .globl _platform_idle
        .globl _int_disabled
+       .globl _udata
 
        .globl map_kernel
        .globl map_process
@@ -279,10 +280,13 @@ _dofork:
         pop bc
 
         ; Make a new process table entry, etc.
+       ld hl,#_udata
+       push hl
         ld  hl, (fork_proc_ptr)
         push hl
-        call _newproc
+        call _makeproc
         pop bc 
+       pop bc
 
         ; runticks = 0;
         ld hl, #0
index b42e333..9afb89b 100644 (file)
@@ -2,11 +2,15 @@
 ;      Generic handling for single process in memory Z80 systems.
 ;
 ;      This code could really do with some optimizing.
+;
+;      FIXME: IRQ enable logic during swap
+;
+;      FIXME: turn on parent first behaviour when ready
 ;
         .module tricks
 
         .globl _ptab_alloc
-        .globl _newproc
+        .globl _makeproc
         .globl _chksigs
         .globl _getproc
         .globl _platform_monitor
         .globl interrupt_handler
        .globl _swapper
        .globl _swapout
+       .globl _swapout_new
        .globl _int_disabled
+       .globl _udata
+       .globl _tmpbuf
+       .globl _tmpfree
 
         ; imported debug symbols
         .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
@@ -37,6 +45,7 @@ _platform_switchout:
         ; save machine state
 
         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:
         push hl ; return code
@@ -60,6 +69,8 @@ swapped: .ascii "_switchin: SWAPPED"
 
 _switchin:
         di
+       ld a,#1
+       ld (_int_disabled),a
         pop bc  ; return address
         pop de  ; new process pointer
 ;
@@ -113,7 +124,7 @@ not_swapped:
         ; check u_data->u_ptab matches what we wanted
         ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab
         or a                    ; clear carry flag
-        sbc hl, de              ; subtract, result will be zero if DE==IX
+        sbc hl, de              ; subtract, result will be zero if DE==HL
         jr nz, switchinfail
 
        ; wants optimising up a bit
@@ -170,22 +181,15 @@ _dofork:
 
         ld (fork_proc_ptr), hl
 
-        ; prepare return value in parent process -- HL = p->p_pid;
-        ld de, #P_TAB__P_PID_OFFSET
-        add hl, de
-        ld a, (hl)
-        inc hl
-        ld h, (hl)
-        ld l, a
-
         ; Save the stack pointer and critical registers.
         ; When this process (the parent) is switched back in, it will be as if
         ; it returns with the value of the child's pid.
-        push hl ; HL still has p->p_pid from above, the return value in the parent
+       ld hl,#0
+        push hl                ;       #0 child
         push ix
         push iy
 
-        ; save kernel stack pointer -- when it comes back in the parent we'll be in
+        ; save kernel stack pointer -- when it comes back in the child we'll be in
         ; _switchin which will immediately return (appearing to be _dofork()
        ; returning) and with HL (ie return code) containing the child PID.
         ; Hurray.
@@ -194,32 +198,81 @@ _dofork:
         ; now we're in a safe state for _switchin to return in the parent
        ; process.
 
-       ld hl, (U_DATA__U_PTAB)
+
+        ; Make a new process table entry, etc.
+
+       ; Copy the parent properties into the temporary udata copy
+       call _tmpbuf
+       ld a,h
+       or l
+       jp z,ohpoo
        push hl
-       call _swapout
+       ex de,hl
+       ld hl, #_udata
+       ld bc, #U_DATA__TOTALSIZE
+       ldir
+
+       ; Recover the buffer pointer
        pop hl
+       push hl
+
+       ; Make the child udata out of the temporary buffer
+       push hl
+        ld hl, (fork_proc_ptr)
+        push hl
+        call _makeproc
+        pop bc
+       pop bc
+
+        ; in the child process, fork() returns zero.
+       ;
+       ; And we exit, with the kernel mapped, the child assembled in the
+       ; copy area as if it had done a switchout, the parent meanwhile
+       ; continues happily on
+
+        ; now we're in a safe state for _switchin to return in the child
+       ; process swap out the image and the new udata
 
+       ; Stack the buffer as a second argument
+       pop hl
+       push hl
+       push hl
+
+       ld hl, (fork_proc_ptr)
+       push hl
+       call _swapout_new
+       pop hl
+       pop hl
+
+       ; Buffer pointer is sitting top of stack still
+       ; so use it as an argument to tmpfree
+       call _tmpfree
+
+       pop hl
+
+       ld hl, (fork_proc_ptr)
+        ; prepare return value in parent process -- HL = p->p_pid;
+        ld de, #P_TAB__P_PID_OFFSET
+        add hl, de
+        ld a, (hl)
+        inc hl
+        ld h, (hl)
+        ld l, a
         ; now the copy operation is complete we can get rid of the stuff
         ; _switchin will be expecting from our copy of the stack.
         pop bc
         pop bc
         pop bc
+       ; Return pid of child we forked into swap
+        ret
 
-        ; Make a new process table entry, etc.
-        ld  hl, (fork_proc_ptr)
-        push hl
-        call _newproc
-        pop bc 
+ohpoo:
+       ld hl,#nobufs
+       call outstring
+       jp _platform_monitor
 
-        ; runticks = 0;
-        ld hl, #0
-        ld (_runticks), hl
-        ; in the child process, fork() returns zero.
-       ;
-       ; And we exit, with the kernel mapped, the child now being deemed
-       ; to be the live uarea. The parent is frozen in time and space as
-       ; if it had done a switchout().
-        ret
+nobufs:
+       .asciz 'nobufs'
 ;
 ;      We can keep a stack in common because we will complete our
 ;      use of it before we switch common block. In this case we have
index a6e5c60..3fc8962 100644 (file)
@@ -56,15 +56,12 @@ load an app from 5D00-wherever. Still ugly.
 
 TODO
 
-Debug floppy handling
-
 Fix memory size reporting 64 v 48K
 
 Floppy drive B - set drive info and propogate it
 
 Floppy drive B - set parameters at init time
 
-Petty coloured stripes when reading/writing floppy
 
 Move buffers into upper part of bank 7 to see if can get kernel below
 C000 + E000 upwards to get bigger user image ?
@@ -79,6 +76,16 @@ user but screen mapped and buffers in rest of 7 ?
 0-3            Kernel
 4-7            User, video in 7
 
+May also be able to later do
+
+0-2            Kernel          3 high kernel common
+                               7 upper 8K buffers and high stubs
+                               7 lower scree
+4/5/6          User 64K        3 high kernel common , 3 low user
+
+4/5/6          User 48K        7 screen, upper 8K buffers
+
+
 0/4 hold page0 copies, 3 7 both hold pageh copies - fine as video is
 in bottom of that chunk.
 
index 981a537..97a1471 100644 (file)
 
 /* Swap based one process in RAM */
 #define CONFIG_SWAP_ONLY
+#define CONFIG_PARENT_FIRST
 #define CONFIG_SPLIT_UDATA
 #define UDATA_BLKS     1
 #define UDATA_SIZE     0x200
 #define CONFIG_DYNAMIC_BUFPOOL
 #define CONFIG_DYNAMIC_SWAP
+#define MAXTICKS       20      /* Has to be high because we are swap only */
 
 /* Custom banking */
 
index 09044ed..8bbb92a 100644 (file)
@@ -268,31 +268,40 @@ ptptr getproc(void)
 /* Newproc fixes up the tables for the child of a fork but also for init
  * Call in the processes context!
  * This process MUST be run immediately (since it sets status P_RUNNING)
+ *
+ * The fork code has already copied the udata into u so we only need to
+ * touch things that changed. u may or may not be the current udata
  */
-void newproc(regptr ptptr p)
+void makeproc(regptr ptptr p, u_data *u)
 {                              /* Passed New process table entry */
-       uint8_t *j;
+       uint8_t *j, *e;
        irqflags_t irq;
+       ptptr pp;
 
        irq = di();
        /* Note that ptab_alloc clears most of the entry */
        /* calculate base page of process based on ptab table offset */
-       udata.u_page = p->p_page;
-       udata.u_page2 = p->p_page2;
+       u->u_page = p->p_page;
+       u->u_page2 = p->p_page2;
 
        program_vectors(&p->p_page);    /* set up vectors in new process and
                                           if needed copy any common code */
-
+#ifdef CONFIG_PARENT_FIRST
+       p->p_status = P_READY;
+#else
        p->p_status = P_RUNNING;
+#endif
        nready++;               /* runnable process count */
 
-       p->p_pptr = udata.u_ptab;
-       p->p_sig[0].s_ignored = udata.u_ptab->p_sig[0].s_ignored;
-       p->p_sig[1].s_ignored = udata.u_ptab->p_sig[1].s_ignored;
-       p->p_sig[0].s_held = udata.u_ptab->p_sig[0].s_held;
-       p->p_sig[1].s_held = udata.u_ptab->p_sig[1].s_held;
-       p->p_tty = udata.u_ptab->p_tty;
-       p->p_uid = udata.u_ptab->p_uid;
+       pp = u->u_ptab;         /* Because it is a copy of the parent */
+
+       p->p_pptr = pp;
+       p->p_sig[0].s_ignored = pp->p_sig[0].s_ignored;
+       p->p_sig[1].s_ignored = pp->p_sig[1].s_ignored;
+       p->p_sig[0].s_held = pp->p_sig[0].s_held;
+       p->p_sig[1].s_held = pp->p_sig[1].s_held;
+       p->p_tty = pp->p_tty;
+       p->p_uid = pp->p_uid;
        /* Set default priority */
        p->p_priority = MAXTICKS;
 
@@ -301,18 +310,20 @@ void newproc(regptr ptptr p)
        p->p_udata = &udata;
 #endif
 
-       udata.u_ptab = p;
+       u->u_ptab = p;  /* Fixup from parent */
 
        memset(&p->p_utime, 0, 4 * sizeof(clock_t));    /* Clear tick counters */
 
        rdtime32(&p->p_time);
-       if (udata.u_cwd)
-               i_ref(udata.u_cwd);
-       if (udata.u_root)
-               i_ref(udata.u_root);
-       udata.u_cursig = 0;
-       udata.u_error = 0;
-       for (j = udata.u_files; j < (udata.u_files + UFTSIZE); ++j) {
+       if (u->u_cwd)
+               i_ref(u->u_cwd);
+       if (u->u_root)
+               i_ref(u->u_root);
+       u->u_cursig = 0;
+       u->u_error = 0;
+
+       e = u->u_files + UFTSIZE;
+       for (j = u->u_files; j < e; ++j) {
                if (*j != NO_FILE)
                        ++of_tab[*j].o_refs;
        }
@@ -453,15 +464,22 @@ void timer_interrupt(void)
        /* Check run time of current process. We don't charge time while
           swapping as the last thing we want to do is to swap a process in
           and decide it took time to swap in so needs to go away again! */
-       /* FIXME: can we kill off inint ? */
        if (!inswap && (++runticks >= udata.u_ptab->p_priority)
-           && !udata.u_insys && inint && nready > 1) {
-                 need_resched = 1;
+           && !udata.u_insys && inint) {
+               /* It might appear to make the best sense to just leave
+                  runticks ticking upwards if nobody else needs to run but
+                  this has two problems. The obvious one is that it may wrap
+                  but less obviously it can also cause thrashing on fork()
+                  in some memory models */
+               if (nready > 1) {
+                       need_resched = 1;
 #ifdef DEBUG_PREEMPT
-               kprintf("[preempt %p %d]", udata.u_ptab,
-                       udata.u_ptab->p_priority);
+                       kprintf("[preempt %p %d]", udata.u_ptab,
+                               udata.u_ptab->p_priority);
 #endif
-        }
+               } else  /* Nobody else to run, user gets new time quantum */
+                       runticks = 0;
+       }
 #endif
 }
 
@@ -502,6 +520,9 @@ void unix_syscall(void)
 
        di();
        if (runticks >= udata.u_ptab->p_priority && nready > 1) {
+#ifdef DEBUG_PREEMPT   
+               kprintf("P: %d %x %d\n", runticks, udata.u_ptab, udata.u_ptab->p_priority);
+#endif         
                /* Time to switch out? - we may have overstayed our welcome inside
                   a syscall so swtch straight afterwards */
                udata.u_ptab->p_status = P_READY;
@@ -892,7 +913,6 @@ void doexit(uint16_t val)
         signal_parent(udata.u_ptab);
        nready--;
        nproc--;
-
        switchin(getproc());
        panic(PANIC_DOEXIT);
 }
index 7e225bf..e3562a2 100644 (file)
@@ -30,6 +30,8 @@
 #include <kdata.h>
 #include <printf.h>
 
+#undef DEBUG
+
 #ifdef CONFIG_SWAP_ONLY
 
 void pagemap_free(ptptr p)
@@ -64,14 +66,14 @@ void pagemap_init(void)
  *     Swap out the memory of a process to make room
  *     for something else
  */
-int swapout(ptptr p)
+int swapout_new(ptptr p, void *u)
 {
        uint16_t page = p->p_page;
        uint16_t blk;
        uint16_t map;
 
 #ifdef DEBUG
-       kprintf("Swapping out %x (%d)\n", p, p->p_page);
+       kprintf("Swapping out %x (%d)\n", p, p->p_pid);
 #endif
        if (!page)
                panic(PANIC_ALREADYSWAP);
@@ -80,24 +82,32 @@ int swapout(ptptr p)
        if (map == 0)
                return ENOMEM;
        blk = map * SWAP_SIZE;
-       /* Write the app (and possibly the uarea etc..) to disk */
+       /* Write the app (and uarea etc..) to disk */
 #ifdef CONFIG_SPLIT_UDATA
-       /* Note the page for the udata bit as it goes direct to udata */
-       swapwrite(SWAPDEV, blk, UDATA_SIZE, (uaddr_t)&udata, 0);
+       /* Write the udata block as kernel. */
+       udata.u_dptr = u;
+       udata.u_block = blk;
+       udata.u_nblock = UDATA_SIZE >> BLKSHIFT;        /* 1 */
+       ((*dev_tab[major(SWAPDEV)].dev_write) (minor(SWAPDEV), 0, 0));
+       /* Use the standard swapwrite helper for the rest */
        swapwrite(SWAPDEV, blk + UDATA_BLKS, SWAPTOP - SWAPBASE,
                  SWAPBASE, 1);
 #else
-       swapwrite(SWAPDEV, blk, SWAPTOP - SWAPBASE,
-                 SWAPBASE, 1);
+#error "Not supported"
 #endif
        p->p_page = 0;
        p->p_page2 = map;
 #ifdef DEBUG
-       kprintf("%x: swapout done %d\n", p, p->p_page);
+       kprintf("%x: swapout done %d\n", p, p->p_page2);
 #endif
        return 0;
 }
 
+int swapout(ptptr p)
+{
+       return swapout_new(p, &udata);
+}
+
 /*
  * Swap ourself in: must be on the swap stack when we do this
  */
@@ -106,24 +116,21 @@ void swapin(ptptr p, uint16_t map)
        uint16_t blk = map * SWAP_SIZE;
 
 #ifdef DEBUG
-       kprintf("Swapin %x, %d\n", p, p->p_page);
+       kprintf("Swapin %x (%d, %d)\n", p, p->p_page2, p->p_pid);
 #endif
        if (!p->p_page) {
                kprintf("%x: nopage!\n", p);
                return;
        }
 
-#ifdef CONFIG_SPLIT_UDATA
-       /* Note the page for the udata bit as it goes direct to udata */
+       /* Note the page for the udata bit as it goes direct to udata and
+          is always in common */
        swapread(SWAPDEV, blk, UDATA_SIZE, (uaddr_t)&udata, 0);
        swapread(SWAPDEV, blk + UDATA_BLKS, SWAPTOP - SWAPBASE,
                 SWAPBASE, 1);
-#else
-       swapread(SWAPDEV, blk, SWAPTOP - SWAPBASE,
-                SWAPBASE, 1);
-#endif
+
 #ifdef DEBUG
-       kprintf("%x: swapin done %d\n", p, p->p_page);
+       kprintf("%x: swapin done %d\n", p, p->p_page2);
 #endif
 }
 
index 1abf55f..56d0c49 100644 (file)
@@ -81,23 +81,26 @@ void add_argument(const char *s)
 
 void create_init(void)
 {
-       uint8_t *j;
+       uint8_t *j, *e;
 
        udata.u_top = PROGLOAD + 512;   /* Plenty for the boot */
        init_process = ptab_alloc();
        udata.u_ptab = init_process;
        init_process->p_top = udata.u_top;
        map_init();
-       newproc(init_process);
+
+       /* wipe file table */
+       e = udata.u_files + UFTSIZE;
+       for (j = udata.u_files; j < e; ++j)
+               *j = NO_FILE;
+
+       makeproc(init_process, &udata);
+       init_process->p_status = P_RUNNING;
 
        udata.u_insys = 1;
 
        init_process->p_status = P_RUNNING;
 
-       /* wipe file table */
-       for (j = udata.u_files; j < (udata.u_files + UFTSIZE); ++j) {
-               *j = NO_FILE;
-       }
        /* Poke the execve arguments into user data space so _execve() can read them back */
        /* Some systems only have a tiny window we can use at boot as most of
           this space is loaded with common memory */
index de76766..6fb8a1a 100644 (file)
@@ -384,8 +384,13 @@ arg_t _fork(void)
        /*
         * We're going to run our child process next, so mark this process as
         * being ready to run
+        *
+        * FIXME: push this down into dofork
         */
+#ifndef CONFIG_PARENT_FIRST
        udata.u_ptab->p_status = P_READY;
+#endif
+
        /*
         * Kick off the new process (the bifurcation happens inside here, we
         * *MAY* returns in both the child and parent contexts, however in a
@@ -412,7 +417,6 @@ arg_t _fork(void)
                nready--;
        }
        irqrestore(irq);
-
        return r;
 }