colinm@runx.ips.oz (Colin McCormack) (07/03/88)
I have spent the last 7 months porting minix v1.2 to 68000 and am close to completion. I can run user programs like ls without too much trouble, although there are still bugs to be worked on. The target machine is a locally produced 68000 and is unlikely to compete with the ST version. At this point I thought I'd drop a line summarising the changes I made and raising a few points of interest. I made a lot of changes to the kernel/mm/fs. The main followed on from a decision to permit different link spaces for all tasks and processes. I moved the proc, mproc and fproc structures out into ram and only keep a pointer to them in kernel/mm/fs (this compiles to more efficient code on my machine, since all pointers are 4 bytes and can be indexed without an expensive multiplication). I allowed kernel and mm to share the map structure to minimise the message traffic that minix uses to keep the two copies in step. I added a message exchange and some extra primitives to permit conditional receives and resend message to self (not that these facilities are used very often - minix messages are usually synchronous.) I changed fs and all tasks to make devices configurable at boot time. Build is also done at boot time. The above were changes I _chose_ to make, not strictly changes that had to be made to port to 68000. I will eventually remove the arrays proc[], mproc[] and fproc[] entirely and use bits in proc structures to indicate type of process. The process number will become a process pointer in this scenario (which is more efficient since my C compiler uses 32 bit ints anyway). The kernel is very much changed, most of the interrupt handling is done in C now, in a general fashion, with all interrupts defined by a small control block - this may be changed when I am in a position to assess its performance impact. Bruce Evans has pointed out that a lot of the time processes line up waiting for disk i/o. This is as you would expect, but the problem appears when you consider that fs is not internally multitasking, so one process waiting for a disk will hold up all other processes wanting to perform _any_ i/o, even character device i/o (whereas character device i/o _can_ be performed by several processes concurrently if the tasks handling the i/o are written to support it). The major difference between character and block i/o in fs is that all character device i/o is performed on behalf of the client process which requested it, whereas all block device i/o is done by the fs _on_behalf_of the_fs_ since block i/o is always indirected through the cache. For this reason, I doubt that block suspension can be handled in the same way as character i/o suspension (also, I think that the character i/o suspend logic in fs depends too much upon the task to implement it.) Since this is proving to be a major bottleneck in fast processor implementations of minix, I think it deserves some attention. Let me point out at the outset that block device i/o is performed by the fs functions get_block() and put_block() which are logically part of the cache and are called in about 32 and 20 places (respectively) through fs. This implies that any traditional method will have to maintain the context of these 52 calls across a suspension. I think this is a very complex undertaking (The way it's often handled is via lots of interlocked finite state machines with a central message-reader/coroutine-dispatcher which I personally find repulsive.) I suggest a different alternative (not fully worked out) for the perusal of the comp.os.minix group. Note that it relies on facilities nobody has yet implemented (to my knowledge) in a complete or acceptable way. Make cache a proper device handled by a task (as Bruce Evans will explain there may be some benefit to having it handled by /dev/ram). So all of the get_ and put_block() calls will be sendrec()s to /dev/cache requesting the cache device to return a block or store a block. Let cache device send messages directly to any of the other block devices and let it perform all of the necessary copying from the cache to the nominated addresses. Make all of fs data structures external to the fs data and bss space (this implies that mm can malloc anywhere in ram and recover the space if the owner dies, and that you 80x86 people can handle so-called "far" pointers). Provide some interprocess synchronisation method like token-passing (or semaphores, or critical regions - whichever you prefer) to sequentialise access to the fs data structures . Modify fs so that it handles all of the data structures currently defined as globals in critical regions. Finally: fork() a new fs for all block device i/o. I would appreciate feedback on this suggestion. Colin McCormack. ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????