Repository navigation
fanctrlplus2: warn when MSI nct6687 system fans need msi_fan_brute_force #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
d0f36fb
fanctrlplus2: warn when MSI nct6687 system fans need msi_fan_brute_force
andrebrait 23c730c
fanctrlplus2: append the option, cover nct6686 and quoted values
andrebrait 8ee08d4
fanctrlplus2: append the option in the settings notice too
andrebrait ede3274
fanctrlplus2: read the loaded brute-force state from the kernel log, …
andrebrait 3c9f4fa
fanctrlplus2: harden the stuck-fan check after review
andrebrait 57e42bb
fanctrlplus2: warn after 10 s stuck at 60% or less, writes included
andrebrait b57707e
fanctrlplus2: create the runtime directory when a fan loop starts
andrebrait a458c6f
fanctrlplus2: let the last modprobe setting of the option win
andrebrait File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| <?php | ||
|
|
||
| require_once __DIR__ . '/../src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php'; | ||
|
|
||
| $failures = []; | ||
| $root = sys_get_temp_dir() . '/fcp_nct6687_notice_' . getmypid(); | ||
| $module = "$root/module"; | ||
| $hwmon = "$root/devices/platform/nct6687.2592/hwmon/hwmon7"; | ||
| $other = "$root/devices/platform/it87.552/hwmon/hwmon3"; | ||
| $modprobe = "$root/modprobe.d"; | ||
|
|
||
| @mkdir("$module/parameters", 0777, true); | ||
| @mkdir($hwmon, 0777, true); | ||
| @mkdir($other, 0777, true); | ||
| @mkdir($modprobe, 0777, true); | ||
| file_put_contents("$hwmon/name", "nct6687\n"); | ||
| file_put_contents("$other/name", "it8728\n"); | ||
| file_put_contents("$modprobe/nct6683.conf", "blacklist nct6683\ninstall nct6683 /bin/false\n"); | ||
|
|
||
| $log = ''; | ||
| $check = function (string $label, bool $expected, array $controllers) use (&$failures, &$log, $module, $modprobe): void { | ||
| $actual = fcp_nct6687_brute_force_missing($controllers, $module, "$modprobe/*.conf", $log); | ||
| if ($actual !== $expected) { | ||
| $failures[] = "$label: expected " . var_export($expected, true) . ', got ' . var_export($actual, true); | ||
| } | ||
| }; | ||
|
|
||
| $sys_fan = ["$other/pwm3", "$hwmon/pwm5"]; | ||
|
|
||
| $check('driver not loaded', false, $sys_fan); | ||
|
|
||
| file_put_contents("$module/parameters/fan_config", "default\n"); | ||
| $check('default register layout', false, $sys_fan); | ||
|
|
||
| file_put_contents("$module/parameters/fan_config", "msi_alt1\n"); | ||
| $check('msi_alt1 without the option', true, $sys_fan); | ||
| $check('first system channel', true, ["$hwmon/pwm3"]); | ||
| $nct6686 = "$root/devices/platform/nct6687.2593/hwmon/hwmon8"; | ||
| @mkdir($nct6686, 0777, true); | ||
| file_put_contents("$nct6686/name", "nct6686\n"); | ||
| $check('nct6686 chip', true, ["$other/pwm3", "$nct6686/pwm3"]); | ||
| $check('only CPU and pump channels', false, ["$hwmon/pwm1", "$hwmon/pwm2", "$hwmon/pwm1_enable", "$hwmon/pwm3_enable"]); | ||
| $check('system channel on another chip', false, ["$other/pwm5"]); | ||
| $check('no fans configured', false, []); | ||
|
|
||
| foreach ([ | ||
| 'options nct6687 msi_fan_brute_force=0' => true, | ||
| 'options nct6687 msi_fan_brute_force=n' => true, | ||
| '# options nct6687 msi_fan_brute_force=1' => true, | ||
| 'options nct6683 msi_fan_brute_force=1' => true, | ||
| 'options nct6687x msi_fan_brute_force=1' => true, | ||
| 'options nct6687 not_msi_fan_brute_force=1' => true, | ||
| 'options nct6687 msi_fan_brute_force=1' => false, | ||
| "# Set by hand\noptions nct6687 msi_fan_brute_force=1" => false, | ||
| "options nct6687 fan_config=msi_alt1 msi-fan-brute-force=Y\n" => false, | ||
| 'options nct6687 msi_fan_brute_force' => false, | ||
| 'options nct6687 msi_fan_brute_force=on' => false, | ||
| 'options nct6687 msi_fan_brute_force="true"' => false, | ||
| "options nct6687 msi_fan_brute_force=1\noptions nct6687 msi_fan_brute_force=0" => true, | ||
| 'options nct6687 msi_fan_brute_force=0 msi_fan_brute_force=1' => false, | ||
| ] as $line => $expected) { | ||
| file_put_contents("$modprobe/nct6687.conf", "$line\n"); | ||
| $check("modprobe line '$line'", $expected, $sys_fan); | ||
| } | ||
|
|
||
| // Across files, modprobe reads them in name order, so the later file wins. | ||
| file_put_contents("$modprobe/zz-off.conf", "options nct6687 msi_fan_brute_force=0\n"); | ||
| $check('a later file disables the option', true, $sys_fan); | ||
| rename("$modprobe/zz-off.conf", "$modprobe/00-off.conf"); | ||
| $check('an earlier file is overridden', false, $sys_fan); | ||
| unlink("$modprobe/00-off.conf"); | ||
| file_put_contents("$modprobe/nct6687.conf", "options nct6687 msi_fan_brute_force=1\n"); | ||
|
|
||
| // The kernel log reports the loaded value, so it overrides the modprobe files | ||
| // (which the loop above left setting the option). | ||
| $enabled = "Oct 6 06:20:01 Tower kernel: nct6687 nct6687.2592: MSI fan brute force mode: enabled\n"; | ||
| $disabled = "[ 7.912345] nct6687 nct6687.2592: MSI fan brute force mode: disabled\n"; | ||
| $log = $disabled; | ||
| $check('kernel log: disabled, modprobe option set after boot', true, $sys_fan); | ||
| $log = "$disabled$enabled"; | ||
| $check('kernel log: latest load enabled', false, $sys_fan); | ||
| $log = "$enabled$disabled"; | ||
| $check('kernel log: latest load disabled', true, $sys_fan); | ||
| file_put_contents("$modprobe/nct6687.conf", "options nct6687 msi_fan_brute_force=0\n"); | ||
| $log = $enabled; | ||
| $check('kernel log: enabled, option passed outside modprobe.d', false, $sys_fan); | ||
| $log = ''; | ||
| unlink("$modprobe/nct6687.conf"); | ||
|
|
||
| touch("$hwmon/fan_control_watchdog"); | ||
| $check('watchdog attribute present', false, $sys_fan); | ||
| $log = $disabled; | ||
| $check('watchdog attribute outranks the kernel log', false, $sys_fan); | ||
|
|
||
| exec('rm -rf ' . escapeshellarg($root)); | ||
|
|
||
| if ($failures) { | ||
| fwrite(STDERR, implode("\n", $failures) . "\n"); | ||
| exit(1); | ||
| } | ||
| echo "nct6687 notice: OK\n"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -u | ||
|
|
||
| root=$(cd "$(dirname "$0")/.." && pwd) | ||
| tmp=$(mktemp -d) | ||
| trap 'rm -rf "$tmp"' EXIT | ||
|
|
||
| fcp_nct6687_module="$tmp/module" | ||
| fcp_notify_bin="$tmp/notify" | ||
| source "$root/src/usr/local/emhttp/plugins/fanctrlplus2/scripts/aux_sensors.sh" | ||
| source "$root/src/usr/local/emhttp/plugins/fanctrlplus2/scripts/disk_group_control.sh" | ||
|
|
||
| failures=0 | ||
| expect_equal() { | ||
| local expected="$1" actual="$2" message="$3" | ||
| if [[ "$expected" == "$actual" ]]; then | ||
| return | ||
| fi | ||
| printf '%s\nExpected: %s\nActual: %s\n' "$message" "$expected" "$actual" >&2 | ||
| failures=$((failures + 1)) | ||
| } | ||
|
|
||
| # ===== fcp_nct6687_msi_channel ===== | ||
| nct="$tmp/hwmon7" other="$tmp/hwmon3" | ||
| mkdir -p "$nct" "$other" "$fcp_nct6687_module/parameters" | ||
| echo nct6687 > "$nct/name" | ||
| echo it8728 > "$other/name" | ||
| channel() { fcp_nct6687_msi_channel "$1" && echo yes || echo no; } | ||
|
|
||
| expect_equal no "$(channel "$nct/pwm5")" "Without the driver loaded, no channel is watched." | ||
| echo default > "$fcp_nct6687_module/parameters/fan_config" | ||
| expect_equal no "$(channel "$nct/pwm5")" "The default register layout applies speed changes." | ||
| echo msi_alt1 > "$fcp_nct6687_module/parameters/fan_config" | ||
| expect_equal yes "$(channel "$nct/pwm3")" "The first system-fan channel is watched." | ||
| expect_equal no "$(channel "$nct/pwm2")" "The pump channel is not affected." | ||
| expect_equal no "$(channel "$other/pwm5")" "Another chip's channel is not watched." | ||
| echo nct6683 > "$other/name" | ||
| expect_equal yes "$(channel "$other/pwm5")" "The driver's other chip names are watched too." | ||
| touch "$nct/fan_control_watchdog" | ||
| expect_equal no "$(channel "$nct/pwm5")" "A driver running with brute force on is not watched." | ||
|
|
||
| # ===== fcp_stuck_since ===== | ||
| expect_equal 100 "$(fcp_stuck_since '' 100 255 154)" "Set to 100%, stuck at 60%: a stretch starts now." | ||
| expect_equal 100 "$(fcp_stuck_since 100 110 166 154)" "A new target above 60% keeps the stretch going." | ||
| expect_equal 100 "$(fcp_stuck_since 100 110 155 154)" "61% is above 60%; a reading of 154 rounds to 60%." | ||
| expect_equal '' "$(fcp_stuck_since 100 110 154 120)" "A target of 60% or less ends the stretch." | ||
| expect_equal '' "$(fcp_stuck_since 100 110 255 155)" "A reading above 60% ends the stretch." | ||
| expect_equal '' "$(fcp_stuck_since 100 110 255 '')" "An unreadable value ends the stretch." | ||
|
|
||
| # ===== fcp_notify_stuck ===== | ||
| printf '#!/bin/bash\nprintf "%%s\\n" "$@" >> "%s/calls"\n' "$tmp" > "$fcp_notify_bin" | ||
| chmod +x "$fcp_notify_bin" | ||
| fcp_notify_stuck HardDriveFans 255 154 "$tmp/flag" | ||
| fcp_notify_stuck HardDriveFans 255 154 "$tmp/flag" | ||
| expect_equal 1 "$(grep -c '^-s$' "$tmp/calls")" "One notification per fan until the flag is cleared at boot." | ||
| expect_equal 1 "$(grep -c '^Set to 100% but the nct6687 controller reports 60%' "$tmp/calls")" \ | ||
| "The notification reports the set and actual speeds as percentages." | ||
| expect_equal 1 "$(grep -cx 'warning' "$tmp/calls")" "The notification is a warning." | ||
| fcp_notify_stuck HardDriveFans 255 154 "$tmp/missing/flag" | ||
| expect_equal 1 "$(grep -c '^-s$' "$tmp/calls")" "Without a flag file, no notification is sent, so ticks cannot repeat it." | ||
|
|
||
| if (( failures > 0 )); then | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "nct6687 stuck-fan tests passed" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
interval_secexceeds 10 seconds (the default is 120 and the supported maximum is 3600), this condition is evaluated only once per control-loop iteration and is followed by the full interval sleep. A stuck stretch first observed here therefore cannot notify after 10 seconds; it notifies only on the next tick—up to an hour later—or not at all if the requested PWM falls below 60% before that tick. Schedule a follow-up read after the remaining 10-second window or poll this check independently of the temperature-control interval.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intended. The check follows the control loop's cadence by design: it warns after 10 seconds' worth of ticks plus the tick that set the speed, so at a 120 s interval that is the second tick. A fan stuck at 60% isn't urgent, and the condition persists, so a later tick still catches it. Users who choose a long interval accept slower reaction from every part of the loop, this check included.