summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_display_engine.c37
-rw-r--r--kernel/gsp_sema_service.c103
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;