From 05bf7f25041cbd801ef82d93b0db324929f7a30a Mon Sep 17 00:00:00 2001 From: Alexander Binzberger Date: Fri, 3 Aug 2018 23:52:19 +0200 Subject: [PATCH 1/5] adding cTDP configuration option --- etc/lenovo_fix.conf | 4 ++++ lenovo_fix.py | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/etc/lenovo_fix.conf b/etc/lenovo_fix.conf index 8077db8..94e5d80 100644 --- a/etc/lenovo_fix.conf +++ b/etc/lenovo_fix.conf @@ -15,6 +15,8 @@ PL2_Tdp_W: 44 PL2_Duration_S: 0.002 # Max allowed temperature before throttling Trip_Temp_C: 85 +# Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL) +cTDP: 0 ## Settings to apply while connected to AC power [AC] @@ -32,6 +34,8 @@ PL2_Duration_S: 0.002 Trip_Temp_C: 95 # Set HWP energy performance hints to 'performance' on high load (EXPERIMENTAL) HWP_Mode: False +# Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL) +cTDP: 0 [UNDERVOLT] # CPU core voltage offset (mV) diff --git a/lenovo_fix.py b/lenovo_fix.py index df0cc1a..4df04d5 100755 --- a/lenovo_fix.py +++ b/lenovo_fix.py @@ -33,6 +33,7 @@ VOLTAGE_PLANES = { } TRIP_TEMP_RANGE = (40, 97) +C_TDP_RANGE = (0, 2) power = {'source': None, 'method': 'polling'} @@ -137,6 +138,11 @@ def calc_reg_values(config): regs[power_source]['MSR_PKG_POWER_LIMIT'] = PL1 | (1 << 15) | (TW1 << 17) | (PL2 << 32) | (1 << 47) | ( TW2 << 49) + # cTDP + c_tdp_target_value = int(config.getfloat(power_source, 'cTDP')) + valid_c_tdp_target_value = valid_trip_temp = min(C_TDP_RANGE[1], max(C_TDP_RANGE[0], c_tdp_target_value)) + regs[power_source]['MSR_CONFIG_TDP_CONTROL'] = valid_c_tdp_target_value + return regs @@ -164,6 +170,10 @@ def power_thread(config, regs, exit_event): # set temperature trip point writemsr(0x1a2, regs[power['source']]['MSR_TEMPERATURE_TARGET']) + # set cTDP + # TODO read MSR 0xCE to check if the operation is possible + writemsr(0x64b, regs[power['source']]['MSR_CONFIG_TDP_CONTROL']) + # set PL1/2 on MSR writemsr(0x610, regs[power['source']]['MSR_PKG_POWER_LIMIT']) # set MCHBAR register to the same PL1/2 values From f684dd3ec4a63a4218738ffb927e43e782bdec50 Mon Sep 17 00:00:00 2001 From: DEvil0000 Date: Mon, 6 Aug 2018 09:51:50 +0200 Subject: [PATCH 2/5] don't crash on old config without the option --- lenovo_fix.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lenovo_fix.py b/lenovo_fix.py index 4df04d5..55ca96f 100755 --- a/lenovo_fix.py +++ b/lenovo_fix.py @@ -139,9 +139,12 @@ def calc_reg_values(config): TW2 << 49) # cTDP - c_tdp_target_value = int(config.getfloat(power_source, 'cTDP')) - valid_c_tdp_target_value = valid_trip_temp = min(C_TDP_RANGE[1], max(C_TDP_RANGE[0], c_tdp_target_value)) - regs[power_source]['MSR_CONFIG_TDP_CONTROL'] = valid_c_tdp_target_value + try: + c_tdp_target_value = int(config.getfloat(power_source, 'cTDP')) + valid_c_tdp_target_value = valid_trip_temp = min(C_TDP_RANGE[1], max(C_TDP_RANGE[0], c_tdp_target_value)) + regs[power_source]['MSR_CONFIG_TDP_CONTROL'] = valid_c_tdp_target_value + except configparser.NoOptionError: + pass return regs From 6b79d308696cde532220499e0c14189b3d69123c Mon Sep 17 00:00:00 2001 From: "A. Binzxxxxxx" Date: Mon, 6 Aug 2018 09:49:26 +0200 Subject: [PATCH 3/5] adding cTDP option to readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5cd43b0..6a9451e 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ I have found that under load my CPU was not always hitting max turbo frequency, I have run **[Geekbench 4](https://browser.geekbench.com/v4/cpu/8656840)** and now I can get a score of 5391/17265! On balance_performance I can reach only 4672/16129, so **15% improvement** in single core and 7% in multicore, not bad ;) +### setting cTDP (EXPERIMENTAL) +On a lot of modern CPUs from Intel one can configure the TDP up or down based on predefined profiles. This is what this option does. For a i7-8650U normal would be 15W, up profile is setting it to 25W and down to 10W. You can lookup the values of your CPU at the Intel product website. + ## Requirements A stripped down version of the python module `python-periphery` is now built-in and it is used for accessing the MCHBAR register by memory mapped I/O. You also need `dbus` and `gobject` python bindings for listening to dbus signals on resume from sleep/hibernate. From 7002abf3034004f89c81b4f11221950455dac7c3 Mon Sep 17 00:00:00 2001 From: DEvil0000 Date: Mon, 6 Aug 2018 11:52:04 +0200 Subject: [PATCH 4/5] add function to readmsr values --- lenovo_fix.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lenovo_fix.py b/lenovo_fix.py index 55ca96f..857ddf7 100755 --- a/lenovo_fix.py +++ b/lenovo_fix.py @@ -59,6 +59,33 @@ def writemsr(msr, val): else: raise e +# returns the value between from_bit and to_bit as unsigned long +def readmsr(msr, from_bit = 0, to_bit = 63): + if from_bit > to_bit: + print('wrong readmsr bit params') + sys.exit(1) + n = ['/dev/cpu/{:d}/msr'.format(x) for x in range(cpu_count())] + if not os.path.exists(n[0]): + try: + subprocess.check_call(('modprobe', 'msr')) + except subprocess.CalledProcessError: + print('[E] Unable to load the msr module.') + sys.exit(1) + try: + for c in n: + f = os.open(c, os.O_RDONLY) + os.lseek(f, msr, os.SEEK_SET) + val = struct.unpack('Q', os.read(f, 8))[0] + os.close(f) + extractor = int(''.join(["0"]*(63-to_bit) + ["1"]*(to_bit+1-from_bit) + ["0"]*from_bit), 2) + return (val & extractor) >> from_bit + except (IOError, OSError) as e: + if e.errno == EPERM or e.errno == EACCES: + print('[E] Unable to read from MSR. Try to disable Secure Boot.') + sys.exit(1) + else: + raise e + def is_on_battery(): with open(SYSFS_POWER_PATH) as f: From ce4f836a20f7a915bcebb63beb28306692bd76ec Mon Sep 17 00:00:00 2001 From: DEvil0000 Date: Tue, 7 Aug 2018 08:42:50 +0200 Subject: [PATCH 5/5] add some checks and read msrs --- lenovo_fix.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lenovo_fix.py b/lenovo_fix.py index 857ddf7..c925fce 100755 --- a/lenovo_fix.py +++ b/lenovo_fix.py @@ -93,9 +93,11 @@ def is_on_battery(): def calc_time_window_vars(t): + # 0.000977 is the time unit of this CPU + time_unit = 1.0/2**readmsr(0x606, 16, 19) for Y in range(2**5): for Z in range(2**2): - if t <= (2**Y) * (1. + Z / 4.) * 0.000977: + if t <= (2**Y) * (1. + Z / 4.) * time_unit: return (Y, Z) raise ValueError('Unable to find a good combination!') @@ -154,11 +156,12 @@ def calc_reg_values(config): regs[power_source]['MSR_TEMPERATURE_TARGET'] = trip_offset << 24 # 0.125 is the power unit of this CPU - PL1 = int(round(config.getfloat(power_source, 'PL1_Tdp_W') / 0.125)) + power_unit = 1.0/2**readmsr(0x606, 0, 3) + PL1 = int(round(config.getfloat(power_source, 'PL1_Tdp_W') / power_unit)) Y, Z = calc_time_window_vars(config.getfloat(power_source, 'PL1_Duration_s')) TW1 = Y | (Z << 5) - PL2 = int(round(config.getfloat(power_source, 'PL2_Tdp_W') / 0.125)) + PL2 = int(round(config.getfloat(power_source, 'PL2_Tdp_W') / power_unit)) Y, Z = calc_time_window_vars(config.getfloat(power_source, 'PL2_Duration_s')) TW2 = Y | (Z << 5) @@ -168,7 +171,7 @@ def calc_reg_values(config): # cTDP try: c_tdp_target_value = int(config.getfloat(power_source, 'cTDP')) - valid_c_tdp_target_value = valid_trip_temp = min(C_TDP_RANGE[1], max(C_TDP_RANGE[0], c_tdp_target_value)) + valid_c_tdp_target_value = min(C_TDP_RANGE[1], max(C_TDP_RANGE[0], c_tdp_target_value)) regs[power_source]['MSR_CONFIG_TDP_CONTROL'] = valid_c_tdp_target_value except configparser.NoOptionError: pass @@ -198,11 +201,16 @@ def power_thread(config, regs, exit_event): power['source'] = 'BATTERY' if is_on_battery() else 'AC' # set temperature trip point - writemsr(0x1a2, regs[power['source']]['MSR_TEMPERATURE_TARGET']) + if readmsr(0xce, 30, 30) != 1: + print("setting temperature target is not supported by this CPU") + else: + writemsr(0x1a2, regs[power['source']]['MSR_TEMPERATURE_TARGET']) # set cTDP - # TODO read MSR 0xCE to check if the operation is possible - writemsr(0x64b, regs[power['source']]['MSR_CONFIG_TDP_CONTROL']) + if readmsr(0xce, 33, 34) < 2: + print("cTDP setting not supported by this cpu") + else: + writemsr(0x64b, regs[power['source']]['MSR_CONFIG_TDP_CONTROL']) # set PL1/2 on MSR writemsr(0x610, regs[power['source']]['MSR_PKG_POWER_LIMIT'])