From 17dacd896fb5c297dc9721aeaf8e0a8b5597ad96 Mon Sep 17 00:00:00 2001 From: seo Date: Fri, 31 Jul 2026 06:11:36 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8B=9C=EB=86=80=EB=A1=9C=EC=A7=80=20?= =?UTF-8?q?=ED=8C=AC=20=EA=B0=80=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 + bin/synology_fan_guard.sh | 113 +++++++++++++++++++++++++++++ systemd/synology-fan-guard.service | 12 +++ 3 files changed, 129 insertions(+) create mode 100755 bin/synology_fan_guard.sh create mode 100644 systemd/synology-fan-guard.service diff --git a/README.md b/README.md index 2527d79..010636e 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,10 @@ Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를 커널 패닉이나 강제 재부팅 분석을 위해 `systemd-journald`는 `/var/log/journal`에 영구 저장합니다. `dtoverlay=ramoops` 기반 pstore는 부팅 직후 `control-pstore-collect.service`가 `/var/log/pstore`로 복사합니다. 현재 pstore에 남은 크래시 조각이 없으면 이 서비스는 조건 불충족으로 `inactive`일 수 있으며 정상입니다. +## 시놀로지 팬 가드 + +시놀로지 CPU 온도는 `k10temp`의 `Tdie` 값을 기준으로 보고, `nct6795`의 실제 회전 팬 `fan2_input`과 `pwm2`를 감시합니다. `bin/synology_fan_guard.sh`는 DSM 기본 팬 제어를 평소에는 유지하고, CPU가 75도 이상이면 `pwm2`를 255로 강제합니다. CPU가 65도 이하로 내려가고 수동 강제 상태라면 DSM 자동제어(`pwm2_enable=5`)로 되돌립니다. systemd 예시는 `systemd/synology-fan-guard.service`에 둡니다. + ## UPS/배터리 상태 UPS가 과부하 상황에서 전압 하강과 강제 종료를 반복해 현재 운영 구성에서는 제거되어 있습니다. 이에 따라 Control은 기본적으로 MAX17048 배터리 센서를 읽지 않고, 배터리 SOC/전압 차트와 잔여 시간 UI도 표시하지 않습니다. diff --git a/bin/synology_fan_guard.sh b/bin/synology_fan_guard.sh new file mode 100755 index 0000000..231e616 --- /dev/null +++ b/bin/synology_fan_guard.sh @@ -0,0 +1,113 @@ +#!/bin/sh +set -eu + +INTERVAL_SECONDS="${INTERVAL_SECONDS:-5}" +HIGH_TEMP_C="${HIGH_TEMP_C:-75}" +LOW_TEMP_C="${LOW_TEMP_C:-65}" +PWM_CHANNEL="${PWM_CHANNEL:-2}" +PWM_FULL="${PWM_FULL:-255}" +PWM_AUTO_ENABLE="${PWM_AUTO_ENABLE:-5}" +PWM_MANUAL_ENABLE="${PWM_MANUAL_ENABLE:-1}" +LAST_MODE="" + +log() { + if command -v logger >/dev/null 2>&1; then + logger -t syno-fan-guard "$*" + fi + printf '%s\n' "$*" +} + +hwmon_by_name() { + target="$1" + for hwmon in /sys/class/hwmon/hwmon*; do + [ -r "$hwmon/name" ] || continue + [ "$(cat "$hwmon/name" 2>/dev/null)" = "$target" ] || continue + printf '%s\n' "$hwmon" + return 0 + done + return 1 +} + +read_cpu_temp_mc() { + cpu_hwmon="$(hwmon_by_name k10temp || true)" + if [ -n "$cpu_hwmon" ]; then + for sensor in temp2_input temp1_input temp3_input; do + path="$cpu_hwmon/$sensor" + [ -r "$path" ] || continue + value="$(cat "$path" 2>/dev/null || printf 0)" + case "$value" in + ''|*[!0-9-]*) continue ;; + esac + [ "$value" -gt 0 ] || continue + printf '%s\n' "$value" + return 0 + done + fi + printf '0\n' +} + +fan_hwmon() { + hwmon_by_name nct6795 +} + +write_if_needed() { + path="$1" + value="$2" + [ -w "$path" ] || return 1 + current="$(cat "$path" 2>/dev/null || printf '')" + [ "$current" = "$value" ] && return 0 + printf '%s\n' "$value" > "$path" +} + +apply_once() { + verbose="${1:-0}" + fan_hwmon_path="$(fan_hwmon)" + pwm_path="$fan_hwmon_path/pwm${PWM_CHANNEL}" + enable_path="$fan_hwmon_path/pwm${PWM_CHANNEL}_enable" + rpm_path="$fan_hwmon_path/fan${PWM_CHANNEL}_input" + temp_mc="$(read_cpu_temp_mc)" + temp_c=$((temp_mc / 1000)) + rpm=0 + [ -r "$rpm_path" ] && rpm="$(cat "$rpm_path" 2>/dev/null || printf 0)" + enable="$(cat "$enable_path" 2>/dev/null || printf '?')" + pwm="$(cat "$pwm_path" 2>/dev/null || printf '?')" + + if [ "$temp_c" -ge "$HIGH_TEMP_C" ]; then + write_if_needed "$enable_path" "$PWM_MANUAL_ENABLE" + write_if_needed "$pwm_path" "$PWM_FULL" + if [ "$LAST_MODE" != "force-full" ] || [ "$verbose" = "1" ]; then + log "force-full temp=${temp_c}C rpm=${rpm} pwm=${pwm} enable=${enable}" + fi + LAST_MODE="force-full" + return 0 + fi + + if [ "$temp_c" -le "$LOW_TEMP_C" ] && [ "$enable" != "$PWM_AUTO_ENABLE" ]; then + write_if_needed "$enable_path" "$PWM_AUTO_ENABLE" + log "restore-auto temp=${temp_c}C rpm=${rpm} pwm=${pwm} enable=${enable}" + LAST_MODE="auto" + return 0 + fi + + if [ "$verbose" = "1" ]; then + log "observe temp=${temp_c}C rpm=${rpm} pwm=${pwm} enable=${enable}" + fi + LAST_MODE="observe" +} + +case "${1:-run}" in + once|status) + apply_once 1 + ;; + run) + log "start high=${HIGH_TEMP_C}C low=${LOW_TEMP_C}C channel=${PWM_CHANNEL}" + while :; do + apply_once 0 || true + sleep "$INTERVAL_SECONDS" + done + ;; + *) + printf 'Usage: %s [run|once|status]\n' "$0" >&2 + exit 2 + ;; +esac diff --git a/systemd/synology-fan-guard.service b/systemd/synology-fan-guard.service new file mode 100644 index 0000000..efab6b7 --- /dev/null +++ b/systemd/synology-fan-guard.service @@ -0,0 +1,12 @@ +[Unit] +Description=Synology CPU temperature fan guard +After=multi-user.target + +[Service] +Type=simple +ExecStart=/usr/local/sbin/synology-fan-guard run +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target