ui/vnc: virtio-gpu page flips drop queued VNC updates (stale pixels, stuck client)
Host environment
- Operating system: Fedora 44 (reproducer container); also seen in production on KubeVirt virt-launcher
- OS/kernel version: Linux x86_64
- Architecture: x86_64
- QEMU flavor: qemu-system-x86_64
- QEMU version: master 73294d09 (11.1.50); the bug dates from 4.0
- QEMU command line (reproducer):
qemu-system-x86_64 -nodefaults -machine q35 -accel kvm -smp 2 -m 512 \
-kernel vmlinuz -initrd initrd.img -append "console=ttyS0 quiet loglevel=1 rdinit=/init" \
-device virtio-gpu-pci -vnc unix:vnc.sock \
-qmp unix:qmp.sock,server=on,wait=off \
-chardev socket,id=ser,path=ser.sock,server=on,wait=on -serial chardev:serEmulated/Virtualized environment
- Operating system: Linux guest with virtio-gpu (production: GNOME/mutter on Wayland; reproducer: a minimal static init that page-flips with DRM SETCRTC)
- Architecture: x86_64
Description of problem
A VNC client keeps stale pixels (corrupted display output) after a virtio-gpu page flip, and in some cases stops receiving updates entirely, until it reconnects.
vnc_update_client() clears a client's dirty bits and moves vs->update into vs->job_update when it queues a job. Every virtio-gpu page flip (SET_SCANOUT to another resource) replaces the display surface: vnc_dpy_switch() -> vnc_abort_display_jobs(), which discards any job the worker thread has not started. Two failures follow:
- Lost pixels. On the page-flip path
vnc_dpy_switch()re-dirties only the guest dirty map. The server surface already holds the dropped rectangles' pixels, sovnc_refresh_server_surface()never marks them dirty for the client again. The client keeps the old pixels until those chunks change again or it sends a non-incremental request. On a GNOME/Wayland guest this shows up as scattered 16-pixel slivers of old text after fast scrolling in a terminal. - Stuck client. If the client sent another request while the job was queued (
vs->update != VNC_STATE_UPDATE_NONE), the dropped job'svs->job_updateis never cleared, andvnc_should_update()refuses every later update for that client, including forced ones.
Related commits: 61e77a5f ("vnc: detect and optimize pageflips", 4.0) introduced the lost pixels; ada8d2e4 (2.12) added job_update; bbcdeb62 ("vnc: fix update stalls", 4.0) fixed the stall only for the update == NONE case.
Steps to reproduce
The attached qemu-vnc-pageflip-repro.tar.gz builds QEMU master unpatched and patched inside a Fedora container (nothing installed on the host), boots a tiny guest that shows a screen of text and then flips between two black buffers at 60 Hz, and connects VNC clients that behave like noVNC (Raw encoding, one incremental request always outstanding). Once the guest is static it compares each client's framebuffer with QMP screendump.
- Extract the tarball on an x86_64 Linux host with podman and
/dev/kvm. ./run.sh --clients 4 --flips 20 --cycles 150(lost pixels)./run.sh --clients 2 --flips 20 --cycles 60 --force-every 10(stuck client)
Results land in ~/qemu-vnc-repro/results. Each pass takes 5-10 minutes.
Additional information
Results on master 73294d09 (x86_64, KVM):
| Run | Unpatched | Patched |
|---|---|---|
| 4 clients, 150 cycles | 35 cycles lost the switch to black (78 client failures: client0 0, client1 18, client2 25, client3 35) | 0 |
| 2 clients, 40 cycles of 400 flips | 1 cycle | 0 |
| 2 clients + non-incremental request every 10 ms, 60 cycles | client1 got no update from cycle 13 to the end of the run | 0 |
| 1 client, 450 cycles | 0 | 0 |
Every lost update was healed by one non-incremental request. Failures follow queue position: the worker starts the first client's job at once and a flip discards the jobs waiting behind it. The single-client case did not reproduce on an idle host, but we see it with a single VNC client on loaded KubeVirt nodes.
Attached: unpatched-client-stale.png (a client's framebuffer still showing the text screen) and unpatched-server.png (QMP screendump at the same moment: solid black).
Patch: https://lists.nongnu.org/archive/html/qemu-devel/2026-10/msg08419.html
qemu-vnc-pageflip-repro.tar.gz
