When a domain invokes a key either explicitly or implicitly, the kernel acts in two phases:
- The dry run where it sets no global state that is meaningful between entries into the kernel except for shared and exclusive locks.
- After all of the locks have been acquired the wet run begins and global state under control of the exclusive locks may be modified and data under the shared locks may be examined.
If some lock cannot be acquired the operation will be deferred or fail and then there are then several cases:
- The domain may be referencing some component of itself or perhaps a component of a domain to which the kernel must return the response (as when the last key of the message is a resume key to another domain).
Some such cases are allowed but slow less optimized general code is required.
- A loop in a meter chain is discovered or other cases of a node participating twice in a transaction.
Such cases are disallowed.
- In a MP configuration two CPUs may be running the kernel or one CPU may be attempting to operate on a node that is a component of another domain that is running on another CPU.
Keykos has no such code just now as it does not support MP yet.
The exclusive locks are in place to detect and avoid these collisions.
It might be necessary to signal the CPU running the locked domain to deschedule and unlock that domain, and then to run the domain attempting the blocked action.
This would make rescind operations very prompt.
After the kernel has responded to the invocation the locks are released.
Each node or page has a count of the number of times it has been locked in shared mode.
It is quite rare for a node to participate in one kernel transaction twice but it was easier to allow than to disallow.
See this note contrasting the function of primitive hardware locks and those of Java.
The “general code” referred to above sounds complicated.
Actually it is simpler than the fast code.
The general code marshals the message from the domain into an area of the kernel thus matching the conceptual steps.
This marshaling is oblivious to the meaning of the message and the kernel object being invoked.
The marshaled message includes the keys from the message.
The invoker is then unlocked.
Enough of the recipient is verified to be in core to receive the response message.
The action is then attempted and resulting message placed in a separate kernel area.
The message is then delivered to the recipient.
An issue is the semantics of an operation that changes how the recipient will receive the response.
The recipient may have been “receptive” before the operation but not after.
If this is a security hole then such operations may be disallowed.
They once were and still may be.
Except for performance, there would be no need for the fast case which merely interprets the message as it is found in the component parts of the invoking domain.