McKenney: So You Want to Rust the Linux Kernel?
McKenney: So You Want to Rust the Linux Kernel?
Posted Oct 11, 2021 0:19 UTC (Mon) by neilbrown (subscriber, #359)In reply to: McKenney: So You Want to Rust the Linux Kernel? by PaulMcKenney
Parent article: McKenney: So You Want to Rust the Linux Kernel?
yes that does help - particularly in providing me with specific points to disagree with - or at least to discuss.
While use of a refcount does provide the opportunity to read the counter value (kref_read()) it is my understanding that the code has no business doing that. The value could change between the moment when it is read and the moment when it is used, making it unreliable at best. However Linux does have a surprisingly large number of calls to kref_read(). Many are in debugging messages, which would be expected to be racy. I haven't analysed the others - probably many are justified. In any case I don't think this ability is core to refcounts in general, just useful in some particular refcount patterns.
While RCU cannot fail to claim the bulk-reference, rcu_dereference() can still return NULL. This can be roughly equivalent to kref_get_unless_zero() failing.
While the CONFIG_PREEMPT=n RCU implementation has no explicit reference counter, it seems likely to me that there is some implicit counter. At any moment there is some set of events which need to happen before a grace period can end. I think they are "a CPU calls schedule() or returns to user-space". The size of this set is the implicit ref-count.
While I accept that replacing reader/writer locking may be a common path to RCU, I don't think that at all justifies presenting such a flawed analogy. Describing RCU as "a way of waiting for already-started things to finish" is equally unjustified, though for completely different reasons. I personally prefer "exploiting deferred destruction" as the description, but even that misses the core idea.
The core idea of any RCU-solution is to use write-side atomics and read-side barriers. They are what replaces the various sorts of spinlocks. The use of rcu_read_lock() provides a context where these barriers and atomics can be used safely - particularly when combined with pointer dereference. I guess you could argue that this parallels a spinlock which provides a context where normal loads and stores can be used safely. Whether that parallel is enough to justify the analogy .... I'm not sure.
I was surprised by the strong contrast you suggested between RCU and hazard-pointers. They are both deferred-destruction mechanisms which seems to me to have similar use cases. In fact your perfbook suggests it may be possible for an implementation to switch between them based on dynamic assessment of performance needs. This points to substantial similarity.
I guess the distinction you are highlighting is that RCU can only hold its bulk-reference for a bounded time (or a least, there are costs that are at least linear in hold time). Hazard-pointers can protect a single pointer for an arbitrary time for comparatively little cost. This allows hazard pointers to *replace* refcounts in a way that RCU cannot.
This suggests I could clarify my analogy to say that "RCU provides an effective TRANSIENT reference-count on *all* objects...".
Or something like that.
The heart of my argument is that a spinlock typically prevents an object from changing, while a refcount prevents it from being destroyed. Surely RCU is more like the latter than the former.
Behind these thoughts is the observation that I read from time to time that RCU is difficult. Similarly that memory-barriers.txt is confusing. These comments remind me of the famous "you are not expected to understand this" in the Unix Edition 6 scheduler. It has always bothered me.
All computing is difficult, but we build abstraction and tools to make it manageable. "While" loops are better than goto's. Recursion is rendered transparent by invariants. Resource management can be greatly simplified by ownership tracking.
Telling people that something is hard, or that it does not need to be understood, is self-defeating as some of them might believe you and not even try.
If RCU is perceived as difficult then we (collectively) would be well served by efforts to find the weak points in our presentation of it and to strengthen them (and you are typically at the forefront of this). I genuinely think that "Like a lock" is one of those weak points.
For myself, I've found that thinking about RCU as a transient refcount on everything (which makes use of atomics safe) made it a lot easier to think about. Similarly the introduction of "read_acquire" and "store_release" terminology makes it much easier to think about memory barriers than "read-memory-barrier" and "write-memory-barrier" ever did.
Maybe the refcount analogy wouldn't work as well for others, and maybe we should avoid analogies altogether. Certainly "a lock that doesn't provide exclusion" doesn't seem like a winning description.
"RCU supports find-grained concurrency control using atomics and memory barriers and particularly allows pointers to be dereferenced without holding any lock" ??
(Some might say "supports lockless programming" but I hate that term as there still are locks - the CPU/memory controller manage them internally)
Thanks.
