#Zinnia
1 messages ยท Page 13 of 1
I have a real array because I don't have that many chunks but you could do a virtually sparse one where it's backing is transient (or do it nothing like this at all)
i keep a ref to the mapped node in the pfndb
of ever page where the node is mapped
so if i fault on phys 0x10000, i just look up the struct page for it and am at the node
this shit is making me reconsider my life choices
:(
rust is slowing me down so much
RIIC++
real
i don't think we use any object relationship in our vfs and/or memory subsystem that can't be modelled in rust
Are you finally dumping rust
not yet
i need to make up my mind
because if i'm going to rewrite YET AGAIN
i'm going to lose it
What does that mean even
refcounting with automatic drop
Damn
(of course a gnu extension)
eh i'll take a break for now
this is doing my head in
i'll come back to this in a few weeks
i suggest not rewriting
when I rewrote my kernel in C++ it slowed me down because I was reimplementing everything that would be in core/alloc in Rust
idk tho it's your choice
maybe you are thinking to complex? or still a c like mindset?
but i only followed Menix Progress sparesely
yea probably
im currently doing page caching for inodes
i don't know how to properly expose it to the kernel
mmapping is easy enough, but actually doing file IO on it is my issue
what exactly is the issue there?
well, a page cache is basically a scatter gather list, right?
so if i map it into the kernel for a single write, would that not be slow?
similarly, how do i handle writes past the current file/cache size
that would require me to remap everything
the other idea is to traverse the sglist myself and just split the data up into and then write it page wise via hhdm
the purpose of the page cache is to cache block io in your case? or file level io?
the inodes are your way of storing file level metadata?
yes
like linux' struct inode or bsd's vnode
it's literally the last piece I'm missing
the inodes are used by the vfs to build and maintain the file/folder tree?
no
i have dentries
inodes keep track of the id, stat, some ops and reference to their owning fs instance
so inodes are fs local?
well you can't rename() across devices, can you?
One of the advantages of a dentry-inode scheme like this is that hardlinks become conceptually very easy; they're just two dentries pointing to the same inode.
Conceptually the page cache in Managarm works like this (but the implementation is a bit more complicated):
- There is a memory object that stores a vector of (possibly absent) pages + their states (absent, in initialization, clean, dirty, in writeback etc.)
- The memory object is mapped into userspace on mmap()
- If an absent page is needed (e.g., due to a page fault), the kernel sends a notification (in Rust terms, think of a tokio mpsc channel) to the FS driver to make the page available
- The FS driver reads the contents from disk and writes them to a newly allocated physical page
- The FS driver tells the kernel that the page has been loaded
- The kernel completes the page fault
If i wanted to implement this in Rust in a monolithic kernel, I'd just let the inode store an Arc<MemoryObject> and use a channel for communication from MemoryObject -> Inode
is it actually stored as a vector array or a tree?
it's stored as a radix tree but that's an implementation detail
basically, like i stated above
as far as the memory object in concerned, it's the same code path as a page fault
but instead of mapping the page, it copies out of it
now this is the part where i got stuck
do i map the pages into an array, or do i just walk the radix tree
and use the hhdm
it probably doesn't really matter
a mapping is better if the kernel needs to access file pages a lot
which is not true in Managarm so we don't do mappings of files into kernel space
we do use mappings in the (userspace) fs driver though
for some operations
well the issue isn't really mapping the pages itself, but what happens when there are more pages than before
i.e. write with SEEK_END
with hhdm i can just add a new page to the tree and access it
with a mapping id have to unmap and remap again
using the hddm vs a mapping are not really conflicting either
you could use the hddm for most operations and a mapping when you really need it
but yeah, keeping permanent mappings of all files sounds like a bad idea
that'd also potentially exhaust the available virtual address space
true
is it a stupid idea to use inodes for anonymous mappings as well
instead of having a separate memoryobject
like, if somebody creates a 128 TiB sparse file
normally if you do this you have a cache of aligned fixed size file windows of like 256kb or something and you have a few thousand of these
and you recycle them
as needed
yeah that makes much more sense
this is what we call an innodb cluster moment
@marble salmon
i guess you can do that, depends on your kernel design
in general it's usually easier to reason about individual parts
well, i'm unsure if it's worth the extra hassle
I'll take a look at the managarm impl though
do the bare minimum and make it better once you actually have problems with the current implementation
I'm not sure if the Managarm implementation is instructive or not
It's probably more complicated than it needs to be for a monolithic kernel
hmm
so just to summarize so i have the facts right. when i want to e.g. write a 16kb block to an empty tmpfs regular file, i:
- call File::write(), which calls the generic callback impl
- check if i have enough pages for the write, which in this case i do not
- allocate 4 pages to fit the new data and assign them to that memory object
- write the first 4kb to the first page
- write the second 4kb to the second page, etc...
No, you'd get -EXDEV
hows userspace
nice
yeah, that's a bit
Do you already have GPT parsing? If so, what do you use to check the CRC32?
i do not
cringe
The crc crate does work freestanding (there's like 6 different crates that do CRC checksums for some reason)
Just use crc32fast
it's literally a table and a for loop
why would anyone need a crate for that
oh right.. rust
Lol
Because I don't feel like reinventing CRC32
it's not called reinventing
also, it takes negative effort to do that
it takes more time to find a crate that works with no_std than to copy the table from wikipedia
lol
that's like using the apic crate
type shit
i'm getting my shit together
AAAAAAAAAAAAAHRRG i'm going insane
rust atomic function pointer handling is the most aids thing ever
@sick flax
interpreter parsing is going well
now i just need to fix the stack and it works
look at pc
tonight
im meming
As one does
but i got more time on my hands now
So hello world or ban????
may god strike my balls if not tonight
God might not but the ban hammer is primed
vc can happen starting at like 3pm or 4
If I were to do something then I might be able to join for an hour maybe 2 ish?
๐ป
You bet
Why do you need atomic function pointers?
niiice
now it's just a matter of connecting the kernel functions to the syscall dispatch table
most posix functions i have already implemented
i'll be working on this until i get the openrc welcome message
then i can finally merge the dev branch again
mlibrust
i should make a proper pty impl
Got pipes?
no
actually, i should probably fix my driver build system issues first
this is aids
Lmao
a basic tty should be fine for now
what's the issue?
i need a way to make certain modules regular rlibs instead of dylibs
i haven't found a clean way to do that with vanilla cargo
i want a tree of crates, like subdir() in meson
because otherwise i'd have to specify every single driver in the cargo workspace
right now i have this bullshit but that only produces dynamic modules
i'd just do that
that's not good if i have like 40 mini drivers and is only 1 part of the problem
the other is still with actually linking built-in drivers into the kernel
i wish cargo wasn't dogshit
maybe just use a feature for that?
like cfg(ps2_builtin)
probably not without massive hacks
sounds cursed
I'd just focus on what cargo does well instead of fighting against cargo
well cargo does nothing well
:D
the thing that you recommend is needlessly verbose and doesn't scale at all
i'll try some shit
maybe i can get it to work without big hacks
well a kernel without unstable is impossible at the moment
you need so much stuff that is nightly only
I'd just make all drivers dylib and list them all in the workspace Cargo.toml
i wonder how they solved this in linux
that's what i have rn
ah wait they dont use cargo at all there
yep
and what's the issue with that?
no built-in drivers
if i could get the few crates i'm using to work with meson i'd 100% just use that instead
[features]
ps2_in_kernel = ["dep:ps2"]
[dependencies]
ps2 = { path = "../drivers/ps2", optional = true }
?
https://elixir.bootlin.com/linux/v6.15.6/source/rust/kernel/alloc.rs
I like how they have SAFETY: comments everywhere
i thought it was a meme
clippy has a safety lint on unsafe functions
ohh lol
i have configured it to fail if you are missing it
thats a lot of comments 
what's the issue with the safety comments?
no issue its just funny
UB is much easier to trigger in unsafe Rust than in C++
like you have to explain yourself
so the safety comments make sense
as you should
and it's also quite easy to produce unsound code when dealing with unsafe
= code that can trigger UB from safe code
doesn't that just produce a dylib dependency on that dylib lol
not as far as i know
free also calls into realloc 
lol yeah
i can make a builtin feature that causes the lib to be linked statically
Is pub unsafe trait Allocator what u meant by the unstable allocator thing?
ah
i use it in many places
If an executable is being produced and the -C prefer-dynamic flag is not specified, then dependencies are first attempted to be found in the rlib format.
if you set the crate type to ["rlib", "dylib"], it should do the right thing โข
if you need a build.rs to build pure rust code, you're probably doing something strange 
i think the problem with built in modules is that they depend on the kernel for the kernel api l, so the kernel canโt depend on them, because that would be a cyclic dependency
not even that
i don't need to reference them by symbol
just need a pointer to the init function on the kernel side
it worked in the C version
is that a problem?
wdym
yes
idโd do what korona said
i'm trying
this is fucking bullshit
@marble salmon i can't do it in the way you suggested
it causes a cyclic dep
because i somehow need to builtin drivers in the kernel binary
i said this in the past already but I'd do a kernel-core crate for the basic APIs
and a kernel-full for everything
I don't think it's silly at all
it's more effort than just doing it with the proper linker commands
you'd face the same issue with dynamic libs in C as well
no you don't
it literally worked in the C version
because the linker takes care of this
your kernel-full can be an almost empty crate that just calls the init functions of all other crates
i don't understand the point of this
modules will need stuff from all of the kernel
and like i said, the way i did it before worked flawlessly
i will die trying
yeah, but that's not a problem. You just need to avoid cycles
But that's no different from avoiding cycles in the dep graph of a userspace program etc
the kernel doesn't really "depend" on builtin, it just needs to be linked together with it
but then there are no cycles anyway
i know that, but how do i express this in cargo 
Because the deps always go from module to kernel-core
i need the code to get linked to the kernel binary
and i don't see any way to do that except for cargo dependencies
tbh i don't even know if that would even work without cargo
for generics or some bullshit
I'm pretty sure what i described above just works
you have deps
kernel-full -> ps2 -> kernel-core
kernel-full -> ahci -> kernel-core
...
and no cycles
wait i might've misunderstood your idea
kernel-full is a binary, kernel-core is an rlib (+ dylib?)
when you said "basic API" i thought you meant split the kernel into essential and non-essential
that's my bad
okay this kinda works
i need to see what happens if i link two modules
I'm more powerful
I'm 73 commits into "driver refactor" and not close to merging
@marble salmon lol
now it basically generated an empty binary with a sole dependency on kernel.kso
not bad
okay koronas idea doesn't work properly unfortunately
it does build, but i can't find a way to make rustc only link dynamically against the kernel when building the dynamic modules
because it either links everything dynamically (even the actual kernel library) or it links the kernel functions into the modules statically
the latter actually boots, but then that defeats the purpose of modules
man shit like this makes me want to go back to C
where i'm safe from this brain damage
I guess you have to expose an ffi in the kernel lib that the modules use to avoid that
dont be a pussy
what you can still do is the kernel_core crate that drivers can link against and a kernel crate that links it into an executable with optional statically linked drivers
did you read above
and not doing either builtin or dynamic modules is an L move
then all you can do is a custom build system
finally
opinionated language ecosystems suck
Why are you trying to make the kernel link depend on your modules? Shouldn't you have it only the other way around?
how do you get builtin modules into the kernel binary
By compiling them directly in the kernel source tree, but even then the kernel doesn't link depend on them.
They register themselves early in startup
and you do that in cargo how?
By putting them into a section, and adding some symbols around that section in my linker script. Keep in mind that I have a primarily C kernel so far.
that's not what i mean
sure you can put them directly in the kernel tree
but how does that resolve the problem
cargo can't include any external crates even if it just needs it a linktime
Like I said: using the special section solves it
Sure it does, so long as it's in the same crate as the kernel
Then you need -Wl,--whole-archive
you cannot specify that with cargo
build.rs only allows a few linker flags
like -l and -L
this isn't an issue with rust, it's with cargo being unflexible
I fixed this by making the kernel crate an rlib, still building with cargo, but linking my way after the fact.
this approach also doesn't work in this case
because i need the kernel to also be a dylib
.
Couldn't you do your final link step into a dynamic object? It wouldn't be controlled by cargo in this case anyway.
wdym not controlled
I just proposed taking manual control of the final kernel link step by making the core kernel an rlib and linking built-in modules not into the kernel via cargo but via this final link step.
which leaves us with this 
IMO you can't compile kernels correctly without a custom build system.
@hoary cave is a hater of !cargo for some reason
what no
i was the first one to suggest gc which was a spin on gn
generate cargo :^)
๐
I like cargo but it can't do everything I need, so I have cmake
i mean tbf how often do you change up kernel configuration
all you need to test every once in a while is all builtin and all dynamic
Then go use it
FWIW, I nostd my entire kernel
i think this is the sane solution
but you need to build them
so you have a kernel_api crate that exposes extern functions and deps
module -> kernel_api
and at runtime your kernel provides all the extern functions
or you could just not bother with modules at all and link everything statically 
That's probably best yes
i don't think this is a cargo problem btw
Also true
this is a "language with monomorphization" problem
it would also happen in C++
if you tried to expose a C++ interface between the kernel and kernel modules (and not just a C interface)
well, you have to do this either way ^
sure, but in C this is done automatically 
What is?
headers provide this ffi
You'll still need an object with the dynamic symbols in or your linker won't allow it.
im trying to convince myself to not give up because of a slight inconvenience
it's not going well
why do you value modules that highly anyway?
cuz its more or less the reason why i even started the project
Sounds like what you actually need to do right now is take a break?
probably
rust has its niceties but i'm missing C so much at a lot of times
i was way more productive and faster
i wonder how much one can do with gnu23 extensions
like auto cleanup or stuff like that
what exactly is cancerous about C++ RAII?
it only works for classes
its not as nice as defer in other languages
u can make like a ScopeGuard wrapper class and stuff ofc
but its still less fluid than just inline defer
you can forget defer but you can't forget a destructor
and yes, scope_exit is equivalent to defer, so you can still defer
nice
i was already able to do self unlocking spinlocks like in rust
with the cleanup attribute
yeah linux already uses it in the latest versions for RCU and stuff
v nice
did you check if there are rust to rust ffi crates?
i'd probably just not do kernel modules if i was doing a monolithic rust kernel
yeah fair
do a microkernel so that you don't need modules 

i wonder how hard that is
i know nothing about microkernel implementations
is there a reason why managarm has so much logic in mlibc?
Managarm as async I/O, maybe that's why?
wdym by "so much logic"?
it's mostly protocol related code
mind you, i know nothing about best practices when it comes to this
couldn't you do something like a supercall? i.e. server id OR'd with the server command?
something like
#define SUPERCALL(server, func, ...) syscall(((u32)server << 32) | (u32)func, #__VA_ARGS)
u32 posix_id = ...;
int sys_read(int fd, char *buf, size_t len) {
SUPERCALL(posix_id, POSIX_READ, id, buf, len)
...
@marble salmon
A synchronous OS could do that yeah
the downside is that for a microkernel the cost of sync io is higher (user1-kernel-driver-kernel-user1) than async io (given sufficient core/process ratio)
is minix a synchronous OS?
I mean meson can do rust idk how stable it is tho
you can pass any linker flag with a target json so...
preprocessed target json with the necessary linker flags lol :3
like, target.json.m5 or something
meson can do rust :3
possibly unstable but I last checked whether rustc's CLI was stabilised like 2 years ago (it wasn't
maybe it got stabilised
cuz linux needs that
you could use a microkernel like api in kernel space to run drivers as modules
what would an async microkernel do differently here
well, Managarm needs to translate the call into a posix protocol request
isnt that just a modular kernel then?
with an unnecessary amount of overhead 
i like the kernel_api solution but
why the hell would your kernel depend on dynamically loaded modules
make every dependency static and optional
then make it so that they can be built as a dylib separately
it doesn't
believe me i tried
hm?
.
it's whatever
i don't have a solution but i understand
and i see why it does that
rustc alone would do the same i guess?
without cargo
maybe not
you could still use cargo but don't make anything depend on anything
then link them together with an extra build step
would that pull in kernel functions into the modules?
like can you get modules built with no kernel functions pulled in
or, you can make packages for kernel modules which have a dylib output AND a rlib output
and vendor the prebuilt rlibs into the kernel build kinda like how portage builds rust stuff AIUI
we are so fucking close to menix hello world mlibc userspace
i wanna see that shit vrol
should I make a modular kernel like menix instead of a microkernel hmm
do you also use rust?
yea
if you only have dynamic modules, that works
:3
good for u
i definitely want to explore microkernels
maybe a C kernel and Rust servers could work
same but I don't really want to deal with having to make a good IPC with no experience
yknow, efficient core, and safe abstractions on top
sounds nice
u know what
I'll keep the gearbox project and name for a microkernel
but I'll first do a modular kernel for learning
so I need a new name ig
and I'll go back to gearbox when I feel like I'm ready for a microkernel
the worst part about writing a rust microkernel is the toolchain
that's extremely annoying
but i like the concept of microkernels more than monoliths
i just feel like a dumbass with implementing it
yea and imo microkernels provide u interesting design challenges
which is more important than practical pros and cons when doing hobby osdev
no systemd? 
No systemd? No bitches
so yeah
it's rewind rewrite time
true
this rust excursion has given me some insights on what i can improve with my design
and what doesn't work
i want to focus on getting a really good scheduler
and efficient IPC
a good "kernel" with good synchronization primitives is probably really good to have yeah
I kinda regret not taking more inspiration from NT etc. in my work
I just refactored a bunch to make it more NTish
It's not too late!
i don't know what the differences between async and sync microkernels are
i might have to read up on things
oh god Davix rewrite incoming?? ๐ญ
i'm infecting everyone
I didn't really rewrite I just added NT concepts onto an existing codebase and refactored it to use them
are you rewriting in c++?
kernels are not sync or async, I/O is
it would honestly be very easy to rewrite it to be more "NT like" (which is really stupid terminology because you can take inspiration from multiple things)
io blocks the thread or not?
and be at the point that I am today
except doing this I'd kind of want to do priority inheritance in a non stupid way
probably a greek god
thor, eir etc.
that wouldn't adhere to the theme.
afaict it's a protocol generator like protobuf
RCU in rust works reasonably well
There are a few popular crates that implement it
crossbeam_epoch and scc have implementations
ah
The trick is usually that the rcu epoch is annotated with a lifetime
which can't leave the rcu protected region due to rust's type system
It's an IDL, allows you to declare interfaces and generate code from that
So it makes it look like those interfaces are simple function calls
i mean if the language is making you run out of stack space when you have 128k stacks i think a rewrite in a different language is at least somewhat warranted
that's why im rewriting in C again

ok vro
my boss just gave me some really cool ideas for how to do IPC
insane
10 years before menix gcc port
i dont think i'm doing async IO yet
ok vro
Copying hurd
coping hard
Menix mlibc hello world when
The rust version?
yes
Damn
wait fr
yea bros rewriting
maybe im using the emotional support item first
if i end up rewriting i'm doing everything properly
no ๐ฅบ
okay maybe not that
but i'm getting fed up with rust
it's just not meant for a kernel
well
rust is not that bad for a kernel
what is worse is fucking cargo
that too
i would if that were possible with no_std
you need either a cross compiler
or somehow do build-std for alloc and core
yeah
-Z build-std doesn't work?
that's a cargo option
has to work somehow given that linux does it
well yes
but i haven't found a way to do it without cargo or building the entirety of rust
I just need it to follow the llvm json spec
it probably doesn't hurt that they're upstream 
being upstream doesn't really matter here? you can just pass a target json
yep
the same way it builds a x86_64-unknown-none std with my custom target json it should build just fine
also is meson completely useless here?
i heard meson has some rust support
yes, but no assistance in that regard
you still have to teach it how to build from source
maybe xargo would help
tf
it's build-std before build-std was a thing
wrapper around cargo
yep u pass it XARGO_RUST_SRC
or it can take from rustup
oooo it builds a sysroot then just makes cargo use that sysroot
linux?
no xargo
you can build a sysroot and instead just use rustc alone with that sysroot
get rid of cargo
it should work
@near tartan can I steal your UACPI binding
and flanterm binding
I already have a flanterm binding but it's build.rs is borked
and it's not reproducible
yes
but they suck
it's just a dumb wrapper
is it reproducible
my bindings are shit and not reproducible
i want to get rid of em
well they build
and the uacpi-rs stuff isnt really done, thanks to university stuff on my side
woe
"fixes."
+500000
-0
currently dealing with stupid issues
nvm i was passing 'example' as an array nstead of an array of an array
mb
just write the OS in meson โ
are we back to a C rewrite
fire
how come you chose to make your C look like rust last time
with structs using PascalCase and being anonymous
and stuff like usize, u32
better vfs, better uapi separation, better arch abstraction, more efficient scheduling
no
less wasteful
last time i literally pushed all gpregs onto the stack on a switch
now i just change the rsp
massive improvement ๐ฒ
say how come you chose to support a bunch of architectures throughout development rather than getting it all right on one architecture and then porting it to the others
how can i make an abstraction over something that might only work on one implementation
understandable
Ru st
2mb ๐ญ
yeah cooked
ill probably just turn on minimal optimizations in debug mode
i almost didn't
i do need to rewrite the scheduler though
just putting tasks in a btreemap is not good โข
also every cpu has their own runqueue with no way of moving tasks between cpus
u should have per-cpu run queues but other cores should be able to access cpu data of other cores
so you can assess the load of other cpus, for example
or put threads on the run queue of other cpus
hm
the runqueues should probably not be part of the percpu structure (but still one queue per cpu)
though i'm not sure how that would work
my percpu code can access data structures from other cpus
you should NOT need more than 8kB for a kernel stack
how so
what do you need more kernel stack for
Do you ever go more than 10 levels deep?
yeah
Also you could conditionally compile so debug has bigger kernel stacks
that's true
ill also put opt level 1
idk if that's a bad idea
yeah fair
but you could just gradually allocate them as you need them
i should also use a memoryobject instead of just hhdming them
you'd need some kind of safe shit so that you don't accidentally overflow the new stack
etc
I'd just make a guard page
Yes, and you need a new stack to generate the pages
hopefully you don't cross the guard page boundary
yeah but
that's what the page fault stack is for
banger
How would that work
have a pf stack that's enough to load the rest
If its per cpu then u would hang the entire cpu while stuff is getting allocated
Plus u can get another page fault while handling that fault
ideally your PFs would still be on their own stack
just trigger a double fault when a kernel stack is overflown
ISTs aren't that flexible
and handle it
Anyway thats not trivial at all and easy to deadlock yourself
can be enabled per interrupt slot
There are like 8 slots I think
i mean the user stack is demand paged, but the kernel stack i can just leave at that
You can overflow the stack while holding a memory management spinlock
Then u cant do anything
true
7 because ist0 means no ist on the idt entry
Anyway demand paging kernel stacks sounds like hell
you can overflow the stack while holding any kind of lock and stall the entire system
Basically yeah
It introduces similar problems to paged kernel code but even worse
Because you dont control when it happens
ill just leave it
So u have to live with 2 megabyte overhead per thread with rust
~small per CPU memory allocators~
btw does linux also give threads the same amount of stack each?
only in debug
I mean user stack
Nah its super dynamic
damn
U can set any size
i guess that's determined by rlimit?
i don't think that works
U can just pass it to the thread via rdi
Like in theory nothing prevents that from working fine
i forgot that we were talking about threads, not processes
It's worse than just this: demand paged kernel stacks instantly trigger a double fault because the interrupt frame can't be written. To avoid this you have to use an IST entry, so now either your page faults are no longer preemptible or you need two kernel stacks per thread
how on earth is rust that inefficient with stack memory usage ๐ญ whaaat
opt-level=0
rust has no initialized heap allocs
like you can't zero initialize an array on the heap
that has to be done either with MaybeUninit
or must have an element type which implements Copy
does the stack usage at least get better in release mode
in rust conceptually things are initialized on the stack and then copied to their actual locations
with optimization the copy is usually elided and done in-place
by how much tho
but in debug mode having large things on the heap or returning large things often results in initialize-then-copy
sometimes even multiple copies
how big of a stack do you need in release mode
I do just fine with 4kb stacks for worker threads and the reaper thread and 8kb for everything else
like i said, i just put any number
and most of that's just for page alignment, i haven't ever seen it actually use more than like 0x1200
imo you should try setting it to 4k and double it until you dont overflow in release mode anymore
yea but if you spawn a lotta threads in the kernel that don't get reaped quickly that would be a little problematic
oh true
BadgerOS uses 16K stacks, 2M seems excessive even for effectively -O0 put-everything-on-stack to me
Do you actually have something that detects stack overflows with an exception, do you check the stack pointer register, or do you just think you need this much?
make the stacks smaller and smaller until you start faulting 
who knows maybe you only need 1 page
i just added a 0
I probably need larger stacks thank just 4K
But not by much
I reap in the housekeeping thread, which can also be told to do other things
Anything that needs to run at some point for GC but can't in current thread goes there.
why not make deferred procedure calls and just allow there to be threads that spawn on-demand to help with that
that is what i do
it is very nice
That requires more scheduler shenanigans and I don't like doing that after how much pain it was last time.
nah it's super easy i did it in 4 hours
struct event_pool {
struct spinlock lock;
struct condvar queue_cv;
struct worker_task tasks[EVENT_POOL_CAPACITY];
struct worker_thread threads[MAX_WORKERS];
uint64_t head; // producer index
uint64_t tail; // consumer index
atomic_uint num_tasks;
atomic_uint num_workers;
atomic_uint idle_workers;
atomic_uint total_spawned;
time_t last_spawn_attempt;
uint64_t core;
atomic_bool currently_spawning;
};
``` and just make policies to spawn worker threads if there are not enough and exit them if each one has had too much time without doing anything
struct worker_thread {
struct thread *thread;
time_t last_active;
time_t inactivity_check_period;
bool timeout_ran;
bool should_exit;
bool is_permanent;
bool present;
};``` super simple ๐
Puts this on ever-growing TODO list
now you can scale your housekeeping threads too
which is probably nice
and rather than hardcoding stuff in the housekeeping threads (i'm assuming u do this) you can use condvars and wake workers to go do work in the event pool(s)
very easy to use ๐
It's an array of tasks with optional timestamp for when to run. Some things, like reclaiming memory from dead threads run once per second while others, like a large part of process exiting logic, run immediately, just on the housekeeping thread.
how does the timestamp here work, is it just using the timer interrupt
we should move to badgeros channel
๐ it's never getting finished
1 rust kernel and 1 C kernel
is it gonna be a microkernel now?
you can do the rust servers thing you wanted to do
everyone thinks microkernel is a bad idea
so i'm not doing that
i don't really have the time atm for it :(
maybe one day
it's a good challenge tho
my microkernel is on hiatus
I'm currently making a monolithic kernel
a monolithic kernel that will NEVER be useful for anything
wait my bad
i wasn't thinking while typing
i don't plan on releasing it
i'll just use it as a testbed
Or have an IST only on double faults and make sure another df cant be triggered
DFs are technically classified as aborts and are not guaranteed to have an accurate rip and thus should not be returned from
oh, damn
Also, this still would require either the entire stack expansion being nonpreemptible or each thread having a separate kernel stack for double faults, otherwise you could get two of them on the same stack simultaneously
And even disregarding those issues, dealing with the deadlocks here would be a nightmare: the stack expansion might have been triggered while holding a memory allocation lock, for example
wdym not guaranteed, wtf would it point to lol
Idk it's just something Intel says
a hardcoded address somewhere in the lapic with a constant ascii string "trolled"
Fwiw I don't think there's ever been a cpu where the rip is unreliable
strange
But you're not supposed to rely on it
but the thing is if it double faults u have no way to recover the original exception values that would've been pushed
so where would u even return to
or like what would u do
Return to the instruction that caused the first fault I guess?
ig
Like, if you do push dword 5, and that value caused a page fault, and the page fault caused a double fault, then it should point to the push
oh ok, then that makes it retriable ig
looks like intel intentionally really doesnt want u to demand kernel stacks
Aborts โ An abort is an exception that does not always report the precise location of the instruction causing the exception and does not allow a restart of the program or task that caused the exception. Aborts are used to report severe errors, such as hardware errors and inconsistent or illegal values in system tables.
A double-fault exception falls in the abort class of exceptions. The program or task cannot be restarted or resumed. The double-fault handler can be used to collect diagnostic information about the state of the machine and/or, when possible, to shut the application and/or system down gracefully or restart the system.
From the SDM
yeah that sucks
Even if there was nothing architecturally preventing it, it still wouldn't really be viable
Because you'd need to make memory allocation reentrant
Otherwise you wouldn't be able to deal with stack expansion during memory allocation
Doing that is infeasible for all but the simplest of memory managers
u could make a reentrant stack only allocator with some pool, like its possible
but it is stupid ig
Also consider what you'd do on OOM here
You can't just terminate the thread, because it might hold a bunch of kernel locks
your best bet is just hang it until there's memory
iirc NT does dynamically grow kernel stacks somehow?
Yeah but then you're stuck in the handler until that memory is available, so now the peeemptibility thing is even worse: the CPU you're on won't be able to do anything until that memory is available
Because consider what happens if you yield away and the new task triggers a stack expansion
Afaik all it does is swap out kernel stacks for certain blocked threads
perhaps
its not guaranteed, and the wdk has a verifier option to disable it for testing
but the default windows kernel stack size is something ridiculously large like 1mb so what are you doing when you need more than that
i've survived just fine with 16kb kernel stacks
if you hit a page fault when trying to execute the first handler, then the rip would point there, and maybe there could be a race condition between updating rip to first handler and the second fault reaching the interrupt circuit
Thats userspace tho
turns out there are clippy lints for large stack frames
time to disallow those
????
what kind of insane default is this???
okay i did some testing
with opt-level = 1, even in debug mode 0x8000 is enough for the kernel stack
0x8000 is still kinda insane but better than 8MB โ ๏ธโ ๏ธ
0x4000 also works
but that might still be silently corrupting stuff
i will change it to actual mapped memory
not just hhdm
u apparently cant demand page it tho
not talking about that
ill map all of it at once
with a guard region
i see
i should probably encapsulate it as a stack object
realistic
i should also implement fork already
and/or proper tty
because rn i catch all r/w calls to 0, 1 and 2 to the logger
instead of having an actual console
I'm not sure how that works yet
like, if stdin points to /dev/tty0, how does it get input?
stdin points to /proc/self/fd/0
and thats tty0 or pts or whatever opened
by default i guess it would be the console
but bash will create a pty and dup that for the child process
so the child process stdin is the read end of the pty
yeah but how does tty0 get the input from devices
well it depends bro
you mean tty0 as in the actual console?
that would be the input subsystem of some sort
like evdev
devices put their inputs on a queue
or a ring buffer
and if the console is allowed to take input
it just pops stuff off the queue
and it translates input events into console things
like ascii letters or ansi escapes
evdev of course assumes you are linux
you might want to implement another interface, idk up to you
and as for userspace that's where libinput comes in, if you have evdev you get it for free* and and in case of your own interface you have to implement a backned, which shouldn't be too hard
the terminal application would interface with the input devices using libinput or something else and get input events that way, and basically do what your kernel does to produce input on /dev/tty0 but it would write it to a pty write end
here's an example, with the MOD_{SHIFT,CTRL_ALT} and NO_MODS macros doing the conversion + write to the pty
that's essentially what you would do in your kernel to convert keyboard inputs to what you write to the console buffer so userspace can read it
Pretty sure what happens is they're fixed but they can be explicitly grown
Like in fastfat
Where code explicitly grows its own stack
That's fine since you know you don't hold locks or whatever
ah
But growing kernel stacks on-demand is a no-no
so the input subsystem has a queue that combines all inputs into one stream?
well, good question
i can't tell you that
but it would appear so..?
you still have to know which device it comes from so they're probably tagged in some way
like a device pointer/id
but as far as my knowledge goes that's what i would assume
good luck ๐


