From 2bfe71e8d8350cef295f1c55e8a44f7783f3c25b Mon Sep 17 00:00:00 2001 From: shadeyg56 <31134255+shadeyg56@users.noreply.github.com> Date: Wed, 1 Feb 2023 09:39:14 -0600 Subject: [PATCH] [Feature] Force use of powersave or performance governors (#476) * add the --force option and governor overrides * update README to reflect --force option * add --state option * update README with --state option * update README to expand on --force functionality * change name of remove() func and remove pickle upon --remove * check if daemon is not running when using --force and change --state * add links to README * change force section wording * add quotes to --force help * add override warning to stats file * hide --get-state flag * consolidate error messages and add daemon checks to --get-state and --stats * add quotes --- README.md | 13 +++++++- auto_cpufreq/core.py | 75 ++++++++++++++++++++++++++++++++------------ bin/auto-cpufreq | 36 ++++++++++++++------- 3 files changed, 91 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 37dcb51..2803131 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ auto-cpufreq is looking for [co-maintainers & open source developers to help sha * [auto-cpufreq modes and options](https://github.com/AdnanHodzic/auto-cpufreq/#auto-cpufreq-modes-and-options) * [monitor](https://github.com/AdnanHodzic/auto-cpufreq/#monitor) * [live](https://github.com/AdnanHodzic/auto-cpufreq/#live) + * [overriding governor](https://github.com/AdnanHodzic/auto-cpufreq/#overriding-governor) * [Install - auto-cpufreq daemon](https://github.com/AdnanHodzic/auto-cpufreq/#install---auto-cpufreq-daemon) * [Remove - auto-cpufreq daemon](https://github.com/AdnanHodzic/auto-cpufreq/#remove---auto-cpufreq-daemon) * [stats](https://github.com/AdnanHodzic/auto-cpufreq/#stats) @@ -207,7 +208,10 @@ auto-cpufreq should be run with with one of the following options: * [stats](https://github.com/AdnanHodzic/auto-cpufreq/#stats) - View live stats of CPU optimizations made by daemon -* config TEXT +* [force=TEXT](https://github.com/AdnanHodzic/auto-cpufreq/#overriding-governor) + - Force use of either the "powersave" or "performance" governor. Setting to "reset" goes back to normal mode + +* config=TEXT - Use config file at defined path * debug @@ -238,6 +242,13 @@ No changes are made to the system, and is solely made for demonstration purposes Necessary changes are temporarily made to the system which are lost with system reboot. This mode is made to evaluate what the system would behave with auto-cpufreq permanently running on the system. +### Overriding governor + +`sudo auto-cpufreq --force=governor` + +Force use of either "powersave" or "performance" governors. Setting to "reset" will go back to normal mode +Please note that any set override will persist even after reboot. + ### Install - auto-cpufreq daemon Necessary changes are made to the system for auto-cpufreq CPU optimization to persist across reboots. The daemon is deployed and then started as a systemd service. Changes are made automatically and live stats are generated for monitoring purposes. diff --git a/auto_cpufreq/core.py b/auto_cpufreq/core.py index b397d69..99a23dc 100644 --- a/auto_cpufreq/core.py +++ b/auto_cpufreq/core.py @@ -10,6 +10,7 @@ import psutil import distro import time import click +import pickle import warnings import configparser import pkg_resources @@ -57,6 +58,9 @@ performance_load_threshold = (50 * CPUS) / 100 auto_cpufreq_stats_path = None auto_cpufreq_stats_file = None +# track governor override +STORE = "/opt/auto-cpufreq/venv/override.pickle" + if os.getenv("PKG_MARKER") == "SNAP": auto_cpufreq_stats_path = Path("/var/snap/auto-cpufreq/current/auto-cpufreq.stats") else: @@ -71,7 +75,6 @@ def file_stats(): auto_cpufreq_stats_file = open(auto_cpufreq_stats_path, "w") sys.stdout = auto_cpufreq_stats_file - def get_config(config_file=""): if not hasattr(get_config, "config"): get_config.config = configparser.ConfigParser() @@ -82,6 +85,26 @@ def get_config(config_file=""): return get_config.config +def get_override(): + if os.path.isfile(STORE): + with open(STORE, "rb") as store: + return pickle.load(store) + else: + return "default" + +def set_override(override): + if override in ["powersave", "performance"]: + with open(STORE, "wb") as store: + pickle.dump(override, store) + print(f"Set governor override to {override}") + elif override == "reset": + if os.path.isfile(STORE): + os.remove(STORE) + print("Governor override removed") + elif override is not None: + print("Invalid option.\nUse force=performance, force=powersave, or force=reset") + + # get distro name try: @@ -314,14 +337,6 @@ def footer(l=79): print("\n" + "-" * l + "\n") -def daemon_not_found(): - print("\n" + "-" * 32 + " Daemon check " + "-" * 33 + "\n") - print( - "ERROR:\n\nDaemon not enabled, must run install first, i.e: \nsudo auto-cpufreq --install" - ) - footer() - - def deploy_complete_msg(): print("\n" + "-" * 17 + " auto-cpufreq daemon installed and running " + "-" * 17 + "\n") print("To view live stats, run:\nauto-cpufreq --stats") @@ -404,7 +419,7 @@ def deploy_daemon_performance(): # remove auto-cpufreq daemon -def remove(): +def remove_daemon(): # check if auto-cpufreq is installed if not os.path.exists("/usr/local/bin/auto-cpufreq-remove"): @@ -426,6 +441,10 @@ def remove(): # remove auto-cpufreq-remove os.remove("/usr/local/bin/auto-cpufreq-remove") + # delete override pickle if it exists + if os.path.exists(STORE): + os.remove(STORE) + # delete stats file if auto_cpufreq_stats_path.exists(): if auto_cpufreq_stats_file is not None: @@ -580,6 +599,8 @@ def set_powersave(): else: gov = get_avail_powersave() print(f'Setting to use: "{gov}" governor') + if get_override() != "default": + print("Warning: governor overwritten using `--force` flag.") run(f"cpufreqctl.auto-cpufreq --governor --set={gov}", shell=True) if ( Path("/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference").exists() @@ -786,6 +807,8 @@ def set_performance(): gov = get_avail_performance() print(f'Setting to use: "{gov}" governor') + if get_override() != "default": + print("Warning: governor overwritten using `--force` flag.") run( f"cpufreqctl.auto-cpufreq --governor --set={gov}", shell=True, @@ -997,7 +1020,12 @@ def set_autofreq(): print("\n" + "-" * 28 + " CPU frequency scaling " + "-" * 28 + "\n") # determine which governor should be used - if charging(): + override = get_override() + if override == "powersave": + set_powersave() + elif override == "performance": + set_performance() + elif charging(): print("Battery is: charging\n") set_performance() else: @@ -1149,20 +1177,12 @@ def sysinfo(): print("\nCPU fan speed:", psutil.sensors_fans()[current_fan][0].current, "RPM") -def no_stats_msg(): - print("\n" + "-" * 29 + " auto-cpufreq stats " + "-" * 30 + "\n") - print( - 'ERROR: auto-cpufreq stats are missing.\n\nMake sure to run: "auto-cpufreq --install" first' - ) - # read stats func def read_stats(): # read stats if os.path.isfile(auto_cpufreq_stats_path): call(["tail", "-n 50", "-f", str(auto_cpufreq_stats_path)], stderr=DEVNULL) - else: - no_stats_msg() footer() @@ -1187,12 +1207,27 @@ def daemon_running_msg(): ) footer() +def daemon_not_running_msg(): + print("\n" + "-" * 24 + " auto-cpufreq not running " + "-" * 30 + "\n") + print( + "ERROR: auto-cpufreq is not running in daemon mode.\n\nMake sure to run \"sudo auto-cpufreq --install\" first" + ) + footer() # check if auto-cpufreq --daemon is running -def running_daemon(): +def running_daemon_check(): if is_running("auto-cpufreq", "--daemon"): daemon_running_msg() exit(1) elif os.getenv("PKG_MARKER") == "SNAP" and dcheck == "enabled": daemon_running_msg() exit(1) + +# check if auto-cpufreq --daemon is not running +def not_running_daemon_check(): + if not is_running("auto-cpufreq", "--daemon"): + daemon_not_running_msg() + exit(1) + elif os.getenv("PKG_MARKER") == "SNAP" and dcheck == "disabled": + daemon_not_running_msg() + exit(1) \ No newline at end of file diff --git a/bin/auto-cpufreq b/bin/auto-cpufreq index 4273550..822f200 100755 --- a/bin/auto-cpufreq +++ b/bin/auto-cpufreq @@ -18,13 +18,13 @@ from auto_cpufreq.power_helper import * @click.command() @click.option("--monitor", is_flag=True, help="Monitor and see suggestions for CPU optimizations") @click.option("--live", is_flag=True, help="Monitor and make (temp.) suggested CPU optimizations") -@click.option( - "--install/--remove", - default=True, - help="Install/remove daemon for (permanent) automatic CPU optimizations", -) +@click.option("--install", is_flag=True, help="Install daemon for (permanent) automatic CPU optimizations") +@click.option("--remove", is_flag=True, help="Remove daemon for (permanent) automatic CPU optimizations") + @click.option("--install_performance", is_flag=True, help="Install daemon in \"performance\" mode, reference:\n https://bit.ly/3bjVZW1") @click.option("--stats", is_flag=True, help="View live stats of CPU optimizations made by daemon") +@click.option("--force", is_flag=False, help="Force use of either \"powersave\" or \"performance\" governors. Setting to \"reset\" will go back to normal mode") +@click.option("--get-state", is_flag=True, hidden=True) @click.option( "--config", is_flag=False, @@ -36,16 +36,23 @@ from auto_cpufreq.power_helper import * @click.option("--donate", is_flag=True, help="Support the project") @click.option("--log", is_flag=True, hidden=True) @click.option("--daemon", is_flag=True, hidden=True) -def main(config, daemon, debug, install, install_performance, live, log, monitor, stats, version, donate): +def main(config, daemon, debug, install, remove, install_performance, live, log, monitor, stats, version, donate, force, get_state): # display info if config file is used def config_info_dialog(): if get_config(config) and hasattr(get_config, "using_cfg_file"): print("\nUsing settings defined in " + config + " file") + # set governor override unless None or invalid + if force is not None: + not_running_daemon_check() + set_override(force) + if len(sys.argv) == 1: + print("\n" + "-" * 32 + " auto-cpufreq " + "-" * 33 + "\n") print("Automatic CPU speed & power optimizer for Linux") + print("\nExample usage:\nauto-cpufreq --monitor") print("\n-----\n") @@ -92,7 +99,7 @@ def main(config, daemon, debug, install, install_performance, live, log, monitor tlp_service_detect() while True: time.sleep(1) - running_daemon() + running_daemon_check() footer() gov_check() cpufreqctl() @@ -114,7 +121,7 @@ def main(config, daemon, debug, install, install_performance, live, log, monitor tlp_service_detect() while True: try: - running_daemon() + running_daemon_check() footer() gov_check() cpufreqctl() @@ -127,6 +134,7 @@ def main(config, daemon, debug, install, install_performance, live, log, monitor print("") sys.exit() elif stats: + not_running_daemon_check() config_info_dialog() print('\nNote: You can quit stats mode by pressing "ctrl+c"') if os.getenv("PKG_MARKER") == "SNAP": @@ -138,6 +146,10 @@ def main(config, daemon, debug, install, install_performance, live, log, monitor read_stats() elif log: deprecated_log_msg() + elif get_state: + not_running_daemon_check() + override = get_override() + print(override) elif debug: # ToDo: add status of GNOME Power Profile service status config_info_dialog() @@ -181,14 +193,14 @@ def main(config, daemon, debug, install, install_performance, live, log, monitor "Reference: https://github.com/AdnanHodzic/auto-cpufreq#configuring-auto-cpufreq\n") else: root_check() - running_daemon() + running_daemon_check() gov_check() deploy_daemon_performance() deploy_complete_msg() elif install: if os.getenv("PKG_MARKER") == "SNAP": root_check() - running_daemon() + running_daemon_check() gnome_power_detect_snap() tlp_service_detect_snap() bluetooth_notif_snap() @@ -198,7 +210,7 @@ def main(config, daemon, debug, install, install_performance, live, log, monitor deploy_complete_msg() else: root_check() - running_daemon() + running_daemon_check() gov_check() deploy_daemon() deploy_complete_msg() @@ -218,7 +230,7 @@ def main(config, daemon, debug, install, install_performance, live, log, monitor remove_complete_msg() else: root_check() - remove() + remove_daemon() remove_complete_msg()