summaryrefslogtreecommitdiffstats
path: root/kernel/main.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-29 08:40:19 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-29 08:40:19 -0400
commit64bcfd6fd72546a2758d664bea39354604babcda (patch)
tree0c529f9a03964da6d77887e19ac3a56d8ec45a18 /kernel/main.c
parentb37cd12a552c7a79a9b965c391fb2f64a8da4912 (diff)
downloadopen-iov-64bcfd6fd72546a2758d664bea39354604babcda.tar.gz
open-iov-64bcfd6fd72546a2758d664bea39354604babcda.zip
sema: fix host stack-overflow hang; robust VA resolution; dispatcher lock fix
- svc_handle_chan_dump()/svc_decode_stream(): static snapshot buffers. The 128-channel local array made an ~18 KB frame, which overflows the 16 KB kernel stack in the function prologue and panicked the host 2-3 s after a VM attached the vGPU (proven from EFI pstore: guard page hit at svc_handle_chan_dump+0x20, sub rsp,0x4850). - svc_pump_channels(): write-back now also persists expected_sem_payload, rescue_last and rescue_fail. The pump works on a snapshot copy, so without this the stale-entry skip and the rescue rate limit could never fire and the payload expectation was reinitialized every pass. - svc_targeted_resolve(): sliced scan (128 MB per call, one VA at a time, failed-VA cache) instead of a full-RAM scan per miss; no longer appends to the sorted map out of order. - svc_rebuild(): merges new scan results into the previous map (fresh translation wins in place, new VAs appended) instead of replacing it wholesale, so live translations survive a scan-yield collapse. - Warning fixes: misleading indentation, unused variable, jiffies typecheck. Frame audit: largest frame now 872 B. - Includes the previous session's uncommitted work: sema_counter / shadow_scan / shadow_dump debug params, exit stage markers, pdev ref fix, and the dynamic dispatcher request_firmware-out-of-spinlock fix.
Diffstat (limited to 'kernel/main.c')
-rw-r--r--kernel/main.c173
1 files changed, 172 insertions, 1 deletions
diff --git a/kernel/main.c b/kernel/main.c
index dcfdeed..0bd4826 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -413,6 +413,8 @@ void nv_vgpu_free_type_info(void)
{
int i;
for (i = 0; i < g_num_vgpu_types; i++) {
+ if (!nv_vgpu_mdev_types[i])
+ continue;
kfree(nv_vgpu_mdev_types[i]->sysfs_name);
kfree(nv_vgpu_mdev_types[i]->pretty_name);
kfree(nv_vgpu_mdev_types[i]);
@@ -501,7 +503,9 @@ void nv_vgpu_mdev_unregister_parent(struct pci_dev *pdev)
pr_info("gsp-resman-vfio: mdev parent unregister for %s\n", pci_name(pdev));
if (g_mdev_parent) {
+ pr_info("gsp-resman-vfio: mdev_unregister_parent: entering\n");
mdev_unregister_parent(g_mdev_parent);
+ pr_info("gsp-resman-vfio: mdev_unregister_parent: returned\n");
kfree(g_mdev_parent);
g_mdev_parent = NULL;
}
@@ -1475,7 +1479,13 @@ static void __exit nv_vgpu_exit(void)
mutex_lock(&nv_vgpu_driver.lock);
list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
if (vgpu->pdev) {
- pdev = vgpu->pdev;
+ /*
+ * Take our own reference: the reference held
+ * by the vgpu is dropped by the mdev remove
+ * path, so using it here and putting it below
+ * would underflow the pci_dev refcount.
+ */
+ pdev = pci_dev_get(vgpu->pdev);
break;
}
}
@@ -1486,7 +1496,9 @@ static void __exit nv_vgpu_exit(void)
pdev = pci_get_device(0x10de, 0x1ed3, NULL);
}
+ pr_info("gsp-resman-vfio: nv_vgpu_exit: stopping sema service\n");
gsp_sema_service_stop();
+ pr_info("gsp-resman-vfio: nv_vgpu_exit: sema service stopped\n");
pr_info("gsp-resman-vfio: nv_vgpu_exit: unregistering mdev driver\n");
/* Unregister mdev driver FIRST, then mdev parent */
@@ -1499,13 +1511,16 @@ static void __exit nv_vgpu_exit(void)
nv_vgpu_mdev_unregister_parent(pdev);
pr_info("gsp-resman-vfio: mdev parent unregistered for %s\n", dev_name(&pdev->dev));
pci_dev_put(pdev);
+ pdev = NULL;
}
pr_info("gsp-resman-vfio: nv_vgpu_exit: destroying class\n");
class_destroy(nv_vgpu_driver.class);
+ pr_info("gsp-resman-vfio: nv_vgpu_exit: class destroyed\n");
pr_info("gsp-resman-vfio: nv_vgpu_exit: freeing type info\n");
nv_vgpu_free_type_info();
+ pr_info("gsp-resman-vfio: nv_vgpu_exit: type info freed\n");
pr_info("NVIDIA vGPU VFIO driver unloaded\n");
}
@@ -2216,6 +2231,129 @@ module_param_cb(scan_seq, &nv_vgpu_scan_seq_ops, NULL, 0200);
MODULE_PARM_DESC(scan_seq, "Scan vidmem for a u32 sequence (hex,comma separated)");
/*
+ * Debug: dump a BAR shadow (0/1/3) as u32 words:
+ * echo <bar>,<offset>[,<count>] > .../parameters/shadow_dump
+ */
+static int nv_vgpu_shadow_dump_set(const char *val, const struct kernel_param *kp)
+{
+ char buf[64];
+ char *c1, *c2;
+ u32 bar = 0, n = 16;
+ u64 off = 0;
+ struct nv_vgpu_device *vgpu;
+
+ strscpy(buf, val, sizeof(buf));
+ c1 = strchr(buf, ',');
+ if (!c1)
+ return -EINVAL;
+ *c1 = '\0';
+ c2 = strchr(c1 + 1, ',');
+ if (c2)
+ *c2 = '\0';
+ if (kstrtou32(buf, 0, &bar) || kstrtoull(c1 + 1, 0, &off))
+ return -EINVAL;
+ if (c2 && kstrtou32(c2 + 1, 0, &n))
+ return -EINVAL;
+ if (bar > 3)
+ return -EINVAL;
+ if (n > 64)
+ n = 64;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ u8 *base = vgpu->bars[bar].vaddr;
+ u64 size = vgpu->bars[bar].size;
+ u32 i;
+
+ if (!base || off + (u64)n * 4 > size) {
+ pr_info("gsp-resman-vfio: shadow dump: bar%u bad (base=%p size=0x%llx)\n",
+ bar, base, size);
+ continue;
+ }
+ for (i = 0; i < n; i++) {
+ u32 v;
+
+ memcpy(&v, base + off + i * 4, 4);
+ pr_info("gsp-resman-vfio: shadow bar%u[0x%llx+%u] = 0x%08x\n",
+ bar, off, i, v);
+ }
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ return 0;
+}
+
+static const struct kernel_param_ops nv_vgpu_shadow_dump_ops = {
+ .set = nv_vgpu_shadow_dump_set,
+};
+module_param_cb(shadow_dump, &nv_vgpu_shadow_dump_ops, NULL, 0200);
+MODULE_PARM_DESC(shadow_dump, "Dump BAR shadow words: bar,offset[,count]");
+
+/*
+ * Debug: scan every BAR shadow (BAR0/BAR1/BAR3) for up to 4 u32 patterns:
+ * echo 0x2001001e[,p2,p3,p4] > .../parameters/shadow_scan
+ */
+static int nv_vgpu_shadow_scan_set(const char *val, const struct kernel_param *kp)
+{
+ char buf[128];
+ char *tok, *cur;
+ u32 pat[4], npat = 0;
+ struct nv_vgpu_device *vgpu;
+
+ strscpy(buf, val, sizeof(buf));
+ cur = buf;
+ while (npat < 4 && (tok = strsep(&cur, ",")) != NULL) {
+ if (kstrtou32(tok, 0, &pat[npat]))
+ break;
+ npat++;
+ }
+ if (!npat)
+ return -EINVAL;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ u32 b;
+
+ for (b = 0; b < 4; b++) {
+ u8 *base = vgpu->bars[b].vaddr;
+ u64 size = vgpu->bars[b].size, off;
+ u32 hits = 0;
+
+ if (!base || !size)
+ continue;
+ for (off = 0; off + (u64)npat * 4 <= size; off += 4) {
+ u32 i, ok = 1;
+
+ for (i = 0; i < npat; i++) {
+ u32 v;
+
+ memcpy(&v, base + off + i * 4, 4);
+ if (v != pat[i]) {
+ ok = 0;
+ break;
+ }
+ }
+ if (ok && hits < 8) {
+ pr_info("gsp-resman-vfio: shadow scan: bar%u hit at 0x%llx\n",
+ b, off);
+ hits++;
+ }
+ }
+ if (hits)
+ pr_info("gsp-resman-vfio: shadow scan: bar%u hits=%u\n",
+ b, hits);
+ }
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ return 0;
+}
+
+static const struct kernel_param_ops nv_vgpu_shadow_scan_ops = {
+ .set = nv_vgpu_shadow_scan_set,
+};
+module_param_cb(shadow_scan, &nv_vgpu_shadow_scan_ops, NULL, 0200);
+MODULE_PARM_DESC(shadow_scan, "Scan BAR shadows (0/1/3) for a u32 sequence");
+
+/*
* Debug: dump the semaphore service's view of a GPU VA (VA->PA map entry and
* the current semaphore slot values):
* echo 0x121000010[,count] > .../parameters/sema_probe
@@ -2336,6 +2474,39 @@ module_param_cb(sema_map_add, &nv_vgpu_sema_map_add_ops, NULL, 0200);
MODULE_PARM_DESC(sema_map_add, "Inject a GPU VA -> guest PA mapping (hex va,hex pa)");
/*
+ * Debug: register a channel's GPU completion counter (guest PA of the u32
+ * tracking counter the GPU would write when a push completes):
+ * echo <h_object>,<pa> > .../parameters/sema_counter
+ */
+static int nv_vgpu_sema_counter_set(const char *val, const struct kernel_param *kp)
+{
+ char buf[64];
+ char *comma;
+ u64 h_object = 0;
+ u64 pa = 0;
+
+ strscpy(buf, val, sizeof(buf));
+ comma = strchr(buf, ',');
+ if (!comma)
+ return -EINVAL;
+ *comma = '\0';
+ /* kstrtoull tolerates the trailing newline sysfs writes carry. */
+ if (kstrtoull(buf, 0, &h_object) || kstrtoull(comma + 1, 0, &pa))
+ return -EINVAL;
+ if (!h_object || h_object > 0xffffffffULL)
+ return -EINVAL;
+
+ return gsp_sema_service_counter_add((u32)h_object, pa);
+}
+
+static const struct kernel_param_ops nv_vgpu_sema_counter_ops = {
+ .set = nv_vgpu_sema_counter_set,
+};
+
+module_param_cb(sema_counter, &nv_vgpu_sema_counter_ops, NULL, 0200);
+MODULE_PARM_DESC(sema_counter, "Register channel GPU completion counter (h_object,pa)");
+
+/*
* Debug: find page-table entries pointing at a physical address, in guest RAM
* and emulated vidmem:
* echo 0x12442b000 > .../parameters/scan_pt