summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_rm_emulation.c36
-rw-r--r--kernel/gsp_sema_service.c58
2 files changed, 88 insertions, 6 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index f203826..8cd82d1 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -3375,11 +3375,19 @@ static void gsp_rm_handle_free(struct gsp_emu_context *ctx, const u8 *elem,
* client's notifier events (hSrcResource 0) linger across runs and the
* fallback wake-up nudge would hit them. Also retire a client's events
* when its root object is freed (hObjOld == hRoot).
+ *
+ * m176: the hEvent match must also match the owning client. Handle
+ * values are recycled across runs, so a late FREE of an old client's
+ * hEvent must never retire a new client's record with the same numeric
+ * handle - the new client's completion wake-up would be lost and it
+ * would wait forever (late-client stall class). hRoot is the freeing
+ * client's own handle in NVOS00_PARAMETERS.
*/
for (i = 0; i < rm->events_n; i++) {
if (!rm->events[i].valid)
continue;
- if (rm->events[i].h_event == hObjOld ||
+ if ((rm->events[i].h_event == hObjOld &&
+ (!hRoot || rm->events[i].h_client == hRoot)) ||
(hObjOld && hObjOld == hRoot &&
rm->events[i].h_client == hRoot)) {
rm->events[i].valid = false;
@@ -3779,8 +3787,32 @@ void gsp_rm_emu_fini(struct gsp_emu_context *ctx)
void gsp_rm_emu_reset(struct gsp_emu_context *ctx)
{
struct gsp_rm_emu *rm = ctx->rm_emu;
+ u32 i, stale;
- if (!rm || !rm->parsed)
+ if (!rm)
+ return;
+
+ /*
+ * m176: a reset means the emulated GSP is gone and every RM client
+ * with it (probe/unload, engine reset). Event records left valid from
+ * the previous session would be posted to with stale
+ * (hClient, hEvent) pairs - the guest RM asserts in CliGetEventInfo
+ * and drops the record - and a recycled hEvent can absorb a wake-up
+ * meant for the new client. Drop them regardless of parse state.
+ */
+ stale = 0;
+ for (i = 0; i < GSPRM_MAX_EVENTS; i++) {
+ if (rm->events[i].valid) {
+ rm->events[i].valid = false;
+ stale++;
+ }
+ }
+ if (stale)
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: reset dropped %u stale event record(s)",
+ stale);
+
+ if (!rm->parsed)
return;
nv_vgpu_info(ctx->vgpu, "vgsp-rm: reset (rpcs=%llu replied=%llu unknown=%llu)",
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 94c3ead..8f23178 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -162,6 +162,12 @@ struct sema_map_entry {
struct svc_channel {
bool valid;
+ u32 session_gen; /* m176: session generation this entry was
+ * created in; learned PA state (counter_pa/
+ * release_pa) is only inherited on a re-
+ * registration within the same generation, so
+ * a recycled handle from a previous client can
+ * never keep the dead session's write targets */
struct nv_vgpu_device *vgpu; /* owning mdev (guest session); the
* table is keyed by (vgpu, h_object)
* because two guests' stock allocators
@@ -478,6 +484,9 @@ static u32 svc_ring_n;
*/
static u8 svc_sess_flush;
static u8 svc_sess_active; /* a channel of a session has registered (until flush) */
+static u32 svc_sess_gen; /* m176: bumped by every executed session flush;
+ * stamps channels/scrubbers so per-session learned
+ * state is never inherited by a recycled handle */
static struct completion svc_sess_flush_done; /* a pending flush has executed */
static unsigned long svc_ring_done; /* CE ranks whose ring is pinned (session state) */
@@ -561,6 +570,7 @@ struct svc_scrub {
u64 submitted; /* blocks submitted since seeding */
u64 authored; /* last authored finish value */
u32 pa_gen; /* scanner registration generation (logged) */
+ u32 session_gen; /* m176: session generation of the last registration */
u32 log_budget;
};
/*
@@ -1191,6 +1201,14 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
DECLARE_BITMAP(keep_rel_done, SVC_GPFIFO_MAX_BITS);
bool same = ch->valid && ch->vgpu == desc->vgpu &&
ch->h_object == desc->h_object;
+ /* m176: only a same-generation re-registration may inherit the
+ * learned write targets. After a session flush the same numeric
+ * handle belongs to a fresh client whose counter/release slots
+ * live in a brand-new page; inheriting the dead session's PAs
+ * made every release land on the old page (and be skipped
+ * against its stale contents) while the new client polled its
+ * own slot forever. */
+ bool same_gen = same && ch->session_gen == svc_sess_gen;
/*
* A re-registration of the same channel must not lose the
@@ -1199,7 +1217,7 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
* counter layout does not change). Same for the increment
* release marks: losing them would re-apply pushes.
*/
- if (same) {
+ if (same_gen) {
keep_counter_pa = ch->counter_pa;
keep_release_pa = ch->release_pa;
keep_gr_index = ch->gr_index;
@@ -1212,7 +1230,8 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
ch->gr_hold_idx = ~0u; /* m156: no hold armed yet */
ch->gr_rehold_idx = ~0u;
ch->gr_nocap_idx = ~0u; /* m164: no no-capture window armed */
- if (same)
+ ch->session_gen = svc_sess_gen; /* m176 */
+ if (same_gen)
bitmap_copy(ch->rel_done, keep_rel_done,
SVC_GPFIFO_MAX_BITS);
/*
@@ -1302,13 +1321,19 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
if (!s)
goto scrub_done; /* table full: max guests */
- /* Re-seed the counting on a fresh scrubber object. */
+ /* 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
+ * flush must start from the new channel's GPPut, not
+ * carry the dead session's counts). */
if (!s->userd_ok ||
+ s->session_gen != svc_sess_gen ||
s->h_object != desc->h_object ||
s->gpfifo_va != desc->gpfifo_va) {
s->seeded = false;
s->submitted = 0;
}
+ s->session_gen = svc_sess_gen;
s->userd_ok = true;
s->h_object = desc->h_object;
s->userd_pa = desc->userd_pa;
@@ -1334,6 +1359,7 @@ scrub_done:
/* P6b wave-1.5 hold state is defined with the forwarding section below. */
static void svc_fwd_hold_abort(struct nv_vgpu_device *vgpu, u32 h_object);
+static void svc_fwd_hold_clear(void); /* m176: session flush drops a stale hold */
/*
* P6b wave-2 inline software fallback for a withheld (skipped) candidate;
* defined with the forwarding section below. Service-thread only.
@@ -1352,6 +1378,7 @@ static void svc_opdef_drop(struct nv_vgpu_device *vgpu,
struct svc_channel *ch, u32 entry_idx);
static void svc_opdef_reset_all(void);
static void svc_opdef_finish_ch(struct svc_channel *ch, u32 idx);
+static u32 svc_opdef_log; /* m176: forward decl for the session flush */
void gsp_sema_service_channel_del(struct nv_vgpu_device *vgpu, u32 h_object)
{
@@ -7821,7 +7848,30 @@ static void svc_session_flush(void)
for (i = 0; i < SVC_MAX_VASPACES; i++)
svc.vas[i].valid = false;
spin_unlock_irqrestore(&svc.vas_lock, flags);
- pr_info("sema-svc: session flush (all channels closed): discovery state reset for the next client\n");
+ /*
+ * m176: carryover state that previously survived the flush and could
+ * poison the next client:
+ * - user-verified scan failures (tr_failed) held the pool VA page for
+ * 60 s even after the new client's page was reachable by targeted
+ * scan - clear them so the next session starts unblocked;
+ * - a forwarding hold whose channel is gone is resolved against a
+ * recycled handle with stale entry indices - drop it (no channels
+ * are live by definition of this flush);
+ * - the captured GR candidate belongs to the dead session's QMD/push;
+ * offering it to a recycled channel would replay a dead launch.
+ */
+ memset(tr_failed, 0, sizeof(tr_failed));
+ tr_fail_next = 0;
+ svc_fwd_hold_clear();
+ memset(&svc_gr_cand, 0, sizeof(svc_gr_cand));
+ /* m176: re-arm the opdef diagnostic budget (it was never per-session,
+ * so a late client's strand could be silent). */
+ svc_opdef_log = 0;
+ /* m176: new session generation - channels and scrubbers re-stamp on
+ * registration and never inherit per-session learned state. */
+ svc_sess_gen++;
+ pr_info("sema-svc: session flush (all channels closed): discovery state reset for the next client (gen %u)\n",
+ svc_sess_gen);
}
/*