summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-08 06:39:50 -0500
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-08 06:39:50 -0500
commitcfa2bf216065c87a185e83a8bf38a6c5dd5788c8 (patch)
tree58495ecf023b18f990cb503bb5fb160b622bb438 /kernel
parent0bfd7a693a9b59def9e1494d34b61794ae04930d (diff)
downloadopen-iov-cfa2bf216065c87a185e83a8bf38a6c5dd5788c8.tar.gz
open-iov-cfa2bf216065c87a185e83a8bf38a6c5dd5788c8.zip
vgsp-disp: never publish GET past PUT; bounded no-parse push dump
r1630: the window channel's first push read returned unparseable bytes (0 methods) and the parser walked off the 4 KB ring, publishing GET=0x1000 while PUT stayed 0x4e4. nvidia-modeset then wedged in 'Error while waiting for GPU progress', Xorg never started and glxinfo/ GZDoom never reached the GL path. Only consume a segment that parses exactly to the driver's PUT. On a short/invalid read, leave consumed unchanged: the pump retries next pass (which also covers a ring the guest kicked before its content is visible) and GET can never move past PUT. Dump the head of an unparseable segment (bounded, disp_dbg) to name what the ring holds. Reset dump budgets per mdev cleanup so each boot keeps its diagnostics.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_display_engine.c30
1 files changed, 30 insertions, 0 deletions
diff --git a/kernel/gsp_display_engine.c b/kernel/gsp_display_engine.c
index c2fab96..5109245 100644
--- a/kernel/gsp_display_engine.c
+++ b/kernel/gsp_display_engine.c
@@ -972,6 +972,32 @@ static void gsp_disp_run(struct nv_vgpu_device *vgpu, struct gsp_disp_chan *ch,
}
}
+ /*
+ * Only publish a read pointer when the segment parsed through to the
+ * driver's PUT. A segment that runs off the 4 KB ring (unwritten or
+ * not-yet-visible content) or hits misaligned data must NOT consume
+ * anything: publishing GET past PUT wedged nvidia-modeset on the
+ * window channel (r1630: GET=0x1000, "Error while waiting for GPU
+ * progress"). Leaving consumed unchanged also retries next pass,
+ * which covers a ring the guest has kicked but not yet flushed.
+ */
+ if (off != put) {
+ if (gsp_disp_dbg && disp.push_dumps < 8) {
+ u32 d, shown = 16;
+
+ disp.push_dumps++;
+ pr_info("h=0x%x push-noparse 0x%x..0x%x (consumed stays 0x%x) head:",
+ ch->hclass, start, put, ch->consumed);
+ if (put > start && put - start < shown)
+ shown = put - start;
+ for (d = 0; d < shown; d++)
+ pr_cont(" %08x",
+ *(u32 *)(disp.pbuf + start + 4 * d));
+ pr_cont("\n");
+ }
+ return;
+ }
+
ch->consumed = off;
ch->last_put = put;
if (gsp_disp_dbg && off != start)
@@ -1111,6 +1137,10 @@ void gsp_disp_engine_vgpu_cleanup(struct nv_vgpu_device *vgpu)
unsigned long flags;
int i;
+ /* Fresh diagnostic budgets for the next mdev/boot. */
+ disp.push_dumps = 0;
+ disp.im_logs = 0;
+
spin_lock_irqsave(&disp.lock, flags);
for (i = 0; i < GSP_DISP_MAX_CHANNELS; i++) {
if (disp.ch[i].valid && disp.ch[i].vgpu == vgpu) {