Improve the disk standby check (#40)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled

This commit is contained in:
Yuhao Zhou
2024-11-01 21:36:19 +08:00
committed by GitHub
parent d0c36a5f4a
commit e0bbe9ee50
11 changed files with 221 additions and 40 deletions

View File

@@ -220,6 +220,15 @@ Please see `scripts/ugreen-leds.conf` for an example.
cp ugreen-blink-disk /usr/bin cp ugreen-blink-disk /usr/bin
``` ```
- (_Optional_) Similarly, to reduce the latency of the standby check, you can enter the `scripts` directory and do the following things:
```bash
# compile the disk standby checker
g++ -std=c++17 -O2 check-standby.cpp -o ugreen-check-standby
# copy the binary file (the path can be changed, see STANDBY_MON_PATH in ugreen-leds.conf)
cp ugreen-check-standby /usr/bin
```
## Disk Mapping ## Disk Mapping
To make the disk LEDs useful, we should map the disk LEDs to correct disk slots. First of all, we should highlight that using `/dev/sdX` is never a smart idea, as it may change at every boot. To make the disk LEDs useful, we should map the disk LEDs to correct disk slots. First of all, we should highlight that using `/dev/sdX` is never a smart idea, as it may change at every boot.

View File

@@ -8,7 +8,7 @@
Name: kmod-%{kmod_name} Name: kmod-%{kmod_name}
Version: 0.1 Version: 0.1
Release: 16%{?dist} Release: 17%{?dist}
Summary: %{kmod_name} kernel module(s) Summary: %{kmod_name} kernel module(s)
Group: System Environment/Kernel Group: System Environment/Kernel
License: GPLv2 License: GPLv2
@@ -83,6 +83,7 @@ popd
pushd scripts pushd scripts
%{__cxx} -std=c++17 -O2 blink-disk.cpp -o ugreen-blink-disk %{__cxx} -std=c++17 -O2 blink-disk.cpp -o ugreen-blink-disk
%{__cxx} -std=c++17 -O2 check-standby.cpp -o ugreen-check-standby
popd popd
whitelist="/lib/modules/kabi-current/kabi_stablelist_%{_target_cpu}" whitelist="/lib/modules/kabi-current/kabi_stablelist_%{_target_cpu}"
@@ -112,6 +113,7 @@ mkdir -p %{buildroot}%{_bindir}/
%{__install} -m 0755 scripts/ugreen-netdevmon %{buildroot}%{_bindir}/ %{__install} -m 0755 scripts/ugreen-netdevmon %{buildroot}%{_bindir}/
%{__install} -m 0755 scripts/ugreen-probe-leds %{buildroot}%{_bindir}/ %{__install} -m 0755 scripts/ugreen-probe-leds %{buildroot}%{_bindir}/
%{__install} -m 0755 scripts/ugreen-blink-disk %{buildroot}%{_bindir}/ %{__install} -m 0755 scripts/ugreen-blink-disk %{buildroot}%{_bindir}/
%{__install} -m 0755 scripts/ugreen-check-standby %{buildroot}%{_bindir}/
mkdir -p %{buildroot}%{_unitdir}/ mkdir -p %{buildroot}%{_unitdir}/
%{__install} -m 0644 scripts/ugreen-netdevmon@.service %{buildroot}%{_unitdir}/ %{__install} -m 0644 scripts/ugreen-netdevmon@.service %{buildroot}%{_unitdir}/
@@ -221,6 +223,7 @@ exit 0
%attr(0755, root, root) %{_bindir}/ugreen-netdevmon %attr(0755, root, root) %{_bindir}/ugreen-netdevmon
%attr(0755, root, root) %{_bindir}/ugreen-probe-leds %attr(0755, root, root) %{_bindir}/ugreen-probe-leds
%attr(0755, root, root) %{_bindir}/ugreen-blink-disk %attr(0755, root, root) %{_bindir}/ugreen-blink-disk
%attr(0755, root, root) %{_bindir}/ugreen-check-standby
%{_unitdir}/ugreen-netdevmon@.service %{_unitdir}/ugreen-netdevmon@.service
%{_unitdir}/ugreen-diskiomon.service %{_unitdir}/ugreen-diskiomon.service

View File

@@ -0,0 +1,17 @@
FROM debian:bookworm
RUN set -ex \
&& sed -i -- 's/Types: deb/Types: deb deb-src/g' /etc/apt/sources.list.d/debian.sources \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
cdbs \
devscripts \
equivs \
fakeroot \
wget \
git \
&& apt-get clean \
&& rm -rf /tmp/* /var/tmp/*

View File

@@ -1,2 +1,8 @@
To build a deb, run `sudo bash build.sh`. Build it in docker:
```
bash docker-run.sh
```
The outputs can be found in `./build` directory.

View File

@@ -15,7 +15,7 @@ Version: $pkgver
Architecture: amd64 Architecture: amd64
Maintainer: Yuhao Zhou <miskcoo@gmail.com> Maintainer: Yuhao Zhou <miskcoo@gmail.com>
Depends: dkms Depends: dkms
Homepage: https://github.com/miskcoo/ugreen_dx4600_leds_controller Homepage: https://github.com/miskcoo/ugreen_leds_controller
Description: UGREEN NAS LED driver Description: UGREEN NAS LED driver
A reverse-engineered LED driver of UGREEN NAS. A reverse-engineered LED driver of UGREEN NAS.
EOF EOF

View File

@@ -15,7 +15,7 @@ Version: $pkgver
Architecture: amd64 Architecture: amd64
Maintainer: Yuhao Zhou <miskcoo@gmail.com> Maintainer: Yuhao Zhou <miskcoo@gmail.com>
Depends: dmidecode, smartmontools Depends: dmidecode, smartmontools
Homepage: https://github.com/miskcoo/ugreen_dx4600_leds_controller Homepage: https://github.com/miskcoo/ugreen_leds_controller
Description: UGREEN NAS LED tools Description: UGREEN NAS LED tools
A reverse-engineered LED tools of UGREEN NAS. A reverse-engineered LED tools of UGREEN NAS.
EOF EOF
@@ -35,11 +35,18 @@ done
mkdir -p $pkgname/etc/systemd/system mkdir -p $pkgname/etc/systemd/system
cp scripts/*.service $pkgname/etc/systemd/system/ cp scripts/*.service $pkgname/etc/systemd/system/
# cp scripts/ugreen-ledmon@.service $pkgname/etc/systemd/system/ # cp scripts/ugreen-ledmon@.service $pkgname/etc/systemd/system/
#
# example config file # example config file
cp scripts/ugreen-leds.conf $pkgname/etc/ugreen-leds.example.conf cp scripts/ugreen-leds.conf $pkgname/etc/ugreen-leds.example.conf
# compile the disk activities monitor
g++ -std=c++17 -O2 scripts/blink-disk.cpp -o ugreen-blink-disk
cp ugreen-blink-disk $pkgname/usr/bin
# compile the disk standby monitor
g++ -std=c++17 -O2 scripts/check-standby.cpp -o ugreen-check-standby
cp ugreen-check-standby $pkgname/usr/bin
# change to root # change to root
chown -R root:root $pkgname/ chown -R root:root $pkgname/

View File

@@ -1,14 +1,16 @@
#!/usr/bin/bash #!/usr/bin/bash
git clone https://github.com/miskcoo/ugreen_dx4600_leds_controller.git set -x
cp build-dkms-deb.sh ugreen_dx4600_leds_controller git clone https://github.com/miskcoo/ugreen_leds_controller.git
cp build-utils-deb.sh ugreen_dx4600_leds_controller cd ugreen_leds_controller
cd ugreen_dx4600_leds_controller if [ ! -z $1 ]; then
bash build-dkms-deb.sh git checkout $1
bash build-utils-deb.sh fi
bash build-scripts/debian/build-dkms-deb.sh
bash build-scripts/debian/build-utils-deb.sh
dpkg-name led-ugreen-dkms.deb dpkg-name led-ugreen-dkms.deb
dpkg-name led-ugreen-utils.deb dpkg-name led-ugreen-utils.deb
mv *.deb ../ mv *.deb ..

View File

@@ -0,0 +1,15 @@
set -x
if [[ ! -d ./build ]]; then
mkdir build
fi
docker build --tag bookworm-build .
docker run \
--rm \
--mount type=bind,source=$(pwd)/build,target=/build \
--mount type=bind,source=$(pwd)/build.sh,target=/build.sh \
bookworm-build \
bash -c "cd /build && bash /build.sh $1"

112
scripts/check-standby.cpp Normal file
View File

@@ -0,0 +1,112 @@
#include <fstream>
#include <thread>
#include <vector>
#include <iostream>
#include <string>
#include <chrono>
#include <optional>
#include <fcntl.h>
#include <linux/hdreg.h>
#include <sys/ioctl.h>
#include <unistd.h>
std::optional<bool> is_standby_mode(const std::string &device) {
int fd = open(device.c_str(), O_RDONLY | O_NONBLOCK);
if (fd == -1) {
std::cerr << "Failed to open device: " << device << std::endl;
return std::nullopt;
}
unsigned char args[4] = { WIN_CHECKPOWERMODE1, 0, 0, 0 };
if (ioctl(fd, HDIO_DRIVE_CMD, args) == -1) {
std::cerr << "ioctl failed in checking power mode of " << device << std::endl;
close(fd);
return std::nullopt;
}
close(fd);
return args[2] == 0x00;
}
int main(int argc, char *argv[]) {
constexpr int preleading_args = 4;
if (argc < preleading_args + 2 || (argc - preleading_args) % 2 != 0) {
std::cerr << "Usage: " << argv[0]
<< " <check interval (in second)>"
<< " <disk standby color>"
<< " <disk normal color>"
<< " <block device 1> <led device 1> <block device 2> <led device 2>...\n";
return 1;
}
int num_devices = (argc - preleading_args) / 2;
int checker_sleep_ms = int(std::stod(argv[1]) * 1000);
std::string standby_color = argv[2];
std::string normal_color = argv[3];
std::vector<std::string> block_device_paths;
std::vector<std::string> led_device_color_paths;
std::cout << normal_color;
for (int i = 0; i < num_devices; i++) {
std::string block_device = argv[preleading_args + 2 * i];
std::string led_device = argv[preleading_args + 2 * i + 1];
block_device_paths.push_back(
"/dev/" + block_device);
led_device_color_paths.push_back(
"/sys/class/leds/" + led_device + "/color");
}
std::vector<bool> device_valid(num_devices, true);
std::vector<bool> device_standby(num_devices, false);
while (true) {
for (int i = 0; i < num_devices; i++) {
if (!device_valid[i]) {
continue;
}
auto is_standby = is_standby_mode(block_device_paths[i]);
if (!is_standby.has_value()) {
device_valid[i] = false;
continue;
}
if (is_standby.value() != device_standby[i]) {
std::fstream led_color(led_device_color_paths[i], std::ios::in | std::ios::out);
if (!led_color) {
std::cerr << "Failed to open led color file: "
<< led_device_color_paths[i] << std::endl;
device_valid[i] = false;
continue;
}
std::string current_color;
std::getline(led_color, current_color);
if (device_standby[i] && current_color == standby_color) {
led_color << normal_color << std::endl;
} else if (!device_standby[i] && current_color == normal_color) {
led_color << standby_color << std::endl;
}
device_standby[i] = is_standby.value();
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(checker_sleep_ms));
}
return 0;
}

View File

@@ -8,6 +8,7 @@ exit-ugreen-diskiomon() {
kill $smart_check_pid 2>/dev/null kill $smart_check_pid 2>/dev/null
kill $zpool_check_pid 2>/dev/null kill $zpool_check_pid 2>/dev/null
kill $disk_online_check_pid 2>/dev/null kill $disk_online_check_pid 2>/dev/null
kill $standby_checker_pid 2>/dev/null
} }
# trap exit and remove lockfile # trap exit and remove lockfile
@@ -111,6 +112,14 @@ BRIGHTNESS_DISK_LEDS=${BRIGHTNESS_DISK_LEDS:="255"}
{ lsmod | grep ledtrig_oneshot > /dev/null; } || { modprobe -v ledtrig_oneshot && sleep 2; } { lsmod | grep ledtrig_oneshot > /dev/null; } || { modprobe -v ledtrig_oneshot && sleep 2; }
function is_disk_healthy_or_standby() {
if [[ "$1" == "$COLOR_DISK_HEALTH" || "$1" == "$COLOR_DISK_STANDBY" ]]; then
return 0 # 0 means successful
else
return 1
fi
}
function disk_enumerating_string() { function disk_enumerating_string() {
if [[ $MAPPING_METHOD == ata ]]; then if [[ $MAPPING_METHOD == ata ]]; then
ls -ahl /sys/block | sed 's/\/$//' | awk '{ ls -ahl /sys/block | sed 's/\/$//' | awk '{
@@ -221,8 +230,9 @@ if [ "$CHECK_ZPOOL" = true ]; then
if [[ -v "zpool_ledmap[${zpool_dev_name}]" ]]; then if [[ -v "zpool_ledmap[${zpool_dev_name}]" ]]; then
led=${zpool_ledmap[$zpool_dev_name]} led=${zpool_ledmap[$zpool_dev_name]}
led_color=$(cat /sys/class/leds/$led/color)
if [[ "$(cat /sys/class/leds/$led/color)" != "$COLOR_DISK_HEALTH" ]]; then if ! is_disk_healthy_or_standby "$led_color"; then
continue; continue;
fi fi
@@ -256,7 +266,9 @@ if [ "$CHECK_SMART" = true ]; then
( (
while true; do while true; do
for led in "${!devices[@]}"; do for led in "${!devices[@]}"; do
if [[ "$(cat /sys/class/leds/$led/color)" != "$COLOR_DISK_HEALTH" ]]; then
led_color=$(cat /sys/class/leds/$led/color)
if ! is_disk_healthy_or_standby "$led_color"; then
continue; continue;
fi fi
@@ -265,9 +277,7 @@ if [ "$CHECK_SMART" = true ]; then
/usr/sbin/smartctl -H /dev/${dev} -n standby,1 &> /dev/null /usr/sbin/smartctl -H /dev/${dev} -n standby,1 &> /dev/null
RET=$? RET=$?
if [[ $RET -eq 1 ]]; then if [[ $RET -gt 1 ]]; then
echo "$COLOR_DISK_STANDBY" > /sys/class/leds/$led/color
elif [[ $RET -gt 1 ]]; then
echo "$COLOR_SMART_FAIL" > /sys/class/leds/$led/color echo "$COLOR_SMART_FAIL" > /sys/class/leds/$led/color
echo Disk failure detected on /dev/$dev at $(date +%Y-%m-%d' '%H:%M:%S) echo Disk failure detected on /dev/$dev at $(date +%Y-%m-%d' '%H:%M:%S)
continue continue
@@ -286,21 +296,7 @@ fi
dev=${devices[$led]} dev=${devices[$led]}
led_color=$(cat /sys/class/leds/$led/color) led_color=$(cat /sys/class/leds/$led/color)
if ! is_disk_healthy_or_standby "$led_color"; then
if [[ "$led_color" == "$COLOR_DISK_STANDBY" ]]; then
# Check if disk is still in standby
/usr/sbin/smartctl -H /dev/${dev} -n standby,1 &> /dev/null
RET=$?
if [[ $RET -eq 0 ]]; then
led_color="$COLOR_DISK_HEALTH"
echo $led_color > /sys/class/leds/$led/color
elif [[ $RET -gt 1 ]]; then
echo "$COLOR_SMART_FAIL" > /sys/class/leds/$led/color
echo Disk failure detected on /dev/$dev at $(date +%Y-%m-%d' '%H:%M:%S)
continue
fi
elif [[ "$led_color" != "$COLOR_DISK_HEALTH" ]]; then
continue; continue;
fi fi
@@ -315,18 +311,26 @@ fi
) & ) &
disk_online_check_pid=$! disk_online_check_pid=$!
diskiomon_parameters() {
for led in "${!devices[@]}"; do
echo ${devices[$led]} $led
done
}
# monitor disk standby modes
STANDBY_MON_PATH=${STANDBY_MON_PATH:=/usr/bin/ugreen-check-standby}
STANDBY_CHECK_INTERVAL=${STANDBY_CHECK_INTERVAL:=1}
if [ -f "${STANDBY_MON_PATH}" ]; then
${STANDBY_MON_PATH} ${STANDBY_CHECK_INTERVAL} "${COLOR_DISK_STANDBY}" "${COLOR_DISK_HEALTH}" $(diskiomon_parameters) &
standby_checker_pid=$1
fi
# monitor disk activities # monitor disk activities
BLINK_MON_PATH=${BLINK_MON_PATH:=/usr/bin/ugreen-blink-disk} BLINK_MON_PATH=${BLINK_MON_PATH:=/usr/bin/ugreen-blink-disk}
if [ -f "${BLINK_MON_PATH}" ]; then if [ -f "${BLINK_MON_PATH}" ]; then
diskiomon_parameters() { ${BLINK_MON_PATH} ${LED_REFRESH_INTERVAL} $(diskiomon_parameters)
echo ${LED_REFRESH_INTERVAL}
for led in "${!devices[@]}"; do
echo ${devices[$led]} $led
done
}
${BLINK_MON_PATH} $(diskiomon_parameters)
else else
declare -A diskio_data_rw declare -A diskio_data_rw

View File

@@ -23,6 +23,12 @@ MAPPING_METHOD=ata
# The path of the compiled diskio monitor (OPTIONAL) # The path of the compiled diskio monitor (OPTIONAL)
BLINK_MON_PATH=/usr/bin/ugreen-blink-disk BLINK_MON_PATH=/usr/bin/ugreen-blink-disk
# The path of the compiled standby monitor (OPTIONAL)
STANDBY_MON_PATH=/usr/bin/ugreen-check-standby
# The sleep time between disk standby checks (default: 1 seconds)
STANDBY_CHECK_INTERVAL=1
# The serial numbers of disks (used only when MAPPING_METHOD=serial) # The serial numbers of disks (used only when MAPPING_METHOD=serial)
# You need to record them before inserting to your NAS, and the corresponding disk slots. # You need to record them before inserting to your NAS, and the corresponding disk slots.
# If you have 4 disks, with serial numbers: SN1 SN2 SN3 SN4, # If you have 4 disks, with serial numbers: SN1 SN2 SN3 SN4,