Skip to content

[BUG] ExitSystemLoadhysteresis, ExitSystemLoadThres, and ExitGFXLoadThres are not scaled by 100 in lpmd_build_config_states(), breaking state exit thresholds #125

Description

@rmeissn

Problem Description

When parsing XML <State> definitions in intel_lpmd, system load thresholds (such as <EntrySystemLoadThres>, <EnterCPULoadThres>, and <EnterGFXLoadThres>) are parsed as integer percentages (e.g. 20 for 20%) and then scaled by 100 to convert them into internal basis points (2000 for 20.00%) in lpmd_build_config_states().

However, all three EXIT threshold tags are missing from the *= 100 scaling loop in lpmd_build_config_states():

  1. <ExitSystemLoadhysteresis> (state->exit_system_load_hyst)
  2. <ExitSystemLoadThres> (state->exit_system_load_thres)
  3. <ExitGFXLoadThres> (state->exit_gfx_load_thres)

As a result:

  • When a user configures <ExitSystemLoadhysteresis>75</ExitSystemLoadhysteresis>, entry_system_load_thres is scaled to 2000 (20.00%), but exit_system_load_hyst remains 75 (0.75%).
  • In config_state_match(), (state->entry_system_load_thres + state->exit_system_load_hyst) evaluates to 2000 + 75 = 2075 (20.75%) instead of the intended 2000 + 7500 = 9500 (95.00%).
  • This causes intel_lpmd to exit the low-power state almost immediately as soon as system load crosses 20.75%, rendering user exit thresholds and hysteresis configurations virtually ineffective.

Code Evidence & Location

File: src/lpmd_state_machine.c
Function: lpmd_build_config_states()

778:		if (state->entry_system_load_thres < 0 || state->entry_system_load_thres > 100)
779:			continue;
780:		else
781:			state->entry_system_load_thres *= 100;
782:
783:		if (state->enter_cpu_load_thres < 0 || state->enter_cpu_load_thres > 100)
784:			continue;
785:		else
786:			state->enter_cpu_load_thres *= 100;
787:
788:		if (state->exit_cpu_load_thres < 0 || state->exit_cpu_load_thres > 100)
789:			continue;
790:		else
791:			state->exit_cpu_load_thres *= 100;
792:
793:		if (state->enter_gfx_load_thres < 0 || state->enter_gfx_load_thres > 100)
794:			continue;
795:		else
796:			state->enter_gfx_load_thres *= 100;

Noticeably missing from the scaling block:

  • state->exit_system_load_hyst
  • state->exit_system_load_thres
  • state->exit_gfx_load_thres

(Note: exit_cpu_load_thres on line 791 WAS included, but exit_system_load_hyst, exit_system_load_thres, and exit_gfx_load_thres were omitted).


Comparison with Matching Logic

In src/lpmd_state_machine.c:

151:	if (state->entry_system_load_thres && state->entry_system_load_thres < bsys) {
152:		if (!state->exit_system_load_hyst)
153:			return 0;
154:		if ((state->entry_load_sys + state->exit_system_load_hyst) < bsys ||
155:		    (state->entry_system_load_thres + state->exit_system_load_hyst) < bsys)
156:			return 0;
157:	}

Because bsys and entry_system_load_thres are scaled to basis points (10000 = 100%), adding an unscaled exit_system_load_hyst (e.g. 75) leads to a unit mismatch (2000 + 75 = 2075 = 20.75%).


Suggested Fix

Add scaling for all missing exit threshold and hysteresis fields in lpmd_build_config_states():

--- a/src/lpmd_state_machine.c
+++ b/src/lpmd_state_machine.c
@@ -781,6 +781,15 @@ int lpmd_build_config_states(struct lpmd_config_t *lpmd_config)
 			state->entry_system_load_thres *= 100;
 
+		if (state->exit_system_load_hyst > 0)
+			state->exit_system_load_hyst *= 100;
+
+		if (state->exit_system_load_thres > 0)
+			state->exit_system_load_thres *= 100;
+
 		if (state->enter_cpu_load_thres < 0 || state->enter_cpu_load_thres > 100)
 			continue;
 		else
 			state->enter_cpu_load_thres *= 100;
 
 		if (state->exit_cpu_load_thres < 0 || state->exit_cpu_load_thres > 100)
 			continue;
 		else
 			state->exit_cpu_load_thres *= 100;
 
 		if (state->enter_gfx_load_thres < 0 || state->enter_gfx_load_thres > 100)
 			continue;
 		else
 			state->enter_gfx_load_thres *= 100;
 
+		if (state->exit_gfx_load_thres > 0)
+			state->exit_gfx_load_thres *= 100;
+
 		state->valid = 1;

Temporary Workaround

Until fixed upstream, users configuring custom <State> blocks in XML can specify <ExitSystemLoadhysteresis>, <ExitSystemLoadThres>, or <ExitGFXLoadThres> directly in basis points (e.g., <ExitSystemLoadhysteresis>5500</ExitSystemLoadhysteresis> for 55%).

Acknowledgements

This bug was found by Google Gemini (Antigravity) due to me having problems with early exiting of the low-power mode, where I thought the system should stay in low-power mode much longer.
The bug report was generated by Google Gemini (Antigravity), confirmed and checked by me.
Tested with the latest code on main.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions