From 0b0921baadfa30d54ef8a614ed76250e66cfbe7f Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Mon, 30 Dec 2024 23:35:19 +0200 Subject: [PATCH] doc: add configuration docs --- API.md | 50 +----- README.md | 4 +- docs/API.md | 49 ++++++ docs/CONFIG.md | 155 ++++++++++++++++++ lact-daemon/Cargo.toml | 2 +- lact-daemon/src/config.rs | 18 ++ ...lact_daemon__config__tests__parse_doc.snap | 54 ++++++ lact/Cargo.toml | 2 +- 8 files changed, 281 insertions(+), 53 deletions(-) mode change 100644 => 120000 API.md create mode 100644 docs/API.md create mode 100644 docs/CONFIG.md create mode 100644 lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap diff --git a/API.md b/API.md deleted file mode 100644 index d7c05467..00000000 --- a/API.md +++ /dev/null @@ -1,49 +0,0 @@ -# Description - -The LACT Daemon exposes a JSON API over a unix socket or TCP, available on `/var/run/lactd.sock` or an arbitrary TCP port. You can configure who has access to the unix socket in `/etc/lact/config.yaml` in the `daemon.admin_groups` field. The TCP listener is disabled by default for security reasons, see [this README section](./README.md#remote-management) for how to enable it. - -The API expects newline-separated JSON objects, and returns a JSON object for every request. - -The general format of requests looks like: -``` -{"command": "command_name", "args": {}} -``` -Note that the type of `args` depends on the specific request, and may be ommited in some cases. - -The response looks like this: -``` -{"status": "ok|error", "data": {}} -``` -Same as `args` in requests, `data` can be of a different type and may not be present depending on the specific request. - -You can try sending commands to socket interactively with `ncat`: -``` -echo '{"command": "list_devices"}' | ncat -U /run/lactd.sock -``` -Example response: -``` -{"status":"ok","data":[{"id":"1002:687F-1043:0555-0000:0b:00.0","name":"Vega 10 XL/XT [Radeon RX Vega 56/64]"}]} -``` - -Here's an example of calling the API with arguments to change a profile: -``` -echo '{"command": "set_profile", "args": {"name":"name-of-the-profile"}}' | ncat -U /run/lactd.sock -``` -In this code, `name-of-the-profile` should be replaced with the name of a profile that you've already created in LACT. - - -# Commands - -For the full list of available commands and responses, you can look at the source code of the schema: [requests](lact-schema/src/request.rs), [the basic response structure](lact-schema/src/response.rs) and [all possible types](lact-schema/src/lib.rs). - -It should also be fairly easy to figure out the API by trial and error, as the error message are quite verbose: - -``` -echo '{"command": "test"}' | ncat -U /run/lactd.sock - -{"status":"error","data":"Failed to deserialize request: unknown variant `test`, expected one of `ping`, `list_devices`, `system_info`, `device_info`, `device_stats`, `device_clocks_info`, `set_fan_control`, `set_power_cap`, `set_performance_level`, `set_clocks_value` at line 1 column 18"} -``` - -# Rust - -If you want to connect to the socket from a Rust program, you can simply import either the `lact-client` or `lact-schema` (if you want to write a custom client) crates from this repository. \ No newline at end of file diff --git a/API.md b/API.md new file mode 120000 index 00000000..1d0e0463 --- /dev/null +++ b/API.md @@ -0,0 +1 @@ +docs/API.md \ No newline at end of file diff --git a/README.md b/README.md index 787c7b8b..f8ca2235 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Current features: - Viewing information about the GPU - Power and thermals monitoring, power limit configuration - Fan curve control -- Overclocking (GPU/VRAM clockspeed and voltage, currently AMD only) +- Overclocking (GPU/VRAM clockspeed and voltage) - Power states configuration (AMD only) Both AMD and Nvidia functionality works on X11, Wayland or even headless sessions. @@ -75,7 +75,7 @@ However the following table shows what functionality can be expected for a given | Vega | Supported | Supported | Supported | Supported | | | RDNA1 (RX 5000) | Supported | Supported | Supported | Supported | | | RDNA2 (RX 6000) | Supported | Supported | Supported | Supported | | -| RDNA3 (RX 7000) | Supported | Limited | Supported | Limited | Fan zero RPM mode is enabled by default even with a custom fan curve, and requires kernel 6.13 (`linux-next` when writing this) to be disabled. The power cap is sometimes reported lower than it should be. See [#255](https://github.com/ilya-zlobintsev/LACT/issues/255) for more info. | +| RDNA3 (RX 7000) | Supported | Limited | Supported | Limited | Fan zero RPM mode is enabled by default even with a custom fan curve, and requires kernel 6.13 to be disabled. The power cap is sometimes reported lower than it should be. See [#255](https://github.com/ilya-zlobintsev/LACT/issues/255) for more info. | GPUs not listed here will still work, but might not have full functionality available. Monitoring/system info will be available everywhere. Integrated GPUs might also only have basic configuration available. diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 00000000..d7c05467 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,49 @@ +# Description + +The LACT Daemon exposes a JSON API over a unix socket or TCP, available on `/var/run/lactd.sock` or an arbitrary TCP port. You can configure who has access to the unix socket in `/etc/lact/config.yaml` in the `daemon.admin_groups` field. The TCP listener is disabled by default for security reasons, see [this README section](./README.md#remote-management) for how to enable it. + +The API expects newline-separated JSON objects, and returns a JSON object for every request. + +The general format of requests looks like: +``` +{"command": "command_name", "args": {}} +``` +Note that the type of `args` depends on the specific request, and may be ommited in some cases. + +The response looks like this: +``` +{"status": "ok|error", "data": {}} +``` +Same as `args` in requests, `data` can be of a different type and may not be present depending on the specific request. + +You can try sending commands to socket interactively with `ncat`: +``` +echo '{"command": "list_devices"}' | ncat -U /run/lactd.sock +``` +Example response: +``` +{"status":"ok","data":[{"id":"1002:687F-1043:0555-0000:0b:00.0","name":"Vega 10 XL/XT [Radeon RX Vega 56/64]"}]} +``` + +Here's an example of calling the API with arguments to change a profile: +``` +echo '{"command": "set_profile", "args": {"name":"name-of-the-profile"}}' | ncat -U /run/lactd.sock +``` +In this code, `name-of-the-profile` should be replaced with the name of a profile that you've already created in LACT. + + +# Commands + +For the full list of available commands and responses, you can look at the source code of the schema: [requests](lact-schema/src/request.rs), [the basic response structure](lact-schema/src/response.rs) and [all possible types](lact-schema/src/lib.rs). + +It should also be fairly easy to figure out the API by trial and error, as the error message are quite verbose: + +``` +echo '{"command": "test"}' | ncat -U /run/lactd.sock + +{"status":"error","data":"Failed to deserialize request: unknown variant `test`, expected one of `ping`, `list_devices`, `system_info`, `device_info`, `device_stats`, `device_clocks_info`, `set_fan_control`, `set_power_cap`, `set_performance_level`, `set_clocks_value` at line 1 column 18"} +``` + +# Rust + +If you want to connect to the socket from a Rust program, you can simply import either the `lact-client` or `lact-schema` (if you want to write a custom client) crates from this repository. \ No newline at end of file diff --git a/docs/CONFIG.md b/docs/CONFIG.md new file mode 100644 index 00000000..16ae81ad --- /dev/null +++ b/docs/CONFIG.md @@ -0,0 +1,155 @@ +# Configuration + +The LACT config file is located in `/etc/lact/config.yaml`, and contains all of the GPU settings that are typically edited in the GUI, as well as a few settings specifying the behaviour of the daemon. +LACT listens for config file changes and reloads all GPU settings automatically, but daemon-related settings such as the logging level or permissions require a service restart (`systemctl restart lactd`). + +Full config file with all possible options: +```yaml +# WARNING: this is only an example of each possible setting. DO NOT COPY THIS CONFIG AS IS. +# Many options don't make sense to be used together, and depend on your hardware. +daemon: + # The logging level of the daemon. + # Possible values: `error`, `warn`, `info` (default), `debug`, `trace` + log_level: info + # User groups who should have access to the daemon. + # WARNING: only the first group from this list that is found on the system is used! + # This is made a list and not a single value to allow this config to work across + # different distros, which might have different groups for an "admin" user. + admin_groups: + - wheel + - sudo + # If set to `true`, this setting makes the LACT daemon not reset + # GPU clocks when changing other settings or when turning off the daemon. + # Can be used to work around a few very specific issues with + # some settings not applying on AMD GPUs. + disable_clocks_cleanup: false + # Daemon's TCP listening address. Not specified by default. + # By default TCP access is disabled, and only a unix socket is present. + # Specifying this option enables the TCP listener. + tcp_listen_address: 127.0.0.1:12853 + +# Period in seconds for how long settings should wait to be confirmed. +# Most GPU setting change commands require a confirmation command to be used +# in order to save these settings to the config. +# If a confirm command is not issued within the configured period (default: 5 seconds) +# the setting will be reverted. +apply_settings_timer: 5 + +# The main GPU configuration map, containing the list of GPUs and their settings. +gpus: + # A GPU config entry. This is the ID of the GPU. + # The ID is formed with a combination of a PCI device id, + # PCI subsystem id and PCI slot name to uniquely identify + # each GPU in the system, even if there are multiple of the same model. + + # You can discover the id of your GPU by either: + # - Changing a setting in the UI, so it's written to the config + # - Using `lact cli list-gpus` + 1002:687F-1043:0555-0000:0b:00.0: + # Whether the daemon should touch fan control settings at all. + # Setting this to `true` requires the `fan_control_settings` field to be present as well. + fan_control_enabled: true + fan_control_settings: + # Fan control mode. Can be either `curve` or `static` + mode: curve + # Static fan speed from 0 to 1. Used when `mode` is `static` + static_speed: 1.0 + # The temperature sensor name to be used with a custom fan curve. + # This can be used to base the fan curve off the`junction` (hotspot) + # temperature instead of the default overall ("edge") tempreature. + # Applicable on most Vega and newer AMD GPUs. + temperature_key: edge + # Interval in milliseconds for how often the GPU temperature should be checked + # when adjusting the fan curve. + interval_ms: 500 + # Custom fan curve used with `mode` set to `curve`. + # The format of the map is temperature to fan speed from 0 to 1. + # Note: on RDNA3+ AMD GPUs this must have 5 entries. + curve: + 40: 0.2 + 50: 0.35 + 60: 0.5 + 70: 0.75 + 80: 1.0 + # Hysteresis setting: when spinning down fans after a temperature drop, + # the target speed needs to be lower for at least this many milliseconds + # for the fan to actually slow down. + # This lets you avoid fan speed jumping around during short drops of load + # (e.g. loading screen in a game). + spindown_delay_ms: 0 + # Hysteresis setting: the minimum temperature change in degrees + # to affect the fan speed. Also used to avoid rapid fan speed changes + # when the temperature only changes e.g. 1 degree. + change_threshold: 0 + # Power management firmware options. Specific to RDNA3+ AMD GPUs. + # Most of these settings are only applied when not using a custom fan curve. + pmfw_options: + # This setting adjusts the PMFW’s behavior about the maximum speed in RPM the fan can spin. + acoustic_limit: 3200 + # This setting adjusts the PMFW’s behavior about the maximum speed in RPM the fan can spin + # when the temperature is not greater than target temperature. + acoustic_target: 1450 + # The minimum speed in RPM that the fan can spin at. + minimum_pwm: 15 + # Target temperature for the GPU in degrees. + # Paring with the acoustic_target setting, they define the maximum speed in RPM + # the fan can spin when the temperature is not greater than target temperature. + target_temperature: 83 + # When set to `true`, allows the fan to be turned turned off when below the + # `zero_rpm_threshold` temperature value. + zero_rpm: true + # Temperature in degrees below which the fan should be turned off when `zero_rpm` is set to true. + zero_rpm_threshold: 50 + # Power limit in watts. + power_cap: 320.0 + # Performance level option for AMD GPUs. + # Can be `auto`, `low`, `high` or `manual`. + performance_level: auto + # Index of an AMD power profile mode. + # Setting this requires `performance_level` to be set to `manual`. + power_profile_mode_index: 0 + # Custom heuristic values when using the custom AMD power profile mode. + # TODO + custom_power_profile_mode_hueristics: [] + # TODO + power_states: {} + # Minimum GPU clockspeed in MHz. + min_core_clock: 300 + # Minimum VRAM clockspeed in MHz. + min_memory_clock: 500 + # Minimum GPU voltage in mV. + min_voltage: 900 + # Maximum GPU clockspeed in MHz. + max_core_clock: 1630 + # Maximum VRAM clockspeed in MHz. + max_memory_clock: 800 + # Maximum GPU voltage in mV. + max_voltage: 1200 + # Voltage offset value in mV for RDNA and newer AMD GPUs. + voltage_offset: 0 + +# Settings profiles +profiles: + # Name of the profile + vkcube: + # GPU settings in this profile. + # It is the same config format that is used for the top-level `gpus` option. + gpus: {} + # Profile activation rule for when this profile shoule be activated + # when using automatic profile switching. + rule: + # Type of the rule. Can be either `process or `gamemode`. + type: process + # Process filter. This is not required when using the gamemode rule type. + filter: + # Name of the process. + name: vkcube + # Process arguments. Not required. + args: --my-arg + +# Current profile to be used. Does not have effect when `auto_switch_profiles` is used. +# Omit this option or set to `null` to use the default profile (settings in the top-level `gpus` entry). +current_profile: vkcube +# If profiles should be switched between automatically based on their configured rules. +auto_switch_profiles: true +``` \ No newline at end of file diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index c6cee722..c4001b0a 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -49,7 +49,7 @@ copes = { git = "https://gitlab.com/corectrl/copes" } divan = { workspace = true } pretty_assertions = { workspace = true } lact-daemon = { path = ".", features = ["bench"] } -insta = { version = "1.41.1", features = ["json"] } +insta = { version = "1.41.1", features = ["json", "yaml"] } [[bench]] name = "daemon" diff --git a/lact-daemon/src/config.rs b/lact-daemon/src/config.rs index 18d8d480..d39e0df2 100644 --- a/lact-daemon/src/config.rs +++ b/lact-daemon/src/config.rs @@ -364,6 +364,7 @@ fn default_apply_settings_timer() -> u64 { mod tests { use super::{ClocksConfiguration, Config, Daemon, FanControlSettings, Gpu}; use crate::server::gpu_controller::fan_control::FanCurve; + use insta::assert_yaml_snapshot; use lact_schema::{FanControlMode, PmfwOptions}; use std::collections::HashMap; @@ -395,6 +396,23 @@ mod tests { assert_eq!(config, deserialized_config); } + #[test] + fn parse_doc() { + let doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../docs/CONFIG.md")); + let example_config_start = doc + .find("```yaml") + .expect("Could not find example config start") + + 7; + let example_config_end = doc[example_config_start..] + .find("```") + .expect("Could not find example config end") + + example_config_start; + let example_config = &doc[example_config_start..example_config_end]; + + let deserialized_config: Config = serde_yaml::from_str(&example_config).unwrap(); + assert_yaml_snapshot!(deserialized_config); + } + #[test] fn clocks_configuration_applied() { let mut gpu = Gpu { diff --git a/lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap b/lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap new file mode 100644 index 00000000..0f241257 --- /dev/null +++ b/lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap @@ -0,0 +1,54 @@ +--- +source: lact-daemon/src/config.rs +expression: deserialized_config +--- +daemon: + log_level: info + admin_groups: + - wheel + - sudo + disable_clocks_cleanup: false + tcp_listen_address: "127.0.0.1:12853" +apply_settings_timer: 5 +gpus: + "1002:687F-1043:0555-0000:0b:00.0": + fan_control_enabled: true + fan_control_settings: + mode: curve + static_speed: 1 + temperature_key: edge + interval_ms: 500 + curve: + 40: 0.2 + 50: 0.35 + 60: 0.5 + 70: 0.75 + 80: 1 + spindown_delay_ms: 0 + change_threshold: 0 + pmfw_options: + acoustic_limit: 3200 + acoustic_target: 1450 + minimum_pwm: 15 + target_temperature: 83 + zero_rpm: true + zero_rpm_threshold: 50 + power_cap: 320 + performance_level: auto + min_core_clock: 300 + min_memory_clock: 500 + min_voltage: 900 + max_core_clock: 1630 + max_memory_clock: 800 + max_voltage: 1200 + voltage_offset: 0 + power_profile_mode_index: 0 +profiles: + vkcube: + rule: + type: process + filter: + name: vkcube + args: "--my-arg" +current_profile: vkcube +auto_switch_profiles: true diff --git a/lact/Cargo.toml b/lact/Cargo.toml index aef63ee8..710d285a 100644 --- a/lact/Cargo.toml +++ b/lact/Cargo.toml @@ -8,7 +8,7 @@ default = ["lact-gui"] adw = ["lact-gui/adw"] [dependencies] -lact-daemon = { path = "../lact-daemon", default-features = false } +lact-daemon = { path = "../lact-daemon" } lact-schema = { path = "../lact-schema", features = ["args"] } lact-cli = { path = "../lact-cli" } lact-gui = { path = "../lact-gui", optional = true }