summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c183
1 files changed, 177 insertions, 6 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 1fa09d0..9d3697f 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -506,6 +506,20 @@ struct svc_channel {
*/
u8 userd_scans;
/*
+ * r828: the RC watchdog's notifier VA, captured from the class2d
+ * SET_NOTIFY_A/B methods on subchannel 3 and zeroed at the NOTIFY
+ * (see svc_notify2d_write). Survives the pass snapshot via the
+ * write-back list.
+ */
+ u64 notify2d_va;
+ /* r829: bounded count of zero-entry (bad-translation) holds; see the
+ * zero-entry hold in svc_pump_channels. Persists via the write-back. */
+ u32 zero_held;
+ /* r831: the registered userd value is a real guest PA (the driver's
+ * own declaration; e.g. the RC watchdog channel). The ring auto-pin's
+ * ring+0x2000 convention must not overwrite it. */
+ bool userd_reg;
+ /*
* r471: the completion-counter publish must never advance the
* guest's word past a push whose content was still unwritten when
* the entry was consumed (the streams_events staging-reuse
@@ -1562,6 +1576,16 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc)
ch->gpfifo_entries = desc->gpfifo_entries;
ch->userd_pa = desc->userd_pa;
ch->userd_as = desc->userd_as ? desc->userd_as : SVC_APERTURE_SYS;
+ /*
+ * r831: a registered userd value that looks like a real guest
+ * PA (the RC watchdog's channel carries its own userd block)
+ * is the driver's own declaration and outranks the ring+0x2000
+ * convention guess the ring auto-pin applies (that guess is
+ * right for the CUDA GR layout, where the registered value is
+ * pool-relative - observed 0x10000). Tiny registered values
+ * stay guesses.
+ */
+ ch->userd_reg = desc->userd_pa >= 0x10000000ULL;
ch->instance_pa = desc->instance_pa;
ch->notifier_pa = desc->notifier_pa;
ch->notifier_as = desc->notifier_as ? desc->notifier_as
@@ -3227,6 +3251,8 @@ static u32 svc_rdw_log;
* appeared. Each scan reports its decision, a scan that found nothing
* reportable logs a sweep summary. */
static u32 svc_userdp_log;
+/* r829: bounded witness for the zero-entry holds (bad ring translations). */
+static u32 svc_zero_entry_log;
static u8 svc_read_last_src;
/* r715: PA the winning source of the last push read resolved to. */
static u64 svc_read_last_pa;
@@ -10742,6 +10768,54 @@ static void svc_push_raw_dump(const u8 *buf, u32 len, u64 push_va,
h_object, push_va, off, len);
}
+/*
+ * r828: the guest kernel's RC watchdog (krcWatchdog_IMPL, 7 s notify
+ * timeout) posts a notifier cycle on its own channel (object 0x31415900)
+ * through the class2d subchannel (NVA06F_SUBCHANNEL_2D = 3): SET_NOTIFY_A/B
+ * name the notifier VA, NOTIFY (WRITE_ONLY) is the GPU's "write a zeroed
+ * NvNotification" - the driver set the status word to 0xFFFF and declares
+ * "GPU is probably locked" when it never comes back as 0. Observed: robust
+ * channels enabled by the GL client (GZDOOM), then NVRM krcWatchdog every
+ * 8 s and Xorg dead on its screen readback. The emulation must author the
+ * notification write, exactly like svc_notify_complete does for the
+ * work-submit token.
+ */
+#define SVC_2D_SUBCH 3 /* NVA06F_SUBCHANNEL_2D */
+#define SVC_2D_SET_NOTIFY_A 0x0104 /* payload: addr bits [56:32] */
+#define SVC_2D_SET_NOTIFY_B 0x0108 /* payload: addr bits [31:0] */
+#define SVC_2D_NOTIFY 0x010c /* payload: 0 = WRITE_ONLY */
+
+static u64 svc_notify2d_done;
+static u32 svc_notify2d_log;
+
+static void svc_notify2d_write(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch)
+{
+ /* 16 bytes = the NvNotification (status word at +14). */
+ u8 zero[16] = { 0 };
+ u64 pa = 0;
+ int ap = SVC_APERTURE_SYS;
+
+ if (!ch->notify2d_va)
+ return;
+ if (svc_lookup(vgpu, ch->notify2d_va, &pa, &ap)) {
+ if (gsp_sema_dbg && svc_notify2d_log < 16) {
+ svc_notify2d_log++;
+ pr_info("sema-notify2d: h=0x%x va=0x%llx unresolved (RC watchdog notifier; guest re-posts every second)\n",
+ ch->h_object, ch->notify2d_va);
+ }
+ return;
+ }
+ if (svc_write_ap(vgpu, pa, svc_ap_of(ap), zero, sizeof(zero)))
+ return;
+ svc_notify2d_done++;
+ if (gsp_sema_dbg && svc_notify2d_log < 16) {
+ svc_notify2d_log++;
+ pr_info("sema-notify2d: h=0x%x va=0x%llx -> pa=0x%llx zeroed (RC watchdog acked)\n",
+ ch->h_object, ch->notify2d_va, pa);
+ }
+}
+
static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
u32 len, u64 page_pa, bool allow_ops,
struct svc_channel *ch, u32 entry_idx,
@@ -11292,6 +11366,33 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
continue;
}
+ if (subch == SVC_2D_SUBCH && count >= 1 && addr >= 0x0100 &&
+ addr <= 0x010c) {
+ /*
+ * r828: the RC watchdog's notifier cycle (class2d
+ * methods on subchannel 3). See svc_notify2d_write:
+ * without the zeroed notification the guest's
+ * krcWatchdog declares the GPU locked after 7 s and
+ * the X/GL stack wedges on screen readback.
+ */
+ switch (addr) {
+ case SVC_2D_SET_NOTIFY_A:
+ ch->notify2d_va = (u64)(v0 & 0x1ffffff) << 32;
+ break;
+ case SVC_2D_SET_NOTIFY_B:
+ ch->notify2d_va |= (u64)v0;
+ break;
+ case SVC_2D_NOTIFY:
+ if (count == 1 && v0 == 0) /* WRITE_ONLY */
+ svc_notify2d_write(vgpu, ch);
+ break;
+ default:
+ break;
+ }
+ off += 4 + count * 4;
+ continue;
+ }
+
if (subch == 0 && addr == 0) {
/* SET_OBJECT on the channel subchannel: a push starts */
n_ops = 0;
@@ -16997,9 +17098,15 @@ static void svc_gr_ring_autopin(struct nv_vgpu_device *vgpu,
&get);
svc_pin_store(ch->gpfifo_va, pin_pa, pin_ap);
svc_lat_store(ch->gpfifo_va, pin_pa, pin_ap);
- ch->userd_pa = pin_pa + SVC_GR_USERD_OFF;
- ch->userd_as = svc_ap_of(pin_ap);
- ch->userd_ok = true;
+ /* r831: keep a registered real-PA userd. */
+ if (ch->userd_reg) {
+ ch->userd_ok = true;
+ } else {
+ ch->userd_pa =
+ pin_pa + SVC_GR_USERD_OFF;
+ ch->userd_as = svc_ap_of(pin_ap);
+ ch->userd_ok = true;
+ }
svc_gr_ring_base = base;
if (svc_ap_of(pin_ap) == SVC_APERTURE_FB)
svc_fb_claim_add(ch->h_object, pin_pa,
@@ -17036,9 +17143,19 @@ static void svc_gr_ring_autopin(struct nv_vgpu_device *vgpu,
&get)) {
svc_pin_store(ch->gpfifo_va, wpa, wap);
svc_lat_store(ch->gpfifo_va, wpa, wap);
- ch->userd_pa = wpa + SVC_GR_USERD_OFF;
- ch->userd_as = wap;
- ch->userd_ok = true;
+ /*
+ * r831: the ring+0x2000 userd convention is a guess
+ * for the CUDA GR layout; a channel whose REGISTERED
+ * userd is a real PA (the RC watchdog) keeps its own
+ * (the candidate gate above probes it as fallback).
+ */
+ if (ch->userd_reg) {
+ ch->userd_ok = true;
+ } else {
+ ch->userd_pa = wpa + SVC_GR_USERD_OFF;
+ ch->userd_as = wap;
+ ch->userd_ok = true;
+ }
svc_gr_ring_base = wpa;
if (wap == SVC_APERTURE_FB)
svc_fb_claim_add(ch->h_object, wpa,
@@ -26782,6 +26899,23 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
}
/*
+ * r832: the RC watchdog's notifier ack must not depend on the
+ * ring reads. Its VA is known from the channel's first decode
+ * (ch->notify2d_va, captured from SET_NOTIFY_A/B) and the ack
+ * is the same zeroed NvNotification the decode path writes:
+ * the guest re-arms status=0xFFFF every cycle and checks it
+ * back to 0 within 7 s. Under heavy GL traffic the watchdog
+ * ring's translation can go stale for stretches (its entries
+ * then read as zeros and are consumed as empty), which starved
+ * exactly this write and wedged the guest with "GPU is
+ * probably locked" while every other channel kept running.
+ * Writing the ack on every pass that sees work pending is
+ * idempotent.
+ */
+ if (!ch->engine_type && ch->notify2d_va && put != get)
+ svc_notify2d_write(vgpu, ch);
+
+ /*
* ISSUE-0002 diag: put snapshots for CE channels, bounded per
* session. The throttle is indexed by rank (the channel struct
* iterated here is a local copy, so a per-channel stamp never
@@ -27224,6 +27358,40 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
}
/*
+ * r829: a zero-length / zero-VA entry is a bad read
+ * (a stale or wrong translation), never work. The old
+ * path consumed it as an empty push and advanced
+ * GPGet; observed r830 on the RC watchdog channel:
+ * two bogus zero entries consumed through a
+ * mis-resolved ring page pushed the userd's GPGet past
+ * the driver's own GPPut, every later userd probe then
+ * read get>put and the channel's REAL userd was
+ * rejected - the channel stopped being serviced and
+ * the guest declared the GPU locked. Hold the entry
+ * instead (bounded; the per-channel counter persists
+ * through the pass snapshot); a corrected translation
+ * (the ring auto-pin lands within seconds) re-reads
+ * it. Past the cap the old consume-as-empty behavior
+ * resumes so an unresolvable ring cannot livelock the
+ * channel.
+ */
+ if (va == 0 || len == 0) {
+ if (ch->zero_held < 512 && !svc_hold_bypass()) {
+ ch->zero_held++;
+ svc.gpfifo_held++;
+ if (gsp_sema_dbg &&
+ svc_zero_entry_log < 16) {
+ svc_zero_entry_log++;
+ pr_info("sema-chan: zero-entry hold h=0x%x idx=%llu raw=%08x%08x (bad ring translation; not consumed)\n",
+ ch->h_object, idx,
+ e1, e0);
+ }
+ stalled = true;
+ continue;
+ }
+ }
+
+ /*
* Cheap wait for an entry already inside its
* expensive-wait window: read + rescue + hold were
* already paid for this VA, and a pin-batch rewind
@@ -29270,6 +29438,9 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
svc.chan[k].gr_ram_logged = ch->gr_ram_logged;
svc.chan[k].gr_ram_ring = ch->gr_ram_ring;
svc.chan[k].userd_scans = ch->userd_scans;
+ svc.chan[k].notify2d_va = ch->notify2d_va;
+ svc.chan[k].zero_held = ch->zero_held;
+ svc.chan[k].userd_reg = ch->userd_reg;
svc.chan[k].logged = ch->logged;
svc.chan[k].logged_expected = ch->logged_expected;
svc.chan[k].notified = ch->notified;