Skip to content

Commit cf835b0

Browse files
zhang-ruirafaeljw
authored andcommitted
powercap: intel_rapl: Fix handling for large time window
When setting the power limit time window, software updates the 'y' bits and 'f' bits in the power limit register, and the value hardware takes follows the formula below Time window = 2 ^ y * (1 + f / 4) * Time_Unit When handling large time window input from userspace, using left shifting breaks in two cases: 1. when ilog2(value) is bigger than 31, in expression "1 << y", left shifting by more than 31 bits has undefined behavior. This breaks 'y'. For example, on an Alderlake platform, "1 << 32" returns 1. 2. when ilog2(value) equals 31, "1 << 31" returns negative value because '1' is recognized as signed int. And this breaks 'f'. Given that 'y' has 5 bits and hardware can never take a value larger than 31, fix the first problem by clamp the time window to the maximum possible value that the hardware can take. Fix the second problem by using unsigned bit left shift. Note that hardware has its own maximum time window limitation, which may be lower than the time window value retrieved from the power limit register. When this happens, hardware clamps the input to its maximum time window limitation. That is why a software clamp is preferred to handle the problem on hand. Signed-off-by: Zhang Rui <[email protected]> [ rjw: Adjusted the comment added by this change ] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent c7cd6f0 commit cf835b0

File tree

1 file changed

+9
-1
lines changed

1 file changed

+9
-1
lines changed

drivers/powercap/intel_rapl_common.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,15 @@ static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
999999

10001000
do_div(value, rp->time_unit);
10011001
y = ilog2(value);
1002-
f = div64_u64(4 * (value - (1 << y)), 1 << y);
1002+
1003+
/*
1004+
* The target hardware field is 7 bits wide, so return all ones
1005+
* if the exponent is too large.
1006+
*/
1007+
if (y > 0x1f)
1008+
return 0x7f;
1009+
1010+
f = div64_u64(4 * (value - (1ULL << y)), 1ULL << y);
10031011
value = (y & 0x1f) | ((f & 0x3) << 5);
10041012
}
10051013
return value;

0 commit comments

Comments
 (0)