#uACPI - a portable and easy-to-integrate ACPI implementation

1 messages · Page 50 of 1

fiery turtle
#

im definitely not supporting aarch32 since that is absolutely wild

#

and i686 seems useless as well

#

i might support compat mode for fun tho

#

for userspace

#

also maybe i should make a thread as to not pollute uacpi

#

which will also make me more embarrassed to abandon it

left orbit
#

it is kinda pointless to support non-64-bit x86 as a platform, since realistically anything nowadays supports 64-bit x86, even the shitty notebook CPUs

#

compatibility mode is kinda cool though, i think i've see only one (hobby) kernel support it

fiery turtle
#

I think pmos supports it

left orbit
#

yes yes exactly

fiery turtle
#

Dunno of other

left orbit
#

i couldn't recall the name lol

fiery turtle
frank canopy
#

yeah compat mode seems neat

jaunty fox
mortal yoke
# frank canopy yeah compat mode seems neat

on the other hand its nice as you can run old stuff but then there is also the non-nice thing about it which is that it allows new apps to be published as 32-bit binaries (which sucks because on eg. linux you need a ton of redundant 32-bit libraries along with 64-bit ones for eg. steam)

#

I'd much prefer if it was not a thing and instead if you want to run old apps you would use an emulator

flat badge
#

My take on this is that 32 bit archs with <= 1gib of memory make some sense

vast kestrel
#

I mean, native 64bit support has its uses apart from pointers

#

x32 abi my beloved

vast kestrel
#

Like, even if you have less than 1gb memory having native 64bit regs is still useful

hollow elm
#

i think the point is more that 32-bit targets with>1GB of ram make little sense to target

kind mantle
#

Because only a few of those targets will exist... When you get to that amount of RAM it makes no sense to limit yourself to 32-bit for any reason other than backwards compatibility... Which isn't exactly something x86 struggles with.

kind mantle
frank canopy
kind mantle
#

FWIW this is why BadgerOS doesn't bother supporting 32-bit at all: If you're going to have an MMU in 2025, you might as well make it 64-bit.

frank canopy
#

i dont plan to support compat mode or 32-bit stuff on imaginarium either

lilac knoll
kind mantle
#

Yes

#

But consider also: MMUs are physically huge

#

Because they consist basically mostly out of a kind of cache called the TLB

#

Any chip that can afford an MMU by this definition will already be aiming for higher amounts of memory.

#

At least nowadays

lilac knoll
kind mantle
#

You could

lilac knoll
#

you would

#

a huge tlb and tiny ram makes no sense

kind mantle
#

But you need to keep in mind that new chips being made get an MMU if they're made for running a modern OS, and don't get one if they're for microcontrollers

lilac knoll
#

there's a middle ground between microcontrollers and full desktop/server capable hardware

kind mantle
#

Nobody (relevant) is making chips with an MMU that don't have at least a GiB of RAM because an MMU only adds value if you want to run a paging operating system on it, which, in turn, is only useful if you can't properly or cleanly achieve your goal without such an operating system.

#

And while Linux lets you run with shockingly small amounts of RAM, relatively few such configurations are actually used that do have an MMU.

lilac knoll
#

well that's just not true 😉

jaunty fox
#

retrobsd (2.11bsd which has paged vm) was ported to several such microcontrollers, one of which something in the microchip pic32 series

lilac knoll
kind mantle
#

Wait WTF no I'm wrong

#

But how does a PIC32 with 512KiB of RAM need an MMU?

#

Granted it's not a big TLB

kind mantle
#

Both

#

And then I found a PIC32 with page-based virtual memory

#

Though it doesn't seem to be entirely a normal TLB from how I read the datasheet?

lilac knoll
kind mantle
#

half a GiB is still in the ballpark though

#

512KiB isn't

jaunty fox
kind mantle
#

Aaand there's the catch

#

I was looking for it because it didn't seem normal

jaunty fox
#

no catch, it's a legitimate and peaceful approach

lilac knoll
jaunty fox
#

motorola coldfire also doing this

kind mantle
#

Well it is a catch for a TLB with merely 16 entries. You're not getting away with a lot of fragmented virtual memory regions like Linux would make eventually if your TLB has 16 entries.

#

Not that Linux runs on 512KiB in the first place

kind mantle
lilac knoll
#

yeah fair

jaunty fox
#

by comparison the 486 had 32 tlb entries

lilac knoll
#

honestly

jaunty fox
#

it's certainly a squeeze

kind mantle
#

Why does my lamp need to connect to a network cmon

kind mantle
#

I guess hobby os for smartwatch confirmed? meme

hollow elm
#

i mean with mips parts of the va space don't go through the tlb

#

kseg0/1 are just directly mapped to phys mem

#

free hhdm

kind mantle
#

I like how this STM32 datasheet says "fuck it" and marks the entire low 2GiB as reserved in the virtual memory map

#

Every day I learn more how cursed computers are

#

Anyway I'm off to speedrun Celeste now

hollow elm
kind mantle
#

I thought it was ARM for some reason

jaunty fox
slim panther
#

still pointless

calm latch
#

and there is a factor of it being cool and getting to run your os on old hardware

#

and being a sanity check for 64 bit code

left orbit
#

32 bit kernels should be banned by the eu

slim panther
#

i haven't ever seen a 32 bit pc in my life -_-

#

that might tell you how old i am

calm latch
#

my first pc was 32 bit

#

you were a couple of years younger than me?

slim panther
#

i'm 22

calm latch
#

i'm 23

slim panther
#

i had my first pc at 9 yo

calm latch
#

i think me too

slim panther
#

and that was an amd bulldozer

calm latch
#

it was pentium m laptop I think?

#

or pentium d

#

whichever had the pae bug

left orbit
#

i think the earliest machine i had was a c2d one

calm latch
calm latch
#

like it's just a 32 bit code segment in GDT, and a couple of if statements to limit virtual memory to 4GB and to get syscall arguments differently

fiery turtle
#

probably because it's difficult to propagate the 4gb limit everywhere

calm latch
#

how so?

left orbit
#

also probably because not many people want it

#

compat mode is pretty useless unless you want to run wine

calm latch
#

it was convenient when I was porting my kernel to 32 bit mode

#

but I guess nobody does it in that order?

frank canopy
#

tbh i think most people just dont support 32 bit kernel mode

#

moreso than not doing it in that order

keen shard
frank canopy
#

i did say most not all lol

keen shard
#

No I know lol

#

I also have issues

frank canopy
#

mood

trim frost
#

uacpi pagefaulting at 0x7FB7B000

#

but i'm pretty sure the address is mapped

dense steppe
#

qemu log and printk log or it didn't happen

trim frost
#
[00:00:00.05977] [ info ] uacpi: FACS 0x000000007FBDD000 00000040
EXCEPTION: PAGE FAULT
Accessed Address: 0x7FB7B000
Error Code: PageFaultErrorCode(0x0)
InterruptStackFrame {
    instruction_pointer: VirtAddr(
        0xffffffff800d858c,
    ),
    code_segment: SegmentSelector {
        index: 1,
        rpl: Ring0,
    },
    cpu_flags: RFlags(
        RESUME_FLAG | INTERRUPT_FLAG | PARITY_FLAG | 0x2,
    ),
    stack_pointer: VirtAddr(
        0xffff80007e8c9790,
    ),
    stack_segment: SegmentSelector {
        index: 2,
        rpl: Ring0,
    },
}

.=-                                                  -*+-   
**+=-:                                               :+++=. 
:++**+    .=++=                                       :+***.
 :*+++*++:+***=         .:::++*+-===-.         -++-   -++++:
  ++***+++**=        .=**++#%#%%#*+*%%#*-.     +***- :=****:
  .+*+++****:      :*%%*==++#%%%*++++*#%%#+.    -**+-*+++***
   :*++*++**:    .*%%*=+***##########*+#%%%%=   -*++***++**-
    -****+++    :#%#%##%%*=--::::-=+#%%%%%#%%*  -++******+: 
     -+***=    .#%#%%%%#-::::::::::::=#%%%%%#%*   -****+:   
       ...     *%#%%#%*:::::::::::::::-#%%%%%#%-    ..      
               #%%%%%*::::.:::::::.::::-%%%%%#%+            
               #%%%%#-:::::........:::::+%%%%#%+            
               *%#%%=::::..:=++*+-:.:::::*%#%#%-            
               :%###-::::-*#%%%%%%#+:::::=%%#%#             
                =##+:::-*%%%######%%#=::::####.             
                 :#*--=#%%#%%%%%%%%#%%+-:-##+.              
                  =%%#####%%%%%%%%%%##%###%%                
                   .::+#%%%%%%%%%%%%%%#*=--:                
                       .:=+*##%%%##*+=:                     
                       

~~~~~~~~~~~~~~~~~~~~~~~~~ Kernel Panic ~~~~~~~~~~~~~~~~~~~~~~~~~
~
~       ERROR: panicked at src/arch/interrupts.rs:96:5 with message: page faulted
~
~       frame 0: rip = 0xffffffff800fba5f
~       frame 1: rip = 0xffffffff800118ca
~       frame 2: rip = 0x7fb7b000
~       frame 3: rip = 0xffffffff800d85d1
~       frame 4: rip = 0xffffffff800d8949
~       frame 5: rip = 0xffffffff800d9561
~       frame 6: rip = 0xffffffff800d93fd
~       frame 7: rip = 0xffffffff800d9677
~       frame 8: rip = 0xffffffff800d97a1
~       frame 9: rip = 0xffffffff800dd031
~       frame 10: rip = 0xffffffff8004161a
~       frame 11: rip = 0xffffffff80017d14
~       frame 12: rip = 0x0
~       stopping code execution and dumping registers
~
~       r15:    0x0000000000000000  -  rsi:    0x000000000000001B
~       r14:    0x0000000000000000  -  rdx:    0xFFFFFFFF80116648
~       r13:    0x0000000000000000  -  rcx:    0xFFFF80007E8C8C58
~       r12:    0x0000000000000000  -  rbx:    0x0000000000000000
~       r11:    0xFFFFFFFF800045CA  -  rax:    0xFFFF80007E8C8F78
~       r10:    0xFFFFFFFF80116648  -  rip:    0xFFFFFFFF800045CA
~       r9:     0x000000000000001B  -  cs:     0x0000000000000008
~       r8:     0xFFFFFFFF80116648  -  rflags: 0x0000000000000096
~       rbp:    0xFFFF80007E8C8C58  -  rsp:    0xFFFF80007E8C8B28
~       rdi:    0xFFFF80007E8C8F78  -  ss:     0x0000000000000010
~       cr2:    0x000000007FB7B000  -  cr3:    0x000000007FF3E000
~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kind mantle
#

Damn your kernel panic is very cool looking

trim frost
fiery turtle
#

info mem in qemu

trim frost
fiery turtle
#

so no its not mapped

trim frost
#

but then uacpi is not mapping it

fiery turtle
#

do u think its a qemu bug

trim frost
#

no no

#

uacpi

fiery turtle
#

do u think its a uacpi bug

trim frost
#

no

fiery turtle
#

i would first add a tracing function that prints everything uacpi tries to map and what you return to it

trim frost
#

this is without if i dont do anything in uacpi_kernel_map

#

so it is mapping things

#

just not everything

fiery turtle
#

yes, see my message above

trim frost
#

after (temporarily) changing the pagesize to 2mb instead of 4kb now the code hangs at uacpi_namespace_initialize()
and i get these errors because i left pci as unimplemented

fiery turtle
#

u can set the log level to debug if u wanna see where it hangs

trim frost
#

how? in uacpi_initialize()?

fiery turtle
#

uacpi_context_set_log_level

trim frost
#

i dont think uacpi-rs bindings have that lol

fiery turtle
#

you're probably using a very old version of uacpi as well

#

are u using the rewrite branch?

trim frost
#

yeah

#

im just using the uacpi-sys though

#

bindings only

fiery turtle
#

maybe it still using the old version where uacpi_initialize takes in a log level

trim frost
#

no

#

uacpi_initialize only takes in flags

fiery turtle
#

then it should have that function

#

at least in C

#

maybe its not exposed to rust

#

only @rustic compass knows

#

or just use addr2line or whatever

trim frost
#

in which file is that function? maybe its not included in the bindgen sources

fiery turtle
#

context.h

trim frost
#

no context.h

fiery turtle
#

alternatively u can change the default log level in sources

#

i think config.h has it

trim frost
#

yeah this is available - uacpi_set_resources

fiery turtle
#

what

trim frost
#

nvm

trim frost
#

does debug log every single instruction?

fiery turtle
#

every aml op

trim frost
#

how long am i gonna have to wait until the hang :D

fiery turtle
#

depends on how fast your logger is

trim frost
#

quite slow because framebuffer

#

44 seconds

#

so what am i supposed to be looking at?

fiery turtle
#

the last thing it logged

trim frost
#

[00:00:44.87663] [ dbug ] uacpi: trying to acquire the global lock from firmware... (attempt 1)
[00:00:44.87853] [ dbug ] uacpi: global lock is owned by firmware, waiting for a release notification...

#

spinlock?

fiery turtle
#

ah ok its a bug in your kernel api

#

either spinlock api, or mutex api, or get_thread_id

trim frost
#

all of those are set to 1 haha

fiery turtle
#

set to 1?

trim frost
#

1 as *mut c_void

#

im gonna set those up now

fiery turtle
#

thats not a problem

#

can u paste the entire thing

trim frost
fiery turtle
#

i dont see a get_thread_id in there

trim frost
#

uacpi_kernel_get_thread_id

fiery turtle
#

its not in the file u posted

trim frost
fiery turtle
#

yeah nvm

#

its because u return 1 for all mutexes

#

handles collide

#

anyway just implement it properly

#

or at least return an incrementing number each time

torpid root
#

lol

trim frost
#

ok that will probably fix the hang but am i supposed to always use 2mb page sizes in the mapping?

#

4kb pagefaults

fiery turtle
#

no, your page mapper is just bugged

#

like i said trace everything uacpi asks to map and the address you return, then compare with the resulting info mem

trim frost
#

fixed the mapper

fiery turtle
#

What was the issue?

trim frost
#

nothing anymore, thanks

fiery turtle
#

Lol np

torpid root
#

dumass forgot a loop and was mapping a single page

fiery turtle
#

Lmfao

trim frost
#

how good is 2408331 ops/s

torpid root
trim frost
#

so im getting better results than people that have a better cpu than me

calm latch
#

which CPU do you have?

trim frost
#

i5 12400f

calm latch
#

it's not that slow...

trim frost
#

never said it was,

fiery turtle
trim frost
#

2681214/s now

#

weirdly enough setting the opt-level to anything other than z or s on release profile makes the os bootloop

fiery turtle
#

Rust ub

#

Optimizes your os out

tired temple
kind mantle
trim frost
#

what does it mean if im getting UACPI_STATUS_INIT_LEVEL_MISMATCH from this?

#

nvm i figured it out

#

i was initing hpet before uacpi

left orbit
#

can't wait for the ultra kernel progress thread

fiery turtle
#

when theres at least something to show lol

kind mantle
#

Many threads, including BadgerOS', start as basically nothing

left orbit
fiery turtle
#

i will make one soon

zinc roost
dense steppe
fiery turtle
#

When I make a good log ring trl

vast kestrel
trim frost
#

what can cause a double fault on reboot?

#

rip is uACPI/source/opregion.c:568

dense steppe
#

the double fault is caused by some "normal" fault handler (e.g. GP or PF) not being installed.

#

aka give QEMU log

fiery turtle
trim frost
dense steppe
trim frost
#

its not a GP or PF, both are installed correctly and work

fiery turtle
#

Then its probably a push to a bogus rsp or something

dense steppe
#

gib qemu log !!

sharp lichen
#

Holy sgit alnost 50k messages, this project is big as heck

dense steppe
#

qemu log or it didn't happen

dense steppe
fiery turtle
#

By that measure nyaux is a bigger project

trim frost
#

bruh

#

it isnt happening without kvm

fiery turtle
#

Could be bogus gdt

#

TCG doesn't check it

trim frost
vast kestrel
#

enable qemu interrupt logging and check exactly what exception is being thrown before the double fault

#

maybe your stack is too small and it can't push the exception?

trim frost
#

and without kvm the double fault doesnt happen

fiery turtle
#

Use bochs

vast kestrel
#

try to give all the exceptions a specialized stack

trim frost
#

hmm ive increased the stack size to 8kb and no more double fault

vast kestrel
#

I would put a stack guard, so you can ensure you never run out of stack accidently

trim frost
#

im gonna set it to 16kb to be safe

fiery turtle
#

Rust is known to consume tons

#

I would put a lot more than 16k

vast kestrel
#

I think linux pushed it to 32k as more rust code enters the kernel

fiery turtle
#

If your entire kernel is rust maybe consider 64 or even 128

trim frost
#

well my kernel is no linux :/

#

i'll consider it

torpid root
trim frost
#

now pf here, what could this be caused by?

fiery turtle
torpid root
#

also I don't think this is how time works

trim frost
fiery turtle
trim frost
#

8kb is fine

#

:/

vast kestrel
#

man, it sounds like there is something really wrong with your code lol

trim frost
#

64kb works

vast kestrel
#

for your own good, put a guard page for your stack and check if that is actually related

trim frost
#

32kb causes bootloop

#

bruh

vast kestrel
#

maybe your allocator is cooked

trim frost
#

no allocator should be fine

#

i trust Talc

vast kestrel
#

maybe its your page allocator

trim frost
#

that part might actually be cooked

vast kestrel
#

and again, stack guards are a life saver when it comes to these stuff

torpid root
frank canopy
#

is there any sort of alignment guarantee on uacpi_resource?

frank canopy
#

anyway imaginarium and zuacpi now can do all resource types that showed up in qemu. will finish and polish this bit up and then push in a few days

fiery turtle
left orbit
#

backwards

slim panther
#

:(

fiery turtle
#

wdym

left orbit
#

thats why i started working on stuff in managarm

slim panther
#

depressing

left orbit
#

i am incapable of creating a project and working on it for extended periods of time

slim panther
#

you are a great help to me tho

fiery turtle
#

ah

#

unlucky

left orbit
#

yep

fiery turtle
slim panther
#

at this point, any second set of eyes is a win in my book

#

so i don't produce utter garbage code

left orbit
#

it’s easier when working on someone else’s code, you don’t have to think about the entire codebase usually

slim panther
#

merge yours with mine :)

left orbit
#

doesn’t work, mine isn’t written in rust

#

and i don’t really have a vision for mine but i don’t want to introduce a bunch on shit code lol

#

i don’t really know what i’m doing is the problem

slim panther
#

pretends that i do

#

i have 0 clue past the surface level

#

i'm just making shit up as i encounter it

left orbit
#

same, but so far it has worked for you, unlike me :^)

slim panther
#

luck

left orbit
fiery turtle
slim panther
#

real

#

you know more than i do in many places

left orbit
#

but not in enough places

#

so i can’t really make something i’m happy woth

fiery turtle
#

you just need to be willing to read linux sources and other crap for hours

#

at least thats what i do

slim panther
#

i despise doing that

#

how do you make an abstraction layer this annoying to work with

fiery turtle
#

im kinda used to it, at this point its relatively easy to navigate

left orbit
#

acquired taste

#

not everyone likes licorice either :^)

fiery turtle
#

yes i kinda hate it as well lol

calm latch
left orbit
#

yeah but now i don't have anything that would be worth contributing to your kernel

#
  • you use gitlab, ew
frank canopy
frank canopy
#

oh cool that's nice

#

that's an easy one to work with in zig

fiery turtle
#

i mean its UB to not align even with C

#

unless i make every resource packed

frank canopy
#

yeah I was just trying to figure out what the alignment was lol

#

cause otherwise im having to aligncast

fiery turtle
#

uacpi tries to be sane where possible

frank canopy
#

my zig code was using align(1) aka c packed just to be as general as possible until I figured it out lol

fiery turtle
#

lol

frank canopy
#

also zig packed is completely different and I keep having to remind myself that c packed isn't the same when talking with c people lol

fiery turtle
#

yeah GCC packed means remove padding AND assume misaligned

left orbit
#

and zig packed means bit packed

frank canopy
#

yup

#

anyway i can remove a bunch of code in one spot and change a 1 to a @alignOf(usize) in another now and have zuacpi still work at least which is nice

#

love removing code

trim frost
#

RUST_PROFILE="smol" make run (this has the most optimization)

left orbit
#

drop the iso instead

#
  • qemu command line
trim frost
#

without ovmfs its slower

zinc roost
#

doesnt compile for me

trim frost
#

why?

zinc roost
#
Updating crates.io index
error: failed to get `flanterm-sys` as a dependency of package `chronos v0.1.0 (/home/cat/prob/chronos/kernel)`

Caused by:
  failed to load source for dependency `flanterm-sys`

Caused by:
  Unable to update /home/cat/prob/chronos/bindings/flanterm-sys

Caused by:
  failed to read `/home/cat/prob/chronos/bindings/flanterm-sys/Cargo.toml`

Caused by:
  No such file or directory (os error 2)
make[1]: *** [GNUmakefile:30: all] Error 101
make[1]: Leaving directory '/home/cat/prob/chronos/kernel'
make: *** [GNUmakefile:70: kernel] Error 2
trim frost
#

did you clone it recursively?

zinc roost
#

oh

trim frost
#

also you will get pci unimplemented errors which cover the screen so you will have to check the terminal debugcon prints for the points

#

i implemented pci now but haven't pushed it yet

zinc roost
#
rust-lld: error: too many errors emitted, stopping now (use --error-limit=0 to see all errors)
          

warning: `chronos` (bin "chronos") generated 1 warning
error: could not compile `chronos` (bin "chronos") due to 1 previous error; 1 warning emitted
trim frost
#

can you show rust-lld errors?

zinc roost
trim frost
#

hm thats wewird

#

are you using nightly rust?

#

that shouldnt matter for linking but

#

jic

zinc roost
#

i just ran make run, and it asked me to add nightly so i did

trim frost
#

what distro are you using?

zinc roost
#

fedora

trim frost
#

im not sure why this is happening...
@torpid root can u test it?

left orbit
#

i like how u start pinging random people to test it for you while i offered to test it for you, you still haven't provided the image and a command line

zinc roost
#

yeah that is much easier

trim frost
#

i'll provide the iso and commandline in a sec

left orbit
#

well, yeah, random as in out of nowhere

#

that's what i seemed like from my point of view

torpid root
trim frost
#

qemu-system-x86_64 -M q35 -cpu host -debugcon stdio -boot order=d,menu=on,splash-time=0 -cdrom chronos-x86_64.iso -enable-kvm -m 2G

trim frost
torpid root
#

you don't compile your dependencies with the same code model as your kernel

#

add -mcmodel=kernel

trim frost
flat badge
#

No that's not a good idea lol

fiery turtle
#

Yes, definitely

#

Instead do serenityos compat

left orbit
# trim frost `qemu-system-x86_64 -M q35 -cpu host -debugcon stdio -boot order=d,menu=on,splas...
[iretq@raptor ~]$ qemu-system-x86_64 -M q35 -cpu host -debugcon stdio -boot order=d,menu=on,splash-time=0 -cdrom chronos-x86_64.iso -enable-kvm -m 2G | grep -a '\(avg'
[00:00:00.00428] [ info ] uacpi: successfully loaded 1 AML blob, 1705 ops in 0ms (avg 3837030/s)
[00:00:00.00364] [ info ] uacpi: successfully loaded 1 AML blob, 1705 ops in 0ms (avg 3837160/s)
[00:00:00.00299] [ info ] uacpi: successfully loaded 1 AML blob, 1705 ops in 0ms (avg 3864224/s)
[00:00:00.00323] [ info ] uacpi: successfully loaded 1 AML blob, 1705 ops in 0ms (avg 3421413/s)
[00:00:00.00305] [ info ] uacpi: successfully loaded 1 AML blob, 1705 ops in 0ms (avg 3519520/s)
[00:00:00.00307] [ info ] uacpi: successfully loaded 1 AML blob, 1705 ops in 0ms (avg 3814513/s)
[00:00:00.00338] [ info ] uacpi: successfully loaded 1 AML blob, 1705 ops in 0ms (avg 3823307/s)```
#

13700K

fiery turtle
left orbit
#

idk lmao

#

maybe

#

i do vividly remember making something like that

#

yeahhh right, we were debating on how to make it get the most cpu time possible

fiery turtle
#

yeah

trim frost
#

i define this function in rust the same way as the kernel api functions

#

what am i doing wrong?

fiery turtle
#

how is C supposed to know what chronos_memcpy is

trim frost
#

how does it know with kernel api functions?

fiery turtle
#

it includes the header where they are declared

#

also just call this function memcpy and it will be picked up by uacpi automatically

#

because it uses __builtin_memcpy

mortal yoke
#

and you also get memcpy from rust compiler builtins so you likely don't need to make one

slim panther
#
use bindgen;
use cc;

fn main() {
    let mut b = cc::Build::new();
    b.files([
        "uacpi/source/default_handlers.c",
        "uacpi/source/event.c",
        "uacpi/source/interpreter.c",
        "uacpi/source/io.c",
        "uacpi/source/mutex.c",
        "uacpi/source/namespace.c",
        "uacpi/source/notify.c",
        "uacpi/source/opcodes.c",
        "uacpi/source/opregion.c",
        "uacpi/source/osi.c",
        "uacpi/source/registers.c",
        "uacpi/source/resources.c",
        "uacpi/source/shareable.c",
        "uacpi/source/stdlib.c",
        "uacpi/source/tables.c",
        "uacpi/source/types.c",
        "uacpi/source/uacpi.c",
        "uacpi/source/utilities.c",
    ])
    .includes(["src/"])
    .includes(["uacpi/include/"])
    .define("UACPI_SIZED_FREES", None)
    .define("UACPI_OVERRIDE_LIBC", None)
    .pic(true)
    .flag("-ffreestanding")
    .flag("-nostdlib");

    if std::env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64" {
        b.flag("-mgeneral-regs-only");
        b.flag("-mno-red-zone");
    }

    b.compile("uacpi");

    let bindings = bindgen::builder()
        .use_core()
        .wrap_unsafe_ops(true)
        .derive_default(true)
        .derive_debug(true)
        .header("src/wrapper.h")
        .clang_arg("-Iuacpi/include/")
        .generate()
        .expect("Unable to generate bindings!");

    let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());

    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Unable to write bindings!");
}
#

this is my bindgen

fiery turtle
#

do you really need sized frees with rust?

slim panther
#

free takes a size

#

or a layout rather

fiery turtle
#

hm ok

trim frost
slim panther
fiery turtle
#

this is redundant btw lol

#

it will automatically call these already

slim panther
#

bruh

fiery turtle
#

but this works if u have a memcpy_but_with_a_different_name

#

like george

trim frost
slim panther
#

make it yourself, but infy said it's redundant

fiery turtle
#
#ifndef __smp_store_release
#define __smp_store_release(p, v)                    \
do {                                    \
    compiletime_assert_atomic_type(*p);                \
    __smp_mb();                            \
    WRITE_ONCE(*p, v);                        \
} while (0)
#endif

#ifndef __smp_load_acquire
#define __smp_load_acquire(p)                        \
({                                    \
    __unqual_scalar_typeof(*p) ___p1 = READ_ONCE(*p);        \
    compiletime_assert_atomic_type(*p);                \
    __smp_mb();                            \
    (typeof(*p))___p1;                        \
})
#endif

@flat badge any idea why linux uses a full barrier for both of these instead of smp_rb() for one and smp_wb() for another?

#

i looked at the commit that introduced this but no one asked this question

trim frost
fiery turtle
#

memcpy, but you already have this in your binary

#

because of what qwinci said

trim frost
#

oh

slim panther
trim frost
#

mine is better

flat badge
fiery turtle
#

yes

flat badge
#

yeah on x86 it compiles to a compiler barrier

fiery turtle
#

but still

#

__smp_* is also arch overridable

flat badge
#

ah the question was not why is it not a acquire/release barrier but why is it not smp_rmb / smp_wmb

fiery turtle
#

yea

flat badge
#

a read-read barrier is weaker than acquire

#

a read-rw barrier would be strong enough

fiery turtle
#

i dont think they even have that

flat badge
#

that's why they use the strong full memory barrier ;D

fiery turtle
#

so in linux terms smp_rb means no reads can be reordered with this read?

#

but not writes

flat badge
#

but compare for example on RISC-V where linux lowers smp_rmb() to RISCV_FENCE(r, r)

#

but acquire to RISCV_FENCE(r, rw)

fiery turtle
#

so the C model is stricter, it doesnt even have a way to express an smp_rb or smp_wb

flat badge
#

a read-read barrier is not strong enough because that'd make it possible to reorder

mutex.acquire()
x = 42
mutex.release()

to

x = 42
mutex.acquire()
mutex.release()
fiery turtle
#

yeah makes sense, so with smp_rb, stores can be reordered in any way

fiery turtle
flat badge
#

the C model is different not finer/coarser

fiery turtle
#

seems coarser to me

flat badge
#

a read-rw barrier is not equivalent to acquire either

fiery turtle
#

you just cant express this

flat badge
#

read-rw is stronger than acquire

fiery turtle
#

why?

flat badge
#

because acquire only synchronizes with release barriers on other CPUs

#

and not with random relaxed accesses

fiery turtle
#

well if we talk about a std::atomic_thread_fence

#

with ACQUIRE

#

it would mean the same

flat badge
#

no

fiery turtle
#

how

flat badge
#

because it's just different?

#

the guarantees are different

fiery turtle
#

i dont understand the difference

flat badge
#

the Linux model is phrased in terms of what accesses before a barrier can be reordered with what accesses after a barrier

fiery turtle
#

right

flat badge
#

the C11 model is phrased in terms of when one access on one CPU happens before another access on a different CPU

fiery turtle
#

same thing just a different angle?

#

happens-before/sequenced-before is just lawyer speak for the exact same thing if i understand this correctly

#

doesnt this basically say they can't be reordered just using a more abstract/formal language?

fiery turtle
flat badge
#

okay, i think you're right that in the case of fences, r-rw is equivalent to acquire

#

and rw-w is equivalent to release

fiery turtle
#

and still dont understand what this is trying to say

flat badge
#

but there is no way to emulate r-rw with smp_rmb() and smp_wmb()

fiery turtle
#

yeah, u must use smp_mb

fiery turtle
flat badge
#

otherwise you can again come up with a silly mutex example

#

otherwise

mutex.acquire()
*x
mutex.release()
#

can be reordered to

#
mutex.acquire()
mutex.release()
*x
fiery turtle
#

wait what do u encode in the order of these letters exactly

flat badge
#

rw-w = no read/write before the barrier can be reordered with a write after the barrier

fiery turtle
#

okay and r-rw means no read/write after the barrier can be reordered with this read?

flat badge
#

r-rw = no read before the barrier can be reordered with a read/write after the barrier

fiery turtle
#

because this sounds kind of inverse from what you're saying

flat badge
#

that's exactly what i'm saying?

#

except that i'm talking about linux style barriers while that paragraph talks about acq/rel load/store

fiery turtle
#

nvm ill have to re-read this probably 50 times to parse it completely

fiery turtle
flat badge
#

yes, what is your question here?

jaunty fox
#

i knew him from the smalltalk community long before i encountered him elsewhere

fiery turtle
#

i dont understand what "happens before everything after the load-acquire"

#

means

fiery turtle
fiery turtle
flat badge
#

it means that if

thread 1:
x.store(42)
thread 2:
x.load()

and thread 2 loads the 42 written by thread 1, then everything that was ordered before the store in thread 1 is also visible to thread 2 after the load

fiery turtle
#

yeah makes sense when you put it like that

#

C gained its popularity because of its flexibility and the ability to function as a "high-level assembler". However, due to the growing complexity of programming both languages and code bases, compiler writers need to extract more and more information from the source code, causing behavior that the programmer did not expect---the dreaded "undefi...

â–¶ Play video
#

pretty good talk

trim frost
#

when does uacpi_kernel_uninstall_interrupt_handler get called?

fiery turtle
#

uacpi_state_reset

trim frost
#

great, pagefault

#

0xFFFFFFFFFFFFFFF8

fiery turtle
#

@flat badge in this example, why is acquire_release on the load()'s not enough? Wouldnt it guarantee that stores before cant be reordered after this one

#

or is the idea that there isnt a synchronization point between them

#

like this makes no sense, because surely if x was a normal variable then it would be guaranteed to be false after an acq_rel load, no?

#

like its this sentence i dont understand

#

if this was the case, then

// t1
foo.store(123, relaxed);
bar.store(321, release);

// t2
bar.load(acquire);
assert(foo.load(relaxed) == 123); // can fail beacause not seq_cst??
#

why wouldn't bar's release be enough here

fiery turtle
#

because the c++ page simply says "no reads or writes in the current thread can be reordered before this load", it doesnt say anything about reordering with a write specifically

fiery turtle
gentle peak
#

foo is atomic, so its store doesn't happen before bar's

fiery turtle
#

hm so my example can literally fail?

gentle peak
#

yeah

fiery turtle
#

lol

fiery turtle
gentle peak
#

it also means that mutex acquires need to be acq rel instead of just acq since otherwise lock(a); lock(b); can be observed as lock(b); lock(a); on other threads

fiery turtle
#

I guess monkuous is wrong then

flat badge
#

thread 2 doesn't load acquire any value written by thread 1 (or vice versa)

#

that's why you need seq_cst

fiery turtle
#

yeah but what if i add an acq_rel barrier in the middle for example

flat badge
#

doesn't do anything?

#

why would it

fiery turtle
#

hm?

#

it makes sure the order is consistent

flat badge
#
x.store()
fence(acq_rel) <--- does nothing here
y.load()
#

acquire prevents reordering earlier loads

fiery turtle
#

the store cannot be reordered after the fence

flat badge
#

there is no earlier load

fiery turtle
#

the load cannot be reordered before

flat badge
#

release prevents reordering future stores

#

and there is also no future store

#

so acq_rel does nothing here

fiery turtle
#

well the docs dont say that

#

no reads or writes in the current thread can be reordered before this load

#

and no reads or writes in the current thread can be reordered after this store

#

it mentions both

flat badge
#

by "this load" this summary means "an earlier load" (or the in the case of a load-acquire instead of a fence, the load done by the load-acquire)

fiery turtle
#

A load operation with this memory order performs the acquire operation on the affected memory location: no reads or writes in the current thread can be reordered before this load.

#

this is the entire text

#

pretty sure this load means the load itself

flat badge
#

there is no load acquire here

fiery turtle
#

for acq_rel it says No memory reads or writes in the current thread can be reordered before the load, nor after the store.

flat badge
#

acq_rel is equivalent to acquire + release

fiery turtle
#

yours seems lighter as it allows reads to be reordered with reads

flat badge
#

if you make it

T1:
x.store(false, release)
a = y.load(acquire)
T2:
y.store(false, release)
b = x.load(acquire)

you can still get a = true, b = true

#

there is simply no synchronization at all here

fiery turtle
#

i agree with this one, since the store is before the acquire

#

acquire means nothing below can be reordered above

flat badge
#

and

T1:
x.store(false)
fence(acq_rel)
a = y.load()
T2:
y.store(false)
fence(acq_rel)
b = x.load()
#

is equivalent to the above

fiery turtle
#

are these relaxed?

fiery turtle
#

but why

#

would it also be a no-op if load and store were in a different order?

flat badge
#

no

#

then it would work

fiery turtle
#

ok i see what u were going for

flat badge
#

well, if you change the order in one of the two threads

fiery turtle
#

i think your definition is lighter still

#

what you're saying is

foo.load(acquire);
print(x); // may be reordered above the load
#

if i understood you correctly

#

but cppref seems to disagree

fiery turtle
gentle peak
fiery turtle
#

it is, but it's both loads and stores

#

i think what you're describing is the equivalent of smp_{rb,wb} which are not acquire/release

gentle peak
#

I'm pretty sure the cppref summary is incorrect here and the formal model doesn't care about stores wrt acq and loads wrt rel

flat badge
#

that is not the situation of the snippet that you posted above

#

where the load is after the store, not before the store

flat badge
#

otherwise stuff could be moved out of a mutex

fiery turtle
#

wait wrong one

#

rw-w = no read/write before the barrier can be reordered with a write after the barrier

#

this implies a read inside the barrier can be reordered with anything

flat badge
#

r-rw (i.e. acquire) prevents #1217009725711847465 message

fiery turtle
#

then it implies that stuff inside the barrier can be reordered with a read after

flat badge
#

the load before the barrier (foo.load) cannot be reordered with anything (the print) after the barrier

fiery turtle
#

which doesnt make sense

flat badge
#

are you aware that if you want to replace load-acquire with a fence, the fence has to be after the load?

fiery turtle
#

what you said is "reads/writes before the barrier can be reoredered with reads after the barrier"

flat badge
#

and likewise if you replace store-release with a fence, the fence has to be before the store?

fiery turtle
#

that i understand

flat badge
#

if you write this #1217009725711847465 message in terms of fences, it is

#
foo.load(relaxed);
fence(acquire)
print(x);
#

and the r-rw fence prevents reordering

fiery turtle
#

yeah, so it cant be reordered

flat badge
#

right

#

It can't be reordered, and i never claimed that it can be

fiery turtle
#

ok so r-rw = no read before the barrier can be reordered with a read/write after the barrier AKA for the acquire barrier, you said that writes before the barrier can be reordered with anything after the barrier

#

correct?

flat badge
#

yes

fiery turtle
#

nvm

flat badge
#

there is no write before the barrier here

fiery turtle
#

you just worded it in a way where i misunderstood it

#

you tried to say this right

flat badge
#

the semantics are literally what i wrote though lol

#

No, i meant literally what i wrote

#

and not anything else

fiery turtle
#

then your semantics are actually stricter

#

because cppref doesnt mention anything about the operations before the barrier

#

it only mentions that operations after cant be reordered

flat badge
#

that doesn't make any sense

#

ordering is always before barrier vs. after barrier

#

you can't say something about the ordering on one side only

fiery turtle
#

what

flat badge
#

and cppref also says something about both sides

fiery turtle
#

it says stuff after cannot be ordered before

flat badge
#

what does "before" mean?

fiery turtle
#

it doesnt mention what can happen to operations before the barrier

#

because its not relevant for the acquire barrier i guess

flat badge
#

there is no such thing as a "nothing-r" or "nothing-rw" or "nothing-w" barrier

#

because it would be a no-op

fiery turtle
#

well thats not true

#

operations before the acquire barrier can happen after the barrier

#

the only limitation is operations after barrier must appear to happen after the barrier

flat badge
#

there is no "after the barrier"

#

if you don't say what "before the barrier" means

fiery turtle
#

stuff before the barrier is not synchronized in any way tho, why is it important to define it

flat badge
#

(1) that's not how it works in the C11 model and (2) that wouldn't make any sense

fiery turtle
#

thats literally how its spelled out A load operation with this memory order performs the acquire operation on the affected memory location: no reads or writes in the current thread can be reordered before this load.

#

mentions absolutely nothing about before the barrier operations

flat badge
#

can be reordered before this load

#

that's "before"

#

if you leave out this sentence, the CPU can delay the barrier indefinitely

fiery turtle
#

yes, but its talking about the operations after the barrier

flat badge
#

until the heat death of the universe

#

and the semantics are preserved

fiery turtle
#

ok, why?

flat badge
#

it could move it into the past, not into the future

#

then everything after the barrier stays after the barrier and there are no memory accesses at all before the barrier

fiery turtle
#

they're there, we just dont talk about them

flat badge
#

the "until the heat death of the universe" scenario applies to barriers with empty "after" side

#

Barriers where one side is empty are meaningless in any case

gentle peak
# fiery turtle ok, why?

fences aren't loads or stores, they effectively raise the ordering of other loads or stores (depending on the order of the fence)

fiery turtle
#

by your definition, a program that starts with an acquire barrier instruction isnt allowed to exist

#

because there isnt anything before it

flat badge
#

bullshit

fiery turtle
#

what im saying is for the purposes of the acquire barrier, the only loads or stores that matter to us, are those that happen after the barrier

#

i've yet to understand why thats not the case

flat badge
#

any read before the acquire fence (this set is empty if the acquire is at program start) cannot be reordered with any read or write after the acquire fence (the entire program)

fiery turtle
#

why read specifically?

flat badge
#

because that's the freaking definition

fiery turtle
#

where

gentle peak
flat badge
#

you cited it 10x already from the cppref summary

#

no reads or writes in the current thread can be reordered before this load

fiery turtle
#

"this load" mentions no prior loads?

flat badge
#

this load refers to

  • the load done by the load-acquire
  • in case of fences: to all previous loads
fiery turtle
#

so then "any read" in your definition means "this read" for a single atomic (non-fence) load?

flat badge
#

the cppref summary is imprecise because it doesn't distinguish fences and load-acquire

#

acquire fences are stronger than load-acquire

fiery turtle
#

yeah i think this is where we misunderstood each other

flat badge
#

because acquire fences apply to all previous loads while load-acquire applies to exactly one load, the one done by the load-acquire

#

likewise, release fences apply to all future stores while store-release applies to exactly one store, the one done by the store-release

fiery turtle
#

so then

any read before the acquire fence (this set is empty if the acquire is at program start) cannot be reordered with any read or write after the acquire fence (the entire program)

for one load-acquire can be transformed into

an acquire load cannot be reordered with any read or write after the acquire load
#

correct?

flat badge
#

yes

fiery turtle
#

lmfao

#

yeah sorry i misunderstood what u were trying to say

#

this is what i meant this whole time as well

#

because fence vs load confusion

fiery turtle
# flat badge yes

btw by "any read before the acquire fence" do u mean only atomic reads, or do non-atomic ones also get promoted to acquire

gentle peak
#

only atomic ones

#

cppreference says both but the spec doesn't agree

fiery turtle
#

so basically non-atomic reads before an acquire fence can be reordered after the fence?

gentle peak
#

if they're after the last atomic load the fence applied to, sure

fiery turtle
#

ah right, if they're before that still works fine

gentle peak
#

or well acquire doesn't prevent reordering things before it to past it so any read can be reordered to after an acquire fence

#

but the same would be true if you replaced the fence with an acquire load

#

but yeah C's fences are only useful for lifting relaxed loads/stores into acquire/release ones

fiery turtle
#

or well, following for release i guess

gentle peak
#

yep

vast kestrel
#

Tbf that's how fences are usually implemented in CPUs

fiery turtle
#

really?

#

lfence/sfence apply to everything no?

vast kestrel
#

x86 is a bit different, because the default semantics are already read acquire store release (or well, technically stronger but still)

#

And cmpxchng is basically seq_cst

#

Or well, the locked version

fiery turtle
#

hm

vast kestrel
#

Mfence is used for seq_cst operations

#

And I think sfence and lfence are only useful for the non-temporal moves

#

Since those bypass tso

fiery turtle
#
 (3) Read (or load) memory barriers.

     A read barrier is an address-dependency barrier plus a guarantee that all
     the LOAD operations specified before the barrier will appear to happen
     before all the LOAD operations specified after the barrier with respect to
     the other components of the system.

     A read barrier is a partial ordering on loads only; it is not required to
     have any effect on stores.

     Read memory barriers imply address-dependency barriers, and so can
     substitute for them.

     [!] Note that read barriers should normally be paired with write barriers;
     see the "SMP barrier pairing" subsection.
vast kestrel
#

If you really wanna have fun I think aarch64 has a format memory model definition, including litmus tests and all the fun stuff

trim frost
vast kestrel
#

Lemme find the aarch64 one

fiery turtle
fiery turtle
vast kestrel
#

I am not gonna pretend like I ever looked at it too deeply, but I just think it's pretty cool

trim frost
vast kestrel
#

Apparently the Linux memory model is also formalized

fiery turtle
vast kestrel
#

What's cool about this is that once they defined all the semantics they want, they can test them against the models of arches that also have their semantics formalized, so they can ensure that they use the correct instructions to implement their barriers to keep the Linux semantics correct

fiery turtle
trim frost
#

im the only one with 3.xxM on the leaderboards

slim panther
calm latch
#

It's about 2kk on 5900X

zinc roost
trim frost
torpid root
slim panther
#

does what?

torpid root
#

page allocations

flat badge
#

For non atomic vars, C doesn't even guarantee that they are written by a single CPU operation

#

So it can't make any synchronization claims about them

#

I also don't think that linux's memory model does

#

Linux has READ_ONCE for this purpose

#

On existing ISAs, properly aligned single instructions accesses are always relaxed atomics

loud ice
#

except that a normal C load is not because it is subject to optimizations ofc

#

do acquire fences even apply to normal reads and writes?

flat badge
#

Depends on what you mean by apply

loud ice
#

have any impact on them

#
#define __READ_ONCE(x)    (*(const volatile __unqual_scalar_typeof(x) *)&(x))

this definition of read_once is somewhat unsound aiui

flat badge
#

yeah the linux memory model is a mess in general

loud ice
#

better than nothing for sure

flat badge
#

and it makes a lot of assumptions about what GCC does

loud ice
#

but its invalid regardless

#

the VERY fun thing is that while a volatile read occuring is a side effect

#

the value of it is not a program input (afaik)

flat badge
#

otherwise, non-atomic accesses could be moved out of mutexes etc

loud ice
#

i.e. it attaches to a surrounding atomic

flat badge
#

if you have

b.load(relaxed)
fence(acquire)
a.load(non_atomic)

then the acquire fence can potentially make the load of b synchronize with a store release

fiery turtle
# flat badge So it can't make any synchronization claims about them

So in the C memory model, how would you even synchronize something like:

atomic_load(x, acquire);
// unrelated data
memcpy(...);
// reload to make sure the state is the same
atomic_load(x, acquire);

With this memcpy can be reordered to after the load since its not an atomic store

loud ice
#

only from reads and writes coming above it

fiery turtle
#

Read the question

loud ice
#

yeah

#

the answer is that you need an atomic store aiui

fiery turtle
#

And making a load acq_rel is UB

#

So how would that work

fiery turtle
#

Have an unrelated atomic store

flat badge
#

you need sequentially consistent fences here

loud ice
#

seqcst works too

#

but thats just a rmw

fiery turtle
loud ice
#

probably not

#

actually maybe

fiery turtle
#

Lol

loud ice
#

no nvm i think it would so long as that second atomic load is present maybe?

fiery turtle
#

And do u actually need a fence or is it enough to keep seq cst on both loads

loud ice
#

the answer is "ask a model checker"

fiery turtle
#

Too skill issued

flat badge
#

btw

fiery turtle
#

Yeah

#

You need release stores there

#

I would just do seq cst on both loads I think

flat badge
#

the last load-acquire doesn't gurantee anything

fiery turtle
#

Well it guarantees synchronization with a previous store, but doesn't prevent reordering with memcpy that's true

flat badge
#

even if you treat the memcpy as atomic accesses, the final load acquire would only guarantee that following loads are more recent than the value loaded by the load-acquire

fiery turtle
#

Linux code used two smp_rb barriers with relaxed loads here btw

loud ice
#

what does smp_rb do?

flat badge
#

it's a read-read barrier

loud ice
#

ah

fiery turtle
#

Prevents read recorders yeah

loud ice
#

so dmb ish on arm

#

and a compiler fence on x86

fiery turtle
#

C memory model is not less stupid that Linux tbh

loud ice
#

at least it maps somewhat better to hw

flat badge
#

the linux memory model is stupid beyond imagination

#

it's also only held up by hopes and dreams that GCC doesn't change its behavior

fiery turtle
#

Lol

flat badge
#

it's not modeled after any real architecture

#

but around "this worked on x86"

left orbit
#

they probably won't because of linux

fiery turtle
#

Well they have a .cat file

loud ice
#

lol

flat badge
#

clang already handles volatile access differently than GCC

fiery turtle
# flat badge it's not modeled after any real architecture

Btw you suggested a seq cst barrier initially, but aren't these barriers basically used to elevate the ordering of surrounding atomic operations? Like what would be the difference of a pair of seq cst barriers vs just marking both loads seq cst

#

And iirc seq cst doesn't play nice with non seq cst stores from other places or whatever

#

There was some weird interaction there

#

So would all stores/loads to that var have be seq cst

flat badge
#

the correct way to do a seqlock is:

left orbit
#

doesn't seq-cst mean no reads or writes before can be moved after that operation and no moves after can be moved before that operation?

fiery turtle
# slim panther ?????

Well acquire only prevents the following stuff to be reordered with the atomic acquire

#

That's just how it works

left orbit
#

why is atomic stuff so complicated to reason about all of a sudden

slim panther
#

real

flat badge
#
seq.load(acquire)
if (dirty)
    retry
data1.load(relaxed)
data2.load(relaxed)
data3.load(relaxed)
fence(acquire)
seq.load(relaxed)
if (mistatch)
   retry
fiery turtle
slim panther
#

hw was a mistake

left orbit
#

it's just compiler optimizations here

flat badge
#

well you posted a seqlock

trim frost
gentle peak
loud ice
#

it does not

flat badge
#

the acquire fence guarantees that the last seq.load is at least as new as all the data reads

loud ice
fiery turtle
#

lol

gentle peak
#

ahh i see

loud ice
#

thats like the point

fiery turtle
#

why have we spent 5 hours talking about atomics here

#

if we could just read the rust docs

trim frost
loud ice
#

ahh yes nothing like rust

gentle peak
left orbit
#

i feel like atomics are one of those "if you think you understand it, think again" things

fiery turtle
loud ice
fiery turtle
left orbit
#

i'm sooo glad i don't understand any of it, i simply don't have to worry about it :^)

loud ice
#

ohh

#

oh wait

fiery turtle
loud ice
#

yeah it does

#

i think

#

or maybe not?

#

i think it might not

fiery turtle
#

i think ill never understand atomics

loud ice
#

time to do the simplest thing

#

experiment

#

hm idk what kind of experiment would work

gentle peak
fiery turtle
loud ice
#

it does i think

fiery turtle
loud ice
#

my understanding is that this code is invalid

gentle peak
#

yeah you'd have to make it yourself

#

but it's not particularly hard

fiery turtle
#

atomic_store(dst[i], src[i], release)?

gentle peak
#

relaxed

fiery turtle
#

and a release barrier after?

gentle peak
#

and more like dst[i] = atomic_load(src[i], relaxed)

loud ice
#

or alternatively

gentle peak
#

then atomic_fence(acquire) and another load

#

the acquire fence will prevent the load after it from being reordered before any of the relaxed loads in the memcpy

fiery turtle
#

hm

loud ice
#
__attribute__((naked)) void atomic_memcpy(char* dst, const char* src, size_t len) {
  // don't tell clang
  asm("jmp memcpy");
}
left orbit
#

lmao

fiery turtle
#

but clang would have to be able to see the atomic loads lol

fiery turtle
#

otherwise it cant prove anything

flat badge
#

write side:

s = seq.load(acquire)
seq.store(s + 1, relaxed)
fence(release)
data1.store(relaxed)
data2.store(relaxed)
data2.store(relaxed)
seq.store(s + 2, release)
loud ice
#

clang has to assume that inline asm performs an unknown sequence of AM operations

gentle peak
loud ice
#

which can include an atomic memcpy

gentle peak
#

it doesn't know anything about what it does, so it has to assume the "worst"

#

which in this case works in our favor

fiery turtle
#

just attribute(alias_of)

flat badge
#

actually the first load can be relaxed i guess

gentle peak
#

or .globl atomic_memcpy; .set atomic_memcpy, memcpy

#

actually idk if that works across shared objects

fiery turtle
loud ice
flat badge
#

with which release?

loud ice
#

asm cannot be

fiery turtle
flat badge
#

the acquire synchronizes with the last update of the seqlock

#

but if that's not needed because the entire contents are overwritten anyway, it can be relaxed

#

depends on what exactly you do with data1 etc

#

if you rmw it, it needs to be acquire

fiery turtle
#

what does seq have to do with the contents

flat badge
#

if you just store independent data, it can be relaxed

fiery turtle
#

then stores can be reordered to before the release

flat badge
#

no, stores can not be reordered before release barriers, that's the point of release

loud ice
fiery turtle
#

data1.store() can be reordered, a release above doesnt protect it

flat badge
flat badge
fiery turtle
#

to after the relaxed load

flat badge
#

a preceeding release fence prevents reordering, yes

fiery turtle
#

reordering of the prior load, yes

#

but not the store that happens after the fence

flat badge
#

it's not clear to me what you mean, can you post the whole listing?

#

where can what be reordered to?

fiery turtle
#
s = seq.load(relaxed)
seq.store(s + 1, relaxed)
fence(release)
data1.store(relaxed)

can be reordered to

s = seq.load(relaxed)
data1.store(relaxed)
seq.store(s + 1, relaxed)
fence(release)
gentle peak
#

the fence turns data1.store into a released store

flat badge
#

yeah this can't happen

gentle peak
#

seq is a memory access before a released store

#

so seq cannot be reordered to after data1

loud ice
#

and then the seq load can be moved before them

flat badge
#

the only reorderings that are possible are reorders of the data1 vs data2 vs data3 stores

fiery turtle
#

wait I always forget whether these fences apply to previous or next operations

gentle peak
#

release applies to next, acquire to prev

fiery turtle
#

whats the intuition here?

loud ice
#

if that program was in fact correct, then this would not panic: ```rs
fn thread1() {
loop {
let data = sh.data.0.load(Ordering::Relaxed);
fence(Ordering::Acquire);
let seq = sh.seq.0.load(Ordering::Relaxed);
assert!(data <= seq);
}
}
fn thread2() {
let mut n = 0;
loop {
sh.data.0.store(n, Ordering::Relaxed);
fence(Ordering::Release);
sh.seq.0.store(n, Ordering::Relaxed);
n += 1;
}
}

#

and it does panic

fiery turtle
#

i think u also misunderstood the operations to which fences apply

#

Maybe(?)

flat badge
loud ice
#

is it not?

#

am i missing fences?

#

ah wait yeah i am

#

okay

gentle peak
#

an acquire fence makes all past loads acquire loads, and thus any future memory ops cannot be reordered to before the last atomic load before the fence

loud ice
# flat badge that is not what the program does
    std::thread::Builder::new()
        .spawn(move || loop {
            let seq1 = sh.seq.0.load(Ordering::Acquire);
            let data = sh.data.0.load(Ordering::Relaxed);
            fence(Ordering::Acquire);
            let seq2 = sh.seq.0.load(Ordering::Relaxed);
            if seq1 == seq2 {
                assert!(data == seq1);
            }
        })
        .unwrap();

    let mut s = 0;
    loop {
        sh.seq.0.store(s, Ordering::Relaxed);
        fence(Ordering::Release);
        sh.data.0.store(s, Ordering::Relaxed);
        sh.seq.0.store(s + 1, Ordering::Release);
        s += 2;
    }

is this better?

#

this still panics, btw

#

loading a number lower than seq1

flat badge
#

the write side is still wrong

loud ice
#

ah

flat badge
#

you want to store s + 1, then s + 2

loud ice
#

i am

#
    std::thread::Builder::new()
        .spawn(move || loop {
            let seq1 = sh.seq.0.load(Ordering::Acquire);
            let data = sh.data.0.load(Ordering::Relaxed);
            fence(Ordering::Acquire);
            let seq2 = sh.seq.0.load(Ordering::Relaxed);
            if seq1 == seq2 {
                assert!(data == seq1, "loaded {seq1} vs {data} vs {seq2}");
            }
        })
        .unwrap();

    loop {
        let s = sh.seq.0.load(Ordering::Relaxed);
        sh.seq.0.store(s + 1, Ordering::Relaxed);
        fence(Ordering::Release);
        sh.data.0.store(s, Ordering::Relaxed);
        sh.seq.0.store(s + 2, Ordering::Release);
    }
``` better?
#

same result

#

because the program is equivalent lol

flat badge
#

sh.data.0.store(s, Ordering::Relaxed); <--

loud ice
#

oh wait

#

yeah

#

nvm

#
    std::thread::Builder::new()
        .spawn(move || loop {
            let seq1 = sh.seq.0.load(Ordering::Acquire);
            let data = sh.data.0.load(Ordering::Relaxed);
            fence(Ordering::Acquire);
            let seq2 = sh.seq.0.load(Ordering::Relaxed);
            if seq1 == seq2 {
                assert!(data >= seq1, "loaded {seq1} vs {data} vs {seq2}");
            }
        })
        .unwrap();

    loop {
        let s = sh.seq.0.load(Ordering::Relaxed);
        sh.seq.0.store(s + 1, Ordering::Relaxed);
        fence(Ordering::Release);
        sh.data.0.store(s, Ordering::Relaxed);
        sh.seq.0.store(s + 2, Ordering::Release);
    }
#

ah wait nvm

flat badge
#

try to convert my pseudocode to a working program challenge (impossible)

loud ice
#

lol

#

i mean yeah your code is broken so like (/j)

#
    std::thread::Builder::new()
        .spawn(move || loop {
            let seq1 = sh.seq.0.load(Ordering::Acquire);
            let data = sh.data.0.load(Ordering::Relaxed);
            fence(Ordering::Acquire);
            let seq2 = sh.seq.0.load(Ordering::Relaxed);
            if seq1 == seq2 {
                assert!(data == seq1, "loaded {seq1} vs {data} vs {seq2}");
            }
        })
        .unwrap();

    loop {
        let s = sh.seq.0.load(Ordering::Relaxed);
        sh.seq.0.store(s + 1, Ordering::Relaxed);
        fence(Ordering::Release);
        sh.data.0.store(s + 2, Ordering::Relaxed);
        sh.seq.0.store(s + 2, Ordering::Release);
        /*unsafe {
            core::arch::asm!("dc cvau, {}", in(reg) &sh.data);
        }*/
    }
#

i tried adding a dc cvau to try making it hit more but it didnt help

flat badge
#

so, does it work now?

loud ice
#

no

#

it loads a value of data higher than both seq1 and seq2

#

or lower if i mess with the dc cvau

fiery turtle
#

Well and release

loud ice
#

acquire would fix it

#

lol

fiery turtle
#

But the fence just promotes your previous relaxed load to acquire

#

No?

flat badge
#

the test is still incorrect

#

and doesn't match my pseudocode

#

because seq1 == seq2 is not the right test ;D

loud ice
#

aahhh

#

okay

fiery turtle
#

Pitust do u know what seqlock does KEKW

flat badge
#

you're missing if seq1 & 1 { continue }

loud ice
#

okay yea works now

fiery turtle
#

It makes concurrent reads and writes to data larger than a pointer possible by making readers retry

#

If seqnum is odd or changed from the initial

flat badge
loud ice
#

ah

#

yeah okay

#

cool

#

yea seems to work so thats neat

fiery turtle
#

What does your code look like now

loud ice
#
    std::thread::Builder::new()
        .spawn(move || loop {
            let seq1 = sh.seq.0.load(Ordering::Acquire);
            if seq1 & 1 != 0 {
                continue;
            }
            let data = sh.data.0.load(Ordering::Relaxed);
            fence(Ordering::Acquire);
            dmb();
            let seq2 = sh.seq.0.load(Ordering::Relaxed);
            if seq1 == seq2 {
                assert!(data == seq1, "loaded {seq1} vs {data} vs {seq2}");
            }
        })
        .unwrap();

    loop {
        let s = sh.seq.0.load(Ordering::Relaxed);
        sh.seq.0.store(s + 1, Ordering::Relaxed);
        fence(Ordering::Release);
        sh.data.0.store(s + 2, Ordering::Relaxed);
        sh.seq.0.store(s + 2, Ordering::Release);
    }
gentle peak
#

why the dmb()

#

it works without it

loud ice
#

i was trying to widen the error window

#

by adding slow crap