summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c472
1 files changed, 429 insertions, 43 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 993e507..c50832e 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -1586,12 +1586,26 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
* channel; the stashed geometry survives, so the scrub path
* does not depend on the channel-table identity at all.
*/
- if (desc->gpfifo_entries >= 4096 && desc->gpfifo_va &&
+ /*
+ * m112b: the CeUtils scrubber is the ONLY exactly-4096-entry
+ * channel (NUM_COPY_BLOCKS). The old `>= 4096` test also
+ * matched every client channel (this driver version: 32768
+ * entries, not the 1024 the comment below assumed), so the
+ * single scrub slot bound to whichever client channel
+ * registered last (observed r701: the slot held h=0xbeee0102
+ * userd=0x8f400/entries=32768 and the pass authored put=0 for
+ * the wrong channel while the real scrubber's payload stayed
+ * zero - the vkAllocateMemory -2 gate).
+ */
+ if (desc->gpfifo_entries == 4096 && desc->gpfifo_va &&
desc->userd_pa) {
struct svc_scrub *s = svc_scrub_get(desc->vgpu, true);
if (!s)
goto scrub_done; /* table full: max guests */
+ pr_info("sema-svc: scrub stash bind vgpu=%px slot=%d h=0x%x entries=%u\n",
+ (void *)desc->vgpu, (int)(s - svc_scrubs),
+ desc->h_object, desc->gpfifo_entries);
/* Re-seed the counting on a fresh scrubber object or a
* new client session (m176: the counter/submitted state
* is per-session; a recycled scrubber handle after a
@@ -1616,6 +1630,16 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
desc->h_object, desc->gpfifo_va,
desc->gpfifo_entries, desc->userd_pa,
s->userd_as);
+ } else if (desc->gpfifo_entries >= 4096 &&
+ desc->gpfifo_entries != 32768) {
+ /* m112b: a non-32768 channel with >=4096 entries that
+ * fails the stash condition (missing VA or USERD PA)
+ * is a scrubber candidate the service cannot author
+ * for - log it so a silent no-authoring boot is
+ * diagnosable. */
+ pr_info_ratelimited("sema-svc: scrubber-stash MISS h=0x%x entries=%u va=0x%llx userd_pa=0x%llx\n",
+ desc->h_object, desc->gpfifo_entries,
+ desc->gpfifo_va, desc->userd_pa);
}
scrub_done:
pr_info("sema-svc: channel 0x%x registered gpfifo=0x%llx entries=%u userd=0x%llx eng=%u cid=%u notif=0x%llx\n",
@@ -1814,6 +1838,17 @@ void gsp_sema_service_vaspace_candidate_add(u32 h_vaspace, u64 pdb,
* the lesser evil; it is logged loudly for the record.
*/
#define SVC_VAS_CAND_AGE_MS 15000u
+/*
+ * r705h: the candidate age-out deadline is runtime-tunable. The parked
+ * COPY_SERVER_PDES candidate for the GL/Vulkan client ages out (UNVERIFIED)
+ * at exactly the moment the client's channels first register, so the
+ * content-verified walk can never anchor it; raising the deadline keeps the
+ * candidate parked until a host tool supplies a verified ring pair via
+ * sema_pdb_discover. 0 disables the age-out entirely.
+ */
+static u32 svc_vas_cand_age_ms = SVC_VAS_CAND_AGE_MS;
+module_param_named(sema_vas_cand_ms, svc_vas_cand_age_ms, uint, 0644);
+MODULE_PARM_DESC(sema_vas_cand_ms, "Parked-VAS-candidate age-out (ms, default 15000; 0 = never)");
static void svc_vas_candidates_age_out(struct nv_vgpu_device *vgpu)
{
@@ -1824,8 +1859,9 @@ static void svc_vas_candidates_age_out(struct nv_vgpu_device *vgpu)
if (!svc_vas_cand[i].valid)
continue;
- if (time_before(jiffies, svc_vas_cand[i].stamp +
- msecs_to_jiffies(SVC_VAS_CAND_AGE_MS)))
+ if (!svc_vas_cand_age_ms ||
+ time_before(jiffies, svc_vas_cand[i].stamp +
+ msecs_to_jiffies(svc_vas_cand_age_ms)))
continue;
memset(&d, 0, sizeof(d));
d.h_vaspace = svc_vas_cand[i].h_vaspace;
@@ -1833,7 +1869,7 @@ static void svc_vas_candidates_age_out(struct nv_vgpu_device *vgpu)
d.num_entries = svc_vas_cand[i].entries;
d.b_vidmem = svc_vas_cand[i].vidmem;
pr_warn("sema-svc: VAS 0x%x page directory registered UNVERIFIED after %u ms parked (pdb=0x%llx entries=%u; the content-verified ring walk never anchored this boot)\n",
- d.h_vaspace, SVC_VAS_CAND_AGE_MS, d.pdb,
+ d.h_vaspace, svc_vas_cand_age_ms, d.pdb,
d.num_entries);
gsp_sema_service_vaspace_add(&d);
svc_vas_cand[i].valid = false;
@@ -2984,6 +3020,15 @@ struct svc_ipin_alt {
static struct svc_ipin_alt svc_ipin_alt[SVC_IPIN_ALT_SLOTS];
static u64 svc_pushalt_hits; /* push reads served by a displaced pair */
static u32 svc_pushalt_log;
+/* r707x: per-client push-read attempt witness (pump read site). */
+static u32 svc_rdw_log;
+static u8 svc_read_last_src;
+/* r715: PA the winning source of the last push read resolved to. */
+static u64 svc_read_last_pa;
+/* r715: per-client push-decode branch witness (pump read site). */
+static u32 svc_pushdec_log;
+/* r707d: bounded push-read failure witness (see svc_read_va_push_ex). */
+static u32 svc_pushfail_log;
static int svc_ipin_alt_lookup(u64 va, u64 *pa, int *ap)
{
@@ -8848,18 +8893,20 @@ static bool svc_stream_parse(const u8 *buf, u32 len)
memcpy(&hdr, buf + off, 4);
if (hdr == 0) {
/*
- * UVM's large page-table pushes begin with a
- * zero-filled header region (observed 0x1004
- * bytes) before the first method, so zeros before
- * the first method are leading padding; zeros
- * after one are trailing padding and end the
- * stream.
+ * NVC06F_DMA_NOP is 0x00000000: a zero word is
+ * NOP padding. UVM's large page-table pushes begin
+ * with a zero-filled header region (observed 0x1004
+ * bytes) and the GR submit pushes carry zero gaps
+ * between method groups (observed r715: the 1432-
+ * and 17316-byte pushes pad between the context
+ * setup and the dispatch groups), so zeros are
+ * skipped anywhere in the stream, not only before
+ * the first method or at the tail. The exact-end
+ * requirement and the valid-header walk below are
+ * the false-positive arbiters.
*/
- if (!methods) {
- off += 4;
- continue;
- }
- break; /* trailing zero padding */
+ off += 4;
+ continue;
}
op = (hdr >> 29) & 0x7;
count = (hdr >> 16) & 0xfff;
@@ -8879,16 +8926,27 @@ static bool svc_stream_parse(const u8 *buf, u32 len)
return false;
off += 4 + count * 4;
break;
+ case 0: /* GRP0_USE_TERT: SET/STORE/USE_SUB_DEV_MASK */
+ case 2: /* GRP2_USE_TERT */
case 4: /* IMMD_DATA_METHOD: header only */
- off += 4;
- break;
case 7: /* END_PB_SEGMENT: header only */
+ /*
+ * r715: the class header defines op 0 as
+ * GRP0_USE_TERT (0) and op 2 as GRP2_USE_TERT -
+ * 4-byte header-only formats. The GR submit
+ * pushes emit 0x0001fff0 (SET_SUBDEVICE_MASK,
+ * value 0xfff) around their semaphore groups; the
+ * old parser rejected the first one on every big
+ * push, so the whole push read implausible and
+ * the entries were skipped without decode
+ * (session 124's consume-without-decode).
+ */
off += 4;
break;
default:
return false;
}
- if (++methods > 1024)
+ if (++methods > 8192)
return false;
}
while (off < len && buf[off] == 0)
@@ -12655,6 +12713,10 @@ static void svc_gr_defer_reset(void);
static void svc_session_flush(void)
{
+ /* r707x: per-client witness budgets. */
+ svc_pushfail_log = 0;
+ svc_rdw_log = 0;
+ svc_pushdec_log = 0;
unsigned long flags;
u32 i;
@@ -13835,7 +13897,14 @@ static bool svc_push_prefix_ok(const u8 *buf, size_t len)
if (off + 4 > len)
return true; /* all zeros: not refutable here */
op = (hdr >> 29) & 0x7;
- if (op == 4 || op == 7) /* header-only methods */
+ /*
+ * r715: op 0 (GRP0_USE_TERT: SET/STORE/USE_SUB_DEV_MASK) and op 2
+ * (GRP2_USE_TERT) are 4-byte header-only formats per the class
+ * header; the GR submit pushes start with 0x0001fff0
+ * (SET_SUBDEVICE_MASK), so a correct injected pin for such a page
+ * was rejected here and the read fell through to the walk.
+ */
+ if (op == 0 || op == 2 || op == 4 || op == 7) /* header-only */
return true;
if (op != 1 && op != 3 && op != 5)
return false;
@@ -13884,6 +13953,21 @@ static u32 svc_pushpg_split_log;
* validated candidate). Reads stay defensively copied through
* svc_read_ap()/vfio_dma_rw(), which enforce the aperture bounds.
*/
+/* r707d: bounded push-read failure witness (see below). */
+static u32 svc_pushfail_log;
+/*
+ * r707e: last content-backed ring pair resolved by any channel. The VAS
+ * candidate verify is retried with it on EVERY pump pass: the parked
+ * COPY_SERVER_PDES candidate for the client's user VAS parks at
+ * device-create and must register before the submit's first push read
+ * (a failed GR entry read is never retried with a fresh VAS), but the ring
+ * resolution and the park race within one pass - the every-pass retry
+ * closes that race in a single boot.
+ */
+static u64 svc_lastgood_ring_va;
+static u64 svc_lastgood_ring_pa;
+
+
static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu,
struct svc_channel *ch, u64 va, void *buf,
size_t len, bool skip_ipin)
@@ -13899,6 +13983,7 @@ static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu,
bool have_win, done = false;
bool first = (p == buf);
int last_rc = -ENOENT;
+ int trusted_rc = 0;
u32 src;
have_win = !svc_pb_win_lookup(ch, page_va, &win_pa);
@@ -13952,7 +14037,9 @@ static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu,
default:
/* learned cache / discovered map / page
* directories / targeted rm_mem resolve */
- if (svc_lookup_trusted(vgpu, va, &pa, &ap))
+ trusted_rc = svc_lookup_trusted(vgpu, va,
+ &pa, &ap);
+ if (trusted_rc)
continue;
break;
}
@@ -14013,10 +14100,34 @@ static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu,
*/
if (src == 3 && first && !memchr_inv(p, 0, chunk))
continue;
+ svc_read_last_src = (u8)src;
+ svc_read_last_pa = pa;
done = true;
}
- if (!done)
+ if (!done) {
+ svc_read_last_src = 0xff;
+ if (svc_pushfail_log < 96) {
+ u64 ipa = 0, apa = 0;
+ int iap = 0, aap = 0;
+ bool has_ipin =
+ (svc_ipin_lookup(va, &ipa, &iap) == 0);
+ bool has_alt =
+ (svc_ipin_alt_lookup(va, &apa, &aap) == 0);
+
+ svc_pushfail_log++;
+ pr_info("sema-pushfail: va=0x%llx chunk=0x%llx page=0x%llx last_rc=%d trusted_rc=%d ipin=%d pa=0x%llx alt=%d pa=0x%llx win=%d pa=0x%llx\n",
+ (unsigned long long)va,
+ (unsigned long long)chunk,
+ (unsigned long long)page_va,
+ last_rc, trusted_rc, has_ipin,
+ (unsigned long long)ipa,
+ has_alt,
+ (unsigned long long)apa,
+ have_win,
+ (unsigned long long)win_pa);
+ }
return last_rc;
+ }
p += chunk;
va += chunk;
len -= chunk;
@@ -15597,6 +15708,58 @@ static bool svc_gr_ram_ring_scan(struct nv_vgpu_device *vgpu,
}
/*
+ * r705h: host-triggered pdb discovery. The automatic latch
+ * (svc_vas_candidates_verify + svc_xvas_pdb_discover) only runs from the RAM
+ * ring scan, which sweeps [0,6GB) before the client's pushes exist and then
+ * gives up for the boot; and per-client VAS state is flushed at session end.
+ * This entry point lets a host tool that CONTENT-LOCATED a verified
+ * {ring VA -> ring PA} pair (the ring page's GPFIFO entries are its
+ * fingerprint) hand the pair to the service while the client is alive, so:
+ * 1. every parked COPY_SERVER_PDES candidate is verified against the pair
+ * (fast: a walk each), and
+ * 2. if none verifies, the vidmem walk-root sweep anchors on the pair.
+ * Registered VASes make the client's pushbuffer VAs walkable and the pump
+ * can then decode the submit's pushes (the fence release among them).
+ */
+static u64 svc_pdb_disc_va;
+static u64 svc_pdb_disc_pa;
+static bool svc_pdb_disc_pending;
+
+/*
+ * r707r: bounded channel rewind (debug). After the VAS pdb anchor lands,
+ * entries skipped before it never re-decode on their own (their unread
+ * window expires into a skip), so the release they carry would be lost for
+ * that put epoch. A host tool asks for a rewind (sema_chan_rewind: a hex
+ * h_object, or "all"); the pump re-processes the channel from slot 0 with
+ * the consumed-slot bitmap and the per-entry rescue state cleared.
+ */
+static u32 svc_rewind_h;
+static bool svc_rewind_all;
+static bool svc_rewind_pending;
+
+int gsp_sema_service_chan_rewind(u32 h_object)
+{
+ if (!svc.task)
+ return -ENODEV;
+ svc_rewind_h = h_object;
+ svc_rewind_all = (h_object == 0xffffffffu);
+ svc_rewind_pending = true;
+ wake_up_process(svc.task);
+ return 0;
+}
+
+int gsp_sema_service_pdb_discover(u64 ring_va, u64 ring_pa)
+{
+ if (!svc.task)
+ return -ENODEV;
+ svc_pdb_disc_va = ring_va & ~0xfffULL;
+ svc_pdb_disc_pa = ring_pa & ~0xfffULL;
+ svc_pdb_disc_pending = true;
+ wake_up_process(svc.task);
+ return 0;
+}
+
+/*
* r496f: discover a kernel-RM client VAS's page directory in the emulated
* VRAM by verification. The CPU RM builds internal-VAS page tables in
* vidmem and never publishes them (SET_PAGE_DIRECTORY is an externally
@@ -23510,6 +23673,15 @@ static void svc_gr_stale_wedge_check(struct nv_vgpu_device *vgpu,
static void svc_pump_channels(struct nv_vgpu_device *vgpu)
{
+ if (svc_pdb_disc_pending) {
+ svc_pdb_disc_pending = false;
+ pr_info("sema-pdb-disc: requested va=0x%llx pa=0x%llx\n",
+ svc_pdb_disc_va, svc_pdb_disc_pa);
+ svc_vas_candidates_verify(vgpu, svc_pdb_disc_va,
+ svc_pdb_disc_pa);
+ svc_xvas_pdb_discover(vgpu, svc_pdb_disc_va,
+ svc_pdb_disc_pa);
+ }
/* Only ever called from the service thread. */
static struct svc_channel local[SVC_MAX_CHANNELS];
/*
@@ -23523,6 +23695,36 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
bool stalled = false;
spin_lock_irqsave(&svc.chan_lock, flags);
+ if (svc_rewind_pending) {
+ svc_rewind_pending = false;
+ for (i = 0; i < SVC_MAX_CHANNELS; i++) {
+ struct svc_channel *c = &svc.chan[i];
+
+ if (!c->valid || c->vgpu != vgpu)
+ continue;
+ if (!svc_rewind_all && c->h_object != svc_rewind_h)
+ continue;
+ pr_info("sema-chan: rewind h=0x%x last_get=%u put=%u (slot bitmap + rescue state cleared)\n",
+ c->h_object,
+ (unsigned int)c->last_get,
+ (unsigned int)c->put_prev);
+ bitmap_zero(c->slot_done_epoch, SVC_GPFIFO_MAX_BITS);
+ c->last_get = 0;
+ c->rescue_fail = 0;
+ c->unread_va = 0;
+ c->unread_until = 0;
+ c->defer_since = 0;
+ }
+ }
+ /*
+ * r707e: every-pass candidate verify with the last-good ring pair.
+ * Cheap (<=16 parked candidates, one walk each) and idempotent; the
+ * first pass after the user-VAS candidate parks registers it, so the
+ * client's very first push reads resolve.
+ */
+ if (svc_lastgood_ring_pa)
+ svc_vas_candidates_verify(vgpu, svc_lastgood_ring_va,
+ svc_lastgood_ring_pa);
/*
* r507o stream-order: walk the CE channels BEFORE the GR channels.
* The client's stream order places its H2D copies before the kernel
@@ -23740,6 +23942,25 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
if (!rrc)
svc_pb_pair(vgpu, ch);
+ if (!rrc && ring_pa) {
+ svc_lastgood_ring_va = ch->gpfifo_va;
+ svc_lastgood_ring_pa = ring_pa;
+ }
+ /*
+ * r707f (FOLD): the moment ANY channel's ring resolves through
+ * the channel object, verify the parked COPY_SERVER_PDES VAS
+ * candidates against that content-backed pair. The candidates
+ * are parked at device-create time, before the client's compute
+ * channels first submit, so registering here lands the VAS page
+ * directory BEFORE the submit's entries get their first push
+ * read - the window the host-triggered path could not win (the
+ * entry's first read and the ring's first resolution happen in
+ * the same pass; a pair that arrives later cannot help, because
+ * a failed GR entry read is never retried with the fresh VAS).
+ */
+ if (!rrc)
+ svc_vas_candidates_verify(vgpu, ch->gpfifo_va,
+ ring_pa);
/*
* ISSUE-0002 (r47 diag): run the deferred CE USERD sweep for a
@@ -24229,9 +24450,25 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
if (pb) {
int rescue_rc;
- bool unreadable =
- svc_read_va_push(vgpu, ch, va,
- pb, len) != 0;
+ int rd_rc;
+ bool unreadable;
+ bool zero_nop = false;
+ bool displaced = false;
+ bool have_ipin = false;
+
+ svc_read_last_src = 0xff;
+ svc_read_last_pa = 0;
+ rd_rc = svc_read_va_push(vgpu, ch, va,
+ pb, len);
+ unreadable = rd_rc != 0;
+ if (gsp_sema_dbg && svc_rdw_log < 6000 &&
+ !ch->engine_type) {
+ svc_rdw_log++;
+ pr_info("sema-rdw: h=0x%x va=0x%llx len=%u rc=%d src=%u\n",
+ ch->h_object, va, len,
+ rd_rc,
+ (unsigned)svc_read_last_src);
+ }
bool injected = false;
bool allow_ops = (ch->engine_type != 0);
@@ -24256,8 +24493,28 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
u64 ipa;
int iap;
- injected = (svc_ipin_lookup(va, &ipa,
- &iap) == 0);
+ have_ipin =
+ (svc_ipin_lookup(va, &ipa,
+ &iap) == 0);
+ /*
+ * r715: the entry's treatment
+ * follows the source that
+ * ACTUALLY answered the read,
+ * not a fresh page-granularity
+ * ipin lookup. The read falls
+ * through to the trusted walk
+ * (src 4) whenever an ipin's
+ * page fails its first-chunk
+ * guard, and taking the
+ * injected branch then skipped
+ * the only source that could
+ * answer: the entry was
+ * consumed without ever
+ * consulting the registered
+ * VAS walk (session 124).
+ */
+ injected = have_ipin &&
+ (svc_read_last_src == 0);
/*
* r496e: an all-zero push on a
* content-scanned-ring channel is the
@@ -24266,7 +24523,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
* translation: accept it and let the
* decode find no ops.
*/
- bool zero_nop = ch->gr_ram_ring &&
+ zero_nop = ch->gr_ram_ring &&
len >= 4 &&
!memchr_inv(pb, 0, len);
@@ -24299,7 +24556,82 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
kfree(fb);
}
} else {
- unreadable = true;
+ /*
+ * r707v: a primary-source
+ * hit (injected pin, window
+ * guess, lat cache) whose
+ * content is implausible
+ * must not shadow the
+ * registered VAS walk - a
+ * stale pin otherwise makes
+ * every push read "resolve"
+ * to the wrong page, fail
+ * plausibility and turn the
+ * entry unreadable forever.
+ * Retry once with the pins
+ * bypassed so the trusted
+ * walk (svc_walk_vaspaces)
+ * answers; accept only a
+ * plausible stream.
+ */
+ u8 *vb = kmalloc(len,
+ GFP_KERNEL);
+
+ if (vb) {
+ u64 wpa = 0;
+ int wap = SVC_APERTURE_SYS;
+ bool ok = false;
+
+ /*
+ * r707w: consult ONLY the
+ * registered VAS walk (the
+ * exact path walk_probe
+ * proves) - not
+ * svc_lookup_trusted, whose
+ * pin/lat hits would answer
+ * with the same wrong page
+ * again and keep the entry
+ * unreadable.
+ */
+ int wr = svc_walk_vaspaces(vgpu,
+ va & ~0xfffULL,
+ &wpa, &wap);
+ int rr = wr ? wr : svc_read_ap(vgpu,
+ wpa + (va & 0xfff),
+ wap == SVC_APERTURE_FB ?
+ SVC_APERTURE_FB :
+ SVC_APERTURE_SYS,
+ vb, len);
+ bool pl = !rr ? svc_method_stream_plausible(vb, len)
+ : false;
+
+ if (gsp_sema_dbg &&
+ svc_pushpg_fb_log < 64) {
+ svc_pushpg_fb_log++;
+ pr_info("sema-pushpg: h=0x%x va=0x%llx viawalk rc=%d pa=0x%llx plaus=%d\n",
+ ch->h_object, va,
+ wr ? wr : rr,
+ wpa + (va & 0xfff),
+ pl ? 1 : 0);
+ }
+ if (!wr && !rr && pl) {
+ memcpy(pb, vb, len);
+ svc.pushpg_fallback++;
+ ok = true;
+ if (gsp_sema_dbg &&
+ svc_pushpg_fb_log < 64) {
+ svc_pushpg_fb_log++;
+ pr_info("sema-pushpg: h=0x%x va=0x%llx viawalk ok pa=0x%llx\n",
+ ch->h_object, va,
+ wpa + (va & 0xfff));
+ }
+ }
+ kfree(vb);
+ if (!ok)
+ unreadable = true;
+ } else {
+ unreadable = true;
+ }
}
/*
* Still implausible (or
@@ -24315,18 +24647,19 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
u8 *db = kmalloc(len,
GFP_KERNEL);
- if (db &&
- svc_push_read_displaced(vgpu,
- ch->h_object, va,
- db, len)) {
- memcpy(pb, db, len);
- injected = false;
- unreadable = false;
- }
- kfree(db);
- }
- }
- }
+ if (db &&
+ svc_push_read_displaced(vgpu,
+ ch->h_object, va,
+ db, len)) {
+ memcpy(pb, db, len);
+ injected = false;
+ unreadable = false;
+ displaced = true;
+ }
+ kfree(db);
+ }
+ }
+ }
/*
* The entry's page was unreadable through
@@ -24345,11 +24678,40 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
memcpy(pb, db, len);
injected = false;
unreadable = false;
+ displaced = true;
}
kfree(db);
}
/*
+ * r715 pushdec witness: name the branch
+ * the entry takes straight after the
+ * read, before the rescue/hold paths.
+ * Session 124 had an entry consumed
+ * between the read (rc=0 src=4) and the
+ * decode with no row naming why; this
+ * line makes the branch and the content
+ * (first 8 bytes) visible on every read.
+ * Budget: 512 rows per client (reset in
+ * svc_session_flush).
+ */
+ if (gsp_sema_dbg && svc_pushdec_log < 512) {
+ svc_pushdec_log++;
+ pr_info("sema-pushdec: h=0x%x va=0x%llx len=%u src=%u pa=0x%llx plausible=%d injected=%d ipin=%d zero_nop=%d displaced=%d unreadable=%d first8=%08x %08x\n",
+ ch->h_object, va, len,
+ (unsigned)svc_read_last_src,
+ svc_read_last_pa,
+ svc_method_stream_plausible(pb, len) ? 1 : 0,
+ injected ? 1 : 0,
+ have_ipin ? 1 : 0,
+ zero_nop ? 1 : 0,
+ displaced ? 1 : 0,
+ unreadable ? 1 : 0,
+ len >= 4 ? *(const u32 *)pb : 0,
+ len >= 8 ? *(const u32 *)(pb + 4) : 0);
+ }
+
+ /*
* Content rescue before any fake-release
* fallback (CE channels): the push bytes
* are in guest RAM even when the VA->PA
@@ -26992,19 +27354,43 @@ static void svc_scrub_pass(struct nv_vgpu_device *vgpu)
u32 put = 0;
u64 delta;
- if (!s || !s->userd_ok || !s->pa_ok || !s->entries)
+ if (!s)
+ return;
+ if (!s->userd_ok || !s->pa_ok || !s->entries) {
+ /* m112b: silent-return diagnostic (bounded per boot). */
+ if (s->log_budget < 8) {
+ s->log_budget++;
+ pr_info("sema-svc: scrub pass idle vgpu=%px slot=%d userd_ok=%d pa_ok=%d entries=%u gen=%u\n",
+ (void *)vgpu, (int)(s - svc_scrubs),
+ s->userd_ok, s->pa_ok, s->entries,
+ s->session_gen);
+ }
return;
+ }
if (svc_read_ap(vgpu, s->userd_pa + SVC_USERD_GP_PUT, s->userd_as,
&put, sizeof(put))) {
/* Retry through the alternate aperture (as registration did). */
if (svc_read_ap(vgpu, s->userd_pa + SVC_USERD_GP_PUT,
s->userd_as == SVC_APERTURE_FB ?
SVC_APERTURE_SYS : SVC_APERTURE_FB,
- &put, sizeof(put)))
+ &put, sizeof(put))) {
+ if (s->log_budget < 12) {
+ s->log_budget++;
+ pr_info("sema-svc: scrub pass read-fail userd=0x%llx as=%u\n",
+ s->userd_pa, s->userd_as);
+ }
return;
+ }
}
- if (put >= s->entries)
+ if (put >= s->entries) {
+ /* m112b: junk-translation diagnostic (bounded). */
+ if (s->log_budget < 12) {
+ s->log_budget++;
+ pr_info("sema-svc: scrub pass junk put=%u entries=%u userd=0x%llx\n",
+ put, s->entries, s->userd_pa);
+ }
return; /* junk translation; retry next pass */
+ }
if (!s->seeded) {
s->seeded = true;