From 5711b1f4ef713b2c659120e00ce3ad9601012892 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 5 Jul 2026 11:08:59 +0200 Subject: [PATCH] fix(backlight): stop per-tick filesystem re-enumeration / I/O flooding The backlight udev worker thread called enumerate_devices() on every epoll_wait timeout, i.e. once per polling interval. enumerate_devices() runs udev_enumerate_scan_devices(), which walks the entire /sys/class/backlight and /sys/class/leds trees and opens/closes the sysfs root and every device path. With no `interval` configured the module polls on its default cadence, so this full re-scan ran continuously even when brightness never changed, flooding the filesystem (observed via fatrace as constant open/close of `/`). Raising `interval` only lowered the cadence, which is why the reporter's `interval: 10` workaround reduced the flood. The full re-enumeration is redundant: the udev monitor already delivers change/add/remove events for the backlight and leds subsystems. On the timeout path, re-read only the sysfs attributes of the devices already tracked (via udev_device_new_from_subsystem_sysname) instead of re-scanning the whole tree. This keeps periodic refresh working for firmware backlights such as acpi_video that may not emit udev change events, while eliminating the tree-wide scan. Device discovery of new/removed devices continues through the udev monitor. Fixes #5020. --- src/util/backlight_backend.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index bf669f15..9ccf7250 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -230,9 +230,21 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, upsert_device(devices, dev.get()); } - // Refresh state if timed out + // Refresh state if timed out. Only re-read the sysfs attributes of the + // devices we already track instead of re-enumerating the whole udev tree + // (udev_enumerate_scan_devices), which walks all of /sys/class/backlight + // and /sys/class/leds and floods the filesystem with open/close syscalls + // on every polling tick (#5020). Device add/remove is already delivered + // by the udev monitor above, so a periodic full re-scan is redundant. if (event_count == 0) { - enumerate_devices(devices, udev.get()); + for (const auto& device : devices) { + std::unique_ptr dev{ + udev_device_new_from_subsystem_sysname(udev.get(), device.subsystem().c_str(), + device.name().c_str())}; + if (dev) { + upsert_device(devices, dev.get()); + } + } } { std::scoped_lock lock(udev_thread_mutex_);