|
|
Subscribe / Log in / New account

Protecting control dependencies with volatile_if()

Protecting control dependencies with volatile_if()

Posted Jun 19, 2021 14:53 UTC (Sat) by Paf (subscriber, #91811)
In reply to: Protecting control dependencies with volatile_if() by bartoc
Parent article: Protecting control dependencies with volatile_if()

I don’t really understand the objection to &= and |= - is it a generalized objection to being able to write bitops like this? Can you say why?


(Log in to post comments)

Protecting control dependencies with volatile_if()

Posted Jun 19, 2021 18:47 UTC (Sat) by kkdwivedi (guest, #130744) [Link]

There is a recent paper (P2327) addressing compound operations (those used to flip bits in particular).

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p...

Protecting control dependencies with volatile_if()

Posted Jun 20, 2021 0:47 UTC (Sun) by Paf (subscriber, #91811) [Link]

Ah, thanks - I realize now I misread the original post as objecting to them. The decision of the standard to deprecate them seems ……. Let’s say “user hostile”.

Protecting control dependencies with volatile_if()

Posted Jun 21, 2021 3:33 UTC (Mon) by foom (subscriber, #14868) [Link]

The problem is that users expect volatile variables to be safe against interrupts/signals. And that is generally the case for read and write operations. So, by analogy, users expect `x |= 5;` on a volatile int to be the way to spell a read-modify-write which is also safe against interrupts. But, it has never guaranteed that -- neither in the spec nor in most implementations.

Protecting control dependencies with volatile_if()

Posted Jun 20, 2021 0:08 UTC (Sun) by tialaramex (subscriber, #21167) [Link]

The intended purpose of volatile is explicitly to cause actual memory reads/ writes, because perhaps the "memory" isn't. it comes into existence because the computer they're writing C on has memory-mapped I/O and early attempts at optimising C compilers are a huge step forward for performance but break some of the device handling code.

Today, volatile is rightly imagined as a property of actions, not variables. "volatile write variable -> $address " => "Literally write this to memory, even though it seems as though we're never reading that memory and so needn't bother, I promise that's actually what must happen". "volatile read $address -> variable" => Literally read this from memory, even though it seems as though we already know the value, I promise that's actually what must be done". Whereas "volatile variable" is too vague.

On some hardware, there may be very fine memory mapping, such that you can actually read or write a smaller value than the natural machine word to special memory regions, maybe the normal word size is 32 bits, but for a small range of "memory" it actually matters whether you use the 16-bit move instruction or the 32-bit one because they behave differently. In some cases this gets finer than a single byte, maybe there's a flag in the bottom bit of address 0x0037 and you can actually read or write that single bit, from machine code and it has completely different behaviour from if you were to try to read or write the whole 0x0037 byte on that hardware.

So, because volatile is defined so openly in C, your compiler vendor can say OK, we make this compiler for this weird hardware, and we promise if you say that this is a _volatile_ byte pointer to 0x0037 and then you use |= 0x01 we will actually emit the machine code for the single bit write, even though that's not how it would be implemented for other occurrences of |= 0x01

But the language standard can't say anything about these platform specific details, so it just leaves that to your the compiler vendor, and with the exception of specialist compiler vendors who care very much about this stuff, the compiler vendor says as little about it as possible, hoping you will go away.

Probably what _ought_ to happen if C was designed from scratch today is that these platforms provide a special intrinsic function and if you actually need to poke the bottom bit of address 0x0037 you call some intrinsic like __foocorp_bit_poke(0x0037, 0, 1) and how that's actually implemented is only a problem for the platform compiler developer. Anybody who needs the intrinsic (e.g. someone porting Linux to the platform) uses it, everybody else is blissfully unaware. But at the time they just added this "volatile" qualifier to the C language as a dumping ground for all features about hardware memory reads/ writes and called it a day.

Protecting control dependencies with volatile_if()

Posted Jun 20, 2021 2:44 UTC (Sun) by willy (subscriber, #9762) [Link]

It's not limited to foocorp though. A PCIe device (yes, even one designed today) can have vastly different behaviour for MMIO accesses that are single byte vs double-byte vs quad-byte. Most don't because that's painful for software to cope with, but you must define some form of generic MMIO accessor or the compiler cannot be used to write a device driver.

Protecting control dependencies with volatile_if()

Posted Jun 20, 2021 22:07 UTC (Sun) by tialaramex (subscriber, #21167) [Link]

Sure, but I was talking about bit-addressing.

Rust (sorry but I've spent lots of time staring at this recently so it's at the top of my head) provides

std::ptr::read_volatile(ptr) and std::ptr::write_votalite(ptr, foo)

If you provide read_volatile with say a pointer to i16 (a 16-bit signed integer), that'll compile and it'll emit an actual double-byte memory read, assuming that i16 pointer was pointing at the MMIO for your PCIe device you'll get what you wanted. If you do it with a pointer to u8 (an 8-bit unsigned integer) that'll do the single-byte read. And the same with the write equivalents.

These are stable but unsafe APIs. That is, Rust promises these are expected to still work tomorrow, but it doesn't vouch for the safety of just doing memory operations on raw addresses, so if this was a terrible idea you get to keep both halves when it breaks. They're defined in a completely generic way, so you could imagine calling read_volatile(pointer-to-4x4-matrix) but of course your CPU is not completely generic so it's between the CPU vendor, the compiler vendor, and the PCIe device maker whether that does anything useful.

However, for bit-addressing it's not at all obvious what the generic thing to do would be. So Rust just doesn't, and I argue that unlike your PCIe MMIO trick, bit addressed volatile memory access is in fact a niche feature in 2021.


Copyright © 2024, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds