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
```
- (_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
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}
Version: 0.1
Release: 16%{?dist}
Release: 17%{?dist}
Summary: %{kmod_name} kernel module(s)
Group: System Environment/Kernel
License: GPLv2
@@ -83,6 +83,7 @@ popd
pushd scripts
%{__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
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-probe-leds %{buildroot}%{_bindir}/
%{__install} -m 0755 scripts/ugreen-blink-disk %{buildroot}%{_bindir}/
%{__install} -m 0755 scripts/ugreen-check-standby %{buildroot}%{_bindir}/
mkdir -p %{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-probe-leds
%attr(0755, root, root) %{_bindir}/ugreen-blink-disk
%attr(0755, root, root) %{_bindir}/ugreen-check-standby
%{_unitdir}/ugreen-netdevmon@.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
Maintainer: Yuhao Zhou <miskcoo@gmail.com>
Depends: dkms
Homepage: https://github.com/miskcoo/ugreen_dx4600_leds_controller
Homepage: https://github.com/miskcoo/ugreen_leds_controller
Description: UGREEN NAS LED driver
A reverse-engineered LED driver of UGREEN NAS.
EOF

View File

@@ -15,7 +15,7 @@ Version: $pkgver
Architecture: amd64
Maintainer: Yuhao Zhou <miskcoo@gmail.com>
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
A reverse-engineered LED tools of UGREEN NAS.
EOF
@@ -35,11 +35,18 @@ done
mkdir -p $pkgname/etc/systemd/system
cp scripts/*.service $pkgname/etc/systemd/system/
# cp scripts/ugreen-ledmon@.service $pkgname/etc/systemd/system/
#
# example config file
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
chown -R root:root $pkgname/

View File

@@ -1,14 +1,16 @@
#!/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
cp build-utils-deb.sh ugreen_dx4600_leds_controller
git clone https://github.com/miskcoo/ugreen_leds_controller.git
cd ugreen_leds_controller
cd ugreen_dx4600_leds_controller
bash build-dkms-deb.sh
bash build-utils-deb.sh
if [ ! -z $1 ]; then
git checkout $1
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-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 $zpool_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
@@ -111,6 +112,14 @@ BRIGHTNESS_DISK_LEDS=${BRIGHTNESS_DISK_LEDS:="255"}
{ 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() {
if [[ $MAPPING_METHOD == ata ]]; then
ls -ahl /sys/block | sed 's/\/$//' | awk '{
@@ -221,8 +230,9 @@ if [ "$CHECK_ZPOOL" = true ]; then
if [[ -v "zpool_ledmap[${zpool_dev_name}]" ]]; then
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;
fi
@@ -256,7 +266,9 @@ if [ "$CHECK_SMART" = true ]; then
(
while true; 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;
fi
@@ -265,9 +277,7 @@ if [ "$CHECK_SMART" = true ]; then
/usr/sbin/smartctl -H /dev/${dev} -n standby,1 &> /dev/null
RET=$?
if [[ $RET -eq 1 ]]; then
echo "$COLOR_DISK_STANDBY" > /sys/class/leds/$led/color
elif [[ $RET -gt 1 ]]; then
if [[ $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
@@ -286,21 +296,7 @@ fi
dev=${devices[$led]}
led_color=$(cat /sys/class/leds/$led/color)
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
if ! is_disk_healthy_or_standby "$led_color"; then
continue;
fi
@@ -315,18 +311,26 @@ fi
) &
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
BLINK_MON_PATH=${BLINK_MON_PATH:=/usr/bin/ugreen-blink-disk}
if [ -f "${BLINK_MON_PATH}" ]; then
diskiomon_parameters() {
echo ${LED_REFRESH_INTERVAL}
for led in "${!devices[@]}"; do
echo ${devices[$led]} $led
done
}
${BLINK_MON_PATH} $(diskiomon_parameters)
${BLINK_MON_PATH} ${LED_REFRESH_INTERVAL} $(diskiomon_parameters)
else
declare -A diskio_data_rw

View File

@@ -23,6 +23,12 @@ MAPPING_METHOD=ata
# The path of the compiled diskio monitor (OPTIONAL)
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)
# 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,