diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-29 08:40:19 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-29 08:40:19 -0400 |
| commit | 64bcfd6fd72546a2758d664bea39354604babcda (patch) | |
| tree | 0c529f9a03964da6d77887e19ac3a56d8ec45a18 /kernel/main.c | |
| parent | b37cd12a552c7a79a9b965c391fb2f64a8da4912 (diff) | |
| download | open-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.c | 173 |
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 |
