summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_sema_service.c205
1 files changed, 200 insertions, 5 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index c9abc71..797c4b5 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -2076,6 +2076,8 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
{
struct svc_channel *ch = NULL;
unsigned long flags;
+ u64 prev_notify2d_va = 0;
+ u32 prev_notify2d_gen = 0;
int i;
if (!desc || !desc->h_object || !desc->gpfifo_va ||
@@ -2127,6 +2129,28 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
}
}
spin_lock_irqsave(&svc.chan_lock, flags);
+ /*
+ * r1652: the GL client's kick channel (0xbfef0002) is re-registered
+ * while the client keeps the class2d notification target it armed on
+ * the previous instance: its short kick push carries NOTIFY 0x10c
+ * without SET_NOTIFY_A/B. The old slot still holds the target after
+ * gsp_sema_service_channel_del() (valid=false only), but this add's
+ * memset() would erase it, leaving the short NOTIFY with no address:
+ * svc_notify2d_write() then drops the completion, no channel event is
+ * posted, and the client spins in its kick/fence wait. Carry the
+ * target across a same-session, same-handle re-registration only; a
+ * different session (or a fresh SET_NOTIFY_A/B) still wins.
+ */
+ for (i = 0; i < SVC_MAX_CHANNELS; i++) {
+ if (svc.chan[i].vgpu != desc->vgpu ||
+ svc.chan[i].h_object != desc->h_object)
+ continue;
+ if (svc.chan[i].notify2d_va) {
+ prev_notify2d_va = svc.chan[i].notify2d_va;
+ prev_notify2d_gen = svc.chan[i].session_gen;
+ break;
+ }
+ }
for (i = 0; i < SVC_MAX_CHANNELS; i++) {
if (svc.chan[i].valid && svc.chan[i].vgpu == desc->vgpu &&
svc.chan[i].h_object == desc->h_object) {
@@ -2201,6 +2225,19 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
ch->counter_gen = keep_counter_gen;
}
/*
+ * r1652: restore the class2d notification target saved before the
+ * memset. A short kick (NOTIFY only) then still has the address the
+ * guest programmed on the previous incarnation of this handle, and
+ * svc_notify2d_write() can author the completion again. The session
+ * generation check keeps a dead session's VA out of a new client;
+ * any SET_NOTIFY_A/B in the new incarnation overwrites it.
+ */
+ if (prev_notify2d_va && prev_notify2d_gen == svc_sess_gen) {
+ ch->notify2d_va = prev_notify2d_va;
+ pr_info_ratelimited("sema-svc: channel 0x%x kept class2d notify va=0x%llx across re-registration\n",
+ ch->h_object, ch->notify2d_va);
+ }
+ /*
* Rank among the live CE channels OF THIS GUEST. The RM hands
* out object handles in creation order and the guest's UVM
* allocates one push buffer, ring and tracking semaphore per
@@ -10144,6 +10181,7 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
bool blob_trusted = false;
const char *blob_src = "none";
const char *op_src = "stream";
+ u64 kick_va;
/*
* SESSION 153 CONT.6 (task 1): record every section arrival (any
@@ -10345,11 +10383,23 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
* GPU's notification write is the only work
* it asks for.
*/
+ /*
+ * r1652: the short kick form carries only the class2d
+ * NOTIFY (0x10c), relying on the notification address
+ * already programmed in the channel context. Use the
+ * per-channel target when this section did not re-arm
+ * it; the carried value comes from the previous
+ * incarnation of the same handle (see
+ * gsp_sema_service_channel_add). Without the carried
+ * target the executor never runs and the GL client's
+ * kick wait spins.
+ */
+ kick_va = svc_gl.kick_notify_va;
+ if (!kick_va && svc_kick_family(ch))
+ kick_va = ch->notify2d_va;
if (gsp_sema_kick_notify &&
- svc_gl.kick_notify_seen &&
- svc_gl.kick_notify_va &&
- svc_kick_notify_exec(vgpu, ch,
- svc_gl.kick_notify_va,
+ svc_gl.kick_notify_seen && kick_va &&
+ svc_kick_notify_exec(vgpu, ch, kick_va,
svc_gl.kick_notify_val,
push_va)) {
/* authored once; a replay pass must not
@@ -18508,8 +18558,20 @@ static bool svc_notify2d_write(struct nv_vgpu_device *vgpu,
u64 pa = 0;
int ap = SVC_APERTURE_SYS;
- if (!ch->notify2d_va)
+ if (!ch->notify2d_va) {
+ /*
+ * r1652: the short kick form (NOTIFY without SET_NOTIFY)
+ * depends on the target carried from the channel's previous
+ * registration. Name the miss so a future loss is visible
+ * instead of a silent spin.
+ */
+ if (gsp_sema_dbg && svc_notify2d_log < 64) {
+ svc_notify2d_log++;
+ pr_info("sema-notify2d: h=0x%x NOTIFY with no class2d target (short kick before SET_NOTIFY; client spin)\n",
+ ch->h_object);
+ }
return false;
+ }
if (svc_lookup(vgpu, ch->notify2d_va, &pa, &ap)) {
if (gsp_sema_dbg && (svc_notify2d_log < 64 ||
ch->h_object == 0xbfef0002)) {
@@ -24521,6 +24583,110 @@ static bool svc_fb_userd_pristine_scan(struct nv_vgpu_device *vgpu,
*/
static bool svc_gr_entry_win_ok(u64 ring_va, u64 eva);
+/*
+ * r1653: capture the class2d kick channel's notification target from the
+ * FIRST GPFIFO entry. The GL client arms the class2d subchannel with
+ * SET_NOTIFY_A/B + NOTIFY in entry 0 and then submits short kick pushes
+ * (NOTIFY only) that rely on the target staying programmed in the channel
+ * context. The pump's walk seed consumes only the newest entries ending at
+ * the live PUT, so on a fresh channel entry 0 can be skipped forever and
+ * every short kick has no address to author - the client then spins in its
+ * kick wait (r1652: h=0xbfef0002, "NOTIFY with no class2d target"). Decode
+ * the skipped entries here, once, before the seed.
+ */
+static bool svc_kick_scan_push(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch, u64 va, u32 len)
+{
+ u8 buf[512];
+ u64 pa = 0;
+ int ap = SVC_APERTURE_SYS;
+ u32 n = min_t(u32, len, sizeof(buf));
+ u32 off = 0;
+
+ if (svc_lookup(vgpu, va, &pa, &ap) &&
+ svc_resolve_va(vgpu, va, &pa, &ap))
+ return false;
+ if (svc_read_ap(vgpu, pa, svc_ap_of(ap), buf, n))
+ return false;
+ while (off + 4 <= n) {
+ u32 hdr, op, count, subch, addr, k;
+
+ memcpy(&hdr, buf + off, 4);
+ op = (hdr >> 29) & 7;
+ count = (hdr >> 16) & 0xfff;
+ subch = (hdr >> 13) & 7;
+ addr = (hdr & 0x1fff) << 2;
+ if (!hdr) {
+ off += 4;
+ continue;
+ }
+ if (op == 1 || op == 3 || op == 5) {
+ if (!count || off + 4 + count * 4 > n)
+ break;
+ if (subch == 3) {
+ for (k = 0; k < count; k++) {
+ u32 m = op == 3 ? addr : addr + 4 * k;
+ u32 v;
+
+ memcpy(&v, buf + off + 4 + 4 * k, 4);
+ if (m == 0x104)
+ ch->notify2d_va =
+ (u64)(v & 0x1ffffff) << 32;
+ else if (m == 0x108) {
+ ch->notify2d_va |= v;
+ if (ch->notify2d_va)
+ return true;
+ }
+ }
+ }
+ off += 4 + count * 4;
+ continue;
+ }
+ off += 4;
+ }
+ return ch->notify2d_va != 0;
+}
+
+static void svc_kick_capture_arm(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch, u64 ring_pa,
+ int ring_ap, u32 uput)
+{
+ u32 e, nscan;
+
+ if (!ch->engine_type || ch->h_object < 0xbfef0000u ||
+ ch->h_object > 0xbfef00ffu || ch->notify2d_va || !ring_pa)
+ return;
+ /*
+ * r1653: the arm is normally entry 0, so a missing/zero live PUT
+ * must not block the decode. Bound the scan by the ring size and
+ * the live PUT when known.
+ */
+ nscan = ch->gpfifo_entries ? ch->gpfifo_entries : 2;
+ if (uput && uput < nscan)
+ nscan = uput;
+ if (nscan > 8)
+ nscan = 8;
+ for (e = 0; e < nscan && !ch->notify2d_va; e++) {
+ u8 ent[8];
+ u32 e0, e1;
+ u64 pva;
+ u32 plen;
+
+ if (svc_read_ap(vgpu, ring_pa + (u64)e * 8, ring_ap, ent,
+ sizeof(ent)))
+ continue;
+ memcpy(&e0, ent, 4);
+ memcpy(&e1, ent + 4, 4);
+ pva = ((u64)(e1 & 0xff) << 32) | (u64)(e0 & 0xfffffffc);
+ plen = ((e1 >> 10) & 0x1fffff) << 2;
+ if (pva && plen >= 4 && plen <= 0x10000)
+ svc_kick_scan_push(vgpu, ch, pva, plen);
+ }
+ if (ch->notify2d_va)
+ pr_info("sema-chan: kick arm captured h=0x%x notify2d=0x%llx from ring entry (first-discovery decode)\n",
+ ch->h_object, ch->notify2d_va);
+}
+
static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
u64 ring_pa, int ring_ap, u32 *put, u32 *get, int rrc)
{
@@ -24827,6 +24993,10 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
ch->userd_pa = upa;
ch->userd_as = SVC_APERTURE_SYS;
ch->userd_ok = true;
+ /* r1653: decode the skipped first entries for the
+ * class2d kick arm before the walk seed below. */
+ svc_kick_capture_arm(vgpu, ch, ring_pa, ring_ap,
+ uput);
/*
* SESSION 154 (M-GL1.2): seed the walk at the live
* block's PUT. The client's put is free-running
@@ -40451,6 +40621,31 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
}
/*
+ * r1653: the class2d kick channel's notification target is
+ * armed in the first GPFIFO entry, which the walk seed can
+ * skip. When the live PUT is known and the target is still
+ * missing, decode the head entries once here (the pump has
+ * the channel's GPFIFO VA and PUT). Without it every short
+ * kick push has no address and the GL client spins.
+ */
+ if (!ch->engine_type && !ch->notify2d_va && put &&
+ ch->h_object >= 0xbfef0000u &&
+ ch->h_object <= 0xbfef00ffu) {
+ if (ring_pa) {
+ svc_kick_capture_arm(vgpu, ch, ring_pa,
+ ring_ap, put);
+ } else {
+ u64 rpa = 0;
+ int rap = SVC_APERTURE_SYS;
+
+ if (!svc_lookup(vgpu, ch->gpfifo_va, &rpa,
+ &rap))
+ svc_kick_capture_arm(vgpu, ch, rpa,
+ rap, put);
+ }
+ }
+
+ /*
* r1650: keep the UVM rank-slot release moving for a short
* window. Two effects need it: the pool page can move / a
* per-client VAS directory can be discovered seconds later