summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_phys_gpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_phys_gpu.c')
-rw-r--r--kernel/gsp_phys_gpu.c385
1 files changed, 367 insertions, 18 deletions
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c
index b1945ee..f1d72f7 100644
--- a/kernel/gsp_phys_gpu.c
+++ b/kernel/gsp_phys_gpu.c
@@ -617,6 +617,25 @@ int gsp_phys_rmcall_gr_prelude_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_prelude_value);
/*
+ * r597: emit the SEM_EXECUTE ACQUIRE groups found in the captured guest
+ * prelude ahead of the rebuilt inline-QMD window (default 0). The verbatim
+ * prelude replay breaks the launch (qmd_rel=0 after an 8.9 s stall, r599),
+ * so the acquire cannot be taken by replaying the prelude; this gate takes
+ * only the wait method groups and leaves the proven window in place.
+ */
+static int gsp_phys_rmcall_gr_acq_param;
+module_param_named(gsp_phys_rmcall_gr_acq, gsp_phys_rmcall_gr_acq_param,
+ int, 0644);
+MODULE_PARM_DESC(gsp_phys_rmcall_gr_acq,
+ "Emit the captured prelude's SEM_EXECUTE ACQUIRE groups ahead of the rebuilt GR window (default 0)");
+
+int gsp_phys_rmcall_gr_acq_value(void)
+{
+ return gsp_phys_rmcall_gr_acq_param;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_acq_value);
+
+/*
* QMD virtualization control gate (default 0 after m26 falsified the
* hypothesis: on Turing the context init value is 0, native CUDA never
* writes 0x29c, and the nonzero enable alone does not schedule). When set,
@@ -638,13 +657,20 @@ int gsp_phys_rmcall_gr_qmdvirt_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_qmdvirt_value);
/*
- * Explicit-PCAS gate (default 1). The rebuilt launch carries the vendor's
- * turingPushPcas(CU_PCAS_TYPE_COPY) pair (SEND_PCAS_A = QMD>>8 followed by
- * SEND_SIGNALING_PCAS_B = INVALIDATE|SCHEDULE). The stock guest push does
- * not emit these (it relies on the IQ2M window's implied copy PCAS); setting
- * this to 0 emits the pure vendor window-only form for the A/B test.
- */
-static int gsp_phys_rmcall_gr_pcas_param = 1;
+ * Explicit-PCAS gate (default 0 since r343 - the double-execution fix).
+ * The rebuilt launch used to carry the vendor's turingPushPcas
+ * (CU_PCAS_TYPE_COPY) pair (SEND_PCAS_A = QMD>>8 followed by
+ * SEND_SIGNALING_PCAS_B = INVALIDATE|SCHEDULE) ADDED to the inline-QMD run
+ * that the stock guest push already carries. The QMD's
+ * REQUIRE_SCHEDULING_PCAS bit is clear, so the inline run schedules by
+ * itself - the explicit pair was a SECOND scheduling edge and the same QMD
+ * was dispatched twice (launch_count: one atomicAdd +100 landed as +200;
+ * victory_reduce exactly 2x; scale/saxpy/memset2d doubled). The stock guest
+ * push has NO PCAS pair (captured push dumps); default 0 restores that
+ * single-edge form. 1 = re-add the pair (A/B debug only - with it on, the
+ * grid executes twice).
+ */
+static int gsp_phys_rmcall_gr_pcas_param;
module_param_named(gsp_phys_rmcall_gr_pcas,
gsp_phys_rmcall_gr_pcas_param, int, 0644);
MODULE_PARM_DESC(gsp_phys_rmcall_gr_pcas,
@@ -657,6 +683,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
@@ -704,20 +753,30 @@ int gsp_phys_rmcall_gr_qmdwin_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_qmdwin_value);
/*
- * m64 forced module-semaphore release (gsp_phys_rmcall_gr_rel_mod; default 0).
- * When set, a forwarded GR launch ignores the resolved guest release page and
- * points QMD release0 at the module window-0 semaphore VA
- * (GSP_PHYS_RMCALL_VA_SEM), which the host polls at the SEM chunk's host VA.
- * Discriminant for "engine completed but the shadow release write was lost"
- * vs "engine never completed": with the module release, a completed QMD is
- * always observable. Also makes the existing module-release fallback
- * observable (it previously set no poll source, so it could never complete).
- */
-static int gsp_phys_rmcall_gr_rel_mod_param;
+ * m64 forced module-semaphore release (gsp_phys_rmcall_gr_rel_mod).
+ *
+ * DEFAULT 1 since r453. When set, a forwarded GR launch ignores the resolved
+ * guest release page and points QMD release0 at the module window-0 semaphore
+ * VA (GSP_PHYS_RMCALL_VA_SEM), which the host polls at the SEM chunk's host
+ * VA; the GUEST-VISIBLE release word is authored by the host only on the
+ * completion edge - after the retire wait and after the bounce copy-out has
+ * shipped the engine's stores to the guest pages. Evidence (r453, the FNV
+ * change-detection fix on top):
+ * - with 0: the guest's release could be satisfied by the engine's own
+ * early write while the host copy-out was still pending, so a client's
+ * stream-ordered readback (streams2's c2, memcpy_roundtrip's memset
+ * phase, streams_events' chain) read PRE-launch content: 11/15 battery.
+ * - with 1: 13/15 battery (streams_events and memcpy_roundtrip flipped
+ * green), no stalls across the full battery, mail+saxpy clean.
+ * The r414 stall hole (a launch that never reached the hold edge left the
+ * guest word unauthored) is closed by the r420b per-outcome authoring path;
+ * one-line rollback: echo 0 > .../parameters/gsp_phys_rmcall_gr_rel_mod.
+ */
+static int gsp_phys_rmcall_gr_rel_mod_param = 1;
module_param_named(gsp_phys_rmcall_gr_rel_mod,
gsp_phys_rmcall_gr_rel_mod_param, int, 0644);
MODULE_PARM_DESC(gsp_phys_rmcall_gr_rel_mod,
- "Force the forwarded GR QMD release0 to the module semaphore (default 0; 1 = m64 engine-completion discriminant)");
+ "Force the forwarded GR QMD release0 to the module semaphore; guest release authored only on the post-copy-out completion edge (default 1 since r453; 0 = historical engine-authored guest release)");
int gsp_phys_rmcall_gr_rel_mod_value(void)
{
@@ -974,6 +1033,261 @@ int gsp_phys_rmcall_gr_nowrite_retry_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_nowrite_retry_value);
/*
+ * F2 fast re-arm (gsp_phys_gr_retry_fast; default 1). r198: the first
+ * forwarded-launch timeout costs 2x5 s before the fresh-entry ladder runs,
+ * yet the m155 extended re-arm (lane-enable replay + GPFIFO/TSG schedule +
+ * doorbell) plus one fresh ring entry recovers most of these launches. With
+ * this on, the first timeout immediately runs that re-arm + a fresh entry
+ * and re-polls with a short window; only if that still fails does the
+ * historical timeout ladder continue. Set to 0 for today's exact behavior.
+ */
+static int gsp_phys_gr_retry_fast_param = 1;
+module_param_named(gsp_phys_gr_retry_fast,
+ gsp_phys_gr_retry_fast_param, int, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_retry_fast,
+ "On the first GR launch timeout, run the extended re-arm + a fresh ring entry immediately and re-poll briefly (default 1; 0 = legacy ladder timing)");
+
+int gsp_phys_gr_retry_fast_value(void)
+{
+ return gsp_phys_gr_retry_fast_param;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_retry_fast_value);
+
+/*
+ * F2 first-poll window (gsp_phys_gr_first_poll_ms; default 2500, clamped to
+ * [1000, 30000]). With gsp_phys_gr_retry_fast=1 the FIRST poll window of a
+ * forwarded GR launch (before any re-arm) uses this instead of the fixed 5 s,
+ * so the fast re-arm path above starts earlier; with retry_fast=0 the launch
+ * keeps today's exact behavior and this knob is not read. The later ladder
+ * windows keep their own constants.
+ */
+static unsigned int gsp_phys_gr_first_poll_ms_param = 2500u;
+module_param_named(gsp_phys_gr_first_poll_ms,
+ gsp_phys_gr_first_poll_ms_param, uint, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_first_poll_ms,
+ "First GR launch poll window in ms, clamped [1000,30000], read when gsp_phys_gr_retry_fast=1 (default 2500)");
+
+unsigned int gsp_phys_gr_first_poll_ms_value(void)
+{
+ unsigned int v = gsp_phys_gr_first_poll_ms_param;
+
+ if (v < 1000u)
+ v = 1000u;
+ if (v > 30000u)
+ v = 30000u;
+ return v;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_first_poll_ms_value);
+
+/*
+ * M91 icache coherence (gsp_phys_gr_icache_inv; default 1). The m91 class is
+ * the transient SM instruction-fetch lottery: the first attempt of a
+ * mid-sequence launch faults on SM garbage ("Illegal Instruction Encoding" /
+ * "Out Of Range Register") while a retry of the identical QMD and identical
+ * shadow content succeeds. The window-0 shadow pool reuses code VAs across
+ * launches (two 8-page banks alternate by submit epoch), so the bytes at a
+ * program VA change between launches, and every captured guest QMD carries
+ * the QMDV02_03 cache-control block with INVALIDATE_INSTRUCTION_CACHE (word 5
+ * bit 30) CLEAR - the field the CUDA driver arms only under its
+ * forceIcacheInvalidate knob. An SM fetch line cached for launch N can then
+ * execute the previous kernel's bytes at the same VA in launch N+2. With this
+ * on, the forwarded QMD sets that bit (only when the QMD carries the driver's
+ * standard cache-control signature, bits 26..29 of word 5, so an unknown QMD
+ * revision is never touched), forcing a refetch at dispatch. 0 restores the
+ * exact pre-change QMD for a one-flip A/B.
+ */
+static int gsp_phys_gr_icache_inv_param = 1;
+module_param_named(gsp_phys_gr_icache_inv, gsp_phys_gr_icache_inv_param,
+ int, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_icache_inv,
+ "Set QMDV02_03 INVALIDATE_INSTRUCTION_CACHE (word5 bit30) on forwarded GR launches whose QMD has the driver cache-control signature (default 1; 0 = pre-m91 QMD words)");
+
+int gsp_phys_gr_icache_inv_value(void)
+{
+ return gsp_phys_gr_icache_inv_param;
+}
+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
+ * attempt counter, so a launch that had already spent attempts on the ladder
+ * (fast re-arm + fresh ring entries) reached the accept path with NO
+ * re-execution at all - the r200/r201/r202 memcpy_roundtrip / multikernel
+ * class (a lost launch silently accepted, client output all wrong). This
+ * budget is dedicated to the no-write loop and independent of the ladder; it
+ * is read once per submission. 0 keeps the accept-without-retry behavior
+ * (still logged loudly). The clamp keeps the worst-case host publishes per
+ * launch (initial + fast re-arm + 2 fresh entries + budget) within the
+ * 8-entry GPFIFO ring.
+ */
+static int gsp_phys_gr_nowrite_retries_param = 3;
+module_param_named(gsp_phys_gr_nowrite_retries,
+ gsp_phys_gr_nowrite_retries_param, int, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_nowrite_retries,
+ "Dedicated re-execution budget for a data-bearing GR launch that wrote nothing tracked, clamped [0,4] (default 3)");
+
+/*
+ * STALLFREE: absolute per-launch wall-clock budget (gsp_phys_gr_wait_max_ms;
+ * default 60000, clamped [10000, 600000]; 0 disables the extra cap and the
+ * built-in ladder bounds stand alone). The GR submit ladder and its retirement
+ * (no-write) retries are individually bounded, but their SUM grows with every
+ * re-arm/re-publish: a launch whose engine ack never arrives could hold the
+ * single-inflight work item for tens of seconds while the guest client waits
+ * on its QMD completion word. When this budget is crossed the submit stops its
+ * ladder immediately (no further re-arm/publish/retry), logs
+ * 'vgsp-gr: GR-STALL-wait-max ... action=fail' and returns -ETIMEDOUT; the
+ * sema service's failure edge then authors the QMD completion release in
+ * software, so the client always completes. A green launch (~0.2-11 s end to
+ * end, including the fast-re-arm recovery) never approaches the default, so
+ * the default does not change green behavior.
+ */
+static unsigned int gsp_phys_gr_wait_max_ms_param = 60000u;
+module_param_named(gsp_phys_gr_wait_max_ms,
+ gsp_phys_gr_wait_max_ms_param, uint, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_wait_max_ms,
+ "Absolute per-launch GR submit budget in ms, clamped [10000,600000]; 0 = only the built-in per-ladder bounds (default 60000)");
+
+unsigned int gsp_phys_gr_wait_max_ms_value(void)
+{
+ unsigned int v = gsp_phys_gr_wait_max_ms_param;
+
+ if (!v)
+ return 0;
+ if (v < 10000u)
+ v = 10000u;
+ if (v > 600000u)
+ v = 600000u;
+ return v;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_wait_max_ms_value);
+
+int gsp_phys_gr_nowrite_retries_value(void)
+{
+ int v = gsp_phys_gr_nowrite_retries_param;
+
+ if (v < 0)
+ v = 0;
+ if (v > 4)
+ v = 4;
+ return v;
+}
+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
+ * quiet for tens of seconds (r409: a ~37 s client gap healed a wedged run;
+ * r416: 58 consecutive no-write launches during a 126 s client run, first
+ * launches green again after the lane sat ~52 s idle; r417: a 20 s host hold
+ * did not heal, a 40 s hold did - "WEDGE-QUIET healed: engine wrote 1
+ * page(s) after the 40000 ms hold"). Immediate retries and
+ * re-arms do not heal it (r409/r154). So when a zero-write outcome follows a
+ * previous accepted zero-write launch, the retry loop first holds this many ms
+ * with NO submissions to the engine, then re-arms (lane enable + schedules)
+ * and re-publishes once. The value doubles per further quiet retry. The
+ * quiet window is recovery-only: a launch that wrote anything never enters it.
+ */
+static unsigned int gsp_phys_gr_wedge_quiet_ms_param = 40000u;
+module_param_named(gsp_phys_gr_wedge_quiet_ms,
+ gsp_phys_gr_wedge_quiet_ms_param, uint, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_wedge_quiet_ms,
+ "Quiet-window ms before retrying a launch on a lane whose previous launch also no-wrote (0=disable, default 40000; r417: a 20 s hold did not heal, 40 s healed)");
+
+unsigned int gsp_phys_gr_wedge_quiet_ms_value(void)
+{
+ unsigned int v = gsp_phys_gr_wedge_quiet_ms_param;
+
+ if (v > 120000u)
+ v = 120000u;
+ return v;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_wedge_quiet_ms_value);
+
+/*
* m69 window-0 shadow pool (gsp_phys_rmcall_gr_shadow_win0; default 0). When
* set, the forwarded GR launch copies the QMD's program/param pages into the
* window-0 shadow pool (module pages at VAs 0x108000..0x10f000, PTEs planted
@@ -1640,6 +1954,41 @@ bool gsp_phys_guest_ram_active(void)
EXPORT_SYMBOL_GPL(gsp_phys_guest_ram_active);
/*
+ * Highest GPA any registered guest-RAM range covers. Conservative global
+ * bound (max across the legacy and every per-mdev record) for consumers that
+ * only need to reject translations pointing past RAM - e.g. the sema push
+ * read's PA ceiling (r1127: a stale VAS walk answered 0x23921d000 = 8.89 GB
+ * for the beef setup push; with the 8/10 GB guest layout the answer is
+ * inside the registered ranges and reads padding zeros, so no bound alone
+ * fixes it - the repeated-zero invalidation does - but any answer ABOVE
+ * this top can never read content). 0 when nothing is registered.
+ */
+u64 gsp_phys_guest_ram_top(void)
+{
+ u64 top = 0;
+ unsigned int i, j;
+
+ mutex_lock(&guest_ram_lock);
+ for (i = 0; i < GSP_PHYS_GUEST_RAM_MDEVS + 1; i++) {
+ const struct gsp_phys_guest_ram_rec *rec =
+ (i == 0) ? &guest_ram_legacy : &guest_ram_mdevs[i - 1];
+
+ if (!rec->file)
+ continue;
+ for (j = 0; j < rec->ranges_n; j++) {
+ u64 end = rec->ranges[j].gpa_base +
+ rec->ranges[j].size;
+
+ if (end > top)
+ top = end;
+ }
+ }
+ mutex_unlock(&guest_ram_lock);
+ return top;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_guest_ram_top);
+
+/*
* Lock-safe one-line snapshot for the gsp-phys-state dump: active flag,
* path, range count and every range (gpa/offset/size). With only the legacy
* record configured (the one-mdev campaign) the output is byte-identical to