#Zinnia

1 messages · Page 12 of 1

near tartan
#

or be a seperate dylib

hybrid island
#

I mean relocatable in the ELF sense, as in not linked yet.

near tartan
#

and meson already gives me this integration

#

instead of having to deal with two build systems

#

not to mention, again, there's no real way to encode this info in cargo.toml

hybrid island
#

You mean you can either encode static or dynamic but not something that depends on config?

near tartan
#

yes

hybrid island
#

Ohh yeah cargo basically explicitly said they don't want to replace make

near tartan
#

i don't like cargo at all

hybrid island
#

And looks like they may even want to get rid of build.rs

near tartan
#

maybe meson has better rustc invocations meme

near tartan
#

@vast lotus what does struct vmm stand for?

vast lotus
#

An address space

#

Bit poorly named

near tartan
#

what's the reference to inode/dentry for?

vast lotus
#

That's the file that was exec'd to create the address space

#

If you pass HYDROGEN_INVALID_HANDLE to hydrogen_fs_fopen you get a file descriptor to it

#

It's intended to be used in the same situations as /proc/self/exe on linux

#

I could've put it in the process as well ig since a successful exec always makes the process single-threaded (kills other threads if current process, otherwise fails if process already has threads), that'd probably be a more logical place to put it but this works as well

near tartan
#

so if i understand it correctly all this does is page table + list of mappings?

vast lotus
#

Yeah

#

The hydrogen vmm is pretty simple but it works (as long as you don't want swap)

near tartan
#

does it just keep track of every call to mmap or something?

vast lotus
#

It keeps track of allocated virtual memory regions

#

It merges them if possible but if not then it effectively tracks mmap calls yes

near tartan
#

so also the mapped fds

vast lotus
#

The mapped memory objects, but that's effectively the same thing

near tartan
#

in your kernel or generally?

vast lotus
#

Oh and it keeps the regions in an AVL tree as well

vast lotus
#

Aside from the vDSO I guess

near tartan
#

ah

#

thank you

near tartan
#

okay I've got the bare bones vfs functions inplemented

#

i could technically already read executables now, but I'm going for a page cache now so i can mmap files

near tartan
hybrid island
near tartan
#

ignore that it took 20 seconds to start aps for some reason

#

hm, very odd

#

the fuck?

hybrid island
#

Is the CPU freq low? Last time I tried an OS of mine on real HW, it was super slow because the CPU freq is relatively slow by default. At least on the platform I used.

near tartan
#

this is the first time this is happening

#

it used to be instant on this laptop

fast mirage
near tartan
#

lol now it's just not starting ap5

#

and 4

fast mirage
#

bru

near tartan
#

hm

#

anyone know what could be causing this

#

it's just refusing to start ap 4

#

after a reboot it does it but now it takes ages again

zealous salmon
#

nice reflection

lofty copper
#

Hair reveal

near tartan
#

hm maybe I am missing a memory barrier

near tartan
#

okay maybe i'll have to reconsider using meson with rust

#

at least the initial setup is not as easy as i had assumed

hoary cave
#

no it's fucking not man

#

it's cancer

near tartan
#

yea

#

technically there's meson_cargo

#

to connect cargo to meson

#

but atp i can just use makefiles

hoary cave
#

rust with makefiles goes crazy

near tartan
#

well what do you think i should do

hoary cave
#

just stick with cargo i think

#

it does what it's supposed to do well

#

(build the kernel)

near tartan
#

it's also supposed to build drivers :(

hoary cave
#

doesn't it?

near tartan
#

it does, but idk if i can do stuff like built-in drivers

#

or like, either modular or built-in drivers

hoary cave
#

well not that easily

#

but technically you could do uhh

near tartan
#

ofc i'd like to stick to cargo but i haven't really found a way to do this

hoary cave
#

like an nvme-builtin feature

#

and make kernel depend on it

#

if that feature is enabled

near tartan
#

hm

hoary cave
#

idk i dont think you can do that with cargo and nothing else

somber solar
#

xmake does rust trl

hoary cave
#

you might want to do something like

#

generate cargo lmao

#

just write your own little tool

#

to like generate Cargo.toml files

near tartan
#

maybe a common build.rs that decides the build type

hoary cave
#

based on kernel configuration

#

can you do that?

near tartan
#

well yes

hoary cave
#

i didn't know

near tartan
#

that's what i do rn

#

kinda

#

just for the lds

hoary cave
#

there's no build type

near tartan
#

not yet

#

oh you mean that

hoary cave
#

build type is determined by crate-type

near tartan
#

you can have two kinds of library types

hoary cave
#

in cargo.toml

near tartan
#

like

hoary cave
#

you don't want a dylib for built in modules

near tartan
#

i know

#

you can do this

#

then the build.rs could determine the crate type to use based on the configuration

hoary cave
#

i didn't know you could do that

#

that's nice

#

but then still

#

there's the linking it to the kernel

near tartan
#

that's easy-ish

hoary cave
#

okay

#

go cook

near tartan
#

like linux, you can have a drivers crate

#

and the build.rs generates the import for it

hoary cave
#

kernel->drivers->nvme->kernel

#

does that work?

#

i guess with extern it does

#

okay you go cook man

near tartan
#

i'm not sure, but i think cargo can break cycles

hoary cave
#

i believe in you

near tartan
#

ok vro

#

i'll leave early today

hoary cave
near tartan
#

i might have to do a page cache to do mmap on files

hoary cave
#

real

near tartan
#

god why

hoary cave
#

idk how my shit is gonna work yet

near tartan
#

zig?

hoary cave
#

but i have blocking mutexes

#

yes

near tartan
#

my mutexes are still blocking too

#

i also have a fucked impl of blocking rcu

hoary cave
#

blocking as in it blocks the thread

#

not as in it spins

near tartan
#

oh

hoary cave
#

lmao

near tartan
#

i mean

#

i guess it shouldn't be too hard

hoary cave
#

no not really

near tartan
#

what do you do

hoary cave
#

i have a WaitObject which has a list of waiters, when you try to .lock a mutex which is already held it creates a WaitEntry which is (thread, blockToken) and adds it to the waiter list then yields

#

and mutex on release pops one entry from the list and wakes it up

near tartan
#

nice

hoary cave
#

yeah it's pretty simple

#

and pretty buggy probably

#

won't find out until later i guess

near tartan
#

i think my percpu code is completely fucked :(((

hoary cave
#

i thought it worked

near tartan
#

yes but that's just an illusion

#

because

#

i realized i can't do per cpu vars like in c++

#

since in c++ iirc those are initialized via a constructor

#

i.e. you can run the same code x amount of times, right?

#

because if i memset the percpu block to 0, that means all the statics are broken now

#

i.e. static function pointers

#

looking at the hex of the percpu section shows that there are multiple non-zero bytes

hoary cave
#

then don't do that lol

near tartan
#

how

#

i can't forbid that

hoary cave
#

you use whatever is in the section as a template

#

when initializing a new percpu you copy the template into the new percpu memory region

near tartan
#

yea but i don't think i can use it as a template if the bsp writes to it 😭

#

maybe i'm overthinking

hoary cave
#

well the bsp shouldn't use the template as it's percpu region

near tartan
#

hm

hoary cave
#

you can do smth like

#

you can provide a special bsp percpu with a linker script

#

just do what you already do for percpu but copy it into a percpu_bsp section or smth

#

and provide symbols for that

near tartan
#

what about saving all percpu data twice

#

since it's a macro

hoary cave
#

well that's what that would do

#

oh

#

real

near tartan
#

because if i can do it at link time i could write to per-cpu data before mman is even set up

#

i'd have like ".percpu.template" and ".percpu"

hoary cave
#

well my solution also does it at link time tf

near tartan
#

huh

#

maybe i can copy it in the linker script

hoary cave
#

yes

#

exactly

#
    /* Per-CPU data block. MUST always come last. */
    .percpu ALIGN(CONSTANT(MAXPAGESIZE)) : {
        LD_PERCPU_START = .;
        *(.percpu.init)
        *(.percpu)
        . = ALIGN(CONSTANT(MAXPAGESIZE));
        LD_PERCPU_END = .;
        LD_DATA_END = .;
    } :percpu```
you can just copy this before the bss section, make it part of data and get symbols to it
#

idk what .percpu.init is but yeah

near tartan
hoary cave
#

maybe what im saying doesnt make much sense but you get the poiunt

#

interesting

#

idk, you cook

#

you are on the right path

near tartan
#

assertos also does it

#

get trolled

somber solar
vast lotus
somber solar
near tartan
somber solar
#

that's mine

near tartan
#

how is mine different

somber solar
somber solar
near tartan
#

yes because if you use that storage for the BSP then you can't use it anymore

#

so that's what we were talking about

somber solar
#

you allocate storage at init

near tartan
#

what if i want to use it before allocations are available

hoary cave
#

imo percpu should be up before anything else

#

it's like such a crucial thing

somber solar
hoary cave
#

threading

somber solar
#

before allocation?

hoary cave
#

yeah

somber solar
#

that doesn't make much sense but you can just have another section just for bsp

near tartan
#

guess wtf we're doing rn

somber solar
#

what I said

hoary cave
near tartan
#

hm okay linker script isn't helping here

#

okay what if i just store the percpu data, and then reserve enough for the bsp

#

then copy it at runtime

somber solar
#

yes

hybrid island
#

I don't have a .percpu section, I just dump a struct pointer in a CPU register trl

near tartan
#

in menix you can define per cpu variables at any point in the code

cyan nexus
#

i dump it into the GSBASE msr

near tartan
#

for example:

cyan nexus
hybrid island
near tartan
#

or rather, start + (cpu_id * size)

storm bobcat
near tartan
#

how

#

it's encoded in the type

storm bobcat
#

because you have per cpu variables all over the code

#

instead of one cpu struct

near tartan
#

so what

storm bobcat
#

harder to follow

#

imo

near tartan
#

i disagree

#

per cpu variables should only be used where they're defined most of the time

#

like, a lapic percpu variable only makes sense to use in the apic code

#

and the rest (like scheduling structures, cpu id, etc) are part of a globally accessible struct

somber solar
#

one large struct is just messy

#

especially when you need to have arch specific members

hybrid island
#

I just name the arch-specific struct the same thing on all architectures.

#

arch_cpulocal_t in my case

near tartan
#

but why

storm bobcat
#

I have struct arch_cpu_cb

near tartan
#

arch specific fields shouldn't be visible outside of the arch code imo

storm bobcat
#

they arent?

#

the port defines struct arch_cpu_cb

near tartan
#

when would you use this

#

i don't see the point at all

storm bobcat
#

to ensure it's portable???

near tartan
#

wat

#

no like, why do that if you're only ever going to access its members in the arch impl

storm bobcat
#

how else would you have architectural per-cpu fields in the struct

storm bobcat
#

and struct arch_cpu_cb is in that struct

near tartan
#

i get that

hybrid island
near tartan
#

my main point is that it's very easy to see which variables are per-cpu and which aren't

#

because they're essentially wrapped in a PerCpu block

hybrid island
#

... but not necessarily in the place you're using it.

#

Because global variables just have names.

near tartan
#

but to use it you have to call a function on that type

hybrid island
#

Whereas, of course, in the cpulocal_t struct, you must always obtain a handle to it first.

near tartan
#

if you try to access them directly you'll get compiler errors

fast mirage
#

why meson rust

near tartan
#

because

#

cargo can't do everything (afaict)

#

we will research tonight

fast mirage
#

but doesnt cargo do its job fine?

#

what else do u need it to do

#

im not that big into rust but i believe cargo should be okay?

hybrid island
# fast mirage why meson rust

As far as we know, Cargo doesn't support the way he wants to do kernel modules that are either statically or dynamically linked via a config option.

near tartan
#

it's missing post build actions, advanced configuration (à la kconfig)

fast mirage
#

makefile trl

#

makes sense

hybrid island
cyan nexus
#

cmake can do post build?

hybrid island
#

Yes but it can't do everything I need it to

cyan nexus
#

it can run linux commands

#

it can do everything

hybrid island
#

Specifically the way I go about some things like launching QEMU are just way easier to do via Makefiles

cyan nexus
#

i do it via terminal

hybrid island
# cyan nexus i do it via terminal

Well yes but make qemu is a whole lot more convenient doing cmake -B build and whatever else to run QEMU, and is especially a lot easier than doing the whole thing by hand.

cyan nexus
#

i prefer to do it by hand so i can stop and launch what i need in that moment

near tartan
#

but in my distro

#

not the kernel

hybrid island
#

I don't make such a distinction just yet

#

I have a severe case of skill no time issue

ionic lava
#

@near tartan do you use your arm64 thinkpad for testing? can you run one for me if so? It's the thing, I asked you before for orange pi, but this time, it moved further and now concern is to investigate on how RAM is placed in the system address space, is it fragmented etc. Building PFN from UEFI memory map turned to be quite a challenge. Knowing memory layout on real hw platforms would be insightful. If you can, then when you have time and will, here's the link to zip archive. Unzip it the way so that those 2 directories go into the root directory of a USB thumbdrive (or SD card if applicable) and from UEFI Boot Manager launch efi\Upptech\antload.efi via the "boot from file" option (or using UEFI shell if available) and show its output.

https://ant-upptech.sourceforge.io/uploads/lybid-arm64-3.zip

near tartan
#

it's 100% fragmented

ionic lava
#

I see.

grave peak
near tartan
#

actually you can just look at the dtb lol

grave peak
#

looks like its exactly one region tho

#

@80000000

steady echo
#

so /usr/dev work ...

near tartan
#

but im gonna put it to /dev

late bramble
#

Menix running Minecraft when

near tartan
#

@sick flax can we get luantis?

#

aka minetest

#

that's c++

ionic lava
# grave peak yeah would be interesting to know what the memory map looks like

Here's how RAM is assigned for Pinetab 2, rk3566 SoC, uboot's UEFI. In the output, 1st number is system address, 2nd is number of pages. Gaps are present. Worse, both edk2 and uboot for some reason use EfiReservedMemoryType, that by the specification shouldn't have been used. And what to do with it, it's neither a real gap, nor should be preserved by the OS in working and sleep states, not bad memory nor even could be read/written. It just screws up the map. But well, it is as it is.

grave peak
#

well firmware engineers wipe their ass with the specification

sick flax
near tartan
#

nope

#

minecraft clone

sick flax
#

Link?

near tartan
#

it works with *BSD

#

so hopes are high

#

it uses CMake to build

fast mirage
near tartan
#

does it make sense to abstract scheduling algorithms

#

aka

pub trait Alg {
    fn next(&self) -> Task;
    fn insert(&self, t: Arc<Task>);
}
mental tinsel
#

sounds cool and I don't see any problem with making traits for these

near tartan
#

bruh cargo can't handle cyclic imports

#

clown system

marble salmon
#

that's good design, cyclic deps are broken anyway

near tartan
#

probably

magic charm
near tartan
#

well my issue atm is that i want to have built-in drivers

#

if i split them into their own crate that will introduce a cyclic dependency

#

because the drivers need the kernel and vice versa

#

the kernel doesn't really depend on it, but i somehow need to link it into the kernel binary

#

or i just say fuck it and leave drivers to be modular

#

sometimes i really do miss the flexibility of c/c++ codebases

near tartan
#

i like the way linux separates drivers into their own dir but makes them non-optional

#

e.g. irqchip

#

tbh i hate everything about cargo

#

it's definitely one of the weaker parts of the rust ecosystem

#

ive said this a billion times but i'd like to use meson with rust, but it's really hard to setup with external crates and such

#

and i need to have a custom build of core and alloc

late bramble
#

I like cargo

#

not everything but I think it's good overall

near tartan
#

it does what they want you to do

#

there's no way to do anything advanced

#

even build scripts are pretty limited

marble salmon
#

Can't you just make a kernel-core crate with all the core infra, then driver crates that depend on that and finally a whole-kernel crate that includes everything?

hoary cave
#

i basically suggested that, i guess

#

at some point at least

#

the only problem is deciding which drivers are builtin and which are not

#

i don't think there's an easy way to do that with cargo

#

but i think doing something like a script which generates toml files with the correct values + a file that can be included that contains a list of builtin modules so kernel can initialize them

near tartan
#

we'll see

hoary cave
#

i don't think there's a way with build scripts to specify the build type

#

in general cargo isn't that flexible and you'll need a custom build system for that

#

it's probably easiest if the build system just falls back to cargo in the end

#

for crate support mainly, i guess

magic charm
near tartan
#

oh yea

#

i "solved" that by having a generic wrapper function that just calls an implementation with a certain api

near tartan
magic charm
#

and you can have circular dependency in a sensible way in that A and B of course don't require to see each other's .c file, B is still effectively "depending" on A, i.e. B.h includes A.h, but both A.c and B.c include A.h and B.h

distant cypress
near tartan
#

oh yeah

magic charm
#

modula has an interface/implementation distinction but the whole implementation must be one file

near tartan
#

tbh i need a rust with c family syntax

#

also cpp is godlike

mental tinsel
#

carbon trl

near tartan
#

not c++

fast mirage
distant cypress
magic charm
distant cypress
#

Is Carbon even used for anything?

mental tinsel
#

it kinda looks like "We have rust at home"

near tartan
#

man

#

another day of not doing anything

somber solar
#

me every day

#

I need motivation but in pills

#

so I can get it whenever I need to

near tartan
#

well the current obstacle is a page cache

#

i looked at maestro but it's not really compatible with my design

#

but idk how to even design it properly

#

the curse of being self taught

storm bobcat
#

huh? just read existing OSs implementations

#

like linux's, NT's, etc

#

idk what that has to do with self taught

near tartan
#

every time i look at the code of another project like linux i feel overwhelmed because i don't even know what i'm looking for

#

i don't know any theory behind anything

#

i just go by what works

storm bobcat
#

learn the theory then read the code then you'll understand much better

near tartan
#

🤯

storm bobcat
#

I personally dont read linux code because I feel like its super messy

near tartan
#

duh

storm bobcat
#

BSD code I understand much more

near tartan
#

me neither

#

bsd code is more readable yes

storm bobcat
#

so yea just pickup a textbook on whatever you want to copy

#

id do linux if I were you, it seems like that's what you're aiming for

#

dont even need to look at the code if u know the theory well

near tartan
#

i do have the tanenbaum book, but it's like 2k pages

storm bobcat
#

no, a linux textbook

#

read the page cache section

near tartan
#

why not a generic book on that topic?

storm bobcat
#

read it again until u understand it, write a python script to simulate it in usermode or something then implement it

storm bobcat
#

and the generic stuff is good as well for a general overview

#

you'd start with a generic book to learn why we need a page cache and how they work generally then you read about how a bunch of OSes actually do it

#

and now you'll have a very good understanding of page caches, why we need them (what problem do they solve) and what typical implementations look like

#

and you do that for every component you work on trl

near tartan
#

💀 see you in 2035

storm bobcat
#

if you already know why we need page caches id just skip the generic book and go straight to an implementation

near tartan
#

ight

storm bobcat
#

do this with what is closest to your kernel atm, I'd do linux if I were you since you seem to have a linux-style vfs iirc

near tartan
#

ye

#

maybe the linux docs say something about it

storm bobcat
#

yes that's very likely

#

there's also a linux memory management book I think

#

any linux book will likely go into details

near tartan
#

oo

late bramble
near tartan
#

like

#

you have read and write on your nodeops

#

i don't have that on every node

storm bobcat
#

is the strictness of rust keeping you from doing something youd want

near tartan
#

no

#

it's definitely not a language issue

#

it's a skill issue

late bramble
near tartan
#

no no, i mean like

#

you cannot call the function on some inodes

#

because i have different ops per inode type

#

with an enum

storm bobcat
near tartan
#

🤯

hybrid island
late bramble
#

I think I fucked up the fileops and nodeops interface btw. There should be an implementation for each filesystem type and file type but I only have one per filesystem type. So I end up having to recheck the type of the file when doing an op on it

near tartan
#

you have one file ops per fs?

late bramble
#

Actually I don't even remember

near tartan
#

xd

late bramble
#

Maybe I should have one fileops per file type, yeah. Not one per fs

near tartan
#

well in my design, files are created/opened by the fs

late bramble
near tartan
#

like abbix said my everything is very much designed like linux

#

in a bad way

mental tinsel
#

are these the same thing

#

I might be talking about something totally different

near tartan
late bramble
near tartan
#

oh

#

i thought by evict you mean write back

late bramble
#

The time thing I have is just a task that writes dirty page to disk

#

But the page stays in cache after being written

mental tinsel
#

my cache is write-through for now :p I will probably implement writeback functionality tho, seems cool

#

thanks mister maestro 👍👍😁

late bramble
#

I used to think the sentence "Unused RAM is wasted RAM" was just an excuse to write inefficient software but then I started to understand it when I wrote the page cache.
You want to use all the available memory on the system to avoid accessing the disk as much as possible

#

It also applies to other cases. If a value is expensive to compute, you may want to use memory to store it instead of recomputing it each time

near tartan
#

I'll adopt the linux page cache strategy

#

at least a basic variation of it

#

looking at the docs it seems sane enough

#

man i miss headers

late bramble
mental tinsel
marble salmon
#

I once wrote a program for a competition that strictly allocated memory in a fifo way

#

So to get a few % in extra allocation performance i made it alloc a couple tib in virtual space

#

and used a bump allocator until it hit the end of the range

#

while madvise(free)ing the head of the range

pine rock
#

google is very unaware that like 99% of companies cannot just rebuild the entire universe whenever they feel like

#

imagine if microsoft had to explain to boomers that can hardly use a computer that they need mvcrt90-abi2.dll rather than mvcrt90-abi1.dll

#

nightmare territory

hoary cave
#

it's already kind of like that

#

with different c runtime versions

near tartan
#

on windows some programs just literally ship their own vc++ runtime

pine rock
#

you can take msvcrt140.dll and run a program that expects 130

hoary cave
#

that's a... stretch

#

you really shouldn't be doing that

#

but you could say the same for msvcrt140-abi1.dll and msvcrt140-abi2.dll :^)

#

it works until it doesn't :^)

hoary cave
pine rock
#

idk this just sounds like making an already pretty painful situation 10x worse because google is difficult

marble salmon
#

tbh not being able to rebuild the world sounds like a huge mess

near tartan
#

true

marble salmon
#

also, realistically the only thing that'd be impossible is linking c++ 23 code into pre c++ 23 code etc

near tartan
#

why?

eternal wharf
# near tartan why?

they could just keep the old code in the .so and add the new code into a new ns

#

like what happened in c++11 with std::string

near tartan
#

oh

#

ew

#

open source ftw

eternal wharf
#

don't think closed source stuff that maintains abi compat does it much different

pine rock
#

closed source dlls from a company you've never heard of

near tartan
#

i think i'm cooking

#

i made a virtual object struct which keeps track of the mapped physical pages, the backing memory, etc

mental tinsel
#

MenixExecutiveMenixObjectAllocator

somber solar
#

MEMOA

near tartan
#

yes

near tartan
#

should a PFNDB have a global mutex or a per-page mutex? 🤔

modern hamlet
#

or ar least a regional one

near tartan
#

true

near tartan
#

okay this freebsd book has a bunch of nice implementation explanations

#

i should read more about os internals instead of going in head first

near tartan
distant cypress
autumn jasper
#

well

#

youll probably find situations where a per-page mutex is good

#

but a "global mutex" for the pfndb is a bad way to think of it

#

instead you should just add mutexes and change them around as your needs evolve

#

for example youll probably have a mutex around the page lists

#

no need to conflate that with some other mutex

near tartan
#

page lists? like the freelist pointers?

autumn jasper
#

that and whatever other page lists you may have

#

depending on your vmm design and page replacement strategy and so on

near tartan
#

i believe i dont have another

#

hm

#

then i need to use atomic pointers

#

or wrap the next pointer in a mutex

#

because rust ™

near tartan
modern hamlet
#

I mentioned "a global one [mutex] for the allocator"

modern hamlet
near tartan
#

i have a mutex around the head of the freelist

#

alright then

modern hamlet
#

unless you can make a lockless freelist allocator

near tartan
#

well it's not really an idea, i'm kind of forced to do that :p

#

otherwise i can't modify the head

modern hamlet
#

my kernel uses a global spinlock for PFNs but that's because I suck

#

either that or my MM depends on PFN allocation being fast and also not blocking because of the former reason

near tartan
#

damn

modern hamlet
#

well okay I was exaggerating a little bit, I don't suck, but I think it might be possible to use a blocking lock instead of a spinlock for my PFN allocation

#

I just don't feel like doing it

near tartan
#

i should look into rcu

grave peak
#

looks inside rcu
rcu

near tartan
#

yes, true

#

but like

#

idk if it'll be easy to abstract in rust

autumn jasper
#

oh yeah a dumber preemption-masking rcu could be useful in implementing a smarter blocking rcu probably huh

grave peak
#

probaly not

#

because rust makes stuff like that hard

near tartan
#

eh

near tartan
#

if i knew how to impl it properly it should not be hard

grave peak
#

linux uses sleeping rcus or whatever i think(?)

autumn jasper
#

yeah but do they use a dumber rcu to imlpement the slepeing rcu

grave peak
#

not sure

autumn jasper
#

thatd be funny

near tartan
#

as far as i understand it you lock an rcu and write to another memory location, then swap pointers atomically to update

grave peak
#

rcu is just, i defer deletion of something until im sure no one is using it anymore

autumn jasper
#

thats too broad of a definition

grave peak
#

on the read side u just copy the pointer after locking it

autumn jasper
#

that could be refcounting too

grave peak
#

i mean refcounting sort of solves this problem as well

autumn jasper
#

it does im just saying that "i defer deletion until im sure no one is using it" is like

#

every garbage collection scheme ever

grave peak
#

yeah

autumn jasper
grave peak
#

yes but in rc u bump some refcount

#

in rcu u dont bump anything

#

so there's less overhead

near tartan
#

hm

grave peak
#

and the person that wants to delete the thing does the work

near tartan
#

i thought it was a synchronization primitive

grave peak
#

in rc you do the work if you're the last to reference the object

grave peak
#

it's a lifetime prolongation primitive

near tartan
#

bruh

autumn jasper
#

basic principle is that with rcu you dont delete the thing until youre sure that everybody who could possibly have touched the thing has passed through a "quiescent point"

#

which is some point where they definitely are not touching the thing anymore

near tartan
#

oh

#

then the wikipedia article is really misleading

grave peak
#

u should really read the linux article instead

autumn jasper
#

so with preemption-blocking rcu for example you can wait for every unblocked thread that existed at the time you touched the thing to pass through the scheduler at least once and then you know nobody has a pointer to the thing

grave peak
#
void rcu_read_lock(void) { }

void rcu_read_unlock(void) { }

void synchronize_rcu(void)
{
        int cpu;

        for_each_possible_cpu(cpu)
                run_on(cpu);
}
autumn jasper
#

and you know that by blocking entry to the scheduler while using the thing

grave peak
#

like this is all u need to know

#

everything on top is just impl details

autumn jasper
#

if you block preemption you only need to make sure every CPU

#

has passed through the scheduler

#

and then you know nobody has a pointer to the thing

#

the stronger thing of "every thread" would only be needed if you didnt block preemption (its also broken because the scheduler could run while he still has a pointer to the thing)

hybrid island
#

I don't know exactly how valid this is but my approach is just going to be to disable interrupts entirely while reading what's behind an RCU pointer, and adding a grace period (currently 50us) before calling free.

autumn jasper
#

thats invalid

#

youll die

grave peak
#

there's an example with a mutex

static DEFINE_RWLOCK(rcu_gp_mutex);

void rcu_read_lock(void)
{
        read_lock(&rcu_gp_mutex);
}

void rcu_read_unlock(void)
{
        read_unlock(&rcu_gp_mutex);
}

void synchronize_rcu(void)
{
        write_lock(&rcu_gp_mutex);
        smp_mb__after_spinlock();
        write_unlock(&rcu_gp_mutex);
}
#

i assume u want this

hybrid island
grave peak
#

so u spin for 50 us then unconditionally free it?

#

thats uhh

#

not an rcu

#

the grace period extends indefinitely for as long as someone is working with a copy

hybrid island
#

Oh.

autumn jasper
#

on a real system that might work like 100% of the time but under virtualization itll quickly break

hybrid island
#

I can add an atomic counter to the data (radix tree's rtree_node_t)

grave peak
#

thats called reference counting (a different technique)

hybrid island
#

Well no, because the thing that frees would wait on it.

autumn jasper
#

because youre still doing the refcounting work plus involving this extra asynchronous context of the free-er for some reason

#

kind of defeats a lot of the point

#

i mean you might want to remove the deletion from the path of the guy who dropped it to zero

#

no big deal, he can just enqueue it to an atomic singly linked list

hybrid island
#

Doesn't this either fatten all my RCU pointers or make it so that only one thread can read at a time?

grave peak
#

yeah if its that expensive to delete u can just have the free callback put it on some list and wake up a work queue

grave peak
hybrid island
#

What does rcu_read_lock's mutex actually do to make that possible then?

grave peak
#

it's a rw lock

#

meaning readers can acquire it any number of times

#

and the write is exclusive

hybrid island
#

...

#

So I make the lock in my nodes an rwlock instead of exclusive, and lock it once for each thread that reads the node's content?

grave peak
#

the lock can be global per kernel binary

#

but yeah

hybrid island
#

Well I'm trying to make my radix tree somewhat inspired by Linux' xarray, which does fine-grained locking with spinlocks.

#

Maybe we should move the discussion of how I implement this over to #1153085124959809616 ?

grave peak
#

yeah

marble salmon
#

RCU is not really an rwlock

#

unlike an rwlock, RCU has no concept of an exclusive locking mode

near tartan
#

oh true i also need an rwlock impl

marble salmon
#

RCU is better seen as a barrier

near tartan
#
void rcu_lock(rcu* rcu) { asm volatile("" ::: "memory"); }
#

got it thank you for this correct information

marble salmon
# marble salmon RCU is better seen as a barrier

think of it like this: there are 3 primitives: lock(), unlock() and sync() and their relation is:
for each lock() that happens on thread T before a sync() in thread S starts, it is guaranteed that there is a corresponding unlock() by thread T that happens before the sync() in thread S completes

grave peak
#

and it probably wouldnt even suck that bad

marble salmon
#

why would you do that instead of using per data structure rwlocks?

#

that doesn't make any sense

grave peak
#

u wouldnt do that and no one argued that it's the thing u do when u rcus

#

its one compliant way to do it tho

marble salmon
#

well, a global rwlock is just stronger than RCU

#

it's not equivalent

grave peak
#

its a superset so its fully complaint tho

marble salmon
#

that's not very interesting though

#

that's like saying: you can implement mutexes in a compliant way by not doing SMP

grave peak
#

thats just false

#

u cant

marble salmon
#

it's true

grave peak
#

u still need mutexes in a uni processor system

marble salmon
#

depends on the specifics

#

you can just disable interrupts instead

#

in any case, "i can implement a weak synchronization mechanism using a strong one" is not really an interesting statement

autumn jasper
grave peak
#

well thats just an extreme example, but the thing about RCUs is their idea can be showcased using a very trivial implementation

autumn jasper
#

even unix v6 had a handful of blocking mutexes (explicitly implemented in ugly ways everywhere they were used) around like blocking io and stuff

marble salmon
grave peak
#

well

#

the write lock is probably only taken for like a few nanoseconds lol

marble salmon
#

what?

#

that's just not true

grave peak
#

u unlock it right after u lock it

#

so yes

#

it acts as a barrier

marble salmon
#

the write lock needs to wait for all pending readers to complete

#

in your rwlock implementation

grave peak
#

lock and unlock are wait free

marble salmon
#

unless you make the rwlock non-fair but then you cannot guarantee forward progress anymore

grave peak
#

in my implementation

#

u never said sync() is wait free

marble salmon
#

there is no way to make lock() and unlock() wait free with an rwlock implementation

grave peak
#

depends on your definition of wait free

marble salmon
#

no, sync() is of course not wait free, there is also no rcu implementation where sync() is wait free

#

that's just impossible

grave peak
#

exactly

marble salmon
#

wait free has a precise definition

grave peak
#

and lock/unlock are wait free

#

even in my example

marble salmon
grave peak
#

since my sync() releases the write lock right after acquiring it

#

isnt that a constant number of instructions

marble salmon
#

no? because if that CPU goes up in magic smoke while the write lock is taken, the readers will be blocked?

grave peak
#

what magic smoke

marble salmon
#

wait free is a worst case guarantee

grave peak
#

like literally the cpu stops working?

marble salmon
#

yes

grave peak
#

uhh lol

marble salmon
#

or if the interconnect that connects the cpu to the rest of the system fails

#

or whatever

grave peak
#

then ill switch to this impl

void rcu_read_lock(void) { }

void rcu_read_unlock(void) { }

void synchronize_rcu(void)
{
        int cpu;

        for_each_possible_cpu(cpu)
                run_on(cpu);
}
marble salmon
#

wait free has a precise meaning and one CPU waiting for anther CPU is not wait free

marble salmon
#

lock() needs to disable preemption for that to work

grave peak
#

well it assumes non preemptible kernel

near tartan
#

what does run_on do

grave peak
#

sched_setaffinity ig?

marble salmon
#

I'd advise everybody to think about synchronization mechanism in terms of what they guarantee and not in terms of how they're implemented

#

and this #1287456798407790684 message is the proper description of what it guarantees

#

if you implement code based on the inner workings of synchronization mechanisms, it's easy to make use of some extra properties that an alternative implementation will not provide

marble salmon
#

if your rwlock is not fair (= it prefers readers), you get fast lock() and unlock() times but you cannot guarantee forward progress of sync() anymore, even if all threads behave correctly

#

if your rwlock is fair, then lock() and unlock() are not fast anymore because a lock() may need to write for a sync() to finish and the sync() may need to wait for another reader to finish

grave peak
#

fair

marble salmon
#

the run_on() toy implementation and proper implementations can avoid both issues at the same time

distant cypress
grave peak
#

Depends how your rw lock is implemented

distant cypress
#

well, if someone write locks then is interrupted, the interrupting context cannot do a read lock.

idle flower
#

lock free pmm and vmm ftw

magic charm
near tartan
#

i hate this

#

oh i can just not let it infer the type

#

i love this

grave peak
near tartan
#

very much so

#

idk implementing data structures was 10x faster in C but you get untriggered UB left n right

#

I've already gotten this far I'm not giving up yet

storm bobcat
#

sunk cost fallacy

grave peak
#

With rust u get the same ub but because it optimized half of your kernel out KEKW

storm bobcat
#

or worse because you have code in unsafe

near tartan
#

well rust keeps me away from a lot of synchronization bullshit

storm bobcat
#

honestly im glad my current project is C even

#

I was focusing too much on C++ stuff with C++ (like making a huge templated library)

near tartan
#

i miss headers

storm bobcat
grave peak
storm bobcat
#

wdym

grave peak
#

Wdym wdym

storm bobcat
#

what am I gonna use what for

grave peak
#

The library

near tartan
storm bobcat
#

basically I had started a rewrite of my kernel and was meant to use the library with it (I wrote it in preparation while studying other OSes)

#

but now I joined ethan's project so the library stays unused

grave peak
#

But u decided to stick to C?

#

Ah

storm bobcat
#

but I learned a bunch of stuff so it doesnt really bother me and it was fun

#

I'm still stealing stuff from it

#

and converting to C

grave peak
#

Yeah

near tartan
#

that's one of the main niceties in rust

#

send + sync

storm bobcat
#

because rust forces a model on you it thinks it's right

#

but that doesnt mean it is for your use case

#

sometimes you do want to race on purpose

near tartan
#

really?

storm bobcat
#

yea

#

one place where I race on purpose to avoid lock contention is load balancing

#

(shameless steal from mintia)

near tartan
#

idk how I'm going to do that in rust

#

i might have to make a special sync primitive for that

storm bobcat
storm bobcat
#

deriving Sync

near tartan
#

that's what i mean

storm bobcat
#

but then how do you lock it

#

if you need to lock it

near tartan
#

FakeLock::lock and FakeLock::fake_lock

storm bobcat
#

crazy

#

that's why you use a good language

#

I swear one day our bullying will prove itself worth it and you'll switch to C meme

near tartan
#

rust comes in extremely handy with refcounted objects and error handling with locks

#

it's basically idiot proof

#

if i didn't have that I'd deadass just use C

storm bobcat
#

does it take into account lock ordering

near tartan
#

locks get destroyed in reverse locking order

storm bobcat
#

thats not what I meant

#

does it allow you to declare a lock ordering and ensures your locks are taken in that order

near tartan
#

i don't understand what order you mean

storm bobcat
#

to avoid deadlocks, it is very useful to have a defined lock ordering where for example you lock A then B and release B and A and you must ALWAYS do that

#

if you lock B before A you break the ordering and you're prone to deadlock

near tartan
#

you could do that with nested locks

near tartan
#

that's then enforced at the type level

#

you can lock A then B and unlock A, but then everything that was part of A becomes invalid

#

so if B is in the struct of A, B becomes invalid

#

if you first unlock A, then it won't compile

storm bobcat
#

yea i dont think thats what I meant

#

but anyway this is tricky to analyze statically

#

you just gotta be not an idiot when writing your code trl

near tartan
storm bobcat
#

but you can still lock B before A

#

even tho your ordering is A before B

near tartan
#

you cannot

storm bobcat
#

theres probably a way you can do this tho I do not know of it

storm bobcat
near tartan
#

yes

storm bobcat
#

B is not inside of A

near tartan
#

oh

#

yea that's different

#

i don't know how you can ensure that tbh

#

do other languages do that?

storm bobcat
#

no

#

I was wondering if rust could

#

but it's probably a tricky problem to statically analyze like I said

near tartan
#

yep

storm bobcat
#

actually

#

not that bad

#

assign a rank to each lock statically

#

I'm sure rustc could do this easily

near tartan
#

oh wait no you can totally do that in rust

#

you can make a lock depend on another

#

during construction

#

e.g. with a weak ref

storm bobcat
near tartan
#

that's neat

storm bobcat
ionic lava
#

Isn't DPC queue being drained at DPC_LEVEL (DISPATCH_LEVEL)? not below.

modern hamlet
#

like, when someone calls Lower IRQL, that's when DPCs get dispatched

ionic lava
#

I meant to say, that they are executed at DPC_LEVEL, not below.

vast lotus
#

DPCs for IRQL x are executed as soon as IRQL < x, but during the execution of the DPCs IRQL is x

modern hamlet
distant cypress
storm bobcat
#

the rank only enforces the ordering

autumn jasper
#

sometimes if you have to lock two objects whose locks are the same "rank" you can still have a consistent ordering by taking them in the order of the objects' addresses

distant cypress
#

yes, but consider e.g. two dentries. it is ok to lock the parent before the child but not vice versa. this is annoying to represent directly as lock ranks.

distant cypress
storm bobcat
#

but I think they could still be helpful for simpler lock relationships

distant cypress
storm bobcat
#

or if you know the number of maximum children beforehand you could do it at runtime (assign a decreasing rank everytime depth increases) but that's not really good

near tartan
#

i'm currently coming up with a way on how to represent mmap'd objects

#

should i store a reference to a struct object in my struct file?

distant cypress
#

That's a design decision.

#

My plan is to have INode be the mmapable object type of thing. And INode is referenced by File anyways.

near tartan
#

hm

#

i have this atm

#

so e.g. each inode has one

#

or a block device

near tartan
distant cypress
#

anon inode

near tartan
#

oh

distant cypress
#

INode is just a struct

near tartan
#

yea

#

i wonder

#

how do you do page cache?

autumn jasper
#

with great care

near tartan
#

bruh

distant cypress
modern hamlet
#

then your page cache will actually hold a weak reference to each of these pages so that it can be immediately removed when the page is to be reclaimed (the page frame isn't mapped anywhere)

near tartan
#

reading from and writing to the cache is a filesystem specific operation, right?

#

as in, implementing a Pager trait on the VFS INode level makes no sense

#

also does it make sense to have a cache for any inode type or just for regular files?

#

(@distant cypress )

mental tinsel
near tartan
#

i am not talking about dentry cache

mental tinsel
#

this way the next lookup can just check that and go "hey we know this doesn't exist"

#

oh oopsies

near tartan
#

i already cache negative entries

mental tinsel
#

oh this is page cache

#

idk what else you would need to cache besides regular files

near tartan
#

i'm talking about whether i should cache an inode's pages:

  • regardless of the file type (anything that isn't a regular file)
  • on the vfs inode level or on the fs impl level
rain oasis
#

id say you do not want to duplicate the caching code for each file system type. Just have a generic file cache with like a radix tree of pages. Then each fs provides "cache ops" like read_page(file, page, offset), write_page(file, page, offset). For other file types/uncacheable types you just ignore the page cache

autumn jasper
#

page cache absolutely needs to be a unified thing

#

thats essential for the vmm to know how to do stuff well

#

i was recommending the vnode cache be per-filesystem

#

or rather, i was recommending the vnode cache be eliminated and replaced with the natural caching you get from above by the reference held by the dirent cache

#

and the lookup structures for vnodes that are currently in memory are given to the filesystem driver

modern hamlet
modern hamlet
#

basically anything that has a set amount of content and has a degree of "random access"

#

exceptions include network adapters, pseudoterminals and character devices

near tartan
#

so inodes and block devices

autumn jasper
#

yeah thats a good thing to point out is that directories should be cached like normal files

#

theyre just files with structured data

#

you can still cat them on some unixes and get the raw data

near tartan
#

yea fair

#

does that yield the fs specific dir structure?

autumn jasper
#

for some reason you can do this specifically with the /dev directory on mac os x but not with any other directory

#

i discovered this when i was like 8 and ive never seen anybody else mention it

#

its bizarre

near tartan
#

i wonder what use this has

autumn jasper
#

actually i think i learned why this is

modern hamlet
#

internally yes

#

but not to user applications

autumn jasper
#

its because the devfs implementation in macos dates back so old that some ls implementations would still read() and parse the directory structure (which is how it always worked in OG unix)

#

so the devfs authors made sure to fake the old bsd ufs directory structure upon read()s

#

so those ls would still work

#

later the ability to read() directories in normal filesystems disappeared

#

but the devfs one remained as a vestige of like 1985

near tartan
#

oh also, how do i use cached entities if they are smaller than the page size? do i need to keep track of the object size in bytes?

autumn jasper
#

yeah

#

the general question you really want to ask is what to do if its not a multiple of the page size

#

the answer is the remainder of the page should just be filled with zeroes

#

and you dont read from there

#

you truncate the read to the byte size

#

if you mmap a file youll see zeroes there

#

unavoidable

#

also unavoidable that people can write stuff to the page there if they mmaped the end of the file writable

near tartan
#

yea i'll just not cache it

autumn jasper
#

it just has no effect

near tartan
#

it's like writing out of bounds of a malloc region

autumn jasper
#

well not exactly because that breaks shit meanwhile this is harmless

#

if you grow the file you just make sure to zero out the new space within the final cached page so that anything previously written there through an mmap'd view doesnt unexpectedly appear in the grown area

#

all my advice on this comes from weird bugs i encountered after i implemented it so u know its legit

near tartan
#

how do i best keep track of objects in an address space? just a linked list or a btree indexed by pn?

autumn jasper
#

you mean mapped regions?

near tartan
#

yea

#

i dont plan on actually mapping an object in the pmap until i hit a page fault

autumn jasper
#

i was planning on using an AVL tree for lookup and a linked list for scanning for a good sized spot to put a mapping

#

so like if you mmap with a preferred base and say "but it can go somewhere else, preferably after this" you can use the AVL tree to look up the region previous to the preferred base (or containing it if its already mapped) and then the linked list to scan for a large enough empty area

#

theres also maple trees from linux

#

which are evil rcu radix trees i think

#

that they use for this purpose

near tartan
#

i can see how radix trees make sense for this

autumn jasper
#

i mean it might literally not be a radix tree i think i read about them exactly one time like 2 years ago

#

might be a b tree variant or something

#

so id google it before taking me at my word

#

all i know is its designed for rcu and only works with rcu (well you can probably do some contrived locking to make it work without rcu but some other data structure probably becomes preferable once you remove the "good with rcu" constraint)

near tartan
#

the struct object abstraction is really nice for me, because i can get rust to automatically handle the writeback and pmm when the object has no more references left

autumn jasper
#

careful about where you lower the refcount then

#

if you dont want to unexpectedly become responsible for shit tons of IO at that moment

#

or potentially deadlock even

near tartan
#

as long as it's mapped somewhere it'll not get dropped

#

i don't manually refcount them

near tartan
#

so this is what i have right now (please correct me if i'm missing anything):

  • a Pager trait with get_pages, put_pages, get_byte_size and has_page callbacks to read from cache, write back to cache, get the size of the object and check if a page can be cached
  • INode implements the Pager trait
  • INode contains an Object, with a weak ref to a Pager impl (the inode pager in this case)
  • you can read, write to an Object
  • a mapped object in a process' address space is an entry in a BTreeMap with a refcounted reference to an Object (and prot), and the start of the mapping as the key
  • during a page fault, the current process' address space is scanned for Objects, if there is one registered at the faulted address, the missing physical page is mapped for the MMU (with potential read-ahead at the pager's discretion)
distant cypress
#

ah, I should've read further down before I answered. meme

marble salmon
#

What does your get_pages and put_pages do?

near tartan
#

put_pages writes them back to the disk

near tartan
#

@distant cypress i'm stealing your idea

#

if i use INodes for anon mappings, i can refcount and free physical memory automatically.

distant cypress
#

I'm one out of many, many people who have had that idea.

near tartan
#

lol

#

if i center everything around inodes, then i can move the caching to the fs layer like will said

distant cypress
#

yeah...

#

the thing with the page cache (and honestly, most of the kernel except drivers) is that it is a large part of both the filesystem layer and the memory manager

near tartan
#

i'll let the fs drivers figure this out

#

for now

near tartan
#

@distant cypress @autumn jasper for writing into the cached pages, should i allocate a contiguous phys region and just hhdm it in the kernel, or should i map it once into the kernel and keep a list of pages that are in that mapping?

#

i've been going back and forth for like a solid hour

#

i can't decide because both have their drawbacks

hoary cave
#

properly mapping it in the kernel has the benefit of being able to swap out the backing physical page without updaing the virtual addresses

near tartan
#

true

hoary cave
#

but if u dont care just hhdm it

near tartan
#

thinking about if i should make a collection type for this

#

eh fuck it for now i'll just do the hhdm thing

#

that's a fs impl detail anyways

modern hamlet
#

in the system there are these things called "system space views" which are fixed size mappings of a file in kernel VA space

#

and they work exactly the same as user VA space mappings would, if you fault on them, the data is loaded from the page cache, etc.

#

now you can achieve similar behavior using the HHDM, but you will have to trigger the loading of the page yourself manually if it doesn't exist, among other things

autumn jasper
#

We came up with a hobby OS ism for this which is "file view cache" or "viewcache" which might be a more descriptive term

#

Cache manager implies it deals with data caching, which it doesn't rly

near tartan
#

so if it's per file, does that mean i should have a viewcache object on the struct inode, or is that going to be an fs impl detail again

autumn jasper
#

The only thing I was suggesting being an fs driver responsibility is lookup of vnodes

near tartan
#

oh

autumn jasper
#

The vnode structure itself should be generic

near tartan
#

yea i already did that from the beginning

autumn jasper
#

The vfs just doesn't care where the filesystem got it from

#

It just says

#

Here's a vnode for a directory and a name. Give me a ref'd vnode for da child

#

Fs driver looks up whatever structure it maintains

#

Importantly this means the fs driver is responsible for its own locking for said structure

#

Removing the vnode from this structure requires a fs callback later

modern hamlet
#

like for example allocating vnodes with an additional bump of X bytes to fit your FSD specific extension

near tartan
#

oh yea how do i deal with a resizing cache if i don't use the hhdm? do i just have to remap it every time its size changes?

autumn jasper
#

Map it in fixed size chunks

#

It can have multiple mapping windows at once

#

a file

#

And you cache these chunks and replace them when needed and stuff

near tartan
#

i mean for the kernel, maybe i'm misunderstanding

#

i need a way to give me a contiguous buffer for file read and write

#

right?

#

i could also just not map it in the kernel and write the buffer by hand by traversing the page list :P

autumn jasper
#

Okay so the file view mapping I was talking about is legit like mmap() like demand paged and all that

#

How I do it is I have a region of kernel address space set aside that I call cache space

#

The page tables for it are pre initialized and I keep a free list of 32kb chunks within it, and I thread this free list thru the PTEs themselves

#

When I need to access a file I check if that 32k aligned region of the file already has a mapping

#

If so I use that, otherwise I grab one off the free list and map it there

#

If none are on the free list I grab one from the list of cached but unreferenced view chunks

#

(which disassociates it from its file and stuff)

#

And replace it

#

In the page fault handler I have special handling for page faults in this region, I actually index an array by the # of chunk within the space and this yields a pointer to a structure representing the chunk and I can super quickly get to the file that way and then fault the page in from its page cache