#charmOS
1 messages Β· Page 4 of 1
yeah its kind of a tough format
a package of packages of structs
so yeah try real hw or look around
do you know of any uses of uACPI that use c-states successfully on real hw
i did not see it in managarm
you would be the first
whaaat
managarm doesnt have any power management
no pmm 
even obos which supports suspend to ram and wake doesnt have c state stuff
do they not want to do it or is this difficult
just not a priority for anyone
iirc by default the cpu runs in the slowest safest config
but they use acpica so idk
and it works for most people
the api is more or less similar
looks like lenox has drivers/acpi/processor_idle.c
yeah u need to take a look at this
for the firmware to expose c state control to u
if (!processor_physically_present(handle)) huh what an odd check to perform
not odd at all lol
some firmware has 9999 processor objects
only some of them map to real cpus
its mostly so they can reuse the same code for different cpus
im pretty sure limine even had cases where the LAPIC addresses were missing from the madt which is kinda sily
ok yes this is great tho i'll look at lenox
yup looks like it's just a "first bother _OSC and then go find C states"
https://elixir.bootlin.com/linux/v6.15.8/source/drivers/acpi/acpi_processor.c#L800 apparently u do that also
claim control via fadt
but yeah
first u tell the platform u can do that
well u can also make your cpu run faster as well
isnt that more about P-states
maybe, ive never written a driver for this stuff
are P states managed via this thing also?
looks like it's a separate thing
_PCT
ah
lol _PPC
I don't think the single piece of hardware I'm running this on actually supports P states since the CPU can't overclock but I'll probably support it anyways just like how I plan to support XHCI even though that laptop (ThinkPad T400) is EHCI
π yippeee
I'll do c-states first tho
then we can use your code as an example of p/cstate management
very tragic that this has not been done by any hobby kernel before with uacpi
no 9GHz overclock on mammogram OS
might be done by react os soon
but that has 9999 layers of abstraction and indirections
so not that great of an example for hobby
do they support C states over there
you'd have to ask @restive laurel
since its NT it might do 'TSC_' or whatever
uh oh
they like doing char literals
interesting
thats acpica code
oh wait nvm yea
i am not seeing anything about c states in reactos
sadg
linux it is then
freebsd might have it if u like reading that more
i was just thinking that yes
idk if they have their acpi stuff in freebsd-src tho
prob somewhere else
o nvm they do, they just dont have something like an explicit drivers/ dir
their dir structure is a bit peculiar yes
freebsd-src/sys/dev/acpica/acpi_cpu.c
maybe idk
ya i think that's it
it's not acpica sources tho so funny to call the directory acpica
I wouldnβt use this as a reference yet
Itβs going to be a while till everything boils down. It looks correct in the right spots
Whatβs in master right now is totally wrong still
are you only able to test things like this on real hardware
I guess I might need to get that up and running soon
C states?
yea
I would
can u link it
this doesnt seem to do pstates but yeah
TIL C allows this lol, never written code like that but that's funny
why wouldnt it
no idea i just never thought it would π₯
ok today is going to be the Great And Fun Time of either writing ASL myself or finding existing tables with C states
I shall first attempt the latter
or I'll probably just ditch this and do XHCI since that's a saner endeavor
yeah nah we're doing XHCI today this ACPI source language thing is actually bad
right yea, xhci, i got the ADDRESS_DEVICE command working
I need some generic USB interface abstraction since I'll also write an EHCI driver since that's what my hardware uses
problem for later tho
lmao why did i write this
"We have if statements at home"
whoopsie
i might start getting sick and tired of these logs soon
idk I'll figure something out
spinning cat loading screen next
I think indenting them will make things look more pleasant so it'll be like
[CATEGORY]: Found device
Message
Message
[CATEGORY]: Initialized``` idk
why is the usb 2.0 spec lying about page numbers
that's weird
this is supposedly on page 248
but it's on page 276
can't even trust table of contents these days π
π€ͺ
oh wait no need for the clang-format commands it doesnt break the comment
nice
bruh
this could've been a bunch of defines
yep thats what i meant
lol that is a silly way to represent numbers
for historical reasons
I got the GET_DESCRIPTOR command to work
chat, is this real
setup -> complete

QEMU gotta be my favorite hardware manufacturer
QEMU is indeed my favorite hardware manufacturer
gonna port mintia mousetest to this OS 
https://en.wikipedia.org/wiki/Host_controller_interface_(USB,_Firewire)#Open_Host_Controller_Interface_2 TIL OHCI was a thing
A USB and Firewire Host Controller Interface (UFHC) is a register-level interface that enables a host controller for USB or IEEE 1394 hardware to communicate with a host controller driver in software. The driver software is typically provided with an operating system of a personal computer, but may also be implemented by application-specific dev...
The OHCI standard for USB is similar to the OHCI standard for IEEE 1394, but supports USB 1.1 (full and low speeds) only; so as a result its register interface looks completely different. Compared with UHCI, it moves more intelligence into the controller, and thus is accordingly much more efficient; this was part of the motivation for defining it. If a computer provides non-x86 USB 1.1, or x86 USB 1.1 from a USB controller that is not made by Intel or VIA, it probably uses OHCI (e.g. OHCI is common on add-in PCI Cards based on an NEC chipset). It has many fewer intellectual property restrictions than UHCI.[2] It only supports 32-bit memory addressing,[3] so it requires an IOMMU or a computationally expensive bounce buffer to work with a 64-bit operating system.[citation needed] OHCI interfaces to the rest of the computer only with memory-mapped I/O.[3]
blistering speed
ok now I need to figure out how to make my usb drivers themselves controller agnostic
so that means, first, I need an abstraction layer for controllers to present simple functions to send usb packets
Then, I'll also need to have an abstraction layer for the controller to set/unset interrupts for devices
I'll also need per-controller ways to rig up interrupts so that things like USB device plug/unplug can be detected and adjusted for
So, my individual USB drivers are fully controller agnostic, but they interact with controller-provided function pointers/methods to perform actions and receive interrupts
So the general flow becomes
XHCI controller init -> XHCI registers all its USB devices somewhere -> USB device init -> USB devices do things
Then, on an unplug
XHCI controller catches the interrupt -> XHCI controller figures out who was unplugged, and has some way to signal to the higher level USB driver "Hey, destroy yourself, you're gonna be gone anyways" -> USB driver cleans up structures -> USB driver says "OK I am done" and XHCI controller goes "Alrighty I'll clean up for good now" -> XHCI controller cleans up internal structures related to that USB device
and on hotplug
XHCI controller catches the interrupt -> XHCI controller allocates structures and enables the new port and allows it to be addressed -> USB device is initialized -> USB device is up
this will be fun
alrighty back to usb stuff today I clean things and introduce interrupt driven responses and start making abstractions
right yes usb ok great something like this should be ok
struct usb_controller_ops {
bool (*submit_control_transfer)(struct usb_controller *ctrl, uint8_t port,
struct usb_packet *pkt);
bool (*submit_bulk_transfer)(struct usb_controller *ctrl, uint8_t port,
struct usb_packet *pkt);
bool (*submit_interrupt_transfer)(struct usb_controller *ctrl, uint8_t port,
struct usb_packet *pkt);
bool (*reset_port)(struct usb_controller *ctrl, uint8_t port);
bool (*get_device_descriptor)(struct usb_controller *ctrl, uint8_t port,
struct usb_device_descriptor *out);
bool (*get_string_descriptor)(struct usb_controller *ctrl, uint8_t port,
uint8_t string_index, char *buf, size_t len);
void (*ring_doorbell)(struct usb_controller *ctrl, uint8_t slot,
uint8_t ep);
};```
π₯
was busy earlizer
get_*_descriptor probably does not need to go in the controller ops
since you cna just do it with control transfers
oh yea true
also control transfers need more parameters probably? or are you planning on having the special values at the start of the packet
uhh I haven't fully planned out the abstraction layer so this will gradually get more improvements over time
I just want some sort of a foggy idea
I got the device string descriptors yesterday
mfw I can't just leak memory in device init for USB because these are removable and I actually have to keep track of allocated objects to free them when the device is unplugged
it's going good rn
made linker section for usb drivers
π₯

the grind is real
I shall prob go nap nap now tho
this is so much fun
I will have such a blast with the TCP stack impl next
once I implement the Fun Times of demand paging I might become the Page Fault Shill idk
i have yet to see a page fault shill here
ok i kind of have absolutely zero user input via keyboard stack so i will just not do that and mount a usb stick instead and see if i can poke it
SUPER SPEED
β‘
i checked my git blame and apparently I have accidentally fully "ship of theseus'd" my codebase
that's kinda funny
subconsciously performing a rewrite on nearly every file
noreply email
Laugh at this user /s
π sad
i went to moovies so no usb work today
tryna set up the interrupt stuff
Which movie
Oh I watched that as well
pascal mentioned
My brain commited suicide
why
What is your current physics knowledge
i am just starting out so linear momentum and a few other things
not very much
Yeah moment you'll get to topics like relativity and quantum mechanics you'll realise how fucking wrong the things said are π
literally any subject ever
Oh yeah did you see the ending credits
every time you reach a new point, it goes "Forget everything you were taught last year"
yes
baby loves reading charles darwins works
Yeah that's the first one
what was the other one
I won't spoil the second one
Just pirate the movie and mmap it backwards to watch the end
mmap it backwards is crazy
are you having a blast
are you superpositioning all over the place rn
I mean I am done studying all those
Next is the college level physics
Heat equations and fuck not
I like physics its a chill subject
famous last words
Yeah then Rotational motion and Thermodynamics shows up
physics is math if math was useful 
Physics is applied maths yeah
dont insult my math minor like this
Engineering is applied physics
from what i've heard from friends pure math sounds like actual insanity
*friends who actually do this stuff and dedicate much of their time to it
prime numbers my beloved
Pure math is like that yeah
ah ok
Then isn't that just prime factorization
im doing a math minor which requires getting to the end of the general undergrad math track (which is like, analysis, comes after calculus) plus a handful of electives
sounds great
its weird that im going to get to the end of the general math track up to the point where people start like, specializing
if theyre a math major
what are you majoring in
cs
shocking discovery
I've yet to
which enginering
Guess what my 5th subject was during high school
civil engineering 
Probably Computer or Electrical
Nah
verilog my beloved
mechanical engineering
my math major friends all say that all math until analysis is rote memorization for babies and then analysis is the first time you actually learn math
What could be my 5th subject
which is scary
Well it was Economics lmao
You'd be close if you said biology there are people who do PCMB
it's like that roblox grow a garden game but it's real life π²
There's a new Roblox game now?
my sister showed me yeah
I don't understand the point of these farming/gardening simulators
It's just waiting glorified as a game
me neither i wrote a program to automate it
do you play games
Sometimes yeah
my end goal with this OS is to port every version of fnaf and run them all at the same time
Wow
up to fnaf 6
Lmao
i think it's a good test and a solid goal because of everything needed to make that happen
also funny
I want to currently get some GTA game running on this
There used to be Re3 but then it got DMCAed
do u have GPU drivers yet
for igpu
game on your motherboard's beeper next
say when did u start programming
didnt u start ur kernel even earlier than me in terms of the age u were when u started it
locked in
I was 14
But locked in when I was 15-16
π²
understandable
programming in the womb
where is neptunes thing
oh ok understandable
dang polaris must have a really well written and yap-free codebase lol
22k sloc
awesome
No and yes
oh ok
coz like mine is creeping up on 20k sloc (it's at like 19.5) so i am mildly concerned
concerned that i have way too much code
but pretty much everything is used so idk
and i've only been at this for 2 months so as time passes this number will definitely be going up a considerate amount
no more 10 second cleanbuild times π
You guys have your own operating systems????
nope
this is not an OS and it's barely a kernel
it's only a "Small C program" at the current stage
i wish i could do that
Most complicated thing I ever made was when I put da garlic in da tuna
yeah me too actually, most complicated thing i ever made was fried rice with chicken
not chicken fried rice tho im not a chicken
Paneer chilli bro very hard to make
also im at like 32k lines and it doesnt do anything yet
so you def dont have too much code
I have just realized that my thread priorities may put me in a bit of a pickle for userspace tools like nice that expect more fine grained control
I might migrate away from multiple lists at queue levels to having multiple red black trees, with the thread classes of URGENT, RT, TS, and BG each having one red black tree, where the threads have a priority_at_level from 0-255 to place themselves in the tree
this way if I have a thread at the TS priority things like nice can actually do something meaningful with the thread priority
"we have cfs at home"
so to avoid starvation, my idea is that I would need to do something like keep track of a thread's runtime activity
threads that are mostly blocking would get "nudged" to higher priority levels in a priority class, since those are more likely to be more interactive, whereas threads that are fond of processor time would get "nudged" to lower priority levels in a class because they are more likely to be long running and rarely IO bound
I think a decent way to handle this is to have perceived thread activity classes, so things like THREAD_ACTIVITY_CLASS_IO_BOUND and such, and have these just "nudge" the priority at their priority class, where I have some heuristic that takes into account a whole bunch of other stuff including the activity class to essentially figure out the most ideal priority_at_level to assign to the thread
so it would be 3 main parts
- detecting what the thread's activity class is
- figuring out what priority_at_level to give it
- figuring out how to keep track of a thread's activity class history so that changes in activity class can be biased based on what the past activity was like.
this way if a thread were to move to IO BOUND after previously being CPU BOUND for a while, that move would be "nudged" in a direction that would require more IO BOUND activity for it to truly change activity classes, compared to a thread that was IO BOUND, then became CPU BOUND, and is now becoming IO BOUND again
the actual thread picking logic is entirely unaware of these activity classes, and just picks based on priority_in_level, but the gradual decay would make it so that threads would always get a chance to run
I could set this up so that every thread would absolutely 100% have to have some point in time where it sits at one of the lowest priorities in its class, so that other threads have a guarantee that they are run
this is so silly
I think I have a better excuse for using that FFNN in the kernel now lmao
finally the AI OS
so it's an """"MLFQ"""" with a """"cfs"""" at each level
"a little bit of this a little bit of that"
I can probably keep ringbuffers for block/sleep/wake reasons, with each ringbuffer also being associated with a small 'histogram' countaing the number of each reason
for activity class
they dont do ringbuffers they just check the activity in the past 5 seconds
they have slptime and runtime and ts_ticks
so their heuristic is
"Sleeping for 80% of the last 5 seconds?" -> boost to interactive
I think this would work ok
but I also would like to track more data since I can tag each sleep/wake reason with a timestamp and essentially accomplish the same task but potentially expand upon this
this way I can use the other data for more info if I have a use for it in the future
I will probably drop the "Multiple RBTs for each priority" idea because there are two priorities (URGENT and BACKGROUND) that are only used in kernel threads and have absolutely no reason to have those more complex heuristics
so for those two I can stick with what I have where I have the lists/queues
then I'll just have two RBTs, one for RT one for TS
honestly I'll probably just do one RBT for TS threads only come to think of it
I think I will just do freebsd's tracking logic + the ringbuffers so I can have coarse_stats and the ringbuffers, and say something like
if (sleep_percent > 80% over last 5 sec)
classify = INTERACTIVE;
else if (frequent short bursts of wakeup/sleep in ringbuffers with intervals < 50 ms)
classify = INTERACTIVE or IO_BOUND;
else if (mostly running in coarse_stats and ringbuffer shows no blocking)
classify = CPU_BOUND;
idk
I could potentially be able to get away with having it run tickless by just tracking sleep and wake times and reasons
perhaps
cool, good luck with that
I couldnt read half of it due to discord not wanting to scroll horizontally
holy yap
is that inspired by something?
ULE and CFS
ULE for thread activity classes and CFS for periods and the RBT
using per-second buckets to track coarse-grained history for the past 10 seconds so that I can still get complete data
20kloc nice
finally back to a number that starts with 2
oh yeah I finally fixed a super rare transient deadlock that was bothering me (I think)
super complicated fix
static void do_wake_other_core(struct scheduler *target, uint64_t core_num) {
if (!target->timeslice_enabled)
lapic_send_ipi(core_num, IRQ_SCHEDULER);
}``` extremly complex
a whole singular if statement
added a cycle to ringbuffer entries to prevent stale data reads
literally nvme
who needs slot machines when you have race conditions
ok great today I got the thread activity logging infra all working and everything seems to be going fine and dandy so tomorrow I figure out how to classify threads
Oh I really like this so far
sorry for the lack of stuff like any userspace at all it has only been two months
π₯
fun tho
Itβs okay, keep it up
That's okay, no one really need userspace
had a "Local Man Crashes Out Because His Thread Struct Is Already 210 Bytes In Size" moment
it ok though
hopefully it is not super bad
I can probably remove these two and get it back down below 200
along with the lock (dunno why I put that there tbh)
nice it's 192 bytes now hooray
block_count 14, sleep_count 0, wake_count 14, block_duration 1, sleep_duration 0, wake duration 872``` i hope this is enough data for activity classification per thread, idk might add more tracking if needed, but I have logs like this for each second for the past 16 seconds
always gotta keep stuff a power of 2 fr
otherwise compiler optimizations no work for the modulo
oh ok phew
https://github.com/torvalds/linux/blob/6a68cec16b647791d448102376a7eec2820e874f/include/linux/sched.h#L817 linux seems to have a massive task_struct wow
big
how curious
__u64 vs u64
wth
Why are you worried about your thread struct being large?
probably cuz its fun to pay attention to details like that
no it's fine
"premature optimization" is bad when youre jumping through crazy hoops to try to optimize a case that might not even turn out to be a bottleneck or something like that
doing relatively low effort optimization from the jump is good though
i noticed ppl have a tendency to overfit to like the names of things that are considered bad practice
like "premature optimization is the root of all evil" and "goto considered harmful" must mean that i should get upset if i see somebody optimizing anything early and get upset if goto ever appears in a codebase
little bit more case-by-case critical thinking would be good probably
for example if you have to go waaaaay out of your way to optimize something that its not at all clear will be an issue in practice, especially if there are complex trade-offs involved, you probably shouldnt
but imo its absolutely good practice to just along the way try to be non-stupid and thereby minimize the number of things cropping up later
especially wrt overall design but also trivial details that can add up if you dont pay attention to them early, where you might later have a lot of out-of-line backtracking to do in your project, when it could have been avoided in-line with the work youre doing right now. in the former circumstance you now have to pay extra energy and time for mental context switching and your net productivity is lower. you're also more likely to miss details when backtracking than when youre already deeply thinking about the component which could result in lower quality code
and it falls in the category of, easy way to straightforwardly improve quality now and avoid potential issues down the line, to just try to be careful with structure sizes
threads are created all the time and I don't want the thread struct to be larger than its stack
why is your stack so small
idk some threads just have one page of stack since they don't need more
just based on what they do
like the threads with a page of stack are worker threads that just run stuff like free()
so do you pass a stack size when allocating stacks for threads
I can optionally do that
struct thread *thread_create(void (*entry_point)(void));
struct thread *thread_create_custom_stack(void (*entry_point)(void),
size_t stack_size);```
struct thread *thread_create(void (*entry_point)(void)) {
return create(entry_point, STACK_SIZE);
}
struct thread *thread_create_custom_stack(void (*entry_point)(void),
size_t stack_size) {
return create(entry_point, stack_size);
}``` ezpz
I should probably rename STACK_SIZE π₯
I have just realized I have done a Major Stupid by not building a faster arena allocator that uses 2MB pages so that for things like page sized page aligned DMA buffers and thread stacks and stuff, I don't need to constantly keep calling into paging code and can just nab pages out of a 2MB arena
π₯ oopsie daisies new todo list task acquired
methinks I can get away with an AVL tree to keep track of 2MB pages with a bitmap for each 2MB page indicating used/freed 4KB portions, and I can "refcount" each 2MB page such that it is completely freed and unmapped and the corresponding structure is deallocated once everything from it is freed
π₯
ok so then the plan for the global hugepage allocator would be to create a tree structure to keep track of all hugepages across all cores, with a small lock for tree modifications, and then keep smaller trees of partially/fully free hugepages per-cpu that are marked as such in the global tree structure (each struct just has a core if it's owned by any)
then each cpu would just find a 2mb hugepage from its core-local structure, and allocate and refcount it, whilst still maintaining it in the global tree structure
and when anything needs to be allocated, I only need to lock for the potential tree modification, (which may be rare since 2MB is 512 x 4KB)
I can keep a small lock around each hugepage, but it will probably be pretty uncontended, and this is just so that I can defer a free from one core to another
and to handle the very rare scenario where one processor has a lock on a hugepage because it's freeing it since the refcount dropped to zero, and another processor came in to allocate said page, I can simply make allocations use a trylock on a hugepage whereas frees will use full, spinning locks so that if one processor allocating a hugepage sees that the lock is held it gives up (preventing the UAF, I think)
bro is a yapper
I probably wanna use rcu here tbh
I can probably honestly get away with lists for each core's local hugepages since I'm most likely only going to have a few of those active at a time, and the overhead of a tree structure is probably not worth it here
but I'll keep the tree for tracking everything everywhere
I can probably do an optimization in the future where I lazily free hugepages. for example, if I just recently freed a hugepage and removed it from the tree structure, rather than immediately calling the buddy allocator to free the physical pages and calling the virtual unmap function, I can add it to some known list of free hugepages that are marked for deletion. this way, if I suddenly encounter a scenario where I need a new hugepage, I just take one from this global list rather than doing the whole call into the buddy allocator
I can pair something like a deferred timer interrupt event with these pages marked for deletion, so that after some time, they get "garbage collected" depending on the memory pressure at the time of enqueue
for example I could keep track of the amount of calls into this allocator in the past X seconds. if this number is high, that means this page i just freed has a higher chance of being used again, so I preserve it for longer. but if the number is low, then there is likely not much happening with the allocator so I can set a much shorter wait period before tossing the page
π₯
I can't believe this is the third time I've reinvented garbage collection
such is programming
since I'm doing the per core thingamajig I can probably start making the buddy allocator numa aware for no reason
i will probably steal many of these ideas over to the slab allocator but this is bigger priroity 4 now
i kinda sorta dont wanna write an avl tree impl or steal an avl tree impl for this, and since i have a full, working rbt impl i'll just nab that
maybe will write avl tree impl later
ok cool bean i got the buddy allocator working and tests are still blazingly π fast π which is very neat so now I start building the hugepage allocator
wait a second my filesystem integration test just got 2 milliseconds faster
how curious lol
very funny
yeah ok I can absolutely make this NUMA aware for fun since I'm making memory things core-local and I can just make my physical memory buddy allocator do that too
this will be so silly
and then for stuff like thread migration I just give each core a list of numa neighbors and only walk that list
π₯
my vm that i used to work on this project on windows just vanished but that's ok i can just set up wsl and i have all commits pushed anyways
π€ͺ
decided to use a min heap instead of a list for the per core lists of partial/free hugepages since it increases the chances of finding a hugepage with enough space in it quickly, and hopefully lock contention will be a little happier since I lock, remove, unlock, modify hugepage, and then lock, insert, unlock
ok I think if I implement the hugepage allocator I can shave another 1.5ish milliseconds off of my filesystem integration test since that's how much time it spends allocating things since the buffers are large
fire
started at 50ms now at 4ms hopefully can get it down to 2 or 3
unsafe extern "C" {
pub unsafe fn kmalloc(n: usize) -> *mut u8;
pub unsafe fn kfree(ptr: *mut u8);
}
use core::alloc::{GlobalAlloc, Layout};
pub struct PeterGriffin;
unsafe impl GlobalAlloc for PeterGriffin {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size();
unsafe { kmalloc(size) }
}
unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) {
unsafe {
kfree(ptr);
}
}
}``` rust π
Your kernel gets Rusty?
I had it in there since the beginning
gonna be using it soon though
perhaps

I will probably use a fallible allocator crate, or use the nightly fallible allocator for rust because I don't want a kernel panic when a userland program cannot be started due to OOM
do you recommend a crate or the nightly feature
I use tons of nightly shit but no crates
Actually no
I use tock-registers and crc
Also I should at some point figure out how to make the default for things that alloc be a Result, instead of panicking
π₯ do you not do allocation failure handling in rust π’
π
The official alloc crate isn't very good at doing that unfortunately, and there's no way to disable all functions that may panic on alloc.
Honestly I was just gonna make my own abstractions for allocation at some point
Badlloc the badger allocator 
revolutionary findings
Actually no
That doesn't do what I want
I want to ban all functions that could panic on OOM
crash out
odd
macos qemu being funny with acpi probably
was not happening b4
hmmmm i updated qemu and problem still happen
funky
ci/cd does fine so it is probably a local problem
yeah idk it just keeps being weird with the macos host
works fine in CI/CD
not on macos tho π₯
bruh i have to boot my linux vm on the mac since macos qemu is just being odd with uacpi
rip
ok seems like lemonlime bootloader is giving me a weird rsdp address
on macos it seems to be 0xffff8000000f5270 which is not a physaddr
funky
ohhh ok i thinsk i fixed it a bit
nvm acpi still being strange
>> UACPI ERROR: Op 0x0002 ('Reserved_0x02'): invalid/unexpected opcode
>> UACPI ERROR: aborting method invocation due to previous error: invalid AML opcode
>> UACPI ERROR: #0 in \()``` why is exclusively my macos machine weird π
literally what is this baloney it does fine in ci cd
perplexing
literally what is this baloney it functions just fine on CI/CD with linux and just dies with macOS
π₯
qemu is being odd probablyh
yea ok it's definitely a problem on macOS qemu
this is so odd
ok yes literally none of my other machines have that problem macOS qemu is just being extremely peculiar
so odd
no more kernel dev for 5 days then because that is how long I will be away from home (and thus using the macbook with cursed acpi behavior)
I will probably spend that time implementing data structures and then porting them to kernel π
minor setback
yeah ok my hunch is that this is a qemu x86-64 regression on the macOS host because I reverted back to a known good commit where everything worked fine (I made the commit from my macbook, so I knew stuff would be working) and this new ACPI corruption problem is present where it previously was not
I really hope this is not the case though but it seems the most likely
uacpi last update was months ago so it's not at fault here (this was an issue I only started seeing today)
bruh
me when I update qemu in 2 weeks and the bug goes away
gonna revert qemu now to see if it really is the problem
brew is being funny and doesnt let me revert to an old version π
i wonder why package managers do this, like arch linux pacman does the same thing and only retains the most recent version of most packages on remote
portage doesnt π₯
Ill once again shill for building qemu from source
Device emulation and assignment
ACPI / SMBIOS
Workaround 'PCI Label Id' Windows bug, which is normally harmless but on localized versions can lead to guest hangs (commit 0b053391985)
bruh lol it seems like their most recent version made an acpi change
>> UACPI ERROR: unable to lookup named object 'οΏ½οΏ½οΏ½' within (or above) scope '\_SB_.PCI0': invalid AML name string
>> UACPI LOG: unable to lookup named object 'οΏ½οΏ½οΏ½' within (or above) scope '\_SB_.PCI0': invalid AML name string
>> UACPI ERROR: aborting method invocation due to previous error: invalid AML name string
>> UACPI LOG: aborting method invocation due to previous error: invalid AML name string
>> UACPI ERROR: #0 in \()
>> UACPI LOG: #0 in \()``` ah it seems to be pci related
rip
the patch shows these new lines of code for qemu
{
Aml *pkg = aml_package(2);
aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index));
aml_append(ifctx, aml_store(pkg, ret));
/*
* Windows calls func=7 without checking if it's available,
* as workaround Microsoft has suggested to return invalid for func7
* Package, so return 2 elements package but only initialize elements
* when acpi_index is supported and leave them uninitialized, which
* leads elements to being Uninitialized ObjectType and should trip
* Windows into discarding result as an unexpected and prevent setting
* bogus 'PCI Label' on the device.
*/
ifctx1 = aml_if(aml_lnot(aml_lor(
aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp)
)));
{
aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero)));
/*
* optional, if not impl. should return null string
*/
aml_append(ifctx1, aml_store(aml_string("%s", ""),
aml_index(ret, one)));
}
aml_append(ifctx, ifctx1);
aml_append(ifctx, aml_return(ret));
}
it seems like this is making an intentionally problematic acpi table to workaround a windows bug
this is such a rip
π π₯
areeee yooooouuu kidddiiiing meeeeeee
i need to patch qemu π₯
what a rip
we switching to virtualbox after this one π£οΈ
Nah that's not it boss
iirc archlinux repos only contain the newest versions because rolling but they have an archive
IIRC some package mangers like dpkg (and apt on top of that) do let you revert without manually downloading from an archive
I'm perplexed as to why this is only prevalent on recent versions and specifically the recent macos version
I shall find out though
very confusing
if it's a bug with the allocator or something then one of my other tests would've surely caught it and I checked and nothing ever overwrites the acpi data stuff
I don't see any other changes to qemu acpi besides that thing recently
π₯
yeah it most likely is an allocator bug
like overwriting data?
it's very odd how this is a recent bug and didn't occur prior to this though
yeah probably
idk why it only reproduces there
its possible it reproduces always but doesnt cause noticable problems
if I rely on the limine memory map and only mark the USABLE RAM as free in the physical memory allocator I don't know why this would crop up but I'll see
im not sure its anything to do with the memory map
maybe their acpi table change moved stuff around and that caused the problem
u allocate with the heap allocator right
ya
id look there
i have a region of virtual memory that is used for my kernel slab allocator and I never see the acpi addresses get mapped there so I don't think it gets overwritten by that
Have you built qemu with the change and without it? Otherwise you're assuming this is the problem when it's not gaurenteed
its unlikely the table gets overwritten
u most likely trash method code or something
I tested on past and recent versions, and on the past version everything is fine and it breaks on the recent version
I also went many commits back to a known working commit and that broke too
when does uacpi error out exactly
uh I'ma check real quick
There's a lot of changes in each version change, and a lot of them are quite hidden unless you look closely into them
I've been bitten by it before, so I'm warning you might be looking at the wrong tree
>> UACPI ERROR: Op 0x00C0 ('Reserved_0xC0'): invalid/unexpected opcode
>> UACPI ERROR: aborting method invocation due to previous error: invalid AML opcode
>> UACPI ERROR: #0 in \()
I think the table name is gone
this should be sure easy to debug
just put a watchpoint
and see who overwrites the address
u said the table name is gone
well I think it's gone since it says in \()
no lol
oh ok
this just means root-level code
ohh right
this is with -M q35, if I remove -M q35, I get this
>> UACPI ERROR: unable to lookup named object 'οΏ½οΏ½οΏ½' within (or above) scope '\_SB_.PCI0': invalid AML name string
>> UACPI ERROR: aborting method invocation due to previous error: invalid AML name string
>> UACPI ERROR: #0 in \()```
so basically start by opening up gdb and putting a breakpoint to the place in interpreter.c that prints this
then check the aml_pc at that location
ok yea I'll look for that then
Returning invalid namestring, cursor is 0xfffff00002821f7d
>> UACPI ERROR: unable to lookup named object 'οΏ½οΏ½οΏ½' within (or above) scope '\_SB_.PCI0': invalid AML name string```
found that thing
yep, just watchpoint that thing
hmmm watchpoint is making things slow down to a halt and then qemu becomes unresponsive
odd
doing a breakpoint and then running to the breakpoint before doing the watchpoint does the same
π₯
yea it just dies, rip
had to sigkill it
maybe this is macos being funny, once i get home and try on linux then i'll see if the same behavior happens
qemu just dies here with the watchpoint
strange
maybe only enable the watchpoint right before calling uacpi_namespace_load?
and dump the data at that pointer before calling it
to be sure its not already garbage
// IDT
idt_init();
// Early device init
k_printf("uacpi init\n");
uacpi_init(rsdp_request.response->address);```
so i have this code, and i break right after idt_init, it runs right up to there with no issue, and i set the watchpoint on the address and `continue` and then it just dies
anyway back to the hugepage allocator, I think I will provide nice interfaces for allocating hugepages that are entirely untracked in the main allocator
this way certain subsystems can essentially keep small chains of hugepages for "mini allocators" and it would probably make the lock contention significantly lower and also improve performance because those subsystems may only ever need single page allocations
probably just some simple ways to make a
struct hugepage_arena {
struct list_head hugepages;
};``` and then just some way to add and remove and allocate from an arena
maybe this will make things happier idk I'll try it out
I kinda realized that my explicit hugepage state tracking of partial, used, or free is entirely unnecessary (I keep track of the number of 4kb pages used per hugepage) so I'll probably swap that field out for something like a set of bitflags
maybe stuff like pinning hugepages to cores so they aren't migrated under memory pressure or something
dunno I'll think of something
I might add an interface for hugepage allocator hints or something
for example if you are unmounting a filesystem, a massive amount of pages are gonna be freed
so you could potentially hint to the hugepage allocator about this and it would do stuff like increase the size of the per core minheaps to avoid doing that during all of the frees or whatever
idk π€ͺ
mallocplusai
hmmm if I'm introducing independently creatable untracked arenas I can probably also recycle them so once some subsystem is done with an arena it can give it back so I reduce calls into the physical memory allocator and the virtual memory mapping stuff
hugepage allocator allocation interface is complete
now I need the page freeing and then the arena management and arena recycling
I will probably make a hugepage translation buffer with a few entries in it, so that when I free from a hugepage rather than doing a full rbt traversal (expensive) I can just check this buffer to map a page to a hugepage struct
we have tlb at home
ok I might be getting distracted by theorem provers and formal verification, I've been taking a more in depth look into this stuff and it seems incredibly interesting
so I may forgor about this for a while but hopefully I can multitask and do a bit of both
such cool things
I probably need an oom handler callback infrastructure sooon π₯
Just panic on OOM like I do 
rust propaganda
oops I forgot to add profiling conditional compilation, I'll probably do that and do things like add fields to my locks to track contention and stuff π₯
I'll probably make each static handler go into some list and have input arguments for how many bytes to try and free and I'll let each oom handler set an available flag so that they don't get repeatedly called and do nothing
aborting on allocation fail better tbh 
that initgraph DAG thing that menix does is looking awfully appealing rn....
eh it's not a problem I can just group the calls to whatever _init() functions there are
so like mem_init() instead of all that
it ok
I will probably spend some time looking at actual kernel OOM handling stuff because this seems like something drastically more nuanced
the simple chain of handler functions is probably alright but there's likely tons and tons more stuff to this
when vmm.c becomes a 20 thousand line file
Interesting how page cache actually lost code over time
efficiency
I just remembered I had a filesystem layer and thus can introduce swapping to disk π²π²
memory management subsystem boutta go hard
page table aging π
meh I'm in 64 bit so I have a morbillion available bits in page tables
first thing's first we finish up the hugepage allocator and introduce arenas that you can dynamically manage and use different allocation techniques (slab, buddy, bump, bitmap) for each page in each arena
then we do oom handling and add profiling in places
then fancy swap stuff
π₯
I believe I only need like 2 bits for this though
diminishing returns after that
can use the rest of the bits for crypto mining π
C polymorphism π₯΄
kinda ugly to do polymorphic code in C but it's alright I guess
bit of switch case/LUT never hurt anyone π€ͺ
I think itβs fine tbh
ok if i rember, the way the clock works is that i keep track of all swappable pages somewhere and under memory pressure i start the clock to check if the A bit is set (and clear it if it is), and then if I encounter some page without the A bit set I move it to an inactive page list and that list eventually gets swapped to disk or something
that would be the most basic impl
and then i would probably use available pte bits as a counter to indicate access recency
π₯
i shall go do reading on this because i forgor
oh yeah the filesystem integration test is finally at one singular millisecond
I am not gonna bother with making it faster I think
it started at almost 100ms I think
so that's cool
i got distracted with cpu stuff so i will be on hiatus to probably start a project implementing risc v
i promise i am not marvin in disguise
Like as an emulator or
implementing it in verilog
technically can be emulated ye
(I will be emulating it most times since I don't wanna have to constantly be around an fpga to work on this)
ok just kidding no hiatus, it'll just be a side project
looking into page LRU clock algorithm research to see what's the most optimal way to go and do this
I thinks I saw multics have something about this
debug duck 3d resin print π
courtesy of a lab I got to work in
they had a bunch of extra resin that had to be used (otherwise it hardens and bad things happen) so I made the duck
you quack me up π¦
hmmm i am running into the little pickle of how to handle a mid-page-swapout write operation
I think what I can do is set the page to read-only at the start of it being swapped so any writes trigger a fault that i handle
That's not really a problem
how come
The page should have been invalidated in all address spaces by the time you're writing it out
ohhh ok so i first invalidate and then write it out
so then any mid-swapout faults would just wait for the write to finish before reading it back from disk?
or what
You could but what I did is just let the page out finish and then when it's done I notice the page was written to while it was being paged out
At which point I free the backing in the pagefile because it's obviously not valid anymore
hmmmmm is this the typical approach?
Think so
ok sounds good
What's your concern
well let's say i have this scenario
page is currently in the swap-queue, and is actually getting swapped to disk
then, a write to that page comes in during this portion of time
what do I do?
is the proper order of things to
mark page as being swapped out -> mark page as RO everywhere -> begin swapping it to disk -> unmap page (set it as not present) ->deallocate page
this way if I catch that fault (upon write), I let the swap operation finish, but then make sure to mark that entry in the swap as invalid, and then map the page back in since the page is still allocated in memory?
I think you are conflating eviction with page out
oh ok so I'm doing eviction then oops
so would this be the proper way to do that?
or nah
hmm i shall check what the terms actually mean lol
so if i have my words correct, page out is the process of writing a page from RAM to disk, right?
and then eviction can be that, but it can also be just deallocating a page without any write to disk if said page is not dirty
so this would be page out then
had to fly on airplane back home so no work today but i did read many cool multics stuff on airplane
riveting
not that noticeable ime,
me when software breaks on O3 because devs have UB
funnily enough ive never got to experience an ub using O3
it could be yet and it could also been mild unnoticeable ubs under the hood
anyway time to read MS docs about APCs
I should have all the necessary infra to do them
π€ͺ π₯ π» time
Antigen Presenting Cells
seems like NT has passive, apc, dispatch, and device isr irqls
neat
so i think I'd just do a queue per thread with irql raise and lower functions and each core has an irql field in the struct
i remember minty a had a big comment about these
might go and find it
lazy apcs when they meet productive apcs
disclaimer: i think my way of doing it is good but its not the same as NT's way
so dont read my thing and think it applies to NT
I shall thoroughly read both and then see which one I think suits me better or steal ideas from both π
thank
oh yeah that uacpi thing went away for some reason after i switched to my new page allocator
π₯
right ok so next order of priorities would be
- apcs (I think they are fun and cool)
- pager and stuff
- implement all that scheduler stuff i was talkin bout (I think i got it figured out)

woah that guy is really spherical
my source tree with full depth git clone is 4.3mb already.... π
wait my binary is smaller than my sources that's so awesome
by like 1/4th too
woah
actually pager first it seems more interesting
π
pager time today π
actually making my other allocators faster is probably more optimal, I saw that linux essentially makes lists of slabs in their slab allocator for cross core frees
probably gonna do a similar "global tree, per core lists" approach
π
does meanix have Paging with the capital P
or is that planned for the future
do you have demand paging
kinda
how does it kinda have that
user programs have it
it's not exactly complicated
you just record your own kind of page tables
then on page fault you fill the page in
rmap lore
sounds great
nsg was talking about a fully demand paged kernel which sounds interesting and problematic
demand paged interrupt stacks 
it's the arch nemesis of the "RAM OS" from osdev reddit
0% in RAM
may have gotten distracted and not worked on allocators at all
apcs are pretty cool
I get to WaitForSingleObjects now
wait i forgot an init function
rookie mistake
all good π
I think I might add on_destroy APCs that run when a thread is destroyed, maybe some things will have use for it
yeah I think doing that could make a lot of stuff a lot cleaner hmmmm
How do your apcs work?
same as most other APCs, they execute when the IRQL drops to PASSIVE and you can optionally choose to mask execution of certain types of APCs in some code segments
right now I just have kernel APCs and special kernel APCs, I'll probably introduce on_destroy APCs that run specifically when a thread is destroyed, so rather than manually checking in a bunch of different subsystems to clean things up, I just walk the chain of on_destroy functions
oh right the proper way of phrasing the first part is "When the IRQL drops below APC_LEVEL"
lol I only have PASSIVE below that rn
I might start using CR8 soon
enum irql {
IRQL_PASSIVE_LEVEL = 0,
IRQL_APC_LEVEL = 1,
IRQL_DISPATCH_LEVEL = 2,
IRQL_DEVICE_LEVEL = 3,
IRQL_HIGH_LEVEL = 4
};
``` astounding gameplay
just realized that because I have the behavior that makes the scheduler go tickless if there is only one thread, I may need to have a mechanism where I go
"If the target core is at an IRQL below APC_LEVEL but it has its tick disabled, I will IPI it to force the reschedule and run the APC"
coz when it's tickless there is no rescheduling happening so no apc would run
/* Poke the target core if there is no preemption on it
* because we need the thread to reschedule to run its APC */
static inline void maybe_force_reschedule(struct thread *t) {
struct scheduler *target = global.schedulers[t->curr_core];
struct core *core = global.cores[t->curr_core];
enum thread_prio_class class = prio_class_of(t->perceived_prio);
if (!target->timeslice_enabled &&
core->current_irql == IRQL_PASSIVE_LEVEL &&
class != THREAD_PRIO_CLASS_RT)
scheduler_force_resched(target);
}
``` astounding
oh dear wait I need to add refcounts and a proper full lock to the threads since this can race kind of badly
whoopsies
π₯
wait i thought i pushed that change this code isnt right
π₯
i wonder why mintia doesnt split up the logic into smaller reusable chunks, idk it feels like a lot of stuff is duplicated
maybe jackal inlining is suboptimal or doesnt exist
Wdym duplated
it just seems like logic repeats itself, like this snippet here
// Add the APC to the list.
RtlInsertAtTailList (
&thread^.LapcListHead, // head
&apc^.Entry, // entry
)
thread^.PendingEventsSet |= KI_THREAD_PENDING_LAPC
apc^.Enqueued = TRUE
apc^.Context1 = context1
apc^.Context2 = context2```
exists in a bunch of places, like
```rs
RtlInsertAtTailList (
&thread^.UapcListHead, // head
&apc^.Entry, // entry
)
thread^.PendingEventsSet |= KI_THREAD_PENDING_UAPC
apc^.Enqueued = TRUE
apc^.Context1 = context1
apc^.Context2 = context2```
and
```rs
// Add the APC to the list.
RtlInsertAtTailList (
&thread^.KapcListHead, // head
&apc^.Entry, // entry
)
thread^.PendingEventsSet |= KI_THREAD_PENDING_KAPC
apc^.Enqueued = TRUE
apc^.Context1 = context1
apc^.Context2 = context2
it's not a big deal i just found it curious that these kinds of things dont have their own functions
i didnt see any inline keyword in jackal
I mean
I would consider it overkill to turn such small common sections into a macro
or other thing
ok understandable, that would make sense for a lang without inline
btw check out KeWait.jkl to find a place where i did decide to create macro hell
how tf do you come up with all ts
by stealing borrowing it from NT
why
we've been talking about it for literally 4 years
I've only been here for one
inchresting
same with gummi lol
will is actually an nt kernel engineer trolling the osdev community I can see behind the mask
i would be wayyyy smarter if that were true
you should add APCs to menix fr
these will be pretty useful once I start having more things that are bound to thread lifetimes so when I clean up a thread I don't need to insert a morbillion statements and can just walk a chain of function pointers
idk what that is even
they are like software interrupts that you can send to a thread to tell the thread to execute something in its context
so if you have stuff that is bound to a thread it is very convenient
like let's say you're waking some threads up from an interrupt but need them to do some work, you can enqueue APCs onto them and they will execute the APCs once it is safe
what stuff tho
if you decide to get fancy and do something like jemalloc where each thread has its own arena, you can enqueue the arena destructor as an on_destroy APC so that rather than hardcoding the arena as a field in the thread you can have it just be destroyed via an APC
this way if you have a thread that never does an alloc that function is never enqueued as an APC
or if you have worker threads with dynamically allocated fields you won't need to track those in the thread and can just use an APC to clean it up when the worker thread goes bye bye
Doing the on_destroy apc prevents a race condition right?
wdym race condition
how would it race if I didn't do that
this is all entirely possible without APCs I just think that they make things more elegant lol
Ohhhh I misread what you said
I think I will use the on_destroy APCs for worker thread cleanup since those are spawned and destroyed on demand
anyway i still dont feel like doing an allocator rewrite for the slab and buddy allocator to make it per-core and parallelized lol, I already recently rewrote my page allocator (hugepage allocator) to be parallelized and I am a little bored of allocators
I think I will go and finish the scheduler thing though because that was something I had planned, and already built out the infra for, so now all I need is proper activity classification, scheduling periods, and to migrate to the RBT with priorities 0-255
kablooey
lol i once again had scheduler slowdowns due to MMIO for reading the timestamp in MS so I just cached an integer for that and now it is all good
what will your scheduler look like?
#1378106389347700946 message
I think it's time to head back into ye old python interpreter to craft heuristics for this scheduler
probably gonna first start off with floating point stuff and migrate to integer
some certified "We have floating point at home" code
ok I will probably just have two trees, one for threads that are not done for the period and one for threads that are done, so when a new period starts i just swap the pointers
shrimple
Thread activity metrics show block ratio 0, sleep ratio 0, run ratio 99, wake freq 1
Thread activity class is CPU BOUND
Priority in level is 1624528883, prio32_base is 1624378656, dynamic_delta is 150227
cached_prio32 is 1624528883, weight_fp is 1475543040, last_class_change_ms is 912``` hooray
numbers are working
Which algo is this?
wdym algo
they're just heuristics
for computing priorities and stuff
Yeah but what is your scheduler algo
i'll write a large markdown message about allat once it's all migrated π
and once i do all this
> git commit -a
[main 6241a7b] [sched]: migrate to red black tree new scheduler
22 files changed, 396 insertions(+), 222 deletions(-)
create mode 100644 kernel/sch/internal.h``` all done
that was a lot of work I'ma take a break and write a yap about it later
had a blast though
spent 1 hour debugging issues where i forgot to set variables
truly incredible
Nice bro
wow the schedule loop still stayed under 3,000 clock cycles with the huge amount of extra logic I added
neat
next sched feature: cgroups

wow, you, like, actually profiled your scheduler?
it literally takes 5 lines of code lol
is it to group threads doing same thing under one entity?
they are control groups for resource limiting and management
cgroups (abbreviated from control groups) is a Linux kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O, etc.) of a collection of processes.
Engineers at Google started the work on this feature in 2006 under the name "process containers". In late 2007, the nomenclature changed to "control groups" to ...
cool stuff
not much scheduler related tho?
you'd need to have some degree of integration with the scheduler to limit CPU usage
π
jobjects
job-s
jobjs
steve?
steve jobs would become steve unemployed if his business went bankrupt
sbeve noobjs
Censor that Iβm on my break at my occ*pation
I am doing the Great And Wonderful Thing of adding conditional compilation for profiling flags and other stuff
truly a blast
linker script gonna bloat up real big fr
hot take cmake does not suck
cargo could never compare
ye I like CMake :3
have you use the ccmake gui
so useful
cargo absolutely could never
so nice π
huh, nah haven't heard of it
i wonder how old mintia did that cool profiling thing to record scheduler ticks (no I will not read old mintia df code)
by incrementing a counter on each tick
also wdym you dont want to read this
the complexity is unmatched
unreadable == complex 
ok yeah this is kinda dumb and would make the cache unhappy so I'll probably migrate this to a per thread thing and only keep track of cheap stuff like work steals globally
Cmake is great
It suck the least out of all build systems ive used
how is meson
heard some good reviews about it
you have to add files manually instead of having some sort of wildcard
Absolutely no idea, heard it addressed some issues with cmake but you wouldn't feel the effect until your cmake becomes way more bloated
linux still going strong with makefiles is impressive
The ancient tombs must not be disturbed
what a fantastic statement
I think I will name the scheduler CATS for the
CATS Adaptive Timesharing Scheduler
may be busy soon because school in a few weeks
probably doing allocator + memory management revamp soon, including the pager π
then I'll move to filesystems and redo all that + proper page cache
might do networking afterwards or finish the xhci thing
then I'm probably set to do some syscall implementations, maybe start working on userland interfaces with some subsystems
I may dabble in userspace but idk
Awesome stuff
Good job
this will be AFTER I do all the other stuff so I'm not getting to userspace running on a UP system that has a 500mb RAMdisk and a tmpfs and hopes and dreams
π₯
Lol fair enough
Right now Iβm working on syscalls
Iβve finished my scheduler for the most part
void ctx_switch(cpu_ctx_t* old, cpu_ctx_t* new_ctx) {
asm volatile (
"pushf\n"
"pusha\n"
"movl %%esp, %0\n"
"movl %1, %%esp\n"
"popa\n"
"popf\n"
"iret\n"
: "=m"(old->esp)
: "m"(new_ctx->esp)
: "memory"
);
}``` why do you iret to context switch?
Yeah youβre right
Should be ret
no but like this would actively break the kernel
do you test your code
No. This works in ia32
how...
are you tying scheduling to timer interrupts?
I was, but now that that is changed, I donβt need iret
ok then that would be why it was working, understandable
why
coz if you iret instead of ret it would π₯
how
i was assuming that this wasnt from interrupt context
iret expects different stuff on the stack
or is it different in 32 bit
yeah iret would need the rip, cs, rflags, rsp and ss
As with a real-address mode interrupt return, the IRET instruction pops the return instruction pointer, return code segment selector, and EFLAGS image from the stack to the EIP, CS, and EFLAGS registers, respectively, and then resumes execution of the interrupted program or procedure.
ya
so if you just call into a function iret would not be happy
but it would work fine there since the task switches were tied to interrupts
i assumed they werent lol
I have just realized I can make the on_destroy APC idea more generic by having on-event APCs
like my DPC worker threads are currently pinned to the core that they spawn on, because there is extra logic to migrate a DPC worker thread from one core to another, due to internal structures that need to be changed, and I haven't implemented that yet
I don't really wanna hardcode that in the scheduler since that seems like it would get quite unwieldly quite fast as I accumulate more and more of these things, so maybe I'll do something like
enum apc_event {
APC_EVENT_THREAD_MIGRATION,
APC_EVENT_THREAD_DESTROY,
APC_EVENT_COUNT,
/* ... */
};```
and give each thread a
```c
struct apc *on_event_apcs[APC_EVENT_COUNT];```
i wonder how i can keep threads pinned to the open thread list so they dont vanish after inactivity
keeps happening with some threads
o well
I have realized that I need to add work pushing where busy cores push their threads onto idle cores to get them to run them
rookie mistake
or I can just make the idle thread try steals in the idle loop but idk
actually I won't need to do work pushing I think I can just IPI the other idle cores and they'll enter schedule() and steal
I can probably just use a simple atomic bitmap for marking cores as idle/running and if I detect more than 1 available thread to run + an idle core I just IPI it to try and get it to take the thread away
ok actually nevermind that is Big Dumb, I should probably come up with a way to represent processor topology in a hierarchical manner so that things like "hyper threads" that share a pipeline are picked first, then cores in the same numa node (if available), before a full CPU scan
and whatever layers I forgot
maybe its be better to spread the load avoiding smt sibling threads when there are free cores
so they dont compete
maybe but it's not a terribly huge deal
anyway yeah I think I don't need a tree to represent CPU topology lol
something like a
enum topo_level { TL_SMT, TL_CORE, TL_LLC, TL_PACKAGE, TL_NUMA, TL_MACHINE, TL_MAX };
and a
struct {
struct topo_node *level[TL_MAX];
uint16_t count[TL_MAX];
} topology;
``` perhaps
i think u16 is fine here since x86 has a max of 2^16 cores supported
lol what a silly thing to worry about
then i put some core structs and whatever else in each topo node π₯
ok so I've been kinda busy lowk but I'll be figuring out how to represent processor topology in software (and parsing it from hardware) and stop doing full cpu scans π₯
full cpu scans?
like scanning all the cores in sequential order
that doesn't scale well if you have many cores
and it's more optimal to scan from "better" cores to "worse" cores relative to your core
like first try SMT siblings before going to other numa nodes or whatever
ok just a note to self here:
if/when I do power management modes (pmms
), I'll need to figure out a way to get all subsystems to "pile up their work" on a few cores if there is a lot of stuff going on so the other cores enter their idle threads and conserve power
like with my scheduler I'd probably wanna shove everything on the least amount of cores necessary to run all the threads optimally and then set some mode to tell the scheduler to not perform logic regarding rebalancing until the mode is disabled
well this isn't really a per subsystem thing nvm
just a scheduler shenanigan
ok so for my CPU packing logic, I'll first obviously need to make a topology aware representation of all cores (duh), and then I'll need a way to figure out which "active" cores and which "victim" cores I want, and have some tunable knob
so at a low cpu packing value, I would just avoid core 15 and then run everything on cores 0-14, whereas at a higher value I might consolidate things to only cores 0-3
and then I could probably also make my scheduler period computation and timeslice distribution aware of this, so that as the cpu packing value increases, the timeslices and scheduling period lengths increase as well
blah blah blah blah
documenting le scheduler rn, been busy π₯
working on trying to see if I can get panic handler hooks working to have tests that are marked as SHOULD_PANIC
void (*panic_handler)(char *file, size_t line, char *function, char *message); maybe something like that as a global handler
"we have rust at home"
I'll probably just wanna split stuff into k_panic and k_bug for that differentiation between recoverable/unrecoverable problems
prob can use setjmp/longjmp
parsing cpu topology and representing it in software today
whats the issue with writing a numa aware allocator?
well you'd probably want other subsystems to be NUMA aware too (scheduler in particular) so that the allocator being NUMA aware also works
but the other troubles are
- figuring out the ideal node to allocate from if this node's address range is exhausted
- structures in the allocator need to balance locality with availability so you don't run out of memory on the local node too fast
- cross-core frees and load balancing
- allocator interfaces need to be more complex (you can't just pass in
size) as you'll need to know what nodes you might be allocating from, and whether to interleave between nodes, etc.
a NUMA aware allocator can't optimally perform if it uses the simple "Flat Memory Model" that we are all oh so familiar with
so you are looking at different "distances" given a node?
yea, you probs know how NUMA works but the idea is that certain cores can access certain regions of memory faster than other regions of memory
and you parse ACPI tables to essentially figure out "distances"
higher distance -> higher latency
> numactl --hardware
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3
node 0 size: 983 MB
node 0 free: 852 MB
node 1 cpus: 4 5 6 7
node 1 size: 1007 MB
node 1 free: 923 MB
node 2 cpus: 8 9 10 11
node 2 size: 943 MB
node 2 free: 812 MB
node 3 cpus: 12 13 14 15
node 3 size: 1007 MB
node 3 free: 916 MB
node distances:
node 0 1 2 3
0: 10 15 20 20
1: 15 10 20 20
2: 20 20 10 15
3: 20 20 15 10 ```
exampl
isn't it just duplicating the allocator state so you have a array of states[] indexed by the node
not so simple because of cross-core frees
if core 5 of node 1 wants to free memory from core 0 of node 0, what do you do?
a common approach is to have local lists of objects to free so when you call free() you drain that list
and you would add whatever you're freeing to that other node/core's list
and also the trouble of running out of memory on the local node and balancing things so that you don't run out of memory too fast
or take that nodes allocator lock?
allocators like this don't have an "allocator lock", they would typically lock much more fine grained like with per-core states
so you are implementing something similar to the slub with a per core slab?
kinda sorta