summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_sema_service.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 5c5074f..910e7ef 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -9081,6 +9081,12 @@ static unsigned long svc_put_log_at[32];
*/
static u32 svc_userd_dbg;
static u32 svc_userd_dbg_ch[32];
+/* SESSION 134: per-CE-channel budget of the first-submission ring captures
+ * (see svc_ce_firstsub_capture) and the start of an unlatched CE channel's
+ * completion hold (see svc_ce_hold_ms). Session state; reset by
+ * svc_session_flush. */
+static u32 svc_ce_firstsub_n[32];
+static unsigned long svc_ce_hold_since[32];
/*
* ISSUE-0002 (r47): deferred FB sweep for a CE channel whose layout USERD
* probe fails at discovery. Session/reset-scoped, scheduled on the first
@@ -13093,6 +13099,8 @@ static void svc_session_flush(void)
memset(svc_put_log_at, 0, sizeof(svc_put_log_at));
svc_userd_dbg = 0;
memset(svc_userd_dbg_ch, 0, sizeof(svc_userd_dbg_ch));
+ memset(svc_ce_firstsub_n, 0, sizeof(svc_ce_firstsub_n));
+ memset(svc_ce_hold_since, 0, sizeof(svc_ce_hold_since));
svc_userd_sweep_log = 0;
memset(svc_userd_sweep_at, 0, sizeof(svc_userd_sweep_at));
memset(svc_userd_sweep_n, 0, sizeof(svc_userd_sweep_n));
@@ -14674,6 +14682,162 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
* pump's completion clear before the normal path resumes. */
#define SVC_NOTIFIER_ERR_HOLD (2 * HZ)
+/*
+ * SESSION 134: CE first-submission capture.
+ *
+ * The CE (copy-engine) rings are placed by the guest's allocator at a
+ * location that varies per boot and is NOT reliably described by the
+ * channel's registered gpfifo VA through the discovered page tables (r763
+ * matched the fixed FB convention and the sweep decoded the inline-SASS
+ * entry; r765-r772 read zeros everywhere, the CE channels never latched,
+ * the driver's inline LAUNCH_DMA code downloads never executed and the
+ * frames stayed black). The logged instance where ring content and USERD
+ * were both live is the channel's first notifier clear - the guest's
+ * doorbell for its first submission, processed by the pump in the same
+ * pass that writes the ring. At that instant this hook:
+ * 1. reads entry0 through the channel's own translation (svc_lookup),
+ * 2. reads entry0 through the trusted walk (the VAS may have just
+ * landed since the registration read),
+ * 3. scans the shadow (0x100 step, first 64 MB, the GR-scan bound) for
+ * a ring-shaped page whose entry0 names a 1MB-aligned push VA in the
+ * CE window [0x120000000,0x121000000), and for every hit runs the
+ * standard USERD gate at +0x10000/+0x2000/+0x1000/+0x0; a candidate
+ * whose USERD reads coherent (or idle 0/0) is PINNED and LATCHED
+ * exactly like svc_ring_autopin's CE path.
+ * Bounded: at most 4 captures per CE channel, 16 witness rows per boot;
+ * resets with the session (svc_session_flush).
+ */
+static u64 svc_ce_firstsub_log;
+
+/*
+ * SESSION 134: bounded completion hold for unlatched CE channels.
+ *
+ * The clear write in svc_notify_complete is what makes the guest treat the
+ * submission as consumed; the driver then recycles the ring and its content
+ * is gone before the client's VAS lands (sema_vas_cand_ms), leaving the CE
+ * ring latch with nothing readable (r769-r774: rrc=-2 at the first
+ * submission, the walk answering a zero page later). Holding the notifier
+ * busy for a bounded window keeps the driver's submission in flight - and
+ * with it the ring content that the regular per-pass latch path
+ * (svc_chan_userd through svc_lookup) needs - until the VAS registers and
+ * the ring reads coherently. 0 = off (the pre-SESSION-134 behaviour).
+ */
+static u64 svc_ce_hold_ms;
+module_param_named(sema_ce_hold_ms, svc_ce_hold_ms, ullong, 0644);
+MODULE_PARM_DESC(sema_ce_hold_ms, "Hold an unlatched CE channel's completion open for up to N ms so the ring stays live for the latch (0 = off)");
+
+static void svc_ce_firstsub_capture(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch)
+{
+ u8 *sbase = vgpu->bars[1].vaddr;
+ u64 lim = min_t(u64, svc_fb_shadow_size(vgpu), 0x4000000ULL);
+ u64 ring_pa = 0, wpa = 0;
+ u32 e0 = 0, e1 = 0, walk_e0 = 0, walk_e1 = 0;
+ u32 scan_hits = 0, latched = 0;
+ u64 latch_off = 0, latch_upa = 0;
+ u32 latch_put = 0, latch_get = 0;
+ int ring_ap = 0, wap = SVC_APERTURE_SYS;
+ int rrc = -1, wrc = -1;
+
+ if (ch->engine_type == 0 || ch->ce_index >= 32)
+ return;
+ if (svc_ce_firstsub_n[ch->ce_index] >= 4)
+ return;
+ svc_ce_firstsub_n[ch->ce_index]++;
+
+ if (sbase && lim >= 0x1000) {
+ /* 1. the channel's own translation at this instant. */
+ rrc = svc_lookup(vgpu, ch->gpfifo_va, &ring_pa, &ring_ap);
+ if (!rrc && ring_pa) {
+ u8 e[8];
+
+ if (!svc_read_ap(vgpu, ring_pa, ring_ap, e,
+ sizeof(e))) {
+ memcpy(&e0, e, 4);
+ memcpy(&e1, e + 4, 4);
+ }
+ }
+ /* 2. the trusted walk at this instant. */
+ if (!svc_lookup_trusted(vgpu, ch->gpfifo_va, &wpa, &wap)) {
+ u8 e[8];
+
+ wrc = 0;
+ if (!svc_read_ap(vgpu, wpa, svc_ap_of(wap), e,
+ sizeof(e))) {
+ memcpy(&walk_e0, e, 4);
+ memcpy(&walk_e1, e + 4, 4);
+ }
+ }
+ /* 3. shadow scan for a CE-window ring page. */
+ {
+ u64 off;
+
+ for (off = 0; off + 0x90 <= lim; off += 0x100) {
+ static const u64 udeltas[] = { 0x10000, 0x2000,
+ 0x1000, 0x0 };
+ u32 h0, h1, hva, hlen;
+ unsigned int ui;
+
+ memcpy(&h0, sbase + off, 4);
+ memcpy(&h1, sbase + off + 4, 4);
+ hva = ((h1 & 0xff) << 32) | (h0 & 0xfffffffc);
+ hlen = ((h1 >> 10) & 0x1fffff) << 2;
+ if ((hva & 0xfffff) || hva < 0x120000000ULL ||
+ hva >= 0x121000000ULL || hlen < 4 ||
+ hlen > 0x10000)
+ continue;
+ scan_hits++;
+ for (ui = 0; ui < ARRAY_SIZE(udeltas); ui++) {
+ u64 upa = off + udeltas[ui];
+ u32 p3 = 0, g3 = 0;
+ int prc;
+
+ if (upa + 4 > lim)
+ continue;
+ prc = svc_userd_probe(vgpu, upa,
+ SVC_APERTURE_FB,
+ ch->gpfifo_entries,
+ &p3, &g3);
+ if (prc && prc != -EAGAIN)
+ continue;
+ /* latch exactly like the CE autopin */
+ svc_pin_store(ch->gpfifo_va, off,
+ SVC_APERTURE_FB);
+ svc_lat_store(ch->gpfifo_va, off,
+ SVC_APERTURE_FB);
+ ch->userd_pa = upa;
+ ch->userd_as = SVC_APERTURE_FB;
+ ch->userd_ok = true;
+ set_bit(ch->ce_index, &svc_ring_done);
+ svc_ring_n++;
+ latched = 1;
+ latch_off = off;
+ latch_upa = upa;
+ latch_put = p3;
+ latch_get = g3;
+ break;
+ }
+ if (latched)
+ break;
+ }
+ }
+ }
+
+ if (gsp_sema_dbg && svc_ce_firstsub_log < 16) {
+ svc_ce_firstsub_log++;
+ pr_info("sema-cefirst: h=0x%x rank=%u n=%u rrc=%d rpa=0x%llx rap=%d e0=0x%x e1=0x%x wrc=%d wpa=0x%llx wap=%d we0=0x%x we1=0x%x scan_hits=%u latch=%u off=0x%llx upa=0x%llx put=%u get=%u\n",
+ ch->h_object, ch->ce_index,
+ svc_ce_firstsub_n[ch->ce_index], rrc, ring_pa,
+ ring_ap, e0, e1, wrc, wpa, wap, walk_e0, walk_e1,
+ scan_hits, latched, latch_off, latch_upa,
+ latch_put, latch_get);
+ }
+ if (latched)
+ pr_info("sema-cefirst: LATCHED h=0x%x rank=%u va=0x%llx -> fb 0x%llx (userd 0x%llx put=%u get=%u; first-submission capture)\n",
+ ch->h_object, ch->ce_index, ch->gpfifo_va,
+ latch_off, latch_upa, latch_put, latch_get);
+}
+
static void svc_notify_complete(struct nv_vgpu_device *vgpu,
struct svc_channel *ch)
{
@@ -14726,6 +14890,41 @@ static void svc_notify_complete(struct nv_vgpu_device *vgpu,
return;
/*
+ * SESSION 134: a visible doorbell means a submission just landed -
+ * for a CE-class channel this is the one instant its ring content
+ * and USERD are both live. Capture/snapshot it BEFORE the clear
+ * write below (the clear is what makes the guest treat the
+ * submission as complete and recycle the ring), see
+ * svc_ce_firstsub_capture.
+ */
+ if (ch->engine_type)
+ svc_ce_firstsub_capture(vgpu, ch);
+
+ /*
+ * SESSION 134: keep an unlatched CE submission open so the ring
+ * content survives until the latch can read it (see
+ * svc_ce_hold_ms). The regular per-pass latch path retries while
+ * the hold runs; the hold expires un-latched into the old
+ * behaviour, so a channel the emulation can never resolve cannot
+ * strand the guest.
+ */
+ if (ch->engine_type && !ch->userd_ok && svc_ce_hold_ms &&
+ ch->ce_index < 32) {
+ if (!svc_ce_hold_since[ch->ce_index])
+ svc_ce_hold_since[ch->ce_index] = jiffies;
+ if (time_before(jiffies,
+ svc_ce_hold_since[ch->ce_index] +
+ msecs_to_jiffies(svc_ce_hold_ms)))
+ return;
+ if (svc_ce_firstsub_log < 16) {
+ svc_ce_firstsub_log++;
+ pr_info("sema-cefirst: h=0x%x rank=%u completion hold expired (%llu ms) unlatched; clearing\n",
+ ch->h_object, ch->ce_index, svc_ce_hold_ms);
+ }
+ svc_ce_hold_since[ch->ce_index] = jiffies;
+ }
+
+ /*
* Clear both slots: they are adjacent, and whichever one the client is
* polling must read as completed for a healthy channel.
*/