diff options
| -rw-r--r-- | kernel/gsp_display_engine.c | 37 | ||||
| -rw-r--r-- | kernel/gsp_sema_service.c | 103 |
2 files changed, 134 insertions, 6 deletions
diff --git a/kernel/gsp_display_engine.c b/kernel/gsp_display_engine.c index a6dd11c..c97458c 100644 --- a/kernel/gsp_display_engine.c +++ b/kernel/gsp_display_engine.c @@ -144,6 +144,7 @@ struct gsp_disp_chan { unsigned long event_retry_at; /* next forced re-post attempt */ u32 event_drop_logs; u32 update_logs; + u32 ctrl_logs; /* Stats. */ u64 updates; @@ -1014,6 +1015,29 @@ void gsp_disp_engine_channel_add(struct nv_vgpu_device *vgpu, u32 hclass, * else is state the engine would apply to the display pipeline (head/window/ * cursor registers) which we deliberately do not render. */ +/* + * Is this UPDATE's notifier armed? + * + * NVC57D (core) SET_NOTIFIER_CONTROL carries a NOTIFY bit (12:12) and the + * class disables the notifier with ctxDMA 0 + control 0. + * + * NVC57E (window) SET_NOTIFIER_CONTROL has only MODE (0:0) and OFFSET + * (11:4); there is no NOTIFY bit. The bound notifier ctxDMA *is* the + * enable, and the class disables with ctxDMA 0 + control 0. Treating + * bit 12 as the arm bit made every window flip compute armed=0, so the + * emulation never wrote FINISHED and never posted the channel event for + * the X server's window channel (0xc57e). The DDX's composite/flip + * waits then sat out their timeout (Xorg log "Wait for channel idle + * timed out") and the display pipeline stalled. + */ +static bool gsp_disp_update_armed(const struct gsp_disp_chan *ch) +{ + if (ch->hclass == GSP_DISP_CLASS_CORE) + return (ch->notifier_ctrl & + GSP_DISP_NOTIFIER_CTRL_NOTIFY) != 0; + return ch->notifier_handle != 0 && ch->notifier_pa_valid; +} + static void gsp_disp_method(struct nv_vgpu_device *vgpu, struct gsp_disp_chan *ch, u32 addr, u32 value, u32 push_off) @@ -1048,11 +1072,15 @@ static void gsp_disp_method(struct nv_vgpu_device *vgpu, } if (addr == notifier_ctrl) { ch->notifier_ctrl = value; - if (gsp_disp_dbg) - pr_info("h=0x%x push=0x%x SET_NOTIFIER_CTRL=0x%x (notify=%u off=%u)\n", + if (gsp_disp_dbg || ch->ctrl_logs < 8) { + ch->ctrl_logs++; + pr_info("h=0x%x push=0x%x SET_NOTIFIER_CTRL=0x%x (notify=%u off=%u armed=%d handle=0x%x pa_valid=%d)\n", ch->hclass, push_off, value, !!(value & GSP_DISP_NOTIFIER_CTRL_NOTIFY), - GSP_DISP_NOTIFIER_CTRL_OFFSET(value)); + GSP_DISP_NOTIFIER_CTRL_OFFSET(value), + gsp_disp_update_armed(ch), + ch->notifier_handle, ch->notifier_pa_valid); + } return; } if (addr == GSP_DISP_M_UPDATE) { @@ -1060,8 +1088,7 @@ static void gsp_disp_method(struct nv_vgpu_device *vgpu, u64 pa = ch->notifier_pa + off_bytes; u32 target = ch->notifier_target; u32 finished = GSP_DISP_NOTIFIER_FINISHED; - bool armed = (ch->notifier_ctrl & - GSP_DISP_NOTIFIER_CTRL_NOTIFY) != 0; + bool armed = gsp_disp_update_armed(ch); bool wrote = false; ch->updates++; diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index c805fb5..5c638a5 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -9106,6 +9106,22 @@ static bool gsp_sema_gl_draw_synth; module_param_named(sema_gl_draw_synth, gsp_sema_gl_draw_synth, bool, 0644); MODULE_PARM_DESC(sema_gl_draw_synth, "Capture a draw-shaped C597 section with the dev fallback operands when the client supplied none (default 0 since r929; witness marks src=draw-synth)"); + +/* + * Apply a decoded GL release forward-only when the draw section is not + * captured. The GLX client's kickoff suffix releases the shared-surface + * refCount (sharedSurface+0x800+0x10*sd) that the X DDX's nvClipIdleChannels + * polls; if the draw section never reaches a capturable flush the release + * stayed in svc_gl/svc_gl_hold_tab and the DDX sat out its 5 s timeout + * ("Wait for channel idle timed out"). The release is the only edge the + * client needs here (no host-side draw work to order against), so applying + * it when the section is skipped is safe; forward-only + jump-bound guards + * make a duplicate/stale apply harmless. + */ +static bool gsp_sema_gl_hold_apply; +module_param_named(sema_gl_hold_apply, gsp_sema_gl_hold_apply, bool, 0644); +MODULE_PARM_DESC(sema_gl_hold_apply, + "Apply a decoded GL release forward-only when the draw section is not captured (default 0)"); #define SVC_GL_SYNTH_PROG_VA 0x121201b00ULL #define SVC_GL_SYNTH_PARAM_VA 0x1214fa220ULL #define SVC_GL_SYNTH_PARAM_SIZE 0x10u @@ -11314,6 +11330,30 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu, } out: /* + * Apply the decoded GL release forward-only, whether the section was + * captured or skipped. The GLX client's kickoff suffix releases the + * shared-surface refCount that the X DDX's nvClipIdleChannels polls; + * when the forwarded draw does not complete (or the section is not + * capturable) the release stayed held and the DDX sat out its 5 s + * timeout. The release is the only edge the client needs; the + * forward-only + jump-bound guards make a duplicate/stale apply + * harmless. Gated by sema_gl_hold_apply for A/B. + */ + if (gsp_sema_gl_hold_apply && svc_gl.rel_va && svc_gl.rel_payload) { + u32 cur = 0; + + if (svc_sem_read(vgpu, svc_gl.rel_va, &cur)) { + svc_pending_add(svc_gl.rel_va, svc_gl.rel_payload, 0); + } else if (svc_gl.rel_payload > cur && + svc_gl.rel_payload - cur <= 0x100000) { + if (!svc_sem_write(vgpu, 0, svc_gl.rel_va, + svc_gl.rel_payload)) + pr_info("sema-gl: hold applied va=0x%llx pay=%u (cur=%u)\n", + svc_gl.rel_va, svc_gl.rel_payload, + cur); + } + } + /* * SESSION 149 R15 (M1.1a): preserve the last valid release across * the reset - it belongs to the channel (decoded from the bracketing * 24 B release pushes), not to this section; wiping it here and at @@ -26390,7 +26430,22 @@ static void svc_client_slot_sweep(struct nv_vgpu_device *vgpu) if (!c->valid || c->vgpu != vgpu || !c->notifier_pa) continue; - if ((c->h_object & 0xfffff000u) != 0xbeef0000u) + /* + * CONT.387: the X DDX's own RM channels (h=0x01000014 and + * siblings, allocated by our RM for the server's nvidia.ko + * clients) use the same push-buffer NvNotification contract + * as the bfef family: pb_armNotifiers() writes 0x8000 at + * notif+0xe before a submit and pb_wait() polls it back to 0. + * The bfef-only filter skipped them, so the DDX's 8 s submit + * wait expired (Xorg log "WAIT (2, 8, 0x8000, ...)" then + * "Wait for channel idle timed out") even though the class2d + * NOTIFY ack had already zeroed the same page at decode time + * (before the arm). Include the 0x01xxxxxx RM-channel family + * in the sweep so the armed record is completed after the arm. + */ + if ((c->h_object & 0xfffff000u) != 0xbeef0000u && + (c->h_object & 0xfffff000u) != 0xbfef0000u && + (c->h_object & 0xff000000u) != 0x01000000u) continue; tab[n].h = c->h_object; tab[n].base = c->notifier_pa; @@ -26403,6 +26458,52 @@ static void svc_client_slot_sweep(struct nv_vgpu_device *vgpu) u64 page = tab[i].base & ~((u64)SEMA_PAGE_SIZE - 1); int k; + /* + * The bfef family (the GLX client's kick channel) is the X + * DDX's 2D channel. Its NvNotification status halfword is + * marked 0x8000 (IN_PROGRESS) before a submit and the GPU + * (or RM) must write it back to 0; the emulation never did, + * so the DDX's 8 s submit wait expired and its recovery + * path (dc3d0) released the channel group and then + * dereferenced it (SIGSEGV at 0x28 in nvidia_drv.so). + * Complete every 0x8000 record in the notifier page. A + * 0xffff here is the delivered-error value and is left + * alone (the beef loop below owns the 0xffff semantics). + */ + if (((tab[i].h & 0xfffff000u) == 0xbfef0000u || + (tab[i].h & 0xff000000u) == 0x01000000u) && + time_after_eq(jiffies, svc_kick_complete_at)) { + static u8 npage[SEMA_PAGE_SIZE]; + static u32 bfef_done_n; + + if (!svc_read_ap(vgpu, page, tab[i].as, npage, + sizeof(npage))) { + for (k = 0; k < SEMA_PAGE_SIZE / 16; k++) { + u16 *st = (u16 *)(npage + 0x0e + + (u64)k * 16); + u16 z = 0; + + if (*st != 0x8000) + continue; + if (svc_write_ap(vgpu, + page + 0x0e + + (u64)k * 16, + tab[i].as, &z, + sizeof(z))) + continue; + bfef_done_n++; + if (gsp_sema_dbg && bfef_done_n <= 64) + pr_info("sema-chan: bfef notif completed h=0x%x slot=%d pa=0x%llx n=%u\n", + tab[i].h, k, + page + 0x0e + + (u64)k * 16, + bfef_done_n); + } + } + } + + if ((tab[i].h & 0xfffff000u) != 0xbeef0000u) + continue; for (k = 0; k < 6; k++) { u64 pa = page + 0x0e + (u64)k * SVC_NOTIFY_SIZE; u16 st = 0, z = 0; |
