Skip to content

fix: platform profile handling when quiet/low-power is missing - #280

Open
Boogie61 wants to merge 4 commits into
OpenGamingCollective:mainfrom
Boogie61:fix/platform-profile-cycle-and-epp
Open

fix: platform profile handling when quiet/low-power is missing#280
Boogie61 wants to merge 4 commits into
OpenGamingCollective:mainfrom
Boogie61:fix/platform-profile-cycle-and-epp

Conversation

@Boogie61

@Boogie61 Boogie61 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

My laptop (ROG Zephyrus G16 GU606AX, kernel 7.1.6) only has two profiles:

$ cat /sys/firmware/acpi/platform_profile_choices
balanced performance

Three things break because of that.

Cycling gets stuck. PlatformProfile::next() only checks choices for LowPower, so from Performance it returns Quiet even when Quiet doesn't exist:

$ asusctl profile next
Error: RogPlatform: platform_profile: (quiet) not supported

Once you're on Performance you can't get back to Balanced. Now it filters the cycle by what the kernel actually reports. Added tests for the two- and three-profile cases.

EPP changes even when the profile doesn't. Both next_platform_profile() and set_platform_profile() set EPP first, then bail out if the profile write fails, leaving EPP where it shouldn't be. After one asusctl profile next:

platform_profile   performance   (unchanged, write failed)
EPP                power         (changed anyway)

So the CPU sits on the most power-saving preference while the profile still says Performance. Now the profile goes first and EPP only follows if it worked.

Battery switching never happens. The existing Quiet fallback needs LowPower to be present, which it isn't here, so the default platform_profile_on_battery: Quiet just fails every time. Unplugged:

             AC            battery
profile      performance   performance   (no change)
throttle     1             1             (no change)

The laptop stays at full performance on battery. Now any unavailable profile falls back to the closest one that exists. Custom is left alone since it never shows up in choices.

Tested on the GU606AX. cargo test -p rog_platform passes, cargo build -p asusd is clean.

…ices

PlatformProfile::next() took `choices` but only consulted it to decide
between LowPower and Quiet. From Performance it therefore returned Quiet
whenever LowPower was absent - including on hardware that has neither.

The ROG Zephyrus G16 GU606AX exposes only balanced and performance:

    $ cat /sys/firmware/acpi/platform_profile_choices
    balanced performance

so cycling was a dead end. Once on Performance the user could not get
back to Balanced at all, by CLI or by hotkey:

    $ asusctl profile next
    Error: org.freedesktop.DBus.Error.NotSupported:
           RogPlatform: platform_profile: (quiet) not supported

next() now filters a canonical cycle order by what the kernel actually
reports, so the cycle always stays inside the supported set. Behaviour on
hardware that does offer Quiet or LowPower is unchanged. If choices comes
back empty the previous logic is kept rather than inventing a profile.

Adds unit tests for the two/three-profile cases and for a current profile
that is not part of the cycle.
…cted

Both next_platform_profile() and set_platform_profile() wrote the CPU
energy-performance preference for the requested profile before trying to
apply the profile itself, then returned early on failure. The EPP change
was never rolled back, so a rejected profile left the machine in a state
that matches neither profile.

Measured on a GU606AX, which offers only balanced and performance. A
single `asusctl profile next` from Performance:

    platform_profile   performance   (unchanged, the set failed)
    EPP                power         (applied anyway - Quiet's EPP)

The CPU is left on the most power-saving preference while the profile
still reads Performance, with no error visible to the user beyond the
one-line D-Bus failure.

Reorder both paths so the profile is validated and applied first, and EPP
and the config write only happen once that succeeded. set_platform_profile
already validated against platform_profile_choices - that check just ran
too late to protect anything.
The apply-time normalization only handled the Quiet -> LowPower rename
from kernel 6.11. Its condition required LowPower to be present, so on
hardware offering neither Quiet nor LowPower the configured profile was
passed through unchanged and the write simply failed.

The default config ships platform_profile_on_battery: Quiet, so on a
GU606AX (balanced + performance only) a fresh install never switched
profile on battery at all. Measured by unplugging:

    platform_profile        performance -> performance (no change)
    throttle_thermal_policy 1           -> 1           (no change)

The laptop stays at full performance on battery, silently. asusd sees the
AC/DC transition but the profile write is rejected every time.

Generalize the check: if the configured profile is not in
platform_profile_choices, fall back to the first available of LowPower,
Quiet, Balanced, Performance and persist that. Custom is excluded - it is
a userspace concept for custom fan curves and never appears in choices,
so normalizing it would clobber a deliberate user setting.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved platform-profile selection when configured profiles are unavailable by choosing the least aggressive available fallback.
    • Prevented profile settings from being saved unless hardware application succeeds.
    • Preserved custom profile settings during selection.
    • Prevented linked performance settings from changing when profile application fails.
    • Fixed profile cycling to wrap correctly through only the profiles supported by the device.
    • Added handling for unknown current profiles and limited profile availability.

Walkthrough

Platform profile cycling now uses hardware-reported choices. Unavailable profiles normalize to a supported fallback. Platform writes now complete before linked EPP updates and configuration persistence.

Changes

Platform profile handling

Layer / File(s) Summary
Available profile cycling
rog-platform/src/platform.rs
PlatformProfile::next filters the fixed cycle to available profiles, wraps within that set, and handles unknown current profiles. Tests cover the new cases.
Profile application ordering
asusd/src/ctrl_platform.rs
Unavailable profiles normalize to a supported fallback. Profile writes now precede linked EPP updates and configuration persistence. Rejected profiles do not change EPP or configuration.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProfileRotation
  participant PlatformProfile_next
  participant ProfileSetter
  participant Platform
  participant EPP_and_Configuration
  ProfileRotation->>PlatformProfile_next: request next available profile
  PlatformProfile_next-->>ProfileRotation: return supported profile
  ProfileRotation->>ProfileSetter: apply selected profile
  ProfileSetter->>Platform: write platform profile
  Platform-->>ProfileSetter: return write result
  ProfileSetter->>EPP_and_Configuration: update linked EPP after success
  ProfileSetter->>EPP_and_Configuration: persist configuration after success
Loading

Suggested labels: asusd, rog-profiles, rog-platform, fix

Suggested reviewers: ghoul4500, neroreflex, scardracs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main fix: handling platform profiles when Quiet or LowPower is unavailable.
Description check ✅ Passed The description explains the problem, implementation, hardware, environment, and test results; the Linux distribution field and checklist details are not critical.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added asusd System Daemon / D-Bus fix Fix a bug or an issue rog-profiles Power Profiles / Fan Curves labels Aug 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@asusd/src/ctrl_platform.rs`:
- Around line 283-315: The platform-profile flow currently persists fallback
choices before applying them and duplicates incomplete normalization in the
AC/battery setters. Consolidate fallback selection into a shared normalization
path used by set_platform_profile_on_battery, set_platform_profile_on_ac, and
set_platform_profile; have it return the available candidate without mutating
configuration, apply that candidate through update_policy_ac_or_bat, and only
after a successful platform write update and persist the source-specific
configuration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b2db393a-2298-4d2c-8ec7-67c72b12c0ec

📥 Commits

Reviewing files that changed from the base of the PR and between 61c39ff and ed67a23.

📒 Files selected for processing (2)
  • asusd/src/ctrl_platform.rs
  • rog-platform/src/platform.rs
📜 Review details
🔇 Additional comments (2)
rog-platform/src/platform.rs (1)

157-189: LGTM!

Also applies to: 192-257

asusd/src/ctrl_platform.rs (1)

508-518: LGTM!

Also applies to: 547-548, 563-569

Comment thread asusd/src/ctrl_platform.rs Outdated
Review feedback: select_power_profile_for_source() wrote the normalized
profile to the config before returning it, but the caller can still fail
to apply it. That records a profile the hardware never took - the same
write-before-confirm mistake this series fixes for EPP.

Split the logic: normalize_profile() only computes, and the config write
moves to update_policy_ac_or_bat() after set_platform_profile() succeeds.

The AC and battery D-Bus setters had their own narrower Quiet -> LowPower
normalization that missed the case where neither exists, and also wrote
the config before applying. Both now share normalize_profile() and the
same store-after-apply helper.
@coderabbitai coderabbitai Bot added the rog-platform GPU Switching / Armoury / WMI label Aug 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@asusd/src/ctrl_platform.rs`:
- Around line 285-286: Prevent PlatformProfile::Custom from reaching the kernel
setter: update the AC and battery policy flows, including
update_policy_ac_or_bat and both power-source setters, to apply Custom entirely
in userspace or reject it before set_platform_profile. Preserve custom EPP
application for stored Custom policies, and add coverage for Custom handling on
both AC and battery sources.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 82607264-1360-4813-a4ad-52cc71498132

📥 Commits

Reviewing files that changed from the base of the PR and between ed67a23 and 647e726.

📒 Files selected for processing (1)
  • asusd/src/ctrl_platform.rs
📜 Review details
🔇 Additional comments (1)
asusd/src/ctrl_platform.rs (1)

404-405: LGTM!

Also applies to: 529-539, 568-590

Comment on lines +285 to +286
if configured == PlatformProfile::Custom {
return None;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect Custom profile semantics and all profile-application call paths.
ast-grep outline rog-platform/src/platform.rs --items all
rg -n -C 5 'PlatformProfile::Custom|normalize_profile|set_platform_profile_on_(battery|ac)|update_policy_ac_or_bat|platform_profile_choices' \
  asusd/src/ctrl_platform.rs rog-platform/src/platform.rs

Repository: OpenGamingCollective/asusctl

Length of output: 19169


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the complete policy update and profile setter implementations,
# plus configuration defaults and existing tests for Custom.
sed -n '250,460p' asusd/src/ctrl_platform.rs
sed -n '550,670p' asusd/src/ctrl_platform.rs
sed -n '850,915p' asusd/src/ctrl_platform.rs
rg -n -C 4 'profile_custom|platform_profile_on_(ac|battery)|Custom' asusd rog-platform | head -n 240

Repository: OpenGamingCollective/asusctl

Length of output: 31603


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Trace AC/battery tuning application and identify test coverage for the two
# power-source setters and update_policy_ac_or_bat.
rg -n -C 8 'async fn run_ac_or_bat_cmd|run_ac_or_bat_cmd|ac_profile_tunings|dc_profile_tunings|set_platform_profile_on_(ac|battery)|update_policy_ac_or_bat' \
  asusd/src/ctrl_platform.rs asusd/src
rg -n --glob '*.{rs,toml}' 'ctrl_platform|platform_profile_on_ac|platform_profile_on_battery|update_policy_ac_or_bat|Custom' \
  asusd rog-platform | head -n 260

Repository: OpenGamingCollective/asusctl

Length of output: 34994


Do not pass PlatformProfile::Custom to the kernel profile setter.

normalize_profile preserves Custom, but both power-source setters pass it to set_platform_profile, which rejects it because it is absent from platform_profile_choices. A stored Custom AC or battery policy also fails during update_policy_ac_or_bat, so its custom EPP is not applied.

Add a userspace-only Custom application path, or reject Custom before it enters an AC or battery policy. Add tests for both power sources.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@asusd/src/ctrl_platform.rs` around lines 285 - 286, Prevent
PlatformProfile::Custom from reaching the kernel setter: update the AC and
battery policy flows, including update_policy_ac_or_bat and both power-source
setters, to apply Custom entirely in userspace or reject it before
set_platform_profile. Preserve custom EPP application for stored Custom
policies, and add coverage for Custom handling on both AC and battery sources.

@Boogie61

Boogie61 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 647e726: normalize_profile() now only computes, and the config write moved to after set_platform_profile() succeeds. The AC and battery setters had the same problem plus their own narrower Quiet->LowPower fallback, so they now share the same path.

The Custom finding is real but predates this branch — the old code passed Custom straight through as well, since the normalization only ever matched Quiet. Leaving Custom untouched here is deliberate so a user setting is not clobbered; it just makes the existing gap easier to see. Fixing it means deciding what Custom should mean at the platform level, either skipping the kernel write and applying only its EPP, or picking an underlying profile. That is a separate change, out of scope for this branch.

@Ghoul4500

Copy link
Copy Markdown
Member

I believe this is a symptom of a bigger issue that asusd doesn't infer the available profiles from what the kernel allows and instead uses the hardcoded enum. If possible, could you make it so this reads the profiles and uses them instead of juggling choices?

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

Labels

asusd System Daemon / D-Bus fix Fix a bug or an issue rog-platform GPU Switching / Armoury / WMI rog-profiles Power Profiles / Fan Curves

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants