intel / intel/intel-lpmd

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

Open Beginner friendly
#125 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
C
Stars
223
Forks
32
Avg merge
1m
Merged PRs (30d)
1

Description

## Problem Description

When parsing XML `` definitions in `intel_lpmd`, system load thresholds (such as ``, ``, and ``) 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. `` (`state->exit_system_load_hyst`)
2. `` (`state->exit_system_load_thres`)
3. `` (`state->exit_gfx_load_thres`)

As a result:
- When a user configures `75`, `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`](file:///home/rmeissner/.git/intel-lpmd/src/lpmd_state_machine.c#L778-L797)
**Function:** `lpmd_build_config_states()`

```c
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`](file:///home/rmeissner/.git/intel-lpmd/src/lpmd_state_machine.c#L151-L157):

```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()`:

```diff
--- 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 `` blocks in XML can specify ``, ``, or `` directly in basis points (e.g., `5500` 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.

Contributor guide

Open the contributing guide

Research direction

Start in src/lpmd_state_machine.c, function lpmd_build_config_states(), and compare its scaling block with config_state_match(). Verify how the three exit fields are parsed and used, then ensure configured percentage values are converted to the same basis-point units as the entry thresholds. Done means the exit hysteresis, system-load, and GFX-load thresholds no longer have a unit mismatch.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
operating-systems
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.