Ver código fonte

Replace upp with amd-smi for TDP control

- Use amd-smi for TDP limit detection and setting
- Read temperatures from sysfs temp*_input sensors
- Detect GPU device names from sysfs
- Remove hardcoded 250W TDP, use dynamic detection
- Remove --config flag from systemd service
Глухов Кирилл Викторович 1 mês atrás
pai
commit
1ddb5445f4
4 arquivos alterados com 27 adições e 46 exclusões
  1. 8 11
      README.md
  2. 17 33
      amd-gpu-temp-control.js
  3. 1 1
      amd-gpu-temp-control.service
  4. 1 1
      install.sh

+ 8 - 11
README.md

@@ -17,18 +17,17 @@ AMD Radeon discrete GPUs with PowerPlay table support:
 
 ## How it works
 
-1. Discovers actual max TDP via `upp get SmallPowerLimit1` at startup (fallback: 120W)
-2. Reads temperature from all `temp*_input` sensors under `/sys/class/drm/card{N}/device/hwmon/`
-3. Compares against configured min/max temperature thresholds
-4. When temperature exceeds the range, linearly scales TDP down via `upp` PowerPlay table modifications
-5. When temperature drops below the range, restores TDP to maximum
-6. Changes are applied in configured step increments to avoid abrupt power shifts
+1. Reads temperature from all `temp*_input` sensors under `/sys/class/drm/card{N}/device/hwmon/`
+2. Compares against configured min/max temperature thresholds
+3. When temperature exceeds the range, scales TDP down via `amd-smi`
+4. When temperature drops below the range, restores TDP to maximum
+5. Changes are applied in configured step increments to avoid abrupt power shifts
 
 ## Requirements
 
 - **Node.js** (v14+, for `fs`/`child_process` only — no npm packages)
-- **upp** — Radeon PowerPlay table tool (`pipx install upp` or build from source)
-- **root** — required for writing PowerPlay tables
+- **amd-smi** — AMD Software (pipx install amdgpu-tools)
+- **root** — required for setting power limits
 
 ## Install
 
@@ -57,8 +56,7 @@ Default config path: `/etc/amd-gpu-temp-control.json`
   "minTdpPercent": 10,
   "maxTdpPercent": 100,
   "tdpStepPercent": 10,
-  "checkIntervalMs": 2000,
-  "uppPath": "upp"
+  "checkIntervalMs": 2000
 }
 ```
 
@@ -70,7 +68,6 @@ Default config path: `/etc/amd-gpu-temp-control.json`
 | `maxTdpPercent`    | 100     | Maximum TDP percentage (full power)                      |
 | `tdpStepPercent`   | 10      | Maximum TDP change per cycle (%)                         |
 | `checkIntervalMs`  | 2000    | Temperature read interval (ms)                           |
-| `uppPath`          | `upp`   | Path to the `upp` binary                                 |
 
 ## Usage
 

+ 17 - 33
amd-gpu-temp-control.js

@@ -11,7 +11,6 @@ const DEFAULT_MIN_TDP_PERCENT = 10;
 const DEFAULT_MAX_TDP_PERCENT = 100;
 const DEFAULT_TDP_STEP_PERCENT = 10;
 const DEFAULT_CHECK_INTERVAL_MS = 2000;
-const DEFAULT_UPP_PATH = 'upp';
 
 function loadConfig(configPath) {
   if (!fs.existsSync(configPath)) {
@@ -29,7 +28,6 @@ function mergeConfig(config) {
     maxTdpPercent: config.maxTdpPercent ?? DEFAULT_MAX_TDP_PERCENT,
     tdpStepPercent: config.tdpStepPercent ?? DEFAULT_TDP_STEP_PERCENT,
     checkIntervalMs: config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS,
-    uppPath: config.uppPath ?? DEFAULT_UPP_PATH,
   };
 }
 
@@ -129,32 +127,20 @@ function getDeviceName(cardNumber) {
   return `card${cardNumber}`;
 }
 
-function getCardPath(cardNumber) {
-  return `/sys/class/drm/card${cardNumber}/device/pp_table`;
-}
-
-function setTdpForCard(cardNumber, power, config) {
-  const cardPath = getCardPath(cardNumber);
-  const cmd = `${config.uppPath} -p ${cardPath} set --write ` +
-    `SmallPowerLimit1=${power} ` +
-    `SmallPowerLimit2=${power} ` +
-    `BoostPowerLimit=${power} ` +
-    `smcPPTable/SocketPowerLimitAc0=${power} ` +
-    `smcPPTable/SocketPowerLimitDc=${power}`;
-  execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
-}
-
-function getMaxTdp(cardNumber, config) {
+function getTdpLimit(cardNumber) {
   try {
-    const cardPath = getCardPath(cardNumber);
-    const out = execSync(
-      `${config.uppPath} -p ${cardPath} get SmallPowerLimit1`,
-      { encoding: 'utf-8', stdio: 'pipe' }
-    ).trim();
-    const match = out.match(/(\d+(?:\.\d+)?)/);
-    if (match) return parseFloat(match[1]);
+    const out = execSync(`amd-smi static --json -g ${cardNumber}`, { encoding: 'utf-8', stdio: 'pipe' }).trim();
+    const data = JSON.parse(out);
+    const gpuData = data.gpu_data[0];
+    if (gpuData && gpuData.limit && gpuData.limit.ppt0 && gpuData.limit.ppt0.max_power_limit) {
+      return gpuData.limit.ppt0.max_power_limit.value;
+    }
   } catch { /* ignore */ }
-  return 120;
+  return 100;
+}
+
+function setTdpForCard(cardNumber, power) {
+  execSync(`amd-smi set -g ${cardNumber} -o ppt0 ${power}`, { encoding: 'utf-8', stdio: 'pipe' });
 }
 
 function parseArgs(argv) {
@@ -198,7 +184,7 @@ function log(message) {
   console.log(`[${timestamp}] ${message}`);
 }
 
-async function main() {
+function main() {
   const configPath = parseArgs(process.argv);
   const rawConfig = loadConfig(configPath);
   const config = mergeConfig(rawConfig || {});
@@ -210,7 +196,6 @@ async function main() {
   log(`TDP range: ${config.minTdpPercent}% - ${config.maxTdpPercent}%`);
   log(`TDP step: ${config.tdpStepPercent}%`);
   log(`Check interval: ${config.checkIntervalMs}ms`);
-  log(`Upp path: ${config.uppPath}`);
   log(`Config: ${configPath}`);
   log(`Monitoring ${cardIndices.length} GPU(s)`);
   log('Press Ctrl+C to stop');
@@ -219,7 +204,7 @@ async function main() {
   const gpuStates = cardIndices.map(cardNumber => ({
     cardNumber,
     deviceName: getDeviceName(cardNumber),
-    maxTdp: getMaxTdp(cardNumber, config),
+    maxTdp: getTdpLimit(cardNumber),
     currentTdpPercent: null,
   }));
 
@@ -274,10 +259,9 @@ async function main() {
           }
 
           if (shouldApply) {
-            const clampedPercent = Math.max(config.minTdpPercent, Math.min(config.maxTdpPercent, gpuState.currentTdpPercent));
-            const power = wattsFromPercent(clampedPercent, gpuState.maxTdp);
-            setTdpForCard(gpuState.cardNumber, power, config);
-            log(`GPU ${gpuState.cardNumber} [${gpuState.deviceName}]: ${temp.toFixed(1)}°C | TDP: ${clampedPercent}% (${power}W)`);
+            const power = wattsFromPercent(gpuState.currentTdpPercent, gpuState.maxTdp);
+            setTdpForCard(gpuState.cardNumber, power);
+            log(`GPU ${gpuState.cardNumber} [${gpuState.deviceName}]: ${temp.toFixed(1)}°C | TDP: ${gpuState.currentTdpPercent}% (${power}W)`);
           }
         } else if (gpuState.currentTdpPercent !== null && iteration % 10 === 0) {
           const currentWatts = wattsFromPercent(gpuState.currentTdpPercent, gpuState.maxTdp);

+ 1 - 1
amd-gpu-temp-control.service

@@ -4,7 +4,7 @@ After=network.target
 
 [Service]
 Type=simple
-ExecStart=/usr/bin/env node /usr/local/bin/amd-gpu-temp-control --config /etc/amd-gpu-temp-control.json
+ExecStart=/usr/bin/env node /usr/local/bin/amd-gpu-temp-control
 Restart=on-failure
 RestartSec=5
 StandardOutput=journal

+ 1 - 1
install.sh

@@ -12,7 +12,7 @@ install() {
   [[ -f "$TPL" ]] || { echo "Missing $NAME.service template"; exit 1; }
   [[ -f "$SRC" ]] || { echo "Missing $NAME.js"; exit 1; }
   command -v node &>/dev/null || { echo "node not found"; exit 1; }
-  command -v upp &>/dev/null || { echo "upp not found in PATH"; exit 1; }
+  command -v amd-smi &>/dev/null || { echo "amd-smi not found in PATH"; exit 1; }
 
   cp "$SRC" "$BINARY"
   chmod +x "$BINARY"