I like to go back at the drawing board. Gives you opportunity to think, to look at what you have, and to reconsider what you need.
The MH's memory map system (pmap) move to machine independent part – similar to the change that did this for the CPUs and IPIs management subsystem) – offered such opportunity for this key component.
Compared to other memory subsystems I have seen in the past, I always thought of MH's virtual memory to be extremely simple. So, before blindly porting a code that shows signs of ageing and where different strata starts emerging, I decided to look at the states of a virtual mapping. Afterall, I expected it to be simple.
The first layer was easy to draw:
I.e., a user PTE is either unmapped, or mapped to a hardware I/O page, or mapped to a memory page. The dot points to the default state, that is at start we expect all virtual memory to be unmapped.
Of course, it's not that simple. Mapped pages can be wired by the kernel, i.e. made unable to be unmapped by userspace programs. The act of wiring a page is still initiated by the user, by exporting a memory to a hwdev. Wiring is needed because exporting a page to a hwdev allows doing DMA on that memory region.
An updated graph will look like this:
Which is – still – very simple. The square represents what's left of the node called Mapped in the earlier figure.
But this is ignoring something really important: copy-on-write. MH supports forking of processes. As essentially any other OS who supports paging, it uses copy-on-write as an optimization for duplicating address spaces: instead of being copied, the memory is shared but set read only.
When a process tries to write to a shared page, it will generate a page fault, which will be forwarded as an interrupt to the process. It is userspace responsibility to create a new page, copying its content, and substitute the read-only shared mapping with a writable copy.
This then means that this state, RO Shared, can only be changed by userspace by unmapping it. There is an exception of course, which is due to an optimization: when all processes but one have all created their own copies, the page is effectively not shared anymore, and can be re-made private.
An important note to add at this point is that copy-on-write is the only case in MH where memory is shared between processes.
Updating the graph yields:
This starts to look interesting, and messy enough to look real. It is not complete, yet.
As said earlier, the read/write information is lost during the process of copy-on-write, so in the current implementation the page returns into being private, but read-only, no matter the original state. This is not a problem, though, as long as the userspace process can handle unexpected RO page faults by fixing them back to writable. The MRG system library supports this.
Adding details about R/W state brings to a more complete graph, with clear signs of state explosion:
Is this complete? Of course not. We're missing information about the executable state of a page. A page can be executable or not, and as long as it is private and not wired its state can be changed.
I won't display this, though, as it is essentially an orthogonal state with regards to the lifetime of a page.