Skip to content

supervisor: x2apic/ioapic and infrastructure#220

Open
iskamag wants to merge 42 commits into
froggey:masterfrom
iskamag:x2apic
Open

supervisor: x2apic/ioapic and infrastructure#220
iskamag wants to merge 42 commits into
froggey:masterfrom
iskamag:x2apic

Conversation

@iskamag

@iskamag iskamag commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Adds:
x2apic
io-apic
directed IPIs
lazy TLB shootdown
fair MCS spinlocks
the skeleton for MSI + irq balancing and scheduler rewrite branches.

Boots into desktop on my machine. Not tested on ARM.

iskamag added 30 commits May 20, 2026 02:10
…ocation

- Change mcs-node slot initform from (%make-mcs-node) to nil to avoid
  cold-generator eval issue with cross-environment function references
- Pre-allocate mcs-node in cold-generator and CPU init functions
- Add %cpuid-1-ecx-early: save/restore EBX around CPUID for early boot
- Skip lapic registers invalid in x2APIC mode (ARP, PPR, logical dest)
- Initialize *io-apics*/*io-apic-active-p* with boundp checks
Comment thread drivers/sound.lisp

@froggey froggey left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a pretty huge patch with a bunch of different features in it. could it be broken up into smaller PRs? it's been pretty difficult to review. Maybe

  • x2apic
  • redesign of tlb shootdowns
  • mcs locks
  • priority inheritance

are the mcs locks supposed to supersede the existing test-and-set spinlocks or live alongside them? if it's possible to completely replace the old spinlocks that'd be nice. I'll point out that the existing spinlock functions/macros are named in a way that's independent of their implementation, (place and symbol refer to where the spinlock lives, rather than the kind of implementation)

Comment thread supervisor/arm64/cpu.lisp
Comment on lines +259 to +260
(setf (cpu-inhibit-scheduling (local-cpu))
(1+ (cpu-inhibit-scheduling (local-cpu))))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (along with the bit that undoes it) feels like something that should be factored out into a macro

Comment thread supervisor/arm64/cpu.lisp
(ensure (not *tlb-shootdown-in-progress*) "TLB shootdown already in progress!")
(setf *tlb-shootdown-in-progress* t)
(setf (cpu-inhibit-scheduling (local-cpu))
(1+ (cpu-inhibit-scheduling (local-cpu)))))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, could factor out into a separate function

Comment thread supervisor/arm64/cpu.lisp
Comment on lines -234 to +251
(broadcast-ipi +wakeup-sgi-id+))
(dolist (cpu *cpus*)
(when (eql (arm64-cpu-state cpu) :online)
(wake-cpu cpu))))

(defun wake-cpu (cpu)
(when (cpu-idle-p cpu)
(send-ipi-to-cpu cpu +wakeup-sgi-id+)))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's something a bit funny about this, if a cpu is offline/never came up then it shouldn't be idle so the online check seems redundant

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i think i see. x86-64's broadcast-ipi checks if the cpu is online but arm's doesn't. I don't think it's needed on arm as the gic wouldn't be configured for an offline cpu and the cpu wouldn't be capable of receiving it.

Comment thread supervisor/arm64/cpu.lisp
Comment on lines +273 to +275
(cond (*debug-magic-button-hold-variable*
(magic-button-ipi-handler interrupt-frame))
(t

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this get merged into the quiesce ipi handler?

Comment thread supervisor/arm64/cpu.lisp
Comment on lines -257 to -270
;; Return this thread to the run queue.
(setf (thread-state current) :runnable)
(push-run-queue current)
(preemption-timer-reset nil)
;; Save thread state.
(save-fpu-state current)
(save-interrupted-state current interrupt-frame)
;; Partially switch to the idle thread.
(setf (thread-state idle) :active))
;; Have now reached a quiescent state.
(sys.int::%atomic-fixnum-add-symbol '*non-quiescent-cpus-remaining*
-1)
(when was-active
;; Finally, return to the idle thread.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all these comments got removed. they're not great comments, but they help me follow the phases of this function

Comment thread supervisor/thread.lisp
Comment on lines +868 to +886
(defun %cleanup-dead-threads ()
"Remove threads marked :dead from the *all-threads* list.
The global thread lock must be held."
(ensure-global-thread-lock-held)
(dolist (thread *rcu-deferred-list*)
(when (thread-global-next thread)
(setf (thread-global-prev (thread-global-next thread)) (thread-global-prev thread)))
(when (thread-global-prev thread)
(setf (thread-global-next (thread-global-prev thread)) (thread-global-next thread)))
(when (eql thread *all-threads*)
(setf *all-threads* (thread-global-next thread))))
(setf *rcu-deferred-list* nil))

(defun cleanup-dead-threads ()
"Remove threads marked :dead from the *all-threads* list."
(without-interrupts
(acquire-global-thread-lock)
(%cleanup-dead-threads)
(release-global-thread-lock)))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as the rcu-synchronize code all seems dead it looks like this is dead too and dead threads will never be cleaned up?

Comment thread supervisor/thread.lisp
(when (wait-queue-p wi)
(lock-wait-queue wi))
(with-symbol-spinlock (*global-thread-lock*)
(with-global-thread-lock nil

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

() not nil

Comment thread supervisor/thread.lisp
(flet ((push-foothold ()
(safe-without-interrupts (thread push-cons)
(with-symbol-spinlock (*global-thread-lock*)
(with-global-thread-lock nil

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

() not nil

Comment thread supervisor/thread.lisp
(defun sample-thread-state (thread)
(safe-without-interrupts (thread)
(with-symbol-spinlock (*global-thread-lock*)
(with-global-thread-lock nil

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

() not nil

Comment on lines -35 to +38
(setf (env:cross-symbol-value environment 'mezzano.supervisor::*bsp-cpu*)
(env:make-structure environment 'mezzano.supervisor::arm64-cpu))
(let ((bsp-cpu (env:make-structure environment 'mezzano.supervisor::arm64-cpu)))
(setf (env:structure-slot-value environment bsp-cpu 'mezzano.supervisor::mcs-node)
(env:make-structure environment 'mezzano.supervisor::mcs-node))
(setf (env:cross-symbol-value environment 'mezzano.supervisor::*bsp-cpu*) bsp-cpu))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, looks like the cold-generator does populate the mcs-node slot, so the conditional initialization can be removed

@iskamag

iskamag commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

thx a lot for taking the time for the code review and putting up with the quality; sorry that I let so much through the cracks, I did not read the code thoroughly enough before sending. I'll start working on these issues later.

The unused stuff though: that's the "skeleton for the scheduler rewrite" part. Technically it could be deferred for later PRs. I will look into that.

I am not used to splitting PRs into series, dealing with their dependencies and all that. So I was kinda scared, and didn't work on MSI or the scheduler while waiting.
But I did mostly develop this branch in "phases", so yes it would've made sense to make several PRs instead of one.

And oh yeah 0 way of testing ARM.

@froggey

froggey commented Jun 28, 2026

Copy link
Copy Markdown
Owner

yeah splitting a big pr into smaller ones can be a bit of a pain but it's a useful skill to have

I'm happy to test on arm when it gets to a more stable state (and make any fixes that might be needed too)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants