From 5711b1f4ef713b2c659120e00ce3ad9601012892 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 5 Jul 2026 11:08:59 +0200 Subject: [PATCH 1/2] 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_); From d518110e6f51ccfb435b307eccef36343fdb5f9d Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 5 Jul 2026 11:07:59 +0200 Subject: [PATCH 2/2] fix(network): recover IP address after a link flap / router reboot The network module only populates the interface address from netlink events (RTM_NEWADDR) or an explicit address dump. The interval timer re-queries WiFi and bandwidth but never re-fetches the address, so the module relies entirely on receiving the RTM_NEWADDR event. Netlink multicast delivery is reliable unless the socket receive buffer overflows, in which case the kernel drops notifications and reports ENOBUFS. During a burst of link/address/route changes -- e.g. a router reboot or a PPPoE redial -- this can drop the RTM_NEWADDR carrying the interface's new IP (after the old one was removed by RTM_DELADDR). With no overrun handling and no periodic resync, the address field stays blank until Waybar is restarted (which re-dumps addresses). Handle the overrun: when nl_recvmsgs_default reports ENOBUFS/NLE_NOMEM, request a fresh link/address (and route, when auto-detecting) dump to resynchronise, instead of silently continuing with lost state. Also enlarge the event socket receive buffer to make overruns less likely in the first place. The fix stays within the event thread, so it adds no new locking or cross-thread socket access. Fixes #5122. --- src/modules/network.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 72a367fc..d3adfbe5 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -183,6 +183,12 @@ void waybar::modules::Network::createEventSocket() { if (nl_socket_set_nonblocking(ev_sock_)) { throw std::runtime_error("Can't set non-blocking on network socket"); } + // Enlarge the socket receive buffer so that a burst of link/address/route + // change notifications (e.g. a router reboot or a PPPoE redial) is less likely + // to overflow it and make the kernel drop messages (ENOBUFS). Overruns are + // still handled in worker() by resynchronising the state, but a larger buffer + // avoids most of them. The kernel caps the request at net.core.rmem_max. + nl_socket_set_buffer_size(ev_sock_, 1024 * 1024, 0); nl_socket_add_memberships(ev_sock_, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0); if (!config_["interface"].isString()) { nl_socket_add_memberships(ev_sock_, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0); @@ -277,6 +283,27 @@ void waybar::modules::Network::worker() { rc = 0; break; } + if (rc == -NLE_NOMEM || errno == ENOBUFS) { + // The kernel dropped multicast notifications because our receive + // buffer overflowed. This happens during a burst of + // link/address/route changes such as a router reboot or a PPPoE + // redial. We have lost track of the current state -- in + // particular the RTM_NEWADDR carrying the interface's new IP + // address may have been dropped -- so request a fresh dump to + // resynchronise. Without this the address (cleared by the + // preceding RTM_DELADDR) would stay blank until Waybar is + // restarted, because nothing else re-queries it (#5122). + spdlog::warn("network: netlink receive buffer overrun, resyncing state"); + want_link_dump_ = true; + want_addr_dump_ = true; + if (!config_["interface"].isString()) { + want_route_dump_ = true; + } + askForStateDump(); + // Keep draining; the next recv proceeds normally now that the + // overrun has been reported. + continue; + } } if (rc < 0) { spdlog::error("nl_recvmsgs_default error: {}", nl_geterror(-rc));