I think people really don't appreciate just how incomplete Linux kernel API docs are, and how Rust solves the problem.
I wrote a pile of Rust abstractions for various subsystems. For practically every single one, I had to read the C source code to understand how to use its API.
Simply reading the function signature and associated doc comment (if any) or explicit docs (if you're lucky and they exist) almost never fully tells you how to safely use the API. Do you need to hold a lock? Does a ref counted arg transfer the ref or does it take its own ref?
When a callback is called are any locks held or do you need to acquire your own? What about free callbacks, are they special? What's the intended locking order? Are there special cases where some operations might take locks in some cases but not others?
Is a NULL argument allowed and valid usage, or not? What happens to reference counts in the error case? Is a returned ref counted pointer already incremented, or is it an implied borrow from a reference owned by a passed argument?
Is the return value always a valid pointer? Can it be NULL? Or maybe it's an ERR_PTR? Maybe both? What about pointers returned via indirect arguments, are those cleared to NULL on error or left alone? Is it valid to pass a NULL ** if you don't need that return pointer?
Sometimes these requirements were reasonable, just unwritten. Sometimes they were a bit too flexible/wild and I had to make some opinionated decisions when writing the Rust abstractions to narrow it down to a safe usage.
Sometimes I had to add extra locking inside the abstraction in order to make it practical to make safe. Sometimes I had to make some small changes to the C side to make it more orthogonal or logical and usable, e.g. to expose an unlocked function to be used with a lock taken.
Sometimes the locking was subtle enough that while I was able to write a safe Rust abstraction, it came with a big doc comment explaining how you have to be careful with usage and drop order to avoid deadlocks (deadlocks are "safe", Rust doesn't inherently protect against them).
Sometimes it was a lost cause without making the C side more reasonable (drm_sched only, really).
However, most of the time the compromises made when writing the Rust abstraction point at issues with the C side design and how it could be improved.
In general the approach is "write the Rust side making as few changes as possible to the C side first to avoid conflict, then maybe propose changes to the C side based on lessons learned" (we haven't really gotten to the second part yet at all).
But the end result of all this is that you CAN, in fact, just look a the Rust API and know how to use it correctly for the most part. You never have to worry about reference counts, about NULL pointers, about forgetting to check results, about dropping refs in error cases.
You never have to worry about holding the right locks, about accidentally forgetting to take a ref or dropping it twice. You never have to wonder how error returns are encoded.
Because if you make a mistake with these things, your code won't compile.
Of course you can still misuse APIs, but the worst that will happen is that you'll get an error return, or maybe a deadlock (deadlocks are easy to debug with lockdep and I wrote a really neat Arc<> integration to catch potential drop/decref related locking errors).
Even with APIs that mostly are fairly rigorously documented (OpenFirmware/Device Tree comes to mind), following all the rules in C is often tedious and error prone. Look at some random OF code in a driver and there's a good chance it leaks references.
(This doesn't really matter for most systems since they don't compile kernels with OF_DYNAMIC so ref counts are ignored, so this never gets noticed and fixed.)
But with my OF Rust abstractions? They do ref counting for you. You can just forget about it.
In the end, coding kernel code in Rust is a huge change from coding C. With C you have two options:
- Wing it and either hope reviewers catch it or suffer debugging subtle oopses
- Spend hours understanding the code before you dare use it, and hope you caught everything.
This adds extra reviewer and maintainer workload too! It means that they need to review submissions to ensure they follow all these hidden rules that aren't documented. Sometimes they miss things. Sometimes the problem is major enough the code needs a big refactor.
All that just goes away with Rust. Poof. Gone. If it compiles it's safe and won't oops or leak references (except unsafe code, but then you only have to review THAT and the rule is it has to be carefully documented).
Of course we still need code reviews, and help from experts in specific subsystems. Rust doesn't magically make code perfect.
But it does get rid of all the silly low level problems and mistakes, so you can focus on the high level ones.
To be clear, I don't blame Linux developers for the incomplete docs. For better or worse, the Linux kernel is very complex and has to deal with a lot of subtlety. Most userspace APIs have much simpler rules you have to follow to use them safely. Kernels are hard!
Even experienced kernel developers get these things wrong all the time. It's not a skill issue. It's simply not possible for humans to keep all of these complex rules in their head and get them right, every single time. We are not built for that.
We need tooling to help us.
The solution is called Rust. Encode all the rules in the code and type system once, and never have to worry about them again.
Just like the solution to coding style arguments is to encode all the rules in an auto formatter and never have to worry about them again (hint hint! ^^)
And then we can stop worrying about all the low-level safety, ownership, and locking problems, and start worrying about more important things like high-level driver and subsystem design.
• • •
Missing some Tweet in this thread? You can try to
force a refresh
Okay, I generally side with *both* users and riggers because this cmo3 or not situation is mostly Live2D's fault but.
This has got to be the worst rigger take I've seen so far. If you can't afford to keep your clients' files safe by default, you should not be doing this job.
A lot of people aren't appreciating how weird and abnormal the Live2D situation is. Thread.
I make music. When I do a comm, the client can ask for stems, which allows them to remix or remaster the song part by part. I don't need to send them my project file and plugins.
When you commission model art, you don't get the raw working PSD file, the artist's brushes, or their editor settings, but you get a cleaned cut PSD file that can be rigged but also built upon for adjustments and additions if necessary.
When you commission a 3D model, you may or may not get a raw .blend file, but whatever final model file can usually be re-imported for adjustments or changes and additions, and different models can be merged together into one.
The bigger issue here is Live2D moc3 is a proprietary file format that has not had meaningful third party tooling/RE.
It's like the only options are you can't service your car at all, or the manufacturer gives you all their source code.
It doesn't have to be like this.
Live2D could easily fix this by allowing moc3 files to be imported into a new cmo3 file "as a black box", set up so that you can remove/hide ArtMeshes and build on the existing parameter inputs and physics settings, but not view/change the actual parameter rigs.
Kernel level anticheat is evil, but also, if you're running this kind of insane setup you kind of should expect it to stop working with any OS/driver update too.
If you're booting your OS from a fake evil noncompliant NVMe drive itself then... yeah you kind of had this coming.
TLDR Valorant isn't bricking anything, it just turns on a system security feature that makes these crazy fake NVMe drives stop working (because they don't follow the NVMe spec)... and I guess some people had Windows installed on them so it stops booting or something.
Almost every thread about Rust for Linux ends up with someone saying "why not Zig instead"? And usually the answer is just "it's less mature" or "nobody pushed it".
I didn't know anything about Zig, so I decided to take a look today... and I'm not very impressed ^^;;
Those are major reasons why I chose Rust for the drm/asahi driver...
It sounds like Zig is trying to be "modern C"... but the whole point of R4L is to not get stuck with C!
All those things Rust has that Zig doesn't are important for the things I'm doing.
Destructors/RAII are fundamental to how the driver tracks and cleans up firmware structures safely and reliably when needed. If I had to write "defer" everywhere it would be a bug-prone mess...
Honestly, I'm kind of sad about Wedson leaving RfL. He developed a huge part of the foundation that made Rust for Linux possible.
I'll still work on DRM (except sched) and driver upstreaming when the core stuff is in place, but I don't know about other subsystems.
At the rate things are going, I wouldn't be surprised if upstreaming the drm/asahi driver isn't possible until 2026 at the earliest. I had hopes for things to move much faster, but that's not possible without active cooperation from existing maintainers, and we aren't getting it.
Reading upstreaming mailing list threads is painful. Every second comment is "why is this not like C" or "do it like C". Nobody is putting any effort into understanding why Rust exists and why it works. It's just superficial "this code is scary and foreign" type reactions.