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():
<ExitSystemLoadhysteresis> (state->exit_system_load_hyst)
<ExitSystemLoadThres> (state->exit_system_load_thres)
<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.
Problem Description
When parsing XML
<State>definitions inintel_lpmd, system load thresholds (such as<EntrySystemLoadThres>,<EnterCPULoadThres>, and<EnterGFXLoadThres>) are parsed as integer percentages (e.g.20for 20%) and then scaled by100to convert them into internal basis points (2000for 20.00%) inlpmd_build_config_states().However, all three EXIT threshold tags are missing from the
*= 100scaling loop inlpmd_build_config_states():<ExitSystemLoadhysteresis>(state->exit_system_load_hyst)<ExitSystemLoadThres>(state->exit_system_load_thres)<ExitGFXLoadThres>(state->exit_gfx_load_thres)As a result:
<ExitSystemLoadhysteresis>75</ExitSystemLoadhysteresis>,entry_system_load_thresis scaled to2000(20.00%), butexit_system_load_hystremains75(0.75%).config_state_match(),(state->entry_system_load_thres + state->exit_system_load_hyst)evaluates to2000 + 75 = 2075(20.75%) instead of the intended2000 + 7500 = 9500(95.00%).intel_lpmdto 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.cFunction:
lpmd_build_config_states()Noticeably missing from the scaling block:
state->exit_system_load_hyststate->exit_system_load_thresstate->exit_gfx_load_thres(Note:
exit_cpu_load_threson line 791 WAS included, butexit_system_load_hyst,exit_system_load_thres, andexit_gfx_load_threswere omitted).Comparison with Matching Logic
In
src/lpmd_state_machine.c:Because
bsysandentry_system_load_thresare scaled to basis points (10000= 100%), adding an unscaledexit_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():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.