Skip to content

kernel: portable code calls the CPU's counter the counter, not the TSC - #944

Merged
Japabu merged 2 commits into
mainfrom
wt/toyos-portable-counter
Oct 15, 2026
Merged

Japabu merged 2 commits into
mainfrom
wt/toyos-portable-counter

Conversation

@Japabu

@Japabu Japabu commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

What this changes

Portable kernel code stops calling the CPU's free-running counter the TSC. On AArch64 cpu::counter reads the generic timer's CNTVCT_EL0; the names now follow the vocabulary the arch layer and clock already used (cpu::counter, clock::set_counter, clock::counter_ticks, ClockPage::counter_at_boot). No behaviour change.

  • clock: TSC_BOOT → COUNTER_AT_BOOT, TSC_PERIOD_FS → COUNTER_PERIOD_FS, tsc_deadline → counter_deadline (callers in deadline, hardlockup/probe, panic_reboot, xhci follow).
  • deadline: AT_TSC → AT_COUNTER. hardlockup: BOUND_TSC → BOUND_TICKS, AT_TSC → AT_COUNTER.
  • hardlockup::PERIOD's doc said x86 reference cycles are TSC ticks; it now states only the unit (cpu::counter ticks, what pmu::arm takes). The x86 fact already lives where it is true, arch/x86_64/pmu.rs's arm.
  • Comments in clock, deadline, hardlockup, panic_reboot, main, xhci/{mod,stop} and kernel/pure/sched/{cpu,hw}.rs say the counter where they said TSC or rdtsc. pure/sched/hw.rs's kernel-side summary names each architecture's timer, IPI and counter instead of x86's LAPIC/ICR/TSC, and Machine::set_timer's doc states the conversion both kernels make (irqchip::arm_one_shot takes a span on both, so KernelHw::set_timer converts) instead of an x86-only LAPIC/TSC-deadline remark. The x86 names left in comments this branch touched (deadline's LAPIC read and arm, clock's NMI rip) now say timer and PC.
  • arch/x86_64/smp.rs follows the rename of the static it cites.

Why

Closes issues/portable-kernel-code-names-the-tsc.md. Its exit — no item or comment outside kernel/src/arch/x86_64/ names the TSC for the counter cpu::counter reads — holds: rg -i tsc kernel/src kernel/pure --glob '!kernel/src/arch/x86_64/**' | rg -vi portsc returns nothing. The rule the issue enforced is already reviewer.md's "generic kernel code does not name architecture implementations", so nothing is folded into a module header.

Evidence

Head 23a96debe on base 979d4c539, on an Apple-silicon macOS host.

The image builds above are from 79a162cda; 23a96debe changes only comments, which cannot change what compiles, and its host CI run re-checks clippy on both kernel targets.

No guest run: the diff renames five private statics and one pub fn within the kernel crate and rewrites comments. A rename the compiler accepts on both targets cannot change what any path computes, so no guest test could observe it.

What is unsure

Anything a reader of main must not miss

Deletes issues/portable-kernel-code-names-the-tsc.md (closed). clock::tsc_deadline is now clock::counter_deadline; a branch still calling the old name fails to compile rather than misbehaving.

On AArch64 `cpu::counter` reads the generic timer's CNTVCT_EL0, yet the
portable kernel named it the TSC. Renamed, no behaviour change:

- clock: TSC_BOOT -> COUNTER_AT_BOOT (the clock page's own field name),
  TSC_PERIOD_FS -> COUNTER_PERIOD_FS, tsc_deadline -> counter_deadline,
  beside the existing set_counter and counter_ticks.
- deadline: AT_TSC -> AT_COUNTER.
- hardlockup: BOUND_TSC -> BOUND_TICKS, AT_TSC -> AT_COUNTER. PERIOD's
  doc no longer restates that x86 reference cycles are TSC ticks; that
  fact stays where it is true, at arch/x86_64/pmu.rs's `arm`.
- Comments in clock, deadline, hardlockup, panic_reboot, main, xhci and
  pure/sched say the counter (or `cpu::counter`) where they said TSC or
  `rdtsc`. pure/sched/hw.rs's kernel-side summary names each
  architecture's timer, IPI and counter rather than x86's LAPIC/ICR/TSC,
  and its TSC-deadline remark is marked as the x86 timer mode it is.
- arch/x86_64/smp.rs follows the rename of the static it cites.

Closes issues/portable-kernel-code-names-the-tsc.md: no item or comment
outside kernel/src/arch/x86_64/ names the TSC for the counter
`cpu::counter` reads (`rg -i tsc` outside that directory, PORTSC aside,
leaves only hw.rs's x86 TSC-deadline timer mode). The rule itself is
reviewer.md's "generic kernel code does not name architecture
implementations", so nothing is folded.
@Japabu

Japabu commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #944 at 79a162c (base origin/main 979d4c5)

Assessment. The rename is the right size and stays inside the kernel crate. I checked the diff hunk by hunk. Every change is an identifier rename (TSC_BOOT→COUNTER_AT_BOOT, TSC_PERIOD_FS→COUNTER_PERIOD_FS, tsc_deadline→counter_deadline, deadline::AT_TSC→AT_COUNTER, hardlockup::{BOUND_TSC, AT_TSC}→{BOUND_TICKS, AT_COUNTER}) or a comment edit. No expression, ordering or type changes, so behaviour cannot change, and the evidence (host CI, --build-only for both arches, logs at evidence 9a891c12, each exit=0) covers what a rename can break. Net: +64/−81, of which −17 is the closed issue.

Exit check. I ran git grep -i tsc over the branch's kernel/src and kernel/pure, excluding arch/x86_64 and portsc. It returns only kernel/pure/sched/hw.rs:94. The rest of kernel/ holds only the toyos-tsc dependency, which x86's cpu.rs alone uses, and x86 boot logs under kernel/sim/tests/scout/. No code anywhere in the tree still uses the old identifiers. The renamed comments I checked are true:

  • hardlockup::PERIOD's unit matches both pmu::arms, and x86's pmu.rs:65 keeps the reference-cycle fact.
  • panic_reboot's cpu::stated_counter_hz is the function it calls.
  • The xHCI TCG remark holds for CNTVCT_EL0 too.
  • smp.rs cites the new static.

Issue citations left alone. This is correct. Root's rule edits an existing issue only when it is taken up or a rule requires it. issues/README.md's move-every-citation rule covers slugs, and no slug names a renamed identifier. ap-tsc-trail-is-assumed-and-never-checked is about x86's TSC and stays true. AT_TSC in the T14 deadline issue sits among line numbers that are a snapshot of an older tree. No citation of the deleted slug remains anywhere in the tree. The closing rule is met: the durable rule ("generic kernel code does not name architecture implementations") already lives in reviewer.md.

No conflict with #946: that branch does not touch clock.rs and calls settles, not tsc_deadline.

Findings

  • BLOCKER: kernel/pure/sched/hw.rs:93-95. The edited comment reads "The x86 kernel's LAPIC one-shot is relative, so it converts; TSC-deadline mode is absolute and will not." This is the line the PR's exit argument rests on, and it is now misleading in portable code. The conversion is in KernelHw::set_timer (kernel/src/hw.rs:57), and it applies on both architectures: AArch64's irqchip::arm_one_shot (arch/aarch64/irqchip.rs:458) takes a span too, even though its comparator is absolute. "The x86 kernel's" tells a reader of the trait that only x86 converts. "TSC-deadline mode … will not" predicts how an implementation that does not exist would behave, and AArch64 already contradicts it. Write the contract once, e.g. "The kernel's one-shot takes a span (irqchip::arm_one_shot), so KernelHw::set_timer converts." Then no x86 name is left in kernel/pure, and the exit holds without the carve-out.

  • SUGGESTION: three x86 names remain in portable comments the PR already touches. They are outside the TSC exit, so this is optional:

    • kernel/src/deadline.rs:168: "the LAPIC timer's arm"
    • deadline.rs:90: "a local APIC read and two writes"
    • clock.rs:151: "an NMI sample's rip"

    "The timer's arm", "a timer read" and "PC" would make the vocabulary consistent.

  • Note for the orchestrator (PR body, not a code finding): "renames four private statics" is five: TSC_BOOT, TSC_PERIOD_FS, deadline::AT_TSC, hardlockup::BOUND_TSC and hardlockup::AT_TSC.

SEND BACK

Review of #944: the trait's doc said only x86's LAPIC one-shot converts
and predicted a TSC-deadline mode that does not exist. Both
architectures' `irqchip::arm_one_shot` take a span (x86_64 apic.rs,
aarch64 irqchip.rs), and `KernelHw::set_timer` (kernel/src/hw.rs) turns
the absolute deadline into one; the doc now says that, leaving no x86
name in kernel/pure.

Also drops the x86 names left in comments this branch already touched:
deadline.rs's "local APIC read and two writes" and "LAPIC timer's arm",
and clock.rs's NMI sample `rip`.
@Japabu

Japabu commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Round 1 addressed at 23a96debe.

  • BLOCKER kernel/pure/sched/hw.rs:93-95 — fixed. I checked the claim before writing it: KernelHw::set_timer (kernel/src/hw.rs:57-59) passes deadline - now to irqchip::arm_one_shot, and both architectures' versions take a span in nanoseconds (arch/x86_64/apic.rs:365, re-exported as irqchip at arch/x86_64/mod.rs:47; arch/aarch64/irqchip.rs:458, which converts through counter_ticks to the absolute comparator). The doc now reads: "The kernel's one-shot takes a span (irqchip::arm_one_shot), so KernelHw::set_timer converts." rg -i tsc kernel/pure and the exit grep over kernel/src (excluding arch/x86_64, PORTSC aside) both return nothing.
  • SUGGESTIONs — accepted. deadline.rs:90 now says "timer register traffic on every wake", deadline.rs:168 says "the timer's arm", and clock.rs:153 says "an NMI sample's PC".
  • PR body: corrected to five statics. The exit argument no longer needs a carve-out.

Checks: cargo run -- --ci host at 23a96debe → exit 0, 86 steps green, including kernel clippy on both arches (log). The --build-only images from 79a162cda still stand: this round changes only comments (3 files, +7/−7), and comments cannot change what compiles or runs.

@Japabu

Japabu commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Review round 2: #944 at 23a96debe (base origin/main 979d4c539)

The new commit 23a96debe changes comments only: 3 files, +7/−7, with no token outside a /// or //.

Round-1 blocker: kernel/pure/sched/hw.rs:93-95. Resolved. The doc now reads: "The kernel's one-shot takes a span (irqchip::arm_one_shot), so KernelHw::set_timer converts." I checked each part:

  • KernelHw::set_timer (kernel/src/hw.rs:57-59) passes deadline.0.saturating_sub(self.now().0) to irqchip::arm_one_shot.
  • Both architectures define pub fn arm_one_shot(nanos: u64), at arch/x86_64/apic.rs:365 and arch/aarch64/irqchip.rs:458.

The sentence is true on both targets and names no x86 mechanism, so the exit argument holds without a carve-out.

Suggestions taken. I checked each one:

  • deadline.rs:90: "timer register traffic on every wake". This is true on both architectures: LAPIC on x86, CNTV_* on AArch64.
  • deadline.rs:168: "the timer's arm". deadline::start() has one caller, main.rs:434, which is portable code after the timer is armed.
  • clock.rs:153: "an NMI sample's PC".

Evidence. --ci host at 23a96debe is at evidence 00f694c9…/944/ci-host-23a96debe.log. It resolves at the full hash and ends "86 step(s), all green" / exit=0. It includes kernel clippy on both architectures. The round-1 --build-only images from 79a162cda remain valid evidence, because this round changes only comments.

BLOCKERS

None.

SUGGESTIONS

None.

LAND

@Japabu
Japabu marked this pull request as ready for review October 15, 2026 10:01
@Japabu
Japabu added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit 80be9e8 Oct 15, 2026
6 checks passed
@Japabu
Japabu deleted the wt/toyos-portable-counter branch October 15, 2026 10:59
Japabu added a commit that referenced this pull request Oct 15, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant