summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c596
-rw-r--r--kernel/gsp_phys_booter.h21
-rw-r--r--kernel/gsp_phys_gpu.c104
-rw-r--r--kernel/gsp_phys_gpu.h12
-rw-r--r--kernel/gsp_sema_service.c352
5 files changed, 1020 insertions, 65 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index b966ac3..fb46754 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -9626,14 +9626,68 @@ static int gsp_phys_rmcall_golden_replay(void __iomem *bar0)
* (swref/published/pascal/gp100/dev_mmu.h:97-119,157;
* uvm_pascal_mmu.c:64-141,235-303).
*/
-static u64 gsp_phys_rmcall_pde(u64 pa)
-{
- return 0xeull | ((pa >> 12) << 8);
+/*
+ * r431/r436 COHERENCY ROOT-CAUSE CANDIDATE: the leaf-PTE aperture for every
+ * host page this backend maps (the ladder windows, the win0 shadow pool, the
+ * bounce/data regions, the release/scratch shadows) was hardcoded to
+ * aperture 3 = SYSTEM_NON_COHERENT_MEMORY. The reference RM derives the
+ * pair from the allocation's CPU cache attribute (fabric_vaspace.c:1123-1134
+ * for the aperture, mem_desc.c memdescGetVolatility for VOL: NV_MEMORY_CACHED
+ * -> SYSTEM_COHERENT (2) + volatile FALSE, NV_MEMORY_UNCACHED -> NON_COHERENT
+ * (3) + volatile TRUE). The r431 experiment used aperture 2 with VOL still 1
+ * (0xd) - a pair the reference RM never produces - and it made the engine's
+ * truncated writes visible sooner but worsened the wedge on a fresh boot.
+ * r436 turns the knob into the full 2x2 matrix so each consistent encoding
+ * can be A/B'd:
+ * 0 = aperture 3 + VOL 1 (0xf) - historical, NV_MEMORY_UNCACHED
+ * 1 = aperture 2 + VOL 1 (0xd) - r431 experiment (inconsistent pair)
+ * 2 = aperture 2 + VOL 0 (0x5) - NV_MEMORY_CACHED (coherent, non-volatile)
+ * 3 = aperture 3 + VOL 0 (0x7) - non-coherent, non-volatile
+ * The PTEs are planted before the engine's first fetch, so the flag is
+ * boot-time only.
+ */
+static int gsp_phys_rmcall_pte_coh_param;
+module_param_named(gsp_phys_rmcall_pte_coh, gsp_phys_rmcall_pte_coh_param,
+ int, 0444);
+MODULE_PARM_DESC(gsp_phys_rmcall_pte_coh,
+ "r436: sysmem leaf-PTE aperture/VOL mode: 0=0xf (aperture3+VOL, historical), 1=0xd (aperture2+VOL, r431), 2=0x5 (aperture2, non-volatile), 3=0x7 (aperture3, non-volatile)");
+
+static u64 gsp_phys_rmcall_sysmem_base_low(void)
+{
+ switch (gsp_phys_rmcall_pte_coh_param) {
+ case 1:
+ return 0xdull; /* aperture 2, VOL 1 */
+ case 2:
+ return 0x5ull; /* aperture 2, VOL 0 */
+ case 3:
+ return 0x7ull; /* aperture 3, VOL 0 */
+ default:
+ return 0xfull; /* aperture 3, VOL 1 (historical) */
+ }
}
static u64 gsp_phys_rmcall_pte(u64 pa)
{
- return 0xfull | ((pa >> 12) << 8);
+ /*
+ * Low nibble: VALID (1) | APERTURE<<1 | VOL (8). Aperture 2 =
+ * SYSTEM_COHERENT_MEMORY -> 1|4|8 = 0xd; aperture 3 = NON_COHERENT
+ * -> 1|6|8 = 0xf. r436: see gsp_phys_rmcall_pte_coh_param.
+ */
+ u64 base = gsp_phys_rmcall_sysmem_base_low();
+
+ return base | ((pa >> 12) << 8);
+}
+
+/*
+ * r436: the single PDE uses the same aperture/VOL pair as the leaf PTEs but
+ * with IS_PDE=0 in bit 0 (the low bit of the PTE base is VALID there), so the
+ * PDE base is the PTE base with bit 0 cleared: 0xe/0xc/0x4/0x6.
+ */
+static u64 gsp_phys_rmcall_pde(u64 pa)
+{
+ u64 base = gsp_phys_rmcall_sysmem_base_low() & ~1ull;
+
+ return base | ((pa >> 12) << 8);
}
/*
@@ -15094,6 +15148,39 @@ static int gsp_phys_rmcall_ce_submit_locked(void __iomem *bar0, u64 src_va,
* semaphore. On timeout the existing status-queue drain runs and the helper
* returns -ETIMEDOUT; it never retries.
*/
+static int gsp_phys_rmcall_gr_resched_locked(void __iomem *bar0);
+static int gsp_phys_rmcall_gr_sem_push_locked(void __iomem *bar0);
+
+/*
+ * r445 wedge hard re-arm: the outcome-retry's warm semaphore probe proves the
+ * engine refused the channel's proven GR block (the sticky post-dispatch
+ * no-store wedge). One bounded recovery attempt instead of an immediate
+ * break: force a context save/unload with a bWait TSG preempt, re-run the
+ * mode-35 lane-enable tail + schedules (the m155 extended re-arm), then
+ * repeat the warm probe. Caller holds gsp_phys_rm_lock. Returns 0 when the
+ * engine executed the probe after the re-arm.
+ */
+static int gsp_phys_gr_wedge_hard_rearm_locked(void __iomem *bar0)
+{
+ u32 pre[2] = { 1u, 0u }; /* bWait=1, bManualTimeout=0 */
+ u32 rpc_result = 0, status = 0;
+ int ret;
+
+ ret = gsp_phys_rmcall_control(bar0, boot_stage.rmcall_tsg,
+ GSP_PHYS_RMCALL_CTRL_TSG_PREEMPT,
+ pre, sizeof(pre), sizeof(pre), NULL, 0,
+ &rpc_result, &status,
+ "TSG PREEMPT bWait=1 (r445 wedge hard re-arm)");
+ pr_warn("vgsp-gr: WEDGE-REARM preempt status=0x%x rpc=0x%x ret=%d\n",
+ status, rpc_result, ret);
+ ret = gsp_phys_rmcall_gr_resched_locked(bar0);
+ pr_warn("vgsp-gr: WEDGE-REARM resched ret=%d\n", ret);
+ ret = gsp_phys_rmcall_gr_sem_push_locked(bar0);
+ pr_warn("vgsp-gr: WEDGE-REARM probe rc=%d (engine %s after re-arm)\n",
+ ret, ret ? "still wedged" : "ALIVE");
+ return ret;
+}
+
static int gsp_phys_rmcall_gr_sem_push_locked(void __iomem *bar0)
{
struct gsp_phys_boot_chunk *userd_c =
@@ -16280,6 +16367,19 @@ EXPORT_SYMBOL_GPL(gsp_phys_backend_guest_ce_copy);
#define GSP_PHYS_RMCALL_GR_CWD_REFCOUNT 1536u
#define GSP_PHYS_RMCALL_GR_CWD_LEVELS 64u
#define GSP_PHYS_RMCALL_GR_MAX_SM_COUNT 24u
+/*
+ * r434 CWD-slot-count path: the falcon PRI write (CUDA's
+ * turingFalconPRIWriteMasked: flag/data/mask through SET_MME_SHADOW_SCRATCH
+ * 0/1/2, then SET_FALCON04 = register offset) and the gated method. Used only
+ * when gsp_phys_rmcall_gr_cwd_slot >= 0.
+ */
+#define GSP_PHYS_RMCALL_GR_MME_SCRATCH0 0x3400u
+#define GSP_PHYS_RMCALL_GR_MME_SCRATCH1 0x3404u
+#define GSP_PHYS_RMCALL_GR_MME_SCRATCH2 0x3408u
+#define GSP_PHYS_RMCALL_GR_SET_FALCON04 0x0510u
+#define GSP_PHYS_RMCALL_GR_SET_CWD_SLOT_COUNT 0x02b0u
+#define GSP_PHYS_RMCALL_PRI_SKED_DEBUG_1 0x00407040u
+#define GSP_PHYS_RMCALL_PRI_SKED_DEBUG_1_EN_CWD_SLOT_COUNT 0x800u
/* Method block bound: init(152) + inline run(67) + PCAS(4) + REPORT_SEM(8) +
* the optional QMD-virt pair(2). */
#define GSP_PHYS_RMCALL_GR_METHOD_WORDS 512u
@@ -16314,6 +16414,83 @@ static u64 gsp_phys_gr_stall_retry;
static u32 gsp_phys_gr_nw_streak;
static u64 gsp_phys_gr_wedge_quiet;
static u64 gsp_phys_gr_wedge_heals;
+/*
+ * r411: once a quiet heal has succeeded on this boot, the lane is in a
+ * wedge-prone episode. r418 showed the wedge can alternate one-no-write /
+ * one-heal, and the healed launch resets nw_streak, so a streak-only trigger
+ * quieted only every second wedged launch and the other was accepted. The
+ * heal count makes the episode sticky for the boot: any later zero-write
+ * launch gets the quiet path on its first retry, so every wedged launch
+ * either heals (engine writes, client data correct) or is loudly classified.
+ */
+static bool gsp_phys_gr_wedge_episode;
+/*
+ * r420: true exactly while a WEDGE-QUIET hold sleeps with NO submission
+ * outstanding. The hold is a deliberate, bounded wait for the sticky wedge to
+ * clear (r410/r411); during it the in-flight forwarded request is NOT done.
+ * The sema service's completion deadlines - the 12 s GR release hold
+ * (SVC_FWD_GR_HOLD_TIMEOUT_MS) and the 3 s per-launch consumption bound
+ * (gsp_phys_gr_hold_max_ms) - are shorter than this hold, so without the
+ * publish they fire UNDER it and author the launch's release word before the
+ * post-hold copy-out: the client then verifies stale data (r419 soak:
+ * `victory_matmul bad=65536` while its launch was held; the authoring paths
+ * are GR-STALL-fwd-hold / the busy-defer inline software release). The
+ * accessor lets both deadlines pause instead; the flag is bounded by the
+ * msleep itself and by the pause caps on the sema side. Single-inflight
+ * submit path, so a plain static.
+ */
+static bool gsp_phys_gr_quiet_hold_on;
+
+bool gsp_phys_gr_quiet_hold_active(void)
+{
+ return READ_ONCE(gsp_phys_gr_quiet_hold_on);
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_quiet_hold_active);
+
+/*
+ * r425 experiment (the second-launch wedge): the stock guest driver emits the
+ * per-channel compute init block ONCE at channel creation (turing_init.c),
+ * long before any launch, while this backend re-emits it as the preamble of
+ * every forwarded launch. The wedge strikes the SECOND launch of a channel, so
+ * test whether the re-emission is the trigger: with gsp_phys_rmcall_gr_init_once
+ * set, the init preamble is emitted only on the first forwarded launch after
+ * the module load (the engine's compute-class/CWD state persists in the
+ * context; every launch still carries its QMD + report semaphore). 0 keeps the
+ * historical per-launch emission (default; runtime-writable A/B).
+ */
+static bool gsp_phys_rmcall_gr_init_once_param;
+module_param_named(gsp_phys_rmcall_gr_init_once, gsp_phys_rmcall_gr_init_once_param,
+ bool, 0644);
+MODULE_PARM_DESC(gsp_phys_rmcall_gr_init_once,
+ "r425: emit the GR compute init preamble only on the first forwarded launch (0 = every launch, default)");
+static bool gsp_phys_rmcall_gr_init_emitted;
+
+/*
+ * r425 stale-QMD witness: the first engine-changed page of the LAST completed
+ * launch (bounce index + post-copy-out checksum). At a zero-write accept the
+ * page is re-scanned; a change proves a late store (a stuck/queued grid or a
+ * stale-QMD execution) landed after its launch was shipped.
+ */
+static u32 gsp_phys_gr_prev_out_bi;
+static u32 gsp_phys_gr_prev_out_csum;
+static bool gsp_phys_gr_prev_out_valid;
+
+/* r425: SKED/FECS/CWD register witness (one line; the healthy-vs-wedged
+ * comparison baseline lives in the same log). */
+static void gsp_phys_gr_sked_wit(void __iomem *bar0, const char *tag)
+{
+ if (!bar0)
+ return;
+ pr_info("vgsp-gr: SKED-WIT %s cwd0=%08x cwd1=%08x cwd2=%08x cwd3=%08x info1=%08x act=%08x grfifo=%08x fecs=%08x new=%08x ctxsw1=%08x qmdvirt0=%08x tpcm0=%08x\n",
+ tag,
+ readl(bar0 + 0x00407060u), readl(bar0 + 0x00407064u),
+ readl(bar0 + 0x00407068u), readl(bar0 + 0x0040706cu),
+ readl(bar0 + 0x00407028u), readl(bar0 + 0x00407054u),
+ readl(bar0 + 0x00400504u), readl(bar0 + 0x00409b00u),
+ readl(bar0 + 0x00409b04u), readl(bar0 + 0x00409400u),
+ readl(bar0 + 0x0040a500u), readl(bar0 + 0x0040a680u));
+}
+
/* m86: program-page override blob (see gsp-phys-gr-override). */
static u8 gsp_phys_gr_override[GSP_PHYS_PAGE_SIZE];
static u32 gsp_phys_gr_override_len;
@@ -16423,6 +16600,29 @@ static u32 *gsp_phys_rmcall_build_gr_init(u32 *p)
GSP_PHYS_RMCALL_GR_MAX_SM_COUNT);
p = gsp_phys_rmcall_push_gr(p, GSP_PHYS_RMCALL_GR_INIT_SM_DISP_CTRL,
0u);
+ /*
+ * r434 CWD-slot-count path (gsp_phys_rmcall_gr_cwd_slot >= 0): masked
+ * PRI write of SKED_DEBUG_1.ENABLE_CWD_SLOT_COUNT (bit 11) through the
+ * falcon scratch registers, then the gated SET_CWD_SLOT_COUNT method -
+ * the CUDA driver's own turingLaunchSetCwdSlotCount sequence. -1 (the
+ * default) emits nothing, so the historical preamble is unchanged.
+ */
+ if (gsp_phys_rmcall_gr_cwd_slot_value() >= 0) {
+ p = gsp_phys_rmcall_push_gr(
+ p, GSP_PHYS_RMCALL_GR_MME_SCRATCH0, 0u);
+ p = gsp_phys_rmcall_push_gr(
+ p, GSP_PHYS_RMCALL_GR_MME_SCRATCH1,
+ GSP_PHYS_RMCALL_PRI_SKED_DEBUG_1_EN_CWD_SLOT_COUNT);
+ p = gsp_phys_rmcall_push_gr(
+ p, GSP_PHYS_RMCALL_GR_MME_SCRATCH2,
+ GSP_PHYS_RMCALL_PRI_SKED_DEBUG_1_EN_CWD_SLOT_COUNT);
+ p = gsp_phys_rmcall_push_gr(
+ p, GSP_PHYS_RMCALL_GR_SET_FALCON04,
+ GSP_PHYS_RMCALL_PRI_SKED_DEBUG_1);
+ p = gsp_phys_rmcall_push_gr(
+ p, GSP_PHYS_RMCALL_GR_SET_CWD_SLOT_COUNT,
+ (u32)gsp_phys_rmcall_gr_cwd_slot_value());
+ }
return p;
}
@@ -16443,7 +16643,17 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
int qmdvirt = gsp_phys_rmcall_gr_qmdvirt_value();
int two_step = gsp_phys_rmcall_gr_pcas_two_step_value();
- p = gsp_phys_rmcall_build_gr_init(p);
+ /*
+ * r425 A/B: with gsp_phys_rmcall_gr_init_once the compute init preamble
+ * is emitted only on the first forwarded launch (the stock driver's
+ * per-channel shape); subsequent launches start at the inline-QMD
+ * window.
+ */
+ if (!READ_ONCE(gsp_phys_rmcall_gr_init_once_param) ||
+ !READ_ONCE(gsp_phys_rmcall_gr_init_emitted)) {
+ p = gsp_phys_rmcall_build_gr_init(p);
+ WRITE_ONCE(gsp_phys_rmcall_gr_init_emitted, true);
+ }
if (qmdvirt)
p = gsp_phys_rmcall_push_gr(p,
GSP_PHYS_RMCALL_GR_SET_QMD_VIRT_CONTROL,
@@ -17690,6 +17900,32 @@ gsp_phys_gr_scan_pages(const struct gsp_phys_gr_submit *req,
return changed;
}
+/*
+ * r434: post-launch deposit dump (hardware-only words). The r430 QMD64 dump
+ * runs at submission, before the engine touches the deposit, so the
+ * hardware-only fields (w50 INNER_GET, w51 INNER_PUT, w52 span-list head,
+ * w53 next-QMD) are always zero there. Reading the same page AFTER the
+ * retire wait shows what the SKED actually consumed: w50/w51 are the CTA
+ * (INNER) get/put counters, so a wedged launch whose grid "completed" with
+ * zero stores is distinguishable from a genuinely dispatched grid by
+ * whether INNER_PUT advanced to the CTA count. One line per launch, gated
+ * by the skedrec param.
+ */
+static void gsp_phys_gr_qmd64_post(const u8 *deposit, const char *tag)
+{
+ char qb[64 * 9 + 1];
+ int qo = 0;
+ u32 i;
+
+ if (!deposit || !gsp_phys_rmcall_gr_skedrec_value())
+ return;
+ for (i = 0; i < GSP_PHYS_GR_QMD_WORDS && qo < (int)sizeof(qb) - 10;
+ i++)
+ qo += scnprintf(qb + qo, sizeof(qb) - qo, "%08x ",
+ get_unaligned_le32(deposit + i * 4));
+ pr_info("vgsp-gr: QMD64-POST %s %s\n", tag, qb);
+}
+
static noinline u32
gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
const u32 *csum_in, u32 *n_tracked)
@@ -17826,6 +18062,82 @@ static bool gsp_phys_gr_qmd_fetched(const u8 *qmd_mem)
}
/*
+ * r427 idempotent fast-accept (CONT.86 ยง6.1). A zero-write launch is either
+ * the no-execute wedge OR a legitimate idempotent re-launch (the same QMD
+ * content with the same staged inputs, whose result is already in place) -
+ * the host cannot tell them apart, and both burn the 40 s + 80 s quiet holds.
+ * The launch identity below (QMD minus the per-launch release0 address and
+ * payload, folded with the staged content checksums of every tracked page) is
+ * recorded for every launch that WROTE; a zero-write launch whose identity
+ * matches an entry accepts immediately, with no retries and no quiet window.
+ * Safe by construction: identical code + identical staged content + a prior
+ * execution that produced this launch's own footprint means the correct
+ * result is already in the buffers; a side-effecting kernel's re-launch is
+ * equally wrong under the current accept (the client sees the stale result
+ * either way) - this only removes the stall.
+ */
+#define GSP_PHYS_GR_IDEM_SLOTS 64u
+static u64 gsp_phys_gr_idem_fp[GSP_PHYS_GR_IDEM_SLOTS];
+static u32 gsp_phys_gr_idem_n;
+
+static u64 gsp_phys_gr_launch_fp(const u32 *qmd,
+ const struct gsp_phys_gr_submit *req,
+ const u32 *csum_in)
+{
+ u64 h = 1469598103934665603ULL;
+ u32 i;
+
+ for (i = 0; i < GSP_PHYS_GR_QMD_WORDS; i++) {
+ /* Skip release0's address (w23/w24) and payload (w25): the
+ * driver allocates a fresh release slot per launch, so they
+ * differ even for an identical re-launch. */
+ u32 w;
+
+ if (i == 23u || i == 24u || i == 25u)
+ continue;
+ w = qmd[i];
+ h ^= w;
+ h *= 1099511628211ULL;
+ }
+ for (i = 0; i < req->n_data; i++) {
+ u32 bi = gsp_phys_gr_data_bi(req->data[i].reloc_va);
+
+ if (bi == GSP_PHYS_GR_DATA_BI_NONE)
+ continue;
+ h ^= csum_in[bi];
+ h *= 1099511628211ULL;
+ }
+ return h;
+}
+
+static bool gsp_phys_gr_idem_match(u64 fp)
+{
+ u32 i;
+
+ for (i = 0; i < gsp_phys_gr_idem_n; i++)
+ if (gsp_phys_gr_idem_fp[i] == fp)
+ return true;
+ return false;
+}
+
+static void gsp_phys_gr_idem_record(u64 fp)
+{
+ u32 i;
+
+ for (i = 0; i < gsp_phys_gr_idem_n; i++)
+ if (gsp_phys_gr_idem_fp[i] == fp)
+ return;
+ if (gsp_phys_gr_idem_n < GSP_PHYS_GR_IDEM_SLOTS) {
+ gsp_phys_gr_idem_fp[gsp_phys_gr_idem_n++] = fp;
+ } else {
+ /* ring: drop the oldest */
+ memmove(&gsp_phys_gr_idem_fp[0], &gsp_phys_gr_idem_fp[1],
+ (GSP_PHYS_GR_IDEM_SLOTS - 1u) * sizeof(u64));
+ gsp_phys_gr_idem_fp[GSP_PHYS_GR_IDEM_SLOTS - 1u] = fp;
+ }
+}
+
+/*
* COPYAUDIT (2026-10-18 copy-completeness audit): per-copy sighting of the
* GR operand staging copies. Every staged page is spot-checked (16 bytes
* at the start and end of the page, source vs destination, right after the
@@ -18109,6 +18421,8 @@ gsp_phys_gr_poll_break_log(u32 w0, u32 want_qmd, u32 w1, u32 since_us,
w0, want_qmd, w1,
(u32)ktime_to_us(ktime_get()) - since_us,
usr_gpget, sub->gpget, sub->gpput);
+ /* r425: the healthy-launch SKED/FECS baseline for the wedge compare. */
+ gsp_phys_gr_sked_wit(gsp_phys_gpu_bar0(), "post-poll");
}
/*
@@ -18260,6 +18574,8 @@ static u32 gsp_phys_m91_fault_start;
static u32 gsp_phys_m91_cap_used;
static bool gsp_phys_m91_cap_suppressed;
static bool gsp_phys_m91_icache_log;
+static bool gsp_phys_r449_ccache_log;
+static bool gsp_phys_gr_rel_membar_log;
static noinline void
gsp_phys_gr_m91_fault_cap(const struct gsp_phys_gr_submit *req,
const u32 *qmd, u64 qmd_dst, const u8 *qmd_dst_mem,
@@ -18569,6 +18885,7 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
const u8 *guest_param_pg;
u32 want_qmd = 0, want_ch = 0;
bool qmd_rel = false, ch_rel = false;
+ bool qmd_rel_redirected = false; /* r420b: release0 -> module sem */
u64 qmd_rel_hpa = 0, ch_rel_hpa = 0;
u64 ch_sem_va = GSP_PHYS_RMCALL_VA_SEM + 4;
u64 qmd_dst = GSP_PHYS_RMCALL_VA_SRC;
@@ -18768,6 +19085,52 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
}
}
/*
+ * r449 SHADER-CONSTANT-CACHE invalidate (gsp_phys_gr_ccache_inv; default
+ * 0 = off): the same cache-control word's bit 31 (the V01_06-layout
+ * SHADER_CONSTANT_CACHE position) is never set by the captured guest
+ * QMDs. The forwarded param page is rewritten in place between launches
+ * (the two-bank win0 pool), so a stale constant-cache line can serve
+ * the PREVIOUS launch's parameters and the current launch's output
+ * pages stay untouched - the post-dispatch no-store signature. Mode 1
+ * sets bit 31; mode 2 forces the whole invalidate set (bits 24..31).
+ * Only when the QMD carries the driver's standard cache-control
+ * signature (bits 26..29) so an unknown QMD revision is never touched.
+ */
+ if (gsp_phys_gr_ccache_inv_value() &&
+ (qmd[5] & 0x3c000000u) == 0x3c000000u) {
+ u32 mode = gsp_phys_gr_ccache_inv_value();
+
+ if (mode == 2)
+ qmd[5] |= 0xff000000u;
+ else
+ qmd[5] |= 1u << 31;
+ if (!gsp_phys_r449_ccache_log) {
+ gsp_phys_r449_ccache_log = true;
+ pr_info("vgsp-gr: r449 constant-cache invalidate mode=%u applied (w5=0x%08x)\n",
+ mode, qmd[5]);
+ }
+ }
+ /*
+ * r433 RELEASE_MEMBAR_TYPE A/B (gsp_phys_gr_rel_membar): force the
+ * forwarded QMD's RELEASE_MEMBAR_TYPE to FE_SYSMEMBAR (QMDV00_06
+ * word 11 bit 14) so the front-end flushes its pending system-memory
+ * stores before writing the release semaphore. The captured guest
+ * QMDs carry FE_NONE, so a grid that completes with its data still
+ * in the GPU's L2 releases the client while no store is host-visible
+ * - the post-dispatch store-loss signature. The release ordering then
+ * guarantees the host's retire scan and copy-out can never observe a
+ * completion whose data has not landed. 0 (default) keeps the
+ * guest's captured value.
+ */
+ if (gsp_phys_gr_rel_membar_value() && !(qmd[11] & (1u << 14))) {
+ qmd[11] |= 1u << 14;
+ if (!gsp_phys_gr_rel_membar_log) {
+ gsp_phys_gr_rel_membar_log = true;
+ pr_info("vgsp-gr: r433 RELEASE_MEMBAR_TYPE forced to FE_SYSMEMBAR in the forwarded QMD (w11=0x%08x)\n",
+ qmd[11]);
+ }
+ }
+ /*
* Two-step PCAS (gsp_phys_rmcall_gr_pcas_two_step): set the QMD's
* REQUIRE_SCHEDULING_PCAS (w4 bit12) so the copy PCAS does not
* schedule and the trailing schedule-only PCAS does.
@@ -18826,6 +19189,15 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
*/
qmd_rel_hpa = 0;
qmd_rel_off = 0;
+ /*
+ * r420b: the ENGINE will not write the guest's QMD
+ * release word this launch (rel_mod, or no targetable
+ * guest release page). Tell the caller so it authors
+ * the word in software on every outcome - otherwise a
+ * launch without deferred release records is never
+ * completed on the guest word (the r414 saxpy hang).
+ */
+ qmd_rel_redirected = true;
}
}
@@ -19585,6 +19957,27 @@ gr_page_unmapped:
/* A stale release from a previous submission must not be observed. */
WRITE_ONCE(*(u32 *)sem_va, 0);
WRITE_ONCE(*(u32 *)(sem_va + 4), 0);
+ /*
+ * r441: zero the GUEST-VISIBLE release words for the first attempt
+ * too, not only for outcome retries. The r439 evidence shows a first
+ * attempt whose poll broke on a STALE value (qmd_rel=0x00000005 from
+ * the previous launch's engine write, want=6) while the engine never
+ * fetched this launch's deposit (sentinel intact, "engine fetch
+ * NONE"): the host then shipped a completion for a launch that did
+ * not run. Zeroing forces the poll to wait for THIS launch's own
+ * engine write; when the engine does not write it, the timeout ladder
+ * and the software-authored completion take over (the honest path),
+ * and the wedge classification stops being satisfiable by a stale
+ * value. The write guard re-validates the target like the retry path.
+ */
+ if (qmd_rel_hpa && qmd_rel_off < GSP_PHYS_PAGE_SIZE &&
+ gsp_sema_gr_write_guard(req->rel_va, qmd_rel_hpa,
+ "gr-rel-zero-first"))
+ WRITE_ONCE(*(u32 *)((u8 *)__va(qmd_rel_hpa) + qmd_rel_off), 0);
+ if (ch_rel_hpa && ch_rel_off < GSP_PHYS_PAGE_SIZE &&
+ gsp_sema_gr_write_guard(req->rel_va, ch_rel_hpa,
+ "gr-chrel-zero-first"))
+ WRITE_ONCE(*(u32 *)((u8 *)__va(ch_rel_hpa) + ch_rel_off), 0);
wmb();
/*
@@ -19611,6 +20004,20 @@ gr_page_unmapped:
pr_info("vgsp-gr: qmd w0..3=%08x %08x %08x %08x w4..7=%08x %08x %08x %08x w23..25=%08x %08x %08x w32/33=%08x %08x w48/49=%08x %08x\n",
qmd[0], qmd[1], qmd[2], qmd[3], qmd[4], qmd[5], qmd[6], qmd[7],
qmd[23], qmd[24], qmd[25], qmd[32], qmd[33], qmd[48], qmd[49]);
+ /*
+ * r430: full 64-word deposit dump for the wedge's healthy-vs-wedged
+ * QMD diff (hardware-only fields w50/w51 INNER_GET/PUT, w52 span-list,
+ * w53 next-QMD pointer are not in the partial line above). Gated by
+ * the skedrec param (default on); one long line per launch.
+ */
+ if (gsp_phys_rmcall_gr_skedrec_value()) {
+ char qb[64 * 9 + 1];
+ int qo = 0;
+
+ for (i = 0; i < GSP_PHYS_GR_QMD_WORDS && qo < (int)sizeof(qb) - 10; i++)
+ qo += scnprintf(qb + qo, sizeof(qb) - qo, "%08x ", qmd[i]);
+ pr_info("vgsp-gr: QMD64 %s\n", qb);
+ }
/* m120: dump the main PT page slots 0x100..0x11f so the PTE content
* the walker would read (win0 + bounce window) is visible in the log. */
{
@@ -19935,8 +20342,14 @@ gr_page_unmapped:
* cancel or dispatch sequence of the launch window is on the
* record even when the poll times out.
*/
- if (gsp_phys_rmcall_gr_skedrec_value() && bar0 &&
- (++sked_tick & 7u) == 0u) {
+ if (gsp_phys_rmcall_gr_skedrec_value() && bar0) {
+ /*
+ * r430: sample EVERY poll iteration (not every 8th):
+ * the wedge's release word lands ~7 ms after the
+ * doorbell, so the old 8-iteration gate saw at most one
+ * sample of the dispatch window. Changes only.
+ */
+ sked_tick++;
sked_sample[0] = ioread32(bar0 + 0x00407060u);
sked_sample[1] = ioread32(bar0 + 0x00407064u);
sked_sample[2] = ioread32(bar0 + 0x00407068u);
@@ -19952,7 +20365,7 @@ gr_page_unmapped:
memcmp(sked_sample, sked_prev,
sizeof(sked_sample))) {
pr_info("vgsp-gr: sked rec +%ums cwd0=%08x cwd1=%08x cwd2=%08x info1=%08x info2=%08x act=%08x esr=%08x grfifo=%08x fecs_cur=%08x fecs_new=%08x ctxsw1=%08x\n",
- (sked_tick / 8u) *
+ sked_tick *
GSP_PHYS_RMCALL_POLL_MS,
sked_sample[0], sked_sample[1],
sked_sample[2], sked_sample[3],
@@ -20488,10 +20901,19 @@ gr_page_unmapped:
if (req->n_data && gsp_phys_gr_data_chunk()->pages) {
u32 n_tracked = 0;
u32 nw_attempts = 0;
+ bool idem = false;
u32 wrote = gsp_phys_gr_output_retire_wait(req, bounce_csum_in,
&n_tracked);
/*
+ * r434: post-launch hardware-only QMD dump (w50/w51 INNER
+ * get/put = the SKED's CTA dispatch counters) so the
+ * healthy-vs-wedged diff has the fields the submission-time
+ * dump cannot show.
+ */
+ gsp_phys_gr_qmd64_post(qmd_dst_mem, "retire");
+
+ /*
* m179: the zero-write test is specific to launches with a
* non-empty TRACKED data set. A launch whose data entries do
* not resolve to any bounce page legitimately reports zero
@@ -20507,7 +20929,24 @@ gr_page_unmapped:
ff ? "OK" : "NONE",
ff ? "overwritten" : "intact", n_tracked);
}
- if (wrote == 0 && n_tracked &&
+ /*
+ * r427 idempotent fast-accept: a zero-write launch whose identity
+ * (QMD minus release0 + staged content checksums) matches an
+ * earlier WRITING launch of this boot is a legitimate idempotent
+ * re-launch - the result is already in place. Accept immediately:
+ * no retries, no quiet holds. (See gsp_phys_gr_launch_fp.)
+ */
+ if (wrote == 0 && n_tracked) {
+ u64 fp = gsp_phys_gr_launch_fp(qmd, req, bounce_csum_in);
+
+ if (gsp_phys_gr_idem_match(fp)) {
+ pr_warn("vgsp-gr: IDEMPOTENT-ACCEPT h=0x%x push=0x%llx fp=0x%llx tracked=%u (same QMD+content as an earlier writing launch; no retries, no hold)\n",
+ req->h_object, req->push_va, fp,
+ n_tracked);
+ idem = true;
+ }
+ }
+ if (wrote == 0 && n_tracked && !idem &&
gsp_phys_rmcall_gr_nowrite_retry_value()) {
u32 nw_retries =
(u32)gsp_phys_gr_nowrite_retries_value();
@@ -20529,7 +20968,8 @@ gr_page_unmapped:
while (wrote == 0 && nw_attempts < nw_retries) {
u32 quiet_ms = 0;
- if (READ_ONCE(gsp_phys_gr_nw_streak) > 0 &&
+ if ((READ_ONCE(gsp_phys_gr_nw_streak) > 0 ||
+ READ_ONCE(gsp_phys_gr_wedge_episode)) &&
nw_attempts <= 1) {
quiet_ms =
gsp_phys_gr_wedge_quiet_ms_value();
@@ -20562,13 +21002,48 @@ gr_page_unmapped:
pr_info("vgsp-gr: outcome retry %u/%u (wrote=%u tracked=%u): refill + re-publish\n",
nw_attempts, nw_retries,
wrote, n_tracked);
+ /*
+ * r445 wedge hard re-arm (gsp_phys_gr_wedge_rearm,
+ * default off): the r445 evidence shows every
+ * post-dispatch no-store episode leaves the warm
+ * ladder sem probe ALIVE (the ladder block executes)
+ * while the client launch keeps writing nothing, so
+ * the plain refill+re-publish retries are futile.
+ * On the FINAL retry attempt, force one context
+ * save/unload (TSG PREEMPT bWait=1) and re-run the
+ * mode-35 lane-enable tail + schedules before the
+ * re-publish, so the retried launch meets a freshly
+ * restored context. Bounded: one preempt per launch
+ * (2 s RPC timeout), only on the last retry.
+ */
+ if (nw_attempts == nw_retries &&
+ gsp_phys_gr_wedge_rearm_value()) {
+ mutex_lock(&gsp_phys_rm_lock);
+ gsp_phys_gr_wedge_hard_rearm_locked(bar0);
+ mutex_unlock(&gsp_phys_rm_lock);
+ }
if (quiet_ms) {
gsp_phys_gr_wedge_quiet++;
pr_warn("vgsp-gr: WEDGE-QUIET hold %u ms before outcome retry %u (push=0x%llx; %u consecutive zero-write launch(es); no submissions in between)\n",
quiet_ms, nw_attempts,
req->push_va,
READ_ONCE(gsp_phys_gr_nw_streak));
+ /*
+ * r420: publish the deliberate hold so
+ * the sema service's completion
+ * deadlines (the 12 s GR release hold
+ * and the 3 s per-launch consumption
+ * bound) pause instead of authoring
+ * this launch's release word under it -
+ * the client must not be released
+ * before the post-hold copy-out
+ * (gsp_phys_gr_quiet_hold_active).
+ */
+ WRITE_ONCE(gsp_phys_gr_quiet_hold_on,
+ true);
msleep(quiet_ms);
+ WRITE_ONCE(gsp_phys_gr_quiet_hold_on,
+ false);
quiet_used = quiet_ms;
pr_warn("vgsp-gr: WEDGE-QUIET hold done (push=0x%llx); lanes re-armed by the retry below\n",
req->push_va);
@@ -20827,12 +21302,16 @@ gr_page_unmapped:
}
wrote = gsp_phys_gr_output_retire_wait(
req, bounce_csum_in, &n_tracked);
+ gsp_phys_gr_qmd64_post(qmd_dst_mem,
+ "retry-retire");
pr_info("vgsp-gr: outcome retry %u done: engine wrote %u page(s)\n",
nw_attempts, wrote);
/* r410: the quiet-window verdict. */
if (quiet_used) {
if (wrote) {
gsp_phys_gr_wedge_heals++;
+ WRITE_ONCE(gsp_phys_gr_wedge_episode,
+ true);
pr_warn("vgsp-gr: WEDGE-QUIET healed: engine wrote %u page(s) after the %u ms hold (streak was %u)\n",
wrote, quiet_used,
READ_ONCE(
@@ -20855,7 +21334,7 @@ gr_page_unmapped:
* loop is disabled (gsp_phys_rmcall_gr_nowrite_retry=0) so a
* zero-write outcome is never silent.
*/
- if (wrote == 0 && n_tracked) {
+ if (wrote == 0 && n_tracked && !idem) {
bool restored = false;
bool fetched = gsp_phys_gr_qmd_fetched(qmd_dst_mem);
@@ -20947,6 +21426,34 @@ gr_page_unmapped:
readl(bar0 + 0x00409008u),
readl(bar0 + 0x00409b00u),
readl(bar0 + 0x00409b04u));
+ /* r425: the wedged-launch SKED/FECS baseline. */
+ gsp_phys_gr_sked_wit(bar0, "accept");
+ /*
+ * r425 stale-store witness: re-scan the last completed
+ * launch's first engine-changed page. A change since its
+ * copy-out proves a late store landed after the launch
+ * was shipped (a stuck/queued grid or a stale-QMD
+ * execution), the mechanism behind the "1 page" heals.
+ */
+ if (gsp_phys_gr_prev_out_valid &&
+ bounce_staged_hpa[gsp_phys_gr_prev_out_bi] == 0 &&
+ gsp_phys_gr_data_chunk()->pages) {
+ const u8 *pp = gsp_phys_chunk_page_va(
+ gsp_phys_gr_data_chunk(),
+ gsp_phys_gr_prev_out_bi);
+ u32 pc = 0, pi;
+
+ if (pp) {
+ for (pi = 0; pi < GSP_PHYS_PAGE_SIZE;
+ pi += 4)
+ pc += get_unaligned_le32(pp + pi);
+ if (pc != gsp_phys_gr_prev_out_csum)
+ pr_warn("vgsp-gr: PREV-OUT-CHANGED bi=%u csum %08x -> %08x (late store after its launch shipped)\n",
+ gsp_phys_gr_prev_out_bi,
+ gsp_phys_gr_prev_out_csum,
+ pc);
+ }
+ }
/*
* (c) Late-store check: a store ramp that retires after
* the host's window is indistinguishable from a lost
@@ -20997,16 +21504,30 @@ gr_page_unmapped:
want_qmd, want_ch);
}
/*
+ * r427: record the launch identity of every launch that WROTE,
+ * so a later idempotent re-launch (zero writes, same identity)
+ * can accept immediately (see gsp_phys_gr_launch_fp).
+ */
+ if (wrote && n_tracked)
+ gsp_phys_gr_idem_record(
+ gsp_phys_gr_launch_fp(qmd, req, bounce_csum_in));
+ /*
* r410: streak bookkeeping for the quiet-window trigger. A
* launch with tracked data that wrote nothing extends the
* zero-write streak; any launch that wrote resets it. Runs
* even when the retry loop is disabled so the streak always
* reflects the accepted-outcome history.
*/
- if (n_tracked)
+ if (n_tracked) {
WRITE_ONCE(gsp_phys_gr_nw_streak,
wrote ? 0u :
READ_ONCE(gsp_phys_gr_nw_streak) + 1u);
+ /* r411: arm the wedge episode on any accepted
+ * zero-write launch, so the next zero-write launch
+ * quiets even after a heal reset the streak. */
+ if (!wrote)
+ WRITE_ONCE(gsp_phys_gr_wedge_episode, true);
+ }
}
/*
* m114: the kernel ran against the pre-commit bounce window; copy the
@@ -21156,6 +21677,16 @@ gr_page_unmapped:
}
if (changed < 8)
chg[changed] = bi;
+ /*
+ * r425: remember the first engine-changed page
+ * (post-copy-out content) for the next zero-write
+ * accept's late-store witness.
+ */
+ if (changed == 0) {
+ gsp_phys_gr_prev_out_bi = bi;
+ gsp_phys_gr_prev_out_csum = csum;
+ gsp_phys_gr_prev_out_valid = true;
+ }
changed++;
}
/*
@@ -21350,8 +21881,20 @@ gr_page_unmapped:
jiffies_to_msecs(jiffies - start), gsp_phys_gr_ok);
/* m128: post-success qmdwin words so a probe that stores a value into
* the qmdwin page (e.g. p6's constant-read dump at w48) is readable
- * from the success log (the post-mortem only prints on timeout). */
- if (boot_stage.rmcall_gr_qmdwin.pages) {
+ * from the success log (the post-mortem only prints on timeout).
+ * r434: read the launch's OWN bank (qmd_dst_mem) when available -
+ * the old bank-0-only read showed a stale page for odd epochs. */
+ if (qmd_dst_mem) {
+ const u32 *qw = (const u32 *)qmd_dst_mem;
+
+ pr_info("vgsp-gr: post-OK qmdwin w48..59=%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ get_unaligned_le32(qw + 48), get_unaligned_le32(qw + 49),
+ get_unaligned_le32(qw + 50), get_unaligned_le32(qw + 51),
+ get_unaligned_le32(qw + 52), get_unaligned_le32(qw + 53),
+ get_unaligned_le32(qw + 54), get_unaligned_le32(qw + 55),
+ get_unaligned_le32(qw + 56), get_unaligned_le32(qw + 57),
+ get_unaligned_le32(qw + 58), get_unaligned_le32(qw + 59));
+ } else if (boot_stage.rmcall_gr_qmdwin.pages) {
const u32 *qw = (const u32 *)gsp_phys_chunk_page_va(
&boot_stage.rmcall_gr_qmdwin, 0);
@@ -21375,6 +21918,7 @@ out:
out->elapsed_ms = jiffies_to_msecs(jiffies - start);
out->rc = ret;
out->fault = fault;
+ out->qmd_rel_redirected = qmd_rel_redirected;
}
return ret;
}
@@ -23468,6 +24012,30 @@ static int gsp_phys_reg_probe_show(struct seq_file *m, void *unused)
sked_cwd1 = gsp_phys_reg_probe_line(m, bar0,
"SKED_DEBUG_CWD_TABLE_1",
0x00407064u);
+ /* r425: the wedge diagnostics - the rest of the SKED CWD table, the
+ * per-8-group GR TPC masks (a zero compute mask would dispatch no
+ * TPCs), and the FE QMD-virtualization control for VEID 0. */
+ (void)gsp_phys_reg_probe_line(m, bar0, "SKED_DEBUG_CWD_TABLE_2",
+ 0x00407068u);
+ (void)gsp_phys_reg_probe_line(m, bar0, "SKED_DEBUG_CWD_TABLE_3",
+ 0x0040706cu);
+ (void)gsp_phys_reg_probe_line(m, bar0, "SKED_DEBUG_INFO_1",
+ 0x00407028u);
+ {
+ u32 gi;
+
+ for (gi = 0; gi < 8u; gi++) {
+ gsp_phys_reg_probe_line(m, bar0,
+ "FE_TPCM_GR_TPC_MASK",
+ 0x0040a680u + gi * 4u);
+ gsp_phys_reg_probe_line(m, bar0,
+ "FE_LAST_GR_TPC_MASK",
+ 0x0040a6a0u + gi * 4u);
+ gsp_phys_reg_probe_line(m, bar0,
+ "FE_CWD_OWN_TPC_MASK",
+ 0x0040a6c0u + gi * 4u);
+ }
+ }
fecs_irq = gsp_phys_reg_probe_line(m, bar0, "FECS_IRQSTAT",
0x00409008u);
fecs_cpuctl = gsp_phys_reg_probe_line(m, bar0, "FECS_CPUCTL",
diff --git a/kernel/gsp_phys_booter.h b/kernel/gsp_phys_booter.h
index d0c5762..80beb54 100644
--- a/kernel/gsp_phys_booter.h
+++ b/kernel/gsp_phys_booter.h
@@ -275,6 +275,17 @@ struct gsp_phys_backend_copy_result {
u32 elapsed_ms; /* return-to-caller elapsed milliseconds */
int rc; /* duplicated function return code */
struct gsp_phys_fault_info fault; /* decoded engine fault, if any */
+ /*
+ * r420b: the forwarded launch's QMD release0 was redirected to the
+ * module semaphore (gsp_phys_rmcall_gr_rel_mod=1, or an unresolved
+ * guest release page), so the ENGINE will never write the guest's own
+ * QMD release word. The caller must author it in software on every
+ * outcome (the same write the hold's success edge performs); without
+ * this a rel_mod=1 launch with no deferred release records is never
+ * completed on the guest word and the client hangs (the r414 saxpy
+ * f0 cur=5 stall).
+ */
+ bool qmd_rel_redirected;
};
int gsp_phys_backend_guest_ce_copy_ex(u64 src_hpa, u32 src_off, u64 dst_hpa,
@@ -436,6 +447,16 @@ struct gsp_phys_gr_submit {
* whose boot submission proved out (rmcall_gr_exec_ok). */
bool gsp_phys_backend_gr_ready(void);
+/*
+ * r420: true exactly while the GR submit path sleeps in a WEDGE-QUIET hold
+ * (a deliberate, bounded wait for the sticky wedge to clear). The in-flight
+ * forwarded request is NOT done during it, so the sema service's completion
+ * deadlines (the 12 s GR release hold and the 3 s per-launch consumption
+ * bound) pause while this returns true instead of authoring the launch's
+ * release word under the hold (the r419 mid-boot stale-data escape).
+ */
+bool gsp_phys_gr_quiet_hold_active(void);
+
int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
struct gsp_phys_backend_copy_result *out);
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c
index b93c254..d1aa06e 100644
--- a/kernel/gsp_phys_gpu.c
+++ b/kernel/gsp_phys_gpu.c
@@ -664,6 +664,29 @@ int gsp_phys_rmcall_gr_pcas_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_pcas_value);
/*
+ * r434 CWD-slot-count path (gsp_phys_rmcall_gr_cwd_slot; default -1 = off).
+ * On Volta+ the SET_CWD_SLOT_COUNT method is gated by the falcon PRI bit
+ * NV_PGRAPH_PRI_SKED_DEBUG_1.ENABLE_CWD_SLOT_COUNT (the CUDA driver's
+ * turingLaunchSetCwdSlotCount: a masked PRI write via SET_MME_SHADOW_SCRATCH
+ * 0/1/2 + SET_FALCON04, then the method itself). When >= 0 the init preamble
+ * emits that PRI write (ENABLE_CWD_SLOT_COUNT=1) followed by
+ * SET_CWD_SLOT_COUNT with the given value, so the wedge's "grid truncated to
+ * ~4 CTAs" signature can be A/B'd against the CWD slot-count watermark.
+ * Read per launch; -1 restores the historical preamble byte-for-byte.
+ */
+static int gsp_phys_rmcall_gr_cwd_slot_param = -1;
+module_param_named(gsp_phys_rmcall_gr_cwd_slot,
+ gsp_phys_rmcall_gr_cwd_slot_param, int, 0644);
+MODULE_PARM_DESC(gsp_phys_rmcall_gr_cwd_slot,
+ "r434: enable the CWD slot count PRI (SKED_DEBUG_1 bit11) and emit SET_CWD_SLOT_COUNT with this value in the GR init preamble (-1 = off, default)");
+
+int gsp_phys_rmcall_gr_cwd_slot_value(void)
+{
+ return gsp_phys_rmcall_gr_cwd_slot_param;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_cwd_slot_value);
+
+/*
* Two-step PCAS gate (default 0). With the explicit PCAS pair on, setting
* this marks the QMD's REQUIRE_SCHEDULING_PCAS (w4 bit12) and emits the copy
* PCAS (INVALIDATE only) followed by a schedule-only PCAS (SCHEDULE), the
@@ -1057,6 +1080,33 @@ int gsp_phys_gr_icache_inv_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_gr_icache_inv_value);
/*
+ * r433 RELEASE_MEMBAR_TYPE A/B (gsp_phys_gr_rel_membar; default 0). The
+ * captured guest QMDs carry RELEASE_MEMBAR_TYPE=FE_NONE (QMDV00_06 word 11
+ * bit 14 clear, w11=0x44010000), so when the front-end completes a grid it
+ * writes the QMD release semaphore without first flushing its pending
+ * system-memory stores. The post-dispatch store-loss signature (the engine
+ * processes the QMD, writes the release, the grid completes and ZERO data
+ * pages are host-visible) is exactly what an unflushed write-back L2 looks
+ * like from the host: the release is host-visible while the data sits in the
+ * GPU's L2. Forcing RELEASE_MEMBAR_TYPE=FE_SYSMEMBAR makes the release
+ * ordering guarantee the stores are in memory before the semaphore write, so
+ * the host's retire scan (and the copy-out) can never observe a completion
+ * whose data has not landed. 0 keeps the guest's captured value (the
+ * historical behaviour) for a one-flip A/B; read per launch.
+ */
+static int gsp_phys_gr_rel_membar_param;
+module_param_named(gsp_phys_gr_rel_membar, gsp_phys_gr_rel_membar_param,
+ int, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_rel_membar,
+ "r433: force QMD RELEASE_MEMBAR_TYPE=FE_SYSMEMBAR (word11 bit14) on forwarded GR launches (default 0 = guest's captured FE_NONE)");
+
+int gsp_phys_gr_rel_membar_value(void)
+{
+ return gsp_phys_gr_rel_membar_param;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_rel_membar_value);
+
+/*
* m179 no-write retry budget (gsp_phys_gr_nowrite_retries; default 3, clamped
* to [0, 4]). The m153 outcome retry loop re-executes a data-bearing launch
* whose retirement wait saw zero engine writes; it shared the timeout ladder's
@@ -1124,6 +1174,60 @@ int gsp_phys_gr_nowrite_retries_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_gr_nowrite_retries_value);
/*
+ * r445 wedge hard re-arm (gsp_phys_gr_wedge_rearm; default 0 = off). When the
+ * outcome-retry's warm GR semaphore probe proves the engine wedged for the
+ * context (the proven-sticky post-dispatch no-store state), this knob makes
+ * the submit attempt ONE bounded recovery instead of breaking immediately:
+ * a TSG PREEMPT(bWait=1) forces the FECS to save/unload the channel's
+ * context, then the mode-35 lane-enable tail + GPFIFO schedules are re-issued
+ * (the m155 extended re-arm) and the semaphore probe is repeated. A pass
+ * means the lane revived for this launch and the normal retry ladder
+ * continues; a fail leaves the historical WEDGED break. Read once per wedge
+ * episode. Off by default for A/B.
+ */
+static int gsp_phys_gr_wedge_rearm_param;
+module_param_named(gsp_phys_gr_wedge_rearm, gsp_phys_gr_wedge_rearm_param,
+ int, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_wedge_rearm,
+ "r445: on the proven engine-wedged sem probe, attempt a bounded preempt+resched hard re-arm before giving up (default 0=off)");
+
+int gsp_phys_gr_wedge_rearm_value(void)
+{
+ return gsp_phys_gr_wedge_rearm_param ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_wedge_rearm_value);
+
+/*
+ * r449 SHADER-CONSTANT-CACHE invalidate (gsp_phys_gr_ccache_inv; default 0).
+ * The forwarded QMD's cache-control block (QMDV02_03 word 5) carries the
+ * per-cache invalidate bits; the captured guest QMDs always set bits 26..29
+ * (texture header/sampler/data + shader DATA) and the M91 fix adds bit 30
+ * (INSTRUCTION), but bit 31 (SHADER CONSTANT - where a kernel's launch
+ * parameters live) is never set. The forwarded launch's param page is
+ * rewritten in place between launches (the two-bank win0 pool), so a stale
+ * constant-cache line can serve the PREVIOUS launch's parameters - the
+ * kernel then computes with the previous launch's pointers/sizes and the
+ * current launch's tracked output pages stay untouched ("no stores").
+ * Mode: 0 = off, 1 = set bit 31 only, 2 = force the full 0xff000000
+ * invalidate set. Read at submission time, so it can be A/B'd at runtime.
+ */
+static int gsp_phys_gr_ccache_inv_param;
+module_param_named(gsp_phys_gr_ccache_inv, gsp_phys_gr_ccache_inv_param,
+ int, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_ccache_inv,
+ "r449: forwarded-QMD shader-constant-cache invalidate: 0=off, 1=bit31, 2=all invalidate bits (default 0)");
+
+unsigned int gsp_phys_gr_ccache_inv_value(void)
+{
+ if (gsp_phys_gr_ccache_inv_param < 0)
+ return 0;
+ if (gsp_phys_gr_ccache_inv_param > 2)
+ return 2;
+ return (unsigned int)gsp_phys_gr_ccache_inv_param;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_ccache_inv_value);
+
+/*
* r410 wedge quiet window (gsp_phys_gr_wedge_quiet_ms; default 40000, clamped
* [0,120000]; 0 disables). The sticky per-lane no-execute wedge (100% of
* launches no-write while kept busy) is proven to CLEAR when the lane stays
diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h
index e02d6d8..36c2220 100644
--- a/kernel/gsp_phys_gpu.h
+++ b/kernel/gsp_phys_gpu.h
@@ -388,10 +388,22 @@ unsigned int gsp_phys_gr_first_poll_ms_value(void);
* (default 1; 0 = pre-m91 QMD words). */
int gsp_phys_gr_icache_inv_value(void);
+/* r433: force QMD RELEASE_MEMBAR_TYPE=FE_SYSMEMBAR (QMDV00_06 word11 bit14)
+ * on forwarded GR launches so the engine flushes pending sysmem stores
+ * before writing the release semaphore (default 0 = guest's FE_NONE). */
+int gsp_phys_gr_rel_membar_value(void);
+
+/* r434: enable the CWD slot count PRI (SKED_DEBUG_1 bit11) and emit
+ * SET_CWD_SLOT_COUNT with this value in the GR init preamble
+ * (-1 = off, default). */
+int gsp_phys_rmcall_gr_cwd_slot_value(void);
+
/* m179: dedicated re-execution budget for a data-bearing GR launch whose
* retirement wait saw zero engine writes, independent of the timeout ladder's
* attempt counter (default 3, clamped [0,4]). */
int gsp_phys_gr_nowrite_retries_value(void);
+int gsp_phys_gr_wedge_rearm_value(void);
+unsigned int gsp_phys_gr_ccache_inv_value(void);
/* STALLFREE: absolute per-launch GR submit wall-clock budget in ms, clamped
* [10000,600000]; 0 = only the built-in per-ladder bounds (default 60000).
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 0354831..b90c948 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -4820,7 +4820,8 @@ static int svc_fill_1d(struct nv_vgpu_device *vgpu, u64 addr, u64 value,
u64 *done_out);
static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src,
size_t total, u32 dst_phys, u32 dst_aperture,
- u32 src_phys, u32 src_aperture, u64 *done_out);
+ u32 src_phys, u32 src_aperture, u64 *done_out,
+ u32 owner_ch, u32 owner_idx, u64 owner_hash);
/*
* r402 row-wise wrapper: a 2D (pitched) fill runs the 1D fill once per
@@ -5938,12 +5939,29 @@ static void svc_copy_audit_chunk(struct nv_vgpu_device *vgpu, u64 dst_va,
struct svc_copy_snap {
u64 dst, src;
size_t total;
+ /*
+ * Owner identity (r444): the channel, ring entry and push-content
+ * hash of the decode pass that captured this snapshot. A snapshot
+ * exists to serve ONE entry's retried copy (the held staging read);
+ * keying it by (dst,src,total) alone let a snapshot captured for an
+ * earlier entry poison a later copy with the same operand triple -
+ * the wedge_ab "bad = mutated-element count" class: step N's H2D
+ * executed from step N-1's snapshot because the re-decode of the
+ * completed step N-1 entry re-captured its (unchanged) source after
+ * its copy had already run, and step N's copy then consumed it.
+ * The pass hash changes whenever the guest rewrites the push, so a
+ * ring slot reused by a fresh push can never match a stale owner.
+ */
+ u64 owner_hash;
+ u32 owner_ch;
+ u32 owner_idx;
u8 *buf;
unsigned long stamp;
};
static struct svc_copy_snap svc_copy_snaps[SVC_COPY_SNAP_N];
-static u8 *svc_copy_snap_lookup(u64 dst, u64 src, size_t total)
+static u8 *svc_copy_snap_lookup(u64 dst, u64 src, size_t total,
+ u32 owner_ch, u32 owner_idx, u64 owner_hash)
{
u32 i;
@@ -5951,12 +5969,16 @@ static u8 *svc_copy_snap_lookup(u64 dst, u64 src, size_t total)
if (svc_copy_snaps[i].buf &&
svc_copy_snaps[i].dst == dst &&
svc_copy_snaps[i].src == src &&
- svc_copy_snaps[i].total == total)
+ svc_copy_snaps[i].total == total &&
+ svc_copy_snaps[i].owner_ch == owner_ch &&
+ svc_copy_snaps[i].owner_idx == owner_idx &&
+ svc_copy_snaps[i].owner_hash == owner_hash)
return svc_copy_snaps[i].buf;
return NULL;
}
-static void svc_copy_snap_free(u64 dst, u64 src, size_t total)
+static void svc_copy_snap_free(u64 dst, u64 src, size_t total,
+ u32 owner_ch, u32 owner_idx, u64 owner_hash)
{
u32 i;
@@ -5964,16 +5986,41 @@ static void svc_copy_snap_free(u64 dst, u64 src, size_t total)
if (svc_copy_snaps[i].buf &&
svc_copy_snaps[i].dst == dst &&
svc_copy_snaps[i].src == src &&
- svc_copy_snaps[i].total == total) {
+ svc_copy_snaps[i].total == total &&
+ svc_copy_snaps[i].owner_ch == owner_ch &&
+ svc_copy_snaps[i].owner_idx == owner_idx &&
+ svc_copy_snaps[i].owner_hash == owner_hash) {
kfree(svc_copy_snaps[i].buf);
svc_copy_snaps[i].buf = NULL;
return;
}
}
+/*
+ * r444: drop every snapshot still owned by one entry. A snapshot that was
+ * not consumed by the entry's own copy execution (the copy ran via the
+ * forwarded hardware path, or a post-execution re-decode re-captured the
+ * unchanged staging) has no future consumer once the entry's release is
+ * delivered - leaving it behind let a LATER entry whose copy shares the
+ * (dst,src,total) triple execute from the stale bytes.
+ */
+static void svc_copy_snap_free_owner(u32 owner_ch, u32 owner_idx)
+{
+ u32 i;
+
+ for (i = 0; i < SVC_COPY_SNAP_N; i++)
+ if (svc_copy_snaps[i].buf &&
+ svc_copy_snaps[i].owner_ch == owner_ch &&
+ svc_copy_snaps[i].owner_idx == owner_idx) {
+ kfree(svc_copy_snaps[i].buf);
+ svc_copy_snaps[i].buf = NULL;
+ }
+}
+
/* Capture the source of a virtual-operand copy about to be held. */
static void svc_copy_snap_capture(struct nv_vgpu_device *vgpu, u64 dst,
- u64 src, size_t total)
+ u64 src, size_t total, u32 owner_ch,
+ u32 owner_idx, u64 owner_hash)
{
struct svc_copy_snap *s = NULL;
u8 *buf;
@@ -5982,7 +6029,8 @@ static void svc_copy_snap_capture(struct nv_vgpu_device *vgpu, u64 dst,
if (!total || total > SVC_COPY_SNAP_MAX)
return;
- if (svc_copy_snap_lookup(dst, src, total))
+ if (svc_copy_snap_lookup(dst, src, total, owner_ch, owner_idx,
+ owner_hash))
return; /* already captured */
for (i = 0; i < SVC_COPY_SNAP_N; i++) {
if (!svc_copy_snaps[i].buf) {
@@ -6019,10 +6067,14 @@ static void svc_copy_snap_capture(struct nv_vgpu_device *vgpu, u64 dst,
s->dst = dst;
s->src = src;
s->total = total;
+ s->owner_ch = owner_ch;
+ s->owner_idx = owner_idx;
+ s->owner_hash = owner_hash;
s->stamp = jiffies;
s->buf = buf;
- pr_info_ratelimited("vgsp-sema: copy snapshot captured dst=0x%llx src=0x%llx total=%zu first4=%08x %08x %08x %08x\n",
- dst, src, total,
+ pr_info_ratelimited("vgsp-sema: copy snapshot captured dst=0x%llx src=0x%llx total=%zu owner=0x%x/%u/%016llx first4=%08x %08x %08x %08x\n",
+ dst, src, total, owner_ch, owner_idx,
+ owner_hash,
total >= 4 ? *(const u32 *)buf : 0,
total >= 8 ? *(const u32 *)(buf + 4) : 0,
total >= 12 ? *(const u32 *)(buf + 8) : 0,
@@ -6039,7 +6091,8 @@ static const char *svc_gr_src_probe(struct nv_vgpu_device *vgpu, u64 va,
u64 *pa_out, u32 *ap_out);
static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
u32 dst_phys, u32 dst_aperture, u32 src_phys, u32 src_aperture,
- u64 *done_out, u32 rows, u32 dpitch, u32 spitch)
+ u64 *done_out, u32 rows, u32 dpitch, u32 spitch,
+ u32 owner_ch, u32 owner_idx, u64 owner_hash)
{
if (rows > 1 && dpitch) {
u64 done = 0;
@@ -6052,7 +6105,8 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
rc = svc_copy_1d(vgpu, dst + (u64)r * dpitch,
src + (u64)r * spitch, total,
dst_phys, dst_aperture, src_phys,
- src_aperture, &row_done);
+ src_aperture, &row_done,
+ owner_ch, owner_idx, owner_hash);
done += row_done;
if (rc)
break;
@@ -6062,12 +6116,13 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
return rc;
}
return svc_copy_1d(vgpu, dst, src, total, dst_phys, dst_aperture,
- src_phys, src_aperture, done_out);
+ src_phys, src_aperture, done_out,
+ owner_ch, owner_idx, owner_hash);
}
static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
u32 dst_phys, u32 dst_aperture, u32 src_phys, u32 src_aperture,
- u64 *done_out)
+ u64 *done_out, u32 owner_ch, u32 owner_idx, u64 owner_hash)
{
u8 bounce[256];
size_t o;
@@ -6098,7 +6153,8 @@ static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t tot
svc.cur_ch, dst, src, total, dst_phys, dst_aperture,
src_phys, src_aperture);
}
- snap = svc_copy_snap_lookup(dst_orig, src_orig, total);
+ snap = svc_copy_snap_lookup(dst_orig, src_orig, total, owner_ch,
+ owner_idx, owner_hash);
if (!dst_phys) {
u64 pa;
@@ -6232,7 +6288,8 @@ static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t tot
*/
if (!src_phys && src_orig >= SVC_VAS_FLOOR)
svc_copy_snap_capture(vgpu, dst_orig, src_orig,
- total);
+ total, owner_ch, owner_idx,
+ owner_hash);
return -ENOENT;
}
/*
@@ -6261,7 +6318,9 @@ static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t tot
dst_orig, dst_prov, ipa);
if (!src_phys && src_orig >= SVC_VAS_FLOOR)
svc_copy_snap_capture(vgpu, dst_orig,
- src_orig, total);
+ src_orig, total,
+ owner_ch, owner_idx,
+ owner_hash);
return -ENOENT;
}
}
@@ -7036,7 +7095,8 @@ static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t tot
svc_ledger_store(dst_orig + po, dst + po, cap);
}
if (snap)
- svc_copy_snap_free(dst_orig, src_orig, total);
+ svc_copy_snap_free(dst_orig, src_orig, total, owner_ch,
+ owner_idx, owner_hash);
if (done_out)
*done_out = (u64)o; /* o == total on the success path */
return 0;
@@ -7238,7 +7298,8 @@ static struct svc_wait_hold *svc_wait_hold_get(u32 h, bool create)
}
static void svc_run_ops(struct nv_vgpu_device *vgpu, struct svc_memset_op *ops,
- u32 n_ops, u32 ch_h, u64 *ops_done, u32 ops_base)
+ u32 n_ops, u32 ch_h, u64 *ops_done, u32 ops_base,
+ u32 entry_idx, u64 pass_hash)
{
u32 i;
static u64 bigop_log;
@@ -7404,7 +7465,8 @@ svc_wait_done:
m->phys, m->aperture,
m->src_phys, m->src_aperture,
&ce_done, m->rows, m->dpitch,
- m->spitch);
+ m->spitch, ch_h, entry_idx,
+ pass_hash);
else
rc = svc_fill(vgpu, m->dst, m->value, m->elem,
m->count, m->phys, m->aperture,
@@ -9385,7 +9447,8 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
* REPORT_SEM comment below. */
if (allow_ops && n_ops && !gsp_sema_skip_ops) {
svc_run_ops(vgpu, ops, n_ops, ch->h_object,
- ops_done, ops_seq - n_ops);
+ ops_done, ops_seq - n_ops,
+ entry_idx, svc_pass_hash);
svc.ring_ops++;
ops_runs++;
}
@@ -9585,7 +9648,8 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
*/
if (allow_ops && n_ops && !gsp_sema_skip_ops) {
svc_run_ops(vgpu, ops, n_ops, ch->h_object,
- ops_done, ops_seq - n_ops);
+ ops_done, ops_seq - n_ops,
+ entry_idx, svc_pass_hash);
svc.ring_ops++;
ops_runs++;
}
@@ -10000,18 +10064,45 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
* r402: a 2D fill (cudaMemset2D):
* LINE_COUNT rows of
* LINE_LENGTH_IN bytes at
- * PITCH_OUT stride. Only a pitch
- * WIDER than the row selects row
- * mode; a flat fill carries
- * pitch == row length (or no
- * pitch method) and keeps the
- * historical single-run path.
+ * PITCH_OUT stride. A pitch WIDER
+ * than the row selects row mode; a
+ * flat fill carries pitch == row
+ * length (or no pitch method) and
+ * keeps the historical single-run
+ * path.
+ *
+ * r420c: LINE_COUNT is
+ * authoritative for the flat case
+ * too - cudaMemset2D with pitch ==
+ * width (victory_memset2d,
+ * pitch=4096/w=4096) is CONTIGUOUS
+ * rows, so the single run must
+ * cover rows*line bytes. The
+ * historical path filled one row
+ * and the readback lost the rest
+ * (bad ~= 12.5M of 12.58M).
*/
- if (st.have_pitch_out &&
- st.line_count > 1 &&
- st.pitch_out > st.line_length) {
- m->rows = st.line_count;
- m->dpitch = st.pitch_out;
+ if (st.line_count > 1) {
+ u32 fpitch = st.have_pitch_out ?
+ st.pitch_out :
+ st.line_length;
+
+ if (fpitch > st.line_length) {
+ m->rows =
+ st.line_count;
+ m->dpitch = fpitch;
+ } else if (st.line_length %
+ m->elem) {
+ m->elem = 1;
+ m->count =
+ st.line_length *
+ st.line_count;
+ } else {
+ m->count =
+ (st.line_length /
+ m->elem) *
+ st.line_count;
+ }
}
if (m->rows > 1 && svc_pitch_log < 32) {
svc_pitch_log++;
@@ -10053,20 +10144,41 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
* LINE_COUNT rows of
* LINE_LENGTH_IN bytes with
* PITCH_IN/PITCH_OUT strides.
- * Row mode only when a pitch is
- * wider than the row (a flat 1D
- * copy keeps pitch == length).
+ * Row mode when a pitch is
+ * wider than the row.
+ *
+ * r420c: pitch == row length
+ * (or no pitch method) makes the
+ * rows CONTIGUOUS, so the single
+ * flat run must cover
+ * rows*line bytes - the
+ * historical path copied one row
+ * (victory_memset2d's pitched
+ * D2H/H2D at pitch 4096 = width
+ * 4096 lost all but the first
+ * row).
*/
- if (st.line_count > 1 &&
- st.have_pitch_out &&
- st.pitch_out > st.line_length) {
- m->rows = st.line_count;
- m->dpitch = st.pitch_out;
- m->spitch = st.have_pitch_in ?
+ if (st.line_count > 1) {
+ u32 dpitch = st.have_pitch_out ?
+ st.pitch_out :
+ st.line_length;
+ u32 spitch = st.have_pitch_in ?
(st.pitch_in ?
st.pitch_in :
- st.pitch_out) :
- st.pitch_out;
+ dpitch) :
+ dpitch;
+
+ if (dpitch == st.line_length &&
+ spitch == st.line_length) {
+ m->bytes =
+ st.line_length *
+ st.line_count;
+ } else {
+ m->rows =
+ st.line_count;
+ m->dpitch = dpitch;
+ m->spitch = spitch;
+ }
}
n_ops++;
ops_seq++;
@@ -10247,7 +10359,10 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
svc_copy_snap_capture(vgpu,
st.offset_out,
st.offset_in,
- st.line_length);
+ st.line_length,
+ ch->h_object,
+ entry_idx,
+ svc_pass_hash);
}
/*
* r308: the `!replay` gate above kept this
@@ -10338,7 +10453,10 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
svc_copy_snap_capture(vgpu,
lm->dst,
lm->src,
- lm->bytes);
+ lm->bytes,
+ ch->h_object,
+ entry_idx,
+ svc_pass_hash);
if (gsp_sema_dbg &&
svc.snap_gate_log < 256) {
svc.snap_gate_log++;
@@ -10376,6 +10494,27 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
lm->elem, lm->count,
lm->aperture, lm->src_aperture,
lm->phys, lm->src_phys);
+ /*
+ * r420d: 2D geometry of every copy op
+ * (the memset2d readback forensics -
+ * the driver decomposes cudaMemcpy2D
+ * into per-push ops whose LINE_COUNT/
+ * PITCH decode must be visible to
+ * tell "one row per op" from "N rows
+ * in one op"). Bounded.
+ */
+ if (lm->type == 1)
+ pr_info("sema-op2d: h=0x%x dst=0x%llx src=0x%llx bytes=%u rows=%u dpitch=%u spitch=%u line=%u lc=%u have_po=%u po=%u have_pi=%u pi=%u\n",
+ ch->h_object, lm->dst,
+ lm->src, lm->bytes,
+ lm->rows, lm->dpitch,
+ lm->spitch,
+ st.line_length,
+ st.line_count,
+ st.have_pitch_out,
+ st.pitch_out,
+ st.have_pitch_in,
+ st.pitch_in);
}
}
@@ -10638,7 +10777,8 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
gsp_sema_skip_ops);
}
svc_run_ops(vgpu, ops, n_ops, ch->h_object,
- ops_done, ops_seq - n_ops);
+ ops_done, ops_seq - n_ops,
+ entry_idx, svc_pass_hash);
svc.ring_ops++;
ops_runs++;
}
@@ -15436,6 +15576,19 @@ static void svc_fwd_soft_copy_inline(struct nv_vgpu_device *vgpu)
* hardware has not produced yet.
*/
#define SVC_FWD_GR_HOLD_TIMEOUT_MS 12000u
+/*
+ * r420: hard cap on the WEDGE-QUIET deferral of the GR hold deadline. The
+ * backend's quiet hold is a bounded msleep (its own caps: <=240 s per hold,
+ * two holds per launch), so the pause can only last a few minutes; the cap is
+ * a defence-in-depth bound so a stuck quiet-hold flag can never defer the
+ * client's completion indefinitely.
+ */
+#define SVC_FWD_GR_HOLD_PAUSE_MAX_MS 600000u
+/*
+ * r420: same defence for the per-launch consumption bound pause in
+ * svc_gr_hold_bound_expired().
+ */
+#define SVC_GR_QUIET_PAUSE_MAX_MS 600000u
struct svc_fwd_hold {
bool active;
@@ -15471,9 +15624,9 @@ struct svc_fwd_hold {
/*
* P6c completion relay: the request selected record relay_index and
* the hardware copy was asked to author it into the guest-visible
- * semaphore page. On the success edge the hold re-reads the guest
- * word and, when it carries the payload, skips that one record's
- * software apply. Cleared with the hold.
+ * semaphore page. On the success edge the hold re-reads the guest word
+ * and, when it carries the payload, skips that one record's software
+ * apply. Cleared with the hold.
*/
bool relay_armed;
u8 relay_index;
@@ -15486,6 +15639,18 @@ struct svc_fwd_hold {
struct svc_fwd_rel rel[SVC_FWD_REL_MAX];
u64 last_rel_va;
unsigned long since;
+ /*
+ * r420: WEDGE-QUIET deferral state. While the backend deliberately
+ * holds the in-flight launch in its bounded quiet window
+ * (gsp_phys_gr_quiet_hold_active), the GR deadline is pushed forward
+ * (paused_until) so the hold cannot author the launch's release word
+ * under the hold - the client would then verify data the post-hold
+ * copy-out has not written yet (the r419 mid-boot escape). The
+ * deferral is itself bounded by SVC_FWD_GR_HOLD_PAUSE_MAX_MS from
+ * `since` and is announced once per hold.
+ */
+ unsigned long paused_until;
+ bool pause_logged;
};
static struct svc_fwd_hold svc_fwd_hold;
@@ -15711,6 +15876,10 @@ static void svc_opdef_clear(struct svc_opdef_ent *e, u32 caller)
e->n ? e->rel[0].va : 0ull,
e->n ? e->rel[0].payload : 0u);
}
+ /* r444: the entry is being consumed; no retry can need its
+ * staging snapshot any more (see svc_copy_snap_free_owner). */
+ if (e->active)
+ svc_copy_snap_free_owner(e->h_object, e->entry_idx);
memset(e, 0, sizeof(*e));
}
@@ -16288,9 +16457,38 @@ static void svc_fwd_outcome_check(struct nv_vgpu_device *vgpu)
static bool svc_fwd_hold_expired(void)
{
- if (svc_fwd_req.gr && !smp_load_acquire(&svc_fwd_req.done))
+ if (svc_fwd_req.gr && !smp_load_acquire(&svc_fwd_req.done)) {
+ /*
+ * r420: while the backend deliberately holds the in-flight
+ * launch in its bounded WEDGE-QUIET window, the GR deadline
+ * must not fire - expiring here authors the launch's release
+ * word (svc_fwd_hold_gr_release) and applies the recorded
+ * releases before the post-hold copy-out, so the client
+ * verifies stale data (the r419 soak's mid-boot
+ * "matmul fails after time"). Pause instead, bounded by
+ * SVC_FWD_GR_HOLD_PAUSE_MAX_MS from the hold's start; the
+ * pause extends the deadline a full GR window past the last
+ * quiet-hold pass, so the backend's retry/accept can finish
+ * before the fallback would author. Announced once per hold.
+ */
+ if (gsp_phys_gr_quiet_hold_active() &&
+ time_before(jiffies, svc_fwd_hold.since +
+ msecs_to_jiffies(SVC_FWD_GR_HOLD_PAUSE_MAX_MS))) {
+ if (!svc_fwd_hold.pause_logged) {
+ svc_fwd_hold.pause_logged = true;
+ pr_warn("vgsp-gr: GR-HOLD-PAUSE va=0x%llx (backend wedge-quiet hold active; release authorship deferred under the bounded hold)\n",
+ svc_fwd_req.gr_push_va);
+ }
+ svc_fwd_hold.paused_until = jiffies +
+ msecs_to_jiffies(SVC_FWD_GR_HOLD_TIMEOUT_MS);
+ return false;
+ }
+ if (svc_fwd_hold.paused_until &&
+ time_before(jiffies, svc_fwd_hold.paused_until))
+ return false;
return time_after(jiffies, svc_fwd_hold.since +
msecs_to_jiffies(SVC_FWD_GR_HOLD_TIMEOUT_MS));
+ }
return time_after(jiffies, svc_fwd_hold.since +
msecs_to_jiffies(SVC_FWD_HOLD_TIMEOUT_MS));
}
@@ -16324,6 +16522,9 @@ static void svc_fwd_hold_engage(struct nv_vgpu_device *vgpu,
svc_fwd_hold.relay_armed = svc_fwd_req.relay;
svc_fwd_hold.relay_index = svc_fwd_req.relay_index;
svc_fwd_hold.fault_delivered = false;
+ /* r420: a fresh hold starts with no WEDGE-QUIET deferral. */
+ svc_fwd_hold.paused_until = 0;
+ svc_fwd_hold.pause_logged = false;
svc.fwd_hold_engaged++;
svc.fwd_rel_pending = n;
}
@@ -16350,6 +16551,8 @@ static void svc_fwd_hold_clear(void)
svc_fwd_hold.relay_armed = false;
svc_fwd_hold.relay_index = 0;
svc_fwd_hold.fault_delivered = false;
+ svc_fwd_hold.paused_until = 0;
+ svc_fwd_hold.pause_logged = false;
svc.fwd_rel_pending = 0;
}
@@ -17114,6 +17317,17 @@ static void svc_fwd_gr_work(void)
ga_va, ga_used, ga_led);
hw_rc = gsp_phys_backend_guest_gr_submit(&gs, &cp);
+ /*
+ * r420b: the backend redirected the launch's QMD release0 to the
+ * module semaphore (rel_mod=1 or no targetable guest release page), so
+ * the ENGINE never writes the guest's own QMD release word. Treat the
+ * word as unobserved so the bounded outcome check (and the hold's
+ * edges) author it in software on every outcome; without this a
+ * rel_mod=1 launch with no deferred release records is never completed
+ * on the guest word and the client hangs (the r414 saxpy stall).
+ */
+ if (cp.qmd_rel_redirected)
+ svc_fwd_req.gr_rel_unobserved = true;
if (!hw_rc) {
svc.fwd_gr_ok++;
} else {
@@ -18063,6 +18277,27 @@ static bool svc_gr_hold_bound_expired(struct svc_channel *ch, u64 launch_va)
ch->gr_hold_deadline = jiffies + msecs_to_jiffies(cap_ms);
ch->gr_hold_exp_logged = false;
}
+ /*
+ * r420: pause the per-launch consumption bound while the backend
+ * deliberately holds the in-flight launch in its bounded WEDGE-QUIET
+ * window. At the bound the pump falls through to the historical
+ * consume, which authors the capture's QMD completion release in
+ * software (GR-STALL-hold) - but the backend is still going to
+ * submit/accept this very launch after the hold and copy its data
+ * out; authoring now releases the client onto stale data (the r419
+ * mid-boot escape). The quiet hold is a bounded msleep (the
+ * backend's own caps), and the deferral here is bounded by
+ * SVC_GR_QUIET_PAUSE_MAX_MS from the bound's arm time as
+ * defence-in-depth. Rate-limited announcement.
+ */
+ if (ch->gr_hold_dead_va && gsp_phys_gr_quiet_hold_active() &&
+ time_before(jiffies, ch->gr_hold_dead_since +
+ msecs_to_jiffies(SVC_GR_QUIET_PAUSE_MAX_MS))) {
+ ch->gr_hold_deadline = jiffies + msecs_to_jiffies(cap_ms);
+ pr_info_ratelimited("vgsp-gr: GR-HOLD-BOUND-PAUSE launch=0x%llx (backend wedge-quiet hold active; consumption bound deferred)\n",
+ launch_va);
+ return false;
+ }
return time_after_eq(jiffies, ch->gr_hold_deadline);
}
@@ -20779,7 +21014,22 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
svc_gr_launch_va(ch)) &&
(ch->gr_hold_idx != (u32)idx ||
time_before(jiffies,
- ch->gr_hold_until))) {
+ ch->gr_hold_until) ||
+ /*
+ * r420: the backend's
+ * bounded WEDGE-QUIET
+ * hold outlives this
+ * one-shot window; keep
+ * re-holding (never
+ * consume-with-sw-release)
+ * while it is active, so
+ * the launch is still
+ * forwarded and copied out
+ * after the hold instead of
+ * being released onto stale
+ * data.
+ */
+ gsp_phys_gr_quiet_hold_active())) {
u32 hold_ms = gsp_sema_gr_grace_ms ?
gsp_sema_gr_grace_ms : 8000;