Revised IPC DesignThis note describes the second iteration of the EROS IPC interface. The revised IPC iterates on the first design to improve the performance of some common cases. The change itself isn't especially earth shattering, but thinking through the implementation on a register starved architecture was tricky. 1. The Original IPCThe first-round EROS IPC mechanism had the following interface:
On return, the entry receive limit field is overwritten with This interface suffers from a number of unfortunate limitations:
In consequence, we have redesigned the IPC interface. The new interface transmits more information for essentially the same cost. For the most part, we have achieved this by trading off the cost of field extraction for cache-resident data motion. The resulting interface is a bit slower in low frequency cases, but considerably faster in the cases that prove to matter. 2. The Revised InterfaceThe revised EROS IPC mechanism has the following interface:
In the new interface, the receive buffer length has been extended to a 32 bit field. While the current interface restricts the transfer to 64K, this limit could be compatibly increased in future implementations. On register rich architectures, including most RISC machines, there is no problem with taking up more registers. While the sender must save/restore and zero any of the transmitted registers that it does not wish to transmit, there are several ameliorating considerations:
3. Impact on Register-Poor ArchitecturesThe main problem with this change is the impact on register-poor architectures. Since the most widely deployed processor (x86) is register poor, this bares thinking on. Since the x86 is probably the most register impoverished architecture, we will describe the implementation for the x86 and leave it to the reader to adapt to similarly deficient architectures. The main complication is that those invocations that do not require a data buffer should be executable from a read-only address space. While this is not the common case, there is some bootstrap code in the EROS system that must be able to run in an immutable space to support certain confinement and security arguments. This means that any values that are mutated as a side-effect of the IPC (receive length, key data) must reside in registers. In addition, the opcode and return code should reside in registers, though it is acceptable for them to be mapped to the same register. The x86 provides only eight user-mode registers: %EAX, %EBX, %ECX, %EDX, %ESI, %EDI, %EBP, and %ESP. With the exception of %ESP, all of these registers can be modified as part of the calling convention, and even %ESP can safely be used as a general-purpose register in an immutable address space. Modifying %EBP presents a challenge to debuggers. This is extremely unfortunate, but in practice the IPC trap is a readily identifiable leaf procedure, and can be recognized by the debugger as a special case (as a recovering debugger architect, I confess that this makes me shudder). On the x86, the hardware will copy values off of the stack on entry into the kernel when performing a privilege crossing
The invocation type (call, return, send) is determined by the kernel entry point. The stack pointer (
struct InvInfo {
Word invType; /* IT_CALL, IT_SEND, or IT_RECEIVE */
Byte *sndPtr;
Word sndLen;
Byte *rcvPtr;
};
In an immutable address space, this data structure must be suitably pre-allocated, and the stack pointer must be set to point to the instance appropriate to the invocation in question. While the sndKeys and rcvKeys fields could be combined, the cost of the marignal shift and mask makes this a wash. Better to keep it simple. Originally, I thought it worthwhile to have a limit on the number of registers transferred or received so as to give the process at least one register that is not mutated by IPC. For the most part this is only a problem in immutable address spaces. If it proves necessary, the applicable limits should be appended to the InvInfo structure. In practice, it turned out that only the process construction/destruction code needs to do even as much as a loop structure while making calls. This loop can simply be unwound, so I decided to stick with the current interface and avoid the additional test in the IPC path. Copyright 1998 by Jonathan Shapiro. All rights reserved. For terms of redistribution, see the GNU General Public License |