From f6184ac3fa8e4a8546bf2d7807be27d23751cc01 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Wed, 11 Nov 2026 04:59:29 -0500 Subject: vgsp: ring-ledger scrub release + gated flip-event fixes (CONT.418-419) - gsp_sema_service: never fabricate scrub completions. The guest decrements its scrub-list size by the reported finish delta without a guard, so a fabricated delta larger than the pending list underflows it and the guest spins (~2^64) under the scrubber mutex (r1766-r1768 X soft-locks and guest panics; root-caused by static analysis + gdbstub decode of lastSeen/lastSubmitted/scrubListSize). The release now mirrors the registered channel's own ring: count new valid pushes (wrap-safe snapshot delta) and set the finish word forward-only to the cumulative count - exactly the work the guest submitted, never more. False ring-page registrations are detected and skipped (a mis-registered ring page had its first GPFIFO entry clobbered by an all-ones write). sema_scrub_ramp=0 keeps the legacy all-ones release for A/B; the finish word is never all-ones with the ledger enabled. - gsp_rm_emulation/gsp_display_engine: gate the hSrcResource==0 -> alloc parent event association behind ev_assoc (default 0) and keep strict display-event posting behind disp_events_strict (default 0) - both remain A/B knobs for the flip-event delivery fix. - Live validation (r1769): 4/4 green boots, 0 panics, 0 wedges; glprobe pixel=ff0000ff; gl_frames 500/500 non-black frames captured to a 45 s 640x480 mp4 via pbuffer -> sysmem -> ivshmem. --- kernel/gsp_display_engine.c | 61 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) (limited to 'kernel/gsp_display_engine.c') diff --git a/kernel/gsp_display_engine.c b/kernel/gsp_display_engine.c index c97458c..98f85fe 100644 --- a/kernel/gsp_display_engine.c +++ b/kernel/gsp_display_engine.c @@ -212,6 +212,19 @@ static unsigned gsp_disp_events = 1; module_param_named(disp_events, gsp_disp_events, uint, 0644); MODULE_PARM_DESC(disp_events, "Post the channel completion event on an armed UPDATE (default 1)"); +/* + * r1765: strict display-event delivery. Default 0 keeps the legacy + * behaviour bit-for-bit (full A/B revert); when set, an armed UPDATE posts + * only to the events registered on that channel (no all-events fallback), + * and unrelated completions/the 0.5 s hint skip display-channel events + * (see gsp_disp_engine_owns_object()). Live A/B: echo 1 > + * /sys/module/vgsp/parameters/disp_events_strict. + */ +static unsigned gsp_disp_events_strict = 0; +module_param_named(disp_events_strict, gsp_disp_events_strict, uint, 0644); +MODULE_PARM_DESC(disp_events_strict, + "Deliver a display UPDATE completion only to events registered on that channel, no all-events fallback (default 0 = legacy; 1 = r1765 strict)"); + static unsigned long long gsp_disp_notifier_pa; module_param_named(disp_notifier_pa, gsp_disp_notifier_pa, ullong, 0644); MODULE_PARM_DESC(disp_notifier_pa, "Explicit notifier surface guest PA override (0 = resolve from the display instance table)"); @@ -1009,6 +1022,36 @@ void gsp_disp_engine_channel_add(struct nv_vgpu_device *vgpu, u32 hclass, hclass, instance, ch->h_object, pb_pa, ch->limit, ch->ctl_off); } +/* + * r1765 flip-event shield predicate: is @h_object a registered display + * channel object of @vgpu whose events must stay off the all-events + * shotgun? Called by gsp_rm_emulation.c before nudging an event in the + * fallback paths. Only meaningful while disp_events_strict is enabled - + * with the legacy default (0) this always returns false, so every shield + * site is inert and the shotgun path is unchanged. + */ +bool gsp_disp_engine_owns_object(struct nv_vgpu_device *vgpu, u32 h_object) +{ + unsigned long flags; + bool owned = false; + int i; + + if (!gsp_disp_engine || !gsp_disp_events_strict || !vgpu || !h_object) + return false; + + 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 && + disp.ch[i].h_object == h_object) { + owned = true; + break; + } + } + spin_unlock_irqrestore(&disp.lock, flags); + return owned; +} +EXPORT_SYMBOL_GPL(gsp_disp_engine_owns_object); + /* * Apply one parsed method. Only the completion-notifier programming and the * UPDATE completion point carry engine semantics for the boot; everything @@ -1138,7 +1181,16 @@ static void gsp_disp_method(struct nv_vgpu_device *vgpu, * layer retry (Xorg spun there at 100 % in r1665). */ if (gsp_disp_events && ch->h_object && wrote) { - int rc = gsp_rm_post_channel_event(vgpu, ch->h_object); + /* + * r1765: strict mode delivers only to the events + * registered on this channel; a miss returns -ENOENT + * and the pump retries instead of shotgunning every + * registered event. + */ + int rc = (gsp_disp_events_strict ? + gsp_rm_post_channel_event_strict : + gsp_rm_post_channel_event)(vgpu, + ch->h_object); if (!rc) { ch->notifier_posts++; @@ -1430,7 +1482,12 @@ void gsp_disp_engine_pump(struct nv_vgpu_device *vgpu) */ if (s->event_pending && s->h_object && gsp_disp_events && time_after_eq(jiffies, s->event_retry_at)) { - if (!gsp_rm_post_channel_event_force(vgpu, s->h_object)) { + /* r1765: retry with the same strict/legacy entry + * point the UPDATE post used. */ + if (!(gsp_disp_events_strict ? + gsp_rm_post_channel_event_force_strict : + gsp_rm_post_channel_event_force)(vgpu, + s->h_object)) { s->event_pending = false; s->notifier_posts++; pr_info("h=0x%x UPDATE completion event re-posted after deferral (posts=%u)\n", -- cgit v1.2.3