summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_sema_service.c65
-rw-r--r--kernel/gsp_sema_service.h8
-rw-r--r--kernel/main.c59
3 files changed, 132 insertions, 0 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 3e5b9dd..f9e8e06 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -26925,3 +26925,68 @@ int gsp_sema_service_sem_write(struct nv_vgpu_device *vgpu, u32 h, u64 va,
svc.post_hint_until = jiffies + msecs_to_jiffies(SVC_POST_HINT_MS);
return rc;
}
+
+/*
+ * r616 diag: manual fault poke for the refused-launch hang experiment. A GR
+ * push that the fill cannot resolve is refused (degenerate data set) and the
+ * entry is consumed with a software release; the authored release lands in
+ * the guest page (measured: kcore read 0x6 == pay on r613d) yet the guest
+ * driver never completes cudaDeviceSynchronize for a launch that never ran.
+ * This writes the channel's error notifier in the exact shape the relayed-
+ * fault edge uses (info32=exceptType, info16=engine, status=0xffff, latched
+ * by notifier_err) and posts the channel event, so a hung client's kernel
+ * can be poked into observing an engine error and ending its wait. Debug
+ * only; harmless when no client is hung (the pump's completion clear resumes
+ * after SVC_NOTIFIER_ERR_HOLD). h_object 0 = every valid channel.
+ */
+int gsp_sema_channel_fault_poke(struct nv_vgpu_device *vgpu, u32 h_object)
+{
+ u8 notif[SVC_NOTIFY_SIZE] = {0};
+ u16 status = SVC_NOTIFY_ERROR_STATUS;
+ u32 info32 = 0x1; /* generic engine exception */
+ int i, done = 0;
+
+ if (!vgpu)
+ return -ENODEV;
+ memcpy(notif + SVC_NOTIFY_INFO32_OFF, &info32, 4);
+ memcpy(notif + SVC_NOTIFY_STATUS_OFF, &status, 2);
+
+ for (i = 0; i < SVC_MAX_CHANNELS; i++) {
+ struct svc_channel *ch = &svc.chan[i];
+ unsigned long flags;
+ u64 pa;
+ u32 as, eng, h;
+ u16 info16;
+
+ spin_lock_irqsave(&svc.chan_lock, flags);
+ if (!ch->valid || ch->vgpu != vgpu || !ch->notifier_pa ||
+ (h_object && ch->h_object != h_object)) {
+ spin_unlock_irqrestore(&svc.chan_lock, flags);
+ continue;
+ }
+ h = ch->h_object;
+ pa = ch->notifier_pa;
+ as = ch->notifier_as;
+ eng = ch->engine_type;
+ /* Latch exactly like the relayed-fault edge so the pump's
+ * completion clear cannot wipe the poke before the guest
+ * reads it. */
+ ch->notifier_err = true;
+ ch->notifier_err_until = jiffies + SVC_NOTIFIER_ERR_HOLD;
+ ch->notifier_err_put = ch->put_prev;
+ spin_unlock_irqrestore(&svc.chan_lock, flags);
+
+ info16 = (u16)eng;
+ memcpy(notif + SVC_NOTIFY_INFO16_OFF, &info16, 2);
+ if (svc_write_ap(vgpu, pa, as, notif, sizeof(notif))) {
+ pr_info("vgsp-fault: POKE ch=0x%x notifier write failed pa=0x%llx as=%u\n",
+ h, pa, as);
+ continue;
+ }
+ pr_info("vgsp-fault: POKE ch=0x%x engine=%u pa=0x%llx as=%u status=0x%x except=0x%x (manual experiment)\n",
+ h, eng, pa, as, status, info32);
+ gsp_rm_post_channel_event(vgpu, h);
+ done++;
+ }
+ return done ? done : -ENOENT;
+}
diff --git a/kernel/gsp_sema_service.h b/kernel/gsp_sema_service.h
index f891de1..fbc7e4d 100644
--- a/kernel/gsp_sema_service.h
+++ b/kernel/gsp_sema_service.h
@@ -44,6 +44,14 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc);
void gsp_sema_service_channel_del(struct nv_vgpu_device *vgpu, u32 h_object);
/*
+ * r616 diag: manually write a channel's error notifier (engine-exception
+ * shape) and post its channel event, to test whether a refused launch's
+ * hung client can be ended by a fault-conversion edge. h_object 0 = every
+ * valid channel of the device. Debug only.
+ */
+int gsp_sema_channel_fault_poke(struct nv_vgpu_device *vgpu, u32 h_object);
+
+/*
* Release every piece of service state that belongs to one mdev before its
* guest session disappears: drop the mdev's channels (per-vgpu session end),
* clear a release hold that names it (never dereference a removed device),
diff --git a/kernel/main.c b/kernel/main.c
index e340b7b..9a5d912 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -3893,5 +3893,64 @@ static const struct kernel_param_ops nv_vgpu_rmpost_ops = {
module_param_cb(rmpost, &nv_vgpu_rmpost_ops, NULL, 0200);
MODULE_PARM_DESC(rmpost, "Post a GSP-RM event to the guest by event index (debug)");
+/*
+ * Debug: poke a channel's error notifier with an engine-exception shape and
+ * post its channel event (the relayed-fault delivery edge, manually):
+ * echo 0x5c000019 > .../parameters/grfaultpoke
+ * echo 0 > .../parameters/grfaultpoke (every valid channel)
+ * Experiment: a refused/degenerate GR launch ends its client's
+ * cudaDeviceSynchronize spin if a synthetic engine error completes the wait
+ * (r616; see gsp_sema_channel_fault_poke).
+ */
+struct nv_vgpu_faultpoke_req {
+ u32 h_object;
+ int rc;
+ struct completion done;
+};
+
+static int nv_vgpu_faultpoke_thread(void *arg)
+{
+ struct nv_vgpu_faultpoke_req *req = arg;
+ struct nv_vgpu_device *vgpu;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ if (atomic_read(&vgpu->vfio_dev_open_count) <= 0)
+ continue;
+ req->rc = gsp_sema_channel_fault_poke(vgpu, req->h_object);
+ pr_info("vgsp: grfaultpoke h=0x%x -> %d\n",
+ req->h_object, req->rc);
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+
+ complete(&req->done);
+ return 0;
+}
+
+static int nv_vgpu_faultpoke_set(const char *val, const struct kernel_param *kp)
+{
+ static struct nv_vgpu_faultpoke_req req;
+ struct task_struct *task;
+
+ memset(&req, 0, sizeof(req));
+ req.rc = -ENODEV;
+ if (kstrtou32(val, 0, &req.h_object))
+ return -EINVAL;
+
+ init_completion(&req.done);
+ task = kthread_run(nv_vgpu_faultpoke_thread, &req, "vgpu-faultpoke");
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ wait_for_completion(&req.done);
+ return 0;
+}
+
+static const struct kernel_param_ops nv_vgpu_faultpoke_ops = {
+ .set = nv_vgpu_faultpoke_set,
+};
+
+module_param_cb(grfaultpoke, &nv_vgpu_faultpoke_ops, NULL, 0200);
+MODULE_PARM_DESC(grfaultpoke, "Write a channel error notifier + post its event (r616 experiment)");
+
module_init(nv_vgpu_init);
module_exit(nv_vgpu_exit);