Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safety, races, and interrupts #26

Closed
jmaargh opened this issue Aug 14, 2022 · 6 comments
Closed

Safety, races, and interrupts #26

jmaargh opened this issue Aug 14, 2022 · 6 comments

Comments

@jmaargh
Copy link

jmaargh commented Aug 14, 2022

This is related to #15

As I see it, there are two problems with the common idea of using a static mut _: Cell for MMIO. As well documented here one of these is ensuring that access is properly volatile and non-dereferencable. As far as I can see, this crate solves that.

However, there are other problems with static mut _: Cell, even if you're not using it for volatile access (including MMIO). The documentation currently says

Volatile access has no automatic synchronization of its own, and so if your target requires some sort of synchronization for volatile accesses of the address in question you must provide the appropriate synchronization in some way external to this type.

which suggests to me that there are cases where using VolAddress through a global const is sound. But I don't think that's the case. The obvious case is where the target has multiple hardware threads. However, I think that interrupts are another source of data races, even on single-core microcontrollers. It may be the case that the compiler produces access instructions that are atomic in some cases, but this is not guaranteed.

I honestly don't know how this can be solved, so I'm mostly opening this issue as a brainstorm. For non-volatile access, you can use atomics with Ordering::Relaxed, safe in the knowledge that it's zero-overhead for most common architectures. But I don't think it's possible to mark an access as both volatile and atomic simultaneously?

@Lokathor
Copy link
Member

In my latest tinkering with GBA stuff, I use a type called GbaCell, which is technically unsound, and it happens to be the case that for simple programs the compiler can't bite me in the butt (yet).

To quote LLVM's Memory Model for Concurrent Operations again:

For each byte of a read R, R_byte may see any write to the same byte, except:

  • If write1 happens before write2, and write2 happens before R_byte, then R_byte does not see write1.
  • If R_byte happens before write3, then R_byte does not see write3.

Given that definition, R_byte is defined as follows:

  • If R is volatile, the result is target-dependent. (Volatile is supposed to give guarantees which can support sig_atomic_t in C/C++, and may be used for accesses to addresses that do not behave like normal memory. It does not generally provide cross-thread synchronization.)

So, volatile accesses short-circuit the normal data race rules (good), and just "do what the hardware does" (okay), but even in a single-threaded situation you can't ensure that volatile and standard accesses stay properly ordered (very bad), Now, at this point you might say "oh we'll use a compiler_fence", but no, rust-lang/unsafe-code-guidelines#347, TLDR those probably don't do anything for us.

The problem with using atomics is that until recently the atomic types didn't actually work on older systems without literal atomic instructions. There's hope though! With rust-lang/rust#100621 we'll be able to declare atomic values (8, 16, and 32 bit) and use atomic load/store (but not the more advanced atomic methods), and these will, when used properly, give correct ordering guarantees. This will finally let us have global mutable data in a sound way on older devices.

But we do have to wait for that PR to roll out, and I'm not sure if there will be other steps after that for it to get into a usable state.

@Lokathor
Copy link
Member

PS: I'm told that LLVM allows an access to be atomic and volatile at the same time, but rust does not expose this even on nightly.

@jmaargh
Copy link
Author

jmaargh commented Aug 17, 2022

Thanks for your responses, really useful info.

PS: I'm told that LLVM allows an access to be atomic and volatile at the same time, but rust does not expose this even on nightly.

Yeah, I've been doing some reading and discovered the same. I should try and figure out the best place to raise this with the standard library team as it seems like a generically valuable feature to support. My personal approach to safe static mut alternatives has been to use Atomic*s with Ordering::Relaxed (incidentally, this works on the GBA).

It's a shame because I'd love to be able to use Rust's guarantees, rather than having to dig through what the LLVM backend implements.

After some more reading, my current thinking is that VolAddress as it stands is probably the best way of solving the problem it solves. You just have to adjust your mental model of what your promising the compiler when you unsafely construct one to include "I've checked and on the architecture I'm targeting this is actually atomic enough to be safe". i.e., the architecture is a part of the contract you're making with the compiler at some point. Right now I think this makes as much sense as it can.

@Lokathor
Copy link
Member

Procedurally: new intrinsics, ones that express new things you couldn't before, are T-lang first. Then T-libs-api eventually gets to pick how it's exposed stably to users once the intrinsic seems to be doing stuff as expected.

@Lokathor
Copy link
Member

But yes, all mmio is basically device specific, so "this works on the exact device I'll use it with" is what you're promising.

@Lokathor
Copy link
Member

What I eventually implemented for GbaCell is just giving up and using inline assembly, so that LLVM has to assume that I might have used atomic synchronization and such and just do what I say without being able to reorder anything across the asm blocks. I think that's really the best that can be done for now.

I'm going to close this issue, since there's not really anything more that can be done by this crate. Further advancements in this area hinges on language developments, not something I can fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants