|
Message
From: Enrico Weigelt<weigelt@m...>
Date: Fri Apr 18 01:29:48 CEST 2008
Subject: [oc] Naive thoughts on interrupts and processes
Hi folks,
a few naive thoughs about interrupts and process switching from an software-only (but embedded experienced) guy ...
First, I'd rather choose a more abstract name: exceptions.
I see two major reasons why an exception can happen:
a) internal: access violations, page faults, syscalls, preemtion b) external: periphery signals data ready or data request, etc
What should happen in these cases ?
* page fault: -> jump into kernel (hypervisor), so it can handle the fault (eg. swapping in). -> kernel doesn't need access to process' address space * access violation: -> jump into kernel to, so it can handle this violation (eg. sending signal to process or even simulating successful access) -> access to process' address space might be necessary * preemtion: -> jump into kernel, so it can find the next process in queue and switch to it. -> kernel doesn't need process' address space * syscall: -> jump into kernel, so it can handle the syscall. -> kernel needs access to process' address space * signal from periphery: -> jump into kernel to handle the event (at least critical parts) -> kernel needs process' address space.
Obviously the most vital (and performance critical) point seems to be safely switching to kernel privileges, blocking further exceptions and saving the processor status (registers, etc). Ideally this should work in ony cycle.
Proposed solution:
* the current process has several access privile flags, which define allowed operations (eg. accessing privileged registers) * the (process visible) register space is bank switched (eg. 4 banks) * an interrupt causes an primitive kind of context switch: * blocking further interrupts * switching register bank (including privilege flags) * loading IP w/ handler vector * loading some working registers with interrupt type, etc * the RETI instruction swiches the whole thing back, restores the privilege and cleares the interrupt block.
This approach (IMHO) has the big advantage that whole switch is quite cheap and safe (kernel doesn't have to safe, init and later restore registers) and easily manipulate the process' status, including emulating privileged operations. To make nesting OS'es easier, the process visible privilege register(s) could also be virtualized: userland's access from the goes to userland register bank and writes can be catched by exceptions. So it should be possible to stack dozens of OS'es together, without them even noticing it.
How to do preemtion:
There could be some (privileged) TTL counter register, which goes down with each cycle. Reaching zero causes the preemt exception. This way, the kernel can easily specify the exact interval for the next process. For realtime processes, even some exceptions (eg. periphery signals) could be blocked and explictly checked at the preemtion point.
Syscalls and kernel processes:
Of course, it should also be easy to make large parts of the kernel (at least syscalls) preemtible / interruptible by exception. As in my design the syscalls happen through exceptions, the best way (IMHO) is that the syscall exception handler just coordinates the switch to kernelspace (switching registers, etc) and return to "normal" (non-exception) mode.
The CPU design can assist here by having more than just two register banks. So we had eg. these banks:
#0 HYPERVISOR: normally exceptions run here #1 MCP: here the kernelspace stuff runs (syscalls, etc) #2 PRIVILEGED: reserved for nested OS kernel or maybe some drivers #3 USER: plain user processes run here
IMHO it's wise that these banks are technically equal, just limited in their privileges by their privilege register.
For an massive multicore system we could even go some bits further:
* the registers live in an separate cache memory * each core's register space is mapped to their own minipage to their own cache minipage (by an simple mux). * same with working memory, which is mapped by an separate MMU * reg-mux and mmu are configured via memory an area (through MMU ;-P) * exceptions are processed via an special exception multiplexer, which can wakeup an configured core. * at least one core is dedicated to the MCP. (for automatic
failover there could be multiple ones and an watchdog mechanism)
* the MCP does the whole work of context switching and exception
handling for all user cores, eg. by switching their register mapping.
What do you think about these ideas ?
cu
--
---------------------------------------------------------------------
Enrico Weigelt == metux IT service - http://www.metux.de/
---------------------------------------------------------------------
Please visit the OpenSource QM Taskforce:
http://wiki.metux.de/public/OpenSource_QM_Taskforce
Patches / Fixes for a lot dozens of packages in dozens of versions:
http://patches.metux.de/
---------------------------------------------------------------------
|
 |