If the lifecycle of inodes is filesystem-specific, it should be managed via filesystem-specific functions.
I had the same question. They're trying to understand (or even document) all the C APIs in order to do the rust work. It sounds like collecting all that information might lead to some [WTFs and] refactoring so questions like this don't come up in the first place, and that would be a good thing.
It's an overview of the VFS layer, which is how they do all the filesystem-specific stuff while maintaining a consistent interface from the kernel.
I'm not an expert by any means but I'm somewhat knowledgeable, there's different functions that can be used to create inodes and then insert them into the cache. `iget_locked()` that's focused on here is a particular pattern of doing it, but not every FS uses that for one reason or another (or doesn't use it in every situation). Ex: FAT doesn't use it because the inode numbers get made-up and the FS maintains its own mapping of FAT position to inodes. There's then also file systems like `proc` which never cache their inode objects (I'm pretty sure that's the case, I don't claim to understand proc :P )
The inode objects themselves still have the same state flow regardless of where they come from, AFAIK, so from a consumer perspective the usage of the `inode` doesn't change. It's only the creation and internal handling of the inode objects by the FS layer that depends based on what the FS needs.
Does Rust need to change to make it easier to call C?
I've done a bit of Rust, and (as a hobbyist,) it's still not clear (to me) how to interoperate with C. (I'm sure someone reading this has done it.) In contrast, in C++ and Objective C, all you need to do is include the right header and call the function. Swift lets you include Objective C files, and you can call C from them.
Maybe Rust as a language needs to bend a little in this case, instead of expecting the kernel developers to bend to the language?
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
Now, if you have a complex library and don't want to write all of the declarations by hand, you can use a tool like bindgen to automatically generate those extern declarations from a C header file: https://github.com/rust-lang/rust-bindgenThere's an argument to be made that something like bindgen could be included in Rust, not requiring a third party dependency and setting up build.rs to invoke it, but that's not really the issue at hand in this article.
The issue is not the low-level bindings, but higher level wrappers that are more idiomatic in Rust. There's no way you're going to be able to have a general tool that can automatically do that from arbitrary C code.
The article is about finding ways of using rust to actually implement kernel fs drivers/etc. Note that any rust code in the kernel is necessarily consuming C interfaces.
Bindgen works quite well for the use case that you are thinking.
You can use the bindgen crate to generate bindings ahead of time, or in a build.rs and include!() the generated bindings.
Normally what people do is create a `-sys` crate that contains only bindings, usually generated. Then their code can `use` the bindings from the sys crate as normal.
> in contrast, in C++ and Objective C, all you need to do is include the right header
and link against the library.
No, because it's already dirt-simple to do. You just declare the C function as 'extern "C"', and then call it. (You will often need to use 'unsafe' and convert or cast references to raw pointers, but that's simple syntax as well.)
There are tools (bindgen being the most used) that can scan C header files and produce the declarations for you, so you don't have to manually copy/paste and type them yourself.
> Maybe Rust as a language needs to bend a little in this case, instead of expecting the kernel developers to bend to the language?
I think you maybe misunderstood the article? There's nothing wrong with the language here. The argument is around how Rust should be used. The Rust-for-Linux developers want to encode semantics into their API calls, using Rust's features and type system, to make these calls safer and less error-prone to use. The people on the C side are afraid that doing so will make it harder for them to evolve the behavior and semantics of their C APIs, because then the Rust APIs will need to be updated as well, and they don't want to sign up for that work.
An alternative that might be more palatable is to not make use of Rust features and the type system in order to encode semantics into the Rust API. That way, it will be easier for C developers, as updating Rust API when C API changes will be mechanical and simple to do. But then we might wonder what the point is of all this Rust work if the Rust-for-Linux developers can't use Rust some features to make better, safer APIs.
> I've done a bit of Rust, and (as a hobbyist,) it's still not clear (to me) how to interoperate with C.
Kinda weird that you currently have the top-voted comment when you admit you don't understand the language well enough to have an informed opinion on the topic at hand.
It wasn’t completely straightforward, but on the whole I figured out everything I needed to within a few days in order to be able to do it.
Calling C would surely be very similar.
https://github.com/tree-sitter/tree-sitter/blob/25c718918084...
Seems like the answer is that it's reimplementing and doesn't use the same names.
Since the Rust function has implicit/automatic behavior depending on how it's state is and how it's used by the callsite, and since the C one doesn't have any implicit/automatic behavior (as in, separate/explicit lifecycle calls must be made "manually"), I don't even see the reason for them to have the same name.
That is to say, having the same name would be somehow wrong since the functions do and serve for different stuff.
But it would make sense, at least from the Rust site, to have documentation referring to the original C name.
I disagree with the negative tone of this thread, I'm quite optimistic given how clearly the parties involved were able to communicate the pain points with zero BS.
I suspect the discussion was about as charged, meandering, and nitpicky as we all expect a PL debate among deeply opinionated geeks to be, and Jake Edge (who wrote this summary) is exceptionally good at removing all that and writing down substance.
We are talking about extremely competent people who worked on a critical piece of software for years and invested a lot of their lives in it, with all pain, effort, experience, and responsibilities that come with that.
That this debate is inscribed is a process that is still ongoing, and in fact, progressing, is a testament to how healthy the situation is.
I was expecting the whole Rust thing to be shut down 10 times, in a flow of distasteful remarks, already.
This means that not only Rust is vindicated as promising for the job, but both teams are willing and up to the task of working on the integration.
Those projects are exhausting, highly under-pressure situations, and they last a long time.
I still find that the report is showing a positive outcome. What do people expect? Move fast and break things?
A barrage of "no" is how it's supposed to go.
Imagine getting this comment about the open source project you contribute to:
"Science advances one funeral at a time"
For this particular work the huge benefit of Rust is its enthusiasm for encapsulating such safety problems in types. Which is indeed what this article is about.
C and particularly the way C is used in the kernel makes it everybody's responsibility to have total knowledge of the tacit rules. That cannot scale. A room full of kernel developers didn't entirely agree on the rules for a data structure they all use!
Rust is very good at making you aware of rules you need to know, and making it not your problem when it can be somebody else's problem to ensure rules are followed. Sometimes the result will be less optimal, but even in the Linux kernel sub-optimal is often the right default and we can provide an (unsafe) escape hatch for people who can afford to learn six more weird rules to maybe get better performance.
lol... you're talking about the linux kernel, written in C.
The vast majority of software over many decades "bottoms out" in C whether in VMs, operating systems, device drivers, etc.
The scale of the success of C is unparalleled.
Using an unsafe block with a very limited blast radius doesn't negate all the guarantees you get in all the rest of your code.
Unsafe blocks limit amount you need to get correct, but you need to get all of them correct. It is not a blast limiter.
This is definitely a strong benefit though.
It's true that you need to have unsafe code to do low level things. But it's a misconception that if you have to use unsafe then Rust isn't a good fit. The point of the safe/unsafe dichotomy in Rust is to clearly mark which bits of the code are unsafe, so that you can focus all your attention on auditing those small pieces and have confidence that everything else will work if you get those bits right.
How much of this is actually 100% unambiguously necessary? Is there a good reason why anything in the filesystem code at all needs to be unsafe?
I suspect it's a very small subset needed in a few places.
I have to admit, while I do enjoy rust in the sense that it makes sense and can really "click" sometimes. For anything asynchronous I find it really rough around the edges. It's not intuitive what's happening under the hood.
One of the major wins of Rust is encoding thread safety in the type system with the `Send` and `Sync` traits.
Yes, but that should be offsetted by easier driver development. See the blog about Rust GPU driver for asahii linux, done in one month. EDIT: Google "tales of the m1 gpu" (author has a very negative opinions about hacker news, read if you like by clicking the link https://asahilinux.org/2022/11/tales-of-the-m1-gpu/)
Is it universal? We'll see in coming years.
That's fundamentally different and harder than a driver being written in rust that uses the Linux's subsystems C APIs.
I can see a lot of drivers taking on the complexity tax of being written in Rust easily. The complexity tax on writing a whole subsystem in Rust seems like an exponentially harder problem.
I am not sure if author (Asahi Lina) has, but the project lead Hector Martin definitely has.
Copy-and-pasting works.
The tax will be seen as a necessity to embrace future and progress.
I'm wondering why do not restrict ourselves to a safe subset instead of jumping into a huge bandwagon of unknown bugs and tradeoffs
(The best current effort is https://sel4.systems/ , which is written in C but has a large proof of safety attached. The language design question is basically: should the proof be part of the language?)
A tax in one place may not be a net negative, if it's used like in the real world to offset other problems. And just saying it will not offset any problems because of a single discussion, that does not have a definite conclusion, comes of as a short argument.
If you mean that not using Rust (or maybe some other languages e.g. Zig or Ada?) means that there can be no innovation in the Linux kernel, I would have to disagree since there's been plenty of progress in plain old c (see for instance io_uring), not to mention the fact that the c language itself could change to make developer ergonomics better - since that seems to be the nub of the problem.
It also raises the question of what happens in the future when Rust is no longer the language du jour - how do we know it's going to last the course? And now there's 2 different codebases, potentially maintained by 2 different diminishing sets of active maintainers.
additional code can replace more complex existing or future code, thus can reduce complexity
Ah, the struggle of legacy naming conventions. I've had success in keeping the same name but when I wanted an alternative name I would just wrap the old name with the new name.
But yeah, naming things is hard.