rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* looking for an informed write up on how to handle serious multicore issues in rust
@ 2025-04-22 14:44 Mateusz Guzik
  2025-04-22 15:46 ` Mateusz Guzik
  2025-04-22 19:37 ` Alice Ryhl
  0 siblings, 2 replies; 3+ messages in thread
From: Mateusz Guzik @ 2025-04-22 14:44 UTC (permalink / raw)
  To: rust-for-linux

Hello.

There is a lot of Rust marketing around "fearless concurrency".  The
features showcased don't cut it in projects with serious needs and I
failed to find a write up on what to do in that case.

Everything I found so far revolves around the following:
- an embarrassingly parallel problem, where you can pawn off
everything -- good, but not applicable for vast majority of the kernel
- type system which prevents non-mp safe code from being used in a
multicore setting  -- nice, but ultimately pretty minor
- some atomic usage, sometimes mixed with unsafe -- does not cover
what's needed (see below)
- wrapping all accesses with a guard obtained by issuing .lock() on an
object -- non-starter for code with serious needs (again see below)

Resources I checked:
- the official book (https://doc.rust-lang.org/stable/book/) -- maybe
the usage is too advanced to mention there, but there are 0 pointers
where to go should you need it
- Rust for Rustaceans (https://rust-for-rustaceans.com/) -- same
- Rust Atomics and Locks (https://marabos.nl/atomics/) -- it describes
some of the nature of the problem, but does not show to deal with it
- Rust for C programmers -- same as first two

I also found the crossbeam crate for reference -- again only has some
of what's needed.

Ultimately you can't fine-grained lock your way to high performance,
neither in terms of scalability nor single-threaded execution. The
reality of hardware is that not only lock contention is terrible (let
alone in a NUMA setting), but the very operation of acquiring a lock
is expensive even if it is cache-hot. Locking for everything may be
tolerable for small-scale userspace programs, it's a no go for a
modern kernel intended to run on boxen with dozens of cores.

Here is an example of what's needed, how C falls short in helping out
to validate correctness and where Rust could help at least in
principle (except *so far* I failed to find any resources on how to do
it). Of course an individual determined to do something wrong will
find a way to bypass any and all safety belts -- for cases like that I
have no expectations of Rust. There is however plenty of honest
mistakes to make here and which Rust could help with.

I'm condensing the crux of it into the following:
struct meh {
       /* read-mostly area */
        seq_t seq;
        long safe_to_read;
        char name_safe_to_read[32];
        atomic_t modifiable_without_the_lock;
       /* ... some other fields */
       /* the lock protecting the struct, explicitly separated from
read-mostly area */
       spin_lock lock ___cacheline_aligned_in_smp;
       long need_the_lock_to_access_in_any_way;
};

The first 3 fields (seq, safe_to_read, name_safe_to_read) must only
ever be written to with the spin lock held.

However, everyone is free to read them without the lock and this
happens frequently.

Suppose the common case reads both safe_to_read and the name and needs
the seq counter to ensure correctness of the result, maybe like this:
long meh_name_to_key(struct meh *obj, const char *name) {
        seq = seq_read(obj->seq);
        if (name_equal(obj->name_safe_to_read, name)) {
                key = READ_ONCE(obj->safe_to_read);
                if (seq_equal(obj->seq, seq)) {
                        return key;
                }
        }
        spin_lock(&meh->lock);
        if (name_equal(obj->name_safe_to_read, name))
                key = obj->safe_to_read;
        spin_unlock(&meh->lock);
        return key;
}

Pretend this is a part of a lockless hash lookup or similar.

If one was to write C code to handle this *with* safety belts, it
would be a lot of hand-rolled asserts and even then there is some
possibility of a screw up.

Note in this case the ->name_safe_to_read array might *change* as it
is being used to compare in name_equal(). False positive/negative
mismatch is handled with the sequence counter.

So I would like a code sample in Rust which handles the above in a
safe and performance-friendly manner. Of course preferably code trying
to misuse the struct would fail to compile, but a debug-only runtime
check would do just fine.

Most notably:
- the code issuing name check + key read *must* validate the sequence
counter before returning
- need_the_lock_to_access_in_any_way must not be accessible without
the lock (duh)
- no writes to the read-mostly area without the lock held, except for
the atomic var

So how do you do this in Rust? I did not cover enough, I can write
self-contained fully-fledged C example.

-- 
Mateusz Guzik <mjguzik gmail.com>

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-04-22 19:37 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-22 14:44 looking for an informed write up on how to handle serious multicore issues in rust Mateusz Guzik
2025-04-22 15:46 ` Mateusz Guzik
2025-04-22 19:37 ` Alice Ryhl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).