TL;DR #
My Logitech MX Master 3 felt laggy on a passively cooled ODROID-H2 running KDE Plasma on Wayland. A layer-by-layer diagnosis over SSH ruled out the mouse, the receiver, and the whole USB path; the input stream was rock solid at 125 Hz. The culprits turned out to be transient 2.4 GHz RF interference from a flaky Bluetooth radio (fixed) and compositor latency on a very weak Intel UHD 600 iGPU (mitigated). Then I went shopping for a Noctua fan and found that picking one for this board is an adventure in 5V headers and 1.25mm pitch connectors.
The Setup #
- Board: ODROID-H2 (
HARDKERNEL / ODROID-H2), Intel Celeron J4115, Gemini Lake, UHD 600 iGPU (max 750 MHz) - RAM: 32 GB DDR4
- OS: Ubuntu, KDE Plasma 6 on Wayland (
kwin_wayland+startplasma-wayland) - Workload: Chrome with ~58 processes / 14 GB RSS, Spotify, several RDP sessions
- Input: Logitech MX Master 3 on the original Unifying receiver (046d:c52b)
- Case: ODROID-H2 “Case Type 4” translucent, stackable — bottom layer for a 2.5" HDD, short middle layer for the board, no fan, no cooler
The box runs in the same room as me, so the lag was easy to feel but hard to blame.
The Symptom #
“Mouse is lagging.” Sometimes barely perceptible, sometimes clearly stuttery — the cursor never quite keeps up. No hard freezes, no disconnects.
Diagnostic Method: Rule Out Layer By Layer #
The classic mistake is to blame the mouse first. Wireless mice can have miserable RF environments, but so do their receivers, their USB hosts, and the compositor that renders the cursor. I went bottom-up:
- Mouse itself — cross-test on another machine + firmware status
- Receiver + USB path — kernel logs, power management, port placement
- Input event stream —
evtesttimestamp analysis, the decisive test - Wayland / KDE compositor — kwin state, GPU rendering
- RF environment — other 2.4 GHz emitters on the board
Everything was done remotely over SSH with sudo, so I had plenty of room to instrument without touching the box physically.
Step 1 — The Mouse #
The hardware is Data: the MX Master 3 is a proven design, so “it’s broken” needs evidence. Installing Solaar gave me the full picture (solaar show):
- Report rate 8 ms (125 Hz), HID++ 4.5
- Battery 100 %, stable link since boot
- Firmware BOT 95.01.B0015 / MPM 19.01.B0015 — that is the current release, so there was nothing to update
One useful gotcha learned here: Solaar cannot flash firmware. Firmware updates aren’t part of the public HID++ protocol — Logitech only ships them through Logi Options+ on Windows/macOS, so unknown commands/images aren’t exposed to tools like Solaar. Good to know before going down a rabbit hole.
Step 2 — Receiver and USB Path #
The Unifying receiver sits on port 1-1 of the USB 2.0 root hub, direct (no hub),
$ lsusb
Bus 001 Device 002: ID 046d:c52b Logitech, Inc. Unifying Receiver <- 1-1
Bus 001 Device 003: ID 413c:2514 Dell Computer Corp. Dell Universal Receiver
Bus 001 Device 004: ID 0bda:8771 Realtek Semiconductor Corp. Bluetooth RadioKernel log was clean — device enumerated once at boot, no resets, no xHCI errors, no re-negotiation since:
[ 6.931588] usb 1-1: New USB device found, idVendor=046d, idProduct=c52b
[ 8.595641] input: Logitech MX Master 3 ...
[ 79.433157] logitech-hidpp-device ...: HID++ 4.5 device connected.Power management was already fine on the receiver (power/control = on). First suspect dead.
Step 3 — The Decisive Test: evtest Timestamps
#
This is the step that ends most arguments. If input events arrive at the kernel in real time, the pointer lag is not the mouse’s fault — it happens downstream (compositor/rendering).
evtest /dev/input/event6 (the MX Master 3 device). An unattended 60 s window produced exactly one stray event (lesson: tell whoever holds the mouse to actually move it). With a real 45 s capture:
- 5 449 SYN reports ≈ 121 updates/sec — matches the 125 Hz poll rate exactly
- ~10 000 relative-motion events, gap analysis shows only one 384 ms gap (hand pause) and a few sub-100 ms blips
---gap analysis---
GAP 0.384s (t=1786881575.937968 -> 1786881576.322082) # actually your hand paused
GAP 0.098s ...
GAP 0.084s ...Verdict: the mouse, receiver, and kernel path are healthy. No dropped frames.
Step 4 — Wayland / KDE and the GPU #
Now the interesting stuff. The box has a Gemini Lake UHD 600 — 12 EU, 750 MHz max, and it’s criminally underpowered for a full KDE desktop plus hardware-accelerated Chrome + Spotify. kwin was burning steady CPU, and Chrome ran a GPU process with --render-node-override=/dev/dri/renderD128.
conclusion-worthy detail in the kernel log:
[442416.407048] i915 0000:00:02.0: [drm] *ERROR* Atomic update failure on pipe A ...That’s a missed display deadline — a compositor frame that couldn’t be programmed in time. On a weak iGPU this turns into exactly the “cursor lags, input is fine” symptom I measured. It only showed up once in the whole boot, so it’s a recurring-risk rather than a hard failure, but it points straight at the compositor/GPU as the latency source after the input layer checks out.
Step 5 — The 2.4 GHz RF Environment #
Here’s what a focused look at the USB tree revealed:
- Realtek BT radio on port 1-4 with
power/control = auto(autosuspend with 2 s delay) - The Dell Bluetooth keyboard (paired to that radio) was reconnecting constantly — dozens of times across the uptime in the kernel log
Each reconnect is RF chaos on the same 2.4 GHz band the Unifying receiver uses. Realtek BT radios on autosuspend are infamous for dropping links; every drop → re-pair churn → interference bursts → intermittent mouse lag. Classic noise.
The Fixes (applied) #
1. Kill BT autosuspend (the RF churn source) #
Runtime fix plus a persistent udev rule — matches both the Logitech receiver (046d) and the Realtek BT radio (0bda):
# /etc/udev/rules.d/99-2g4-radio-autosuspend.rules
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{power/control}="on"
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{power/control}="on"2. Raise the i915 GPU floor #
Reducing render-latency spikes by keeping the GPU off the bottom of its range and past the ramp-up lag:
$ echo 400 | sudo tee /sys/class/drm/card0/gt_min_freq_mhz # was min=100, max=750
$ # persisted via /etc/udev/rules.d/99-i915-gtfreq.rules, verified with udevadm trigger --action=addResult: measurably better. The mouse still gets slightly jerky bursts under heavy Chrome load — which fits the “iGPU saturation” diagnosis from Step 4, since the input stream stays clean. That one remaining symptom is the next thing on the list (test Chrome with --disable-gpu).
Thermals (since there’s no cooler) #
Went hunting for temperatures while I was at it — a fanless board running this much must be watched:
| Sensor | Value |
|---|---|
| CPU package (cores) | 60 – 81 °C (spikes under load) |
| acpitz / chipset | 72 – 73 °C |
| NICs (r8169) | 52 – 55 °C |
| Critical / trip | 105 °C crit, acpitz trip 95 °C |
Frequencies never dropped below ~2.1 GHz and there were no throttle events in the kernel log — the dips to 2.0 GHz correlate with idle, not heat. So no thermal problem today, but 81 °C spikes on a fanless box running 24/7 isn’t margin I love. Enter the fan hunt.
The Fan Hunt: More Complicated Than Expected #
The header is NOT standard #
The ODROID-H2’s fan connector (Hardkernel’s own spec) is:
- 4-pin, 1.25 mm pitch (not the standard PC 4-pin)
- 5 V with PWM input + tacho output
- The kernel already exposes it:
cooling_device4/type = Fan(state 0 = no fan attached). Plug one in and the board’s firmware curve takes over PWM by temperature.
That means a regular 12 V Noctua fan won’t work — it won’t reach useful speed on 5 V. You need the 5 V PWM variants Noctua makes (NF-A4x10 5V, NF-A4x20 5V, NF-A6x25 5V, NF-A9 5V, NF-A12x25 5V, …).
Reader, I measured the case and got 120 mm #
Case Type 4, translucent. I didn’t have the board to hand, so I took this from my own measurements:
| Measurement | Estimate |
|---|---|
| Screw-hole spacing (H/V, centre) | ~105 mm / ~105 mm |
| Diagonal hole spacing | ~148.5 mm |
| Grommet O.D. | ~9–10 mm |
| Through-hole diameter | ~4–5 mm |
| Central opening | ~100–105 mm |
The 105 × 105 mm mounting pattern is exactly a 120 mm fan cutout (a 92 mm fan would be 82.5 mm; 140 mm would be 124.5 mm). So the case was designed for a 120 mm fan, and there is a perfect Noctua: the NF-A12x25 5V PWM — 120×120×25, mounting holes 105×105, 5 V ✓, and it ships with the OmniJoin adapter set + USB cable, which is precisely the tool for rewiring it to a proprietary 1.25 mm-pitch header.
Alternates if a 120 mm won’t clear the short mobo layer:
- NF-A4x20 5V PWM (40×20 mm) — screw/zip-tie straight onto the passive CPU heatsink; stays inside the layer
- NF-A9 5V PWM (92 mm) — if there’s a 92 mm grate in play, quietest airflow per dB
Airflow direction: blow IN #
Deliberated this properly because it’s easy to get wrong:
- Exhaust would starve it. The HDD layer is below and the board’s bottom plate blocks under-side intake, so a top exhaust fan has almost nowhere to pull fresh air from.
- Blow in (push down onto the heatsink) keeps the intake path short, cools the boards and SoC directly, and creates positive pressure that exits through the case’s side vents.
- Keep the fan as close to the heatsink as possible; if the gap is large the airflow diffuses before it does useful work.
So: Noctua side-table facing the case interior, pushing air down. Mounted outside the cover is fine if the layer lacks 25 mm of headroom.
Shopping List (amazon.de) #
These are category/search links so they don’t rot when stock changes:
- Noctua NF-A12x25 5V PWM (120 mm) — the planned fan
- Noctua NF-A4x20 PWM 5V (40 mm) — heatsink-direct fallback
- Noctua NF-A4x10 PWM 5V (40 mm slim) — when the layer is really short
- Noctua NF-A9 PWM 5V (92 mm) — quiet ceiling-fan option
- JST-GH / 1.25 mm pitch connectors — if you’d rather crimp than OmniJoin
Remember: must be the 5V PWM variant — the H2 header won’t drive a 12 V fan properly.
What’s Still Open #
- Residual jerkiness under Chrome load → the iGPU-saturation theory says test
--disable-gpuon Chrome/Spotify; if that flattens it, the 750 MHz UHD 600 is the wall and the answer is fewer tabs (58 processes / 14 GB RSS!) or a lighter browser for some tasks. - Fan install is still on the bench; the wiring (OmniJoin ↔ 1.25 mm header) is the only fiddly bit.
- Long-term temp margin — after the fan arrives: expect idle ~45–50 °C and load spikes far under the current 81 °C.
Takeaways #
- Timestamp the input events before blaming hardware.
evtestgap analysis splits “mouse problem” from “compositor problem” in one shot — here it exonerated the entire input chain. - 2.4 GHz is crowded even inside one box. A flaky BT radio’s reconnect churn can make an otherwise-fine mouse lag. Check
power/controlon every wireless device’s USB port. - “No exact firmware” ≠ old firmware. Solaar showed the latest release was already installed; and Solaar can’t flash it regardless.
- Fan shopping for SBCs needs the spec, not the pitch. 5 V header, 1.25 mm pitch, 105 mm fan pattern — three independent gotchas that would each brick a naive purchase.