I came across the post about Milk-V Titan, and there was a comment asking about the lack of the V extension would hinder running Ubuntu 25.10 which was targetting a particular RISC-V configuration, and it made me wonder if there were an opportunity for micro kernels to exploit.
Now, up-front: it’s been literally decades since I had an OS design class, and my knowledge of OS design is superficial; and while I’ve always been interested in RISC architectures, the depth of my knowledge of that also dates back to the 90’s. In particular (my knowledge of) RISC-V’s extension design approach is really, really shallow. It’s all at a lower level than I’ve concerned myself with for years and years. So I’m hoping for an ELI-16 conversation.
What I was thinking was that a challenge of RISC-V’s design is that operating systems can’t rely on extensions being available, which (in my mind) means either a lot of really specific kernel builds – like, potentially an exponential number – or a similar number of code paths in the kernel code, making for more complicated and consequently more buggy kernels (per the McConnell rule). It made me wonder if this is not, then, an opportunity for micro kernels to shine, by exploiting an ability to load extension-specific modules based on a given CPU capability set.
As I see it, the practicality of this depends on whether the extensions would be isolatable to kernel modules, or whether (like the FP extension) it’d just be so intrinsic that even the core kernel would need to vary. Even so, wouldn’t having a permutation of core kernel builds be smaller, more manageable, and less bug-prone than permutations of monolithic kernels?
Given the number of different possible RISC-V combinations, would a micro kernel design not have an intrinsic advantage over monolithic kernels, and be able to exploit the more modular nature of their design?
edit clarification
Perhaps I’ve missed something that disagrees with this, but as far as I know Ubuntu’s decision to target the latest profile is primarily a question of how they are configuring the compiler when building the binary packages: it will be configured so that, for example, the compiler’s autovector optimizations are allowed to transform scalar code into vector code when that’s productive, and so the resulting binaries are not guaranteed to run on a processor that doesn’t support V.
The kernel itself has code to support context switching when running userspace code that uses V to preserve the contents of all the extra registers, but the kernel can detect that at runtime, so it doesn’t require separate kernel builds. This is similar to how adding kernel support for the various x86 SIMD extensions didn’t prevent the kernel from running without them.
Therefore I don’t think the kernel’s internal architecture makes a great deal of difference to this situation. Ubuntu could, if they wanted to, keep building packages with the compiler configured to target the old profile, in which case only software that explicitly uses the new extensions in its own source code (rather than as an automatic optimization in the compiler) would still work, but those binaries would not exploit the new extensions even when they are available. I assume Ubuntu just wants to maximise the performance benefits of using V and are betting that new hardware will become available soon enough that they can get away with not maintaining two parallel sets of packages targeting different RISC-V profiles for the entire LTS period.
(Separately, I don’t think there’s anything to prevent having a Linux loadable kernel module containing V instructions and loading it into a kernel that doesn’t use them otherwise, as long as the code in that module is careful to avoid leaving the V registers in a wrong state when control returns to the rest of the kernel. The main kernel would be oblivious, unless the module were buggy.)
Isn’t that what kernel optimized distributions do, though? Compile out all of those code paths, making for more efficient kernel execution? Sure, you can build a kernel with every option enabled, and you have a kernel that will run on any hardware with that architecture, but it’s bloated, big, takes longer to load, and is slower. Back in the day, when we had no other choice, we went through that awful kernel configuration menu and hand selected options based on exactly what our hardware supported; it was expected, if you wanted decent execution times, and for your kernel image to not take up all of your limited HD.
My suspicion is that these sort of CPU-level RISC-V feature sets are so low level they’d be in in the micro-kernel core, so the modularity wouldn’t help. However, microkernels being much smaller, recompiling for a given feature set and producing a smaller binary with more efficient code paths (and the kernel subsequently not having to repeatedly check for the existence of vector support - the cost for that must be at least a branch) would be fast and improve efficiency at the cost of a much smaller core compile.
I guess the part I’m having trouble with is that the microkernel vs. monolith question is mostly about what privileges different parts of the system run under, rather than how they are compiled and loaded into memory and what CPU features they use.
I would agree that any sort of modular kernel could make it possible to choose on a driver-by-driver basis whether to load a module built with vector instructions or without. But I don’t think it’s greatly important whether those modules are running as isolated userspace processes with limited privilege or all inside the kernel with supervisor privilege: the important thing is that you be able to decide dynamically (at driver load time) whether to load the version built for the vector extension or the version that does not expect it.
Whatever strategy you use there is some variation of the problem of making sure different parts of the system can cooperate in their use of the vector registers. For drivers in processes microkernel-style that probably looks like normal context switching with the whole vector register set. For a Linux-style kernel module where the driver is just a bunch of functions loaded and linked in the main kernel address space, that’s a question of defining and following a consistent calling convention, which could potentially have less overhead because the callee knows which subset of the registers it is using and so only needs to preserve those across a call, whereas a full context switch would presumably need to save and restore all of them.
(Since we’re talking hypotheticals here I’m intentionally ignoring the detail that the Linux kernel typically avoids using extensions that involve additional CPU state because it avoids the need for saving and restoring all of those extra registers on kernel entry and exit. I expect that this question of what Ubuntu supports is more about normal userspace programs and how that are compiled, rather than the kernel itself. I don’t know that for certain though, since I don’t know exactly what they are planning to change about distribution build process under the new policy.)
Yeah, I think you’re right. You have to re-compiled everything, not just the core kernel, so it doesn’t save you anything. It’s not as if modules are portable bytecode or something.
I clearly wasn’t thinking it through.