From f10714c927bab9f93ddaeb2a6786b42cb41264a3 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Mon, 9 Nov 2026 08:56:21 -0500 Subject: vgsp: retry deferred display completions; escalate the UVM init-slot release Two live walls from the r1649/r1650 windowed-GZDoom runs: 1. Display completion wake-up. All NV01_EVENT registrations carry hSrcResource=0, so gsp_rm_post_channel_event() always takes the shotgun fallback, which skips events posted within the 250 ms hint dedup. When the display UPDATE is the channel's last push, the pump's retry was gated on new work (put != consumed) and never ran, so the X server's flip wait spun in _nv018351rm/os_schedule forever. Add gsp_rm_post_channel_event_force() (bypasses only the hint dedup; the event burst cap and status-ring free-space discipline still apply), a per-channel event_retry_at, and an idle-pass retry every 250 ms that logs "re-posted after deferral". Also move event_burst_at after the cap check so refused retries cannot keep the window fresh. 2. UVM channel init slot. r1651 gdb proved the channel's tracker entry waits for value 2 while the authored pool word reads 1; a manual write of 2 cleared uvm_push_end_and_wait. The channel's control-entry + init pushes are boot-time configuration, so keep the rank slot moving for 10 s after first submission: one attempt per 250 ms per rank, escalating 1->2->3->4 through every published VAS translation. Values beyond that need the real CE push decode. Evidence: backups/evidence-20261002-victory/r1649/ (X spin perf, event journal), r1650/ (re-post lines), r1651/ (tracker entry value=2, manual write clears the spin). --- kernel/gsp_display_engine.c | 75 +++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 27 deletions(-) (limited to 'kernel/gsp_display_engine.c') diff --git a/kernel/gsp_display_engine.c b/kernel/gsp_display_engine.c index f96fc6a..c7c9dca 100644 --- a/kernel/gsp_display_engine.c +++ b/kernel/gsp_display_engine.c @@ -101,6 +101,11 @@ #define GSP_DISP_NOTIFIER_CTRL_NOTIFY BIT(12) /* The CONTROL offset field is in 16-byte units; expose it in bytes. */ #define GSP_DISP_NOTIFIER_CTRL_OFFSET(c) ((((c) >> 4) & 0xff) * 16) +/* Coalesce a refused completion wake-up, then re-post it. Must be at least + * the RM event hint dedup window (HZ / 4 = 250 ms) so the retry is not + * coalesced into the same post that starved it; at most four attempts/s per + * waiting channel. */ +#define GSP_DISP_EVENT_RETRY_MS 250 /* Display instance memory (v03_00 layout). */ #define GSP_DISP_INST_HASH_OFF 0x0000 @@ -136,6 +141,7 @@ struct gsp_disp_chan { u32 notifier_writes; u32 notifier_posts; bool event_pending; /* completion event send failed; retry in pump */ + unsigned long event_retry_at; /* next forced re-post attempt */ u32 event_drop_logs; u32 update_logs; @@ -1115,6 +1121,8 @@ static void gsp_disp_method(struct nv_vgpu_device *vgpu, * pump instead of giving up. */ ch->event_pending = true; + ch->event_retry_at = jiffies + + msecs_to_jiffies(GSP_DISP_EVENT_RETRY_MS); if (ch->event_drop_logs < 8) { ch->event_drop_logs++; pr_info("h=0x%x UPDATE completion event deferred rc=%d (hObj=0x%x)\n", @@ -1353,39 +1361,51 @@ void gsp_disp_engine_pump(struct nv_vgpu_device *vgpu) s->hclass, s->instance, put, s->consumed, s->ctl_off); } - if (put == s->consumed) - continue; - - gsp_disp_run(vgpu, s, put); + if (put != s->consumed) { + gsp_disp_run(vgpu, s, put); - /* - * Publish our read pointer; the driver's nvEvoMakeRoom and - * EvoCoreReadGet rely on it to reuse the ring. - */ - gsp_disp_bar0_write(vgpu, s->ctl_off + GSP_DISP_REG_GET, - s->consumed); - get = gsp_disp_bar0_read(vgpu, - s->ctl_off + GSP_DISP_REG_GET); - - if (disp.log_budget < 4096) { - u64 age = (u64)(now - disp.boot_jiffies) * - (NSEC_PER_SEC / HZ); - - disp.log_budget++; - pr_info("pump h=0x%x inst=%u put=0x%x get=0x%x consumed=0x%x at %llu ms\n", - s->hclass, s->instance, put, get, s->consumed, - div_u64(age, 1000000)); + /* + * Publish our read pointer; the driver's nvEvoMakeRoom + * and EvoCoreReadGet rely on it to reuse the ring. + */ + gsp_disp_bar0_write(vgpu, s->ctl_off + GSP_DISP_REG_GET, + s->consumed); + get = gsp_disp_bar0_read(vgpu, + s->ctl_off + GSP_DISP_REG_GET); + + if (disp.log_budget < 4096) { + u64 age = (u64)(now - disp.boot_jiffies) * + (NSEC_PER_SEC / HZ); + + disp.log_budget++; + pr_info("pump h=0x%x inst=%u put=0x%x get=0x%x consumed=0x%x at %llu ms\n", + s->hclass, s->instance, put, get, + s->consumed, div_u64(age, 1000000)); + } } /* - * Retry a completion event whose send was refused (burst - * cap / transient queue state). Runs outside disp.lock: - * the post takes the RM status-queue lock. + * Retry a completion event whose send was refused (burst cap / + * transient queue state, or the hint dedup coalescing it into a + * post the waiter never saw). Runs outside disp.lock (the post + * takes the RM status-queue lock) and on idle passes too: the + * fatal case is an UPDATE that is the channel's last push, so a + * retry gated on new work never runs (r1649). The forced post + * ignores the per-event hint dedup; the retry window bounds it + * to one attempt per 250 ms per waiting channel while the burst + * cap still guards the status ring. */ if (s->event_pending && s->h_object && gsp_disp_events && - !gsp_rm_post_channel_event(vgpu, s->h_object)) { - s->event_pending = false; - s->notifier_posts++; + time_after_eq(jiffies, s->event_retry_at)) { + if (!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", + s->hclass, s->notifier_posts); + } else { + s->event_retry_at = jiffies + + msecs_to_jiffies(GSP_DISP_EVENT_RETRY_MS); + } } /* Write the bookkeeping back if the slot still names this @@ -1412,6 +1432,7 @@ void gsp_disp_engine_pump(struct nv_vgpu_device *vgpu) c->notifier_writes = s->notifier_writes; c->notifier_posts = s->notifier_posts; c->event_pending = s->event_pending; + c->event_retry_at = s->event_retry_at; c->event_drop_logs = s->event_drop_logs; c->update_logs = s->update_logs; c->logged_update = s->logged_update; -- cgit v1.2.3