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.
This commit is contained in:
Alex
2026-07-05 11:09:46 +02:00
parent 969855f99e
commit 5711b1f4ef
+14 -2
View File
@@ -230,9 +230,21 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval,
upsert_device(devices, dev.get()); 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) { if (event_count == 0) {
enumerate_devices(devices, udev.get()); for (const auto& device : devices) {
std::unique_ptr<udev_device, UdevDeviceDeleter> 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<std::mutex> lock(udev_thread_mutex_); std::scoped_lock<std::mutex> lock(udev_thread_mutex_);