From d9f8a360ae17cb3386b4d7e660a69fb92c81367e Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Thu, 1 Oct 2026 00:14:09 -0400 Subject: gsp-phys: harden the D0 lock (pre-lock readiness, no unlock without rebind, strict board check) --- kernel/gsp_phys_gpu.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c index f9fffc4..40759a1 100644 --- a/kernel/gsp_phys_gpu.c +++ b/kernel/gsp_phys_gpu.c @@ -26,9 +26,14 @@ * the whole unbind -> claim -> release window. The GPU config ID and the * root-port link status are re-read after the claim and again before * rebinding the distribution driver; an unresponsive board aborts the - * handoff and the locks are deliberately left in place. Residual risk: a - * platform power cut that is not driven by PCI D3 (EC/SMI or thermal) has - * the same signature; if it happens, do not retry in that boot. + * handoff and the locks are deliberately left in place. The root-port lock + * is the load-bearing one: once the port cannot enter D3, a driverless + * GPU's own D3hot alone cannot cut the board rail. The GPU-side runtime-PM + * forbid is best-effort only, because the distribution driver's remove may + * itself consume the GPU's runtime-PM reference; the port lock and + * no_d3cold are the real guarantees. Residual risk: a platform power cut + * that is not driven by PCI D3 (EC/SMI or thermal) has the same signature; + * if it happens, do not retry in that boot. * * Therefore claim now requires BOTH gsp_phys_gpu=1 AND * gsp_phys_hazard_ack=1, and must not be enabled without a reviewed @@ -511,8 +516,8 @@ static int gsp_phys_board_check(struct pci_dev *pdev, const char *phase) int ret; ret = pci_read_config_dword(pdev, PCI_VENDOR_ID, &id); - if (ret != PCIBIOS_SUCCESSFUL || id == 0xffffffff || - (id & 0xffff) == 0x0000) { + if (ret != PCIBIOS_SUCCESSFUL || PCI_POSSIBLE_ERROR(id) || + (id & 0xffff) != GSP_PHYS_VENDOR) { WARN(1, "gsp-phys: board unresponsive %s: %s config id=0x%08x rc=%d; aborting - do not retry in this boot\n", phase, pci_name(pdev), id, ret); return -EIO; @@ -586,6 +591,20 @@ int gsp_phys_gpu_claim(void) return -ENODEV; } + /* + * Pre-lock readiness: pm_runtime_forbid() below calls rpm_resume(), + * which on an already power-gated board runs the exact freeze path + * we are trying to avoid. If the GPU or its root port is already + * runtime-suspended, refuse instead of resuming it. + */ + if (pdev->dev.power.runtime_status == RPM_SUSPENDED || + (pci_upstream_bridge(pdev) && + pci_upstream_bridge(pdev)->dev.power.runtime_status == RPM_SUSPENDED)) { + pr_err("gsp-phys: refusing claim: GPU or root port already runtime-suspended; the board may be power-gated (refusing to resume it)\n"); + pci_dev_put(pdev); + return -EBUSY; + } + /* * Lock the whole board and its root port into D0 before the unbind; * see the hazard note at the top of this file. @@ -697,13 +716,15 @@ void gsp_phys_gpu_release(void) ret = device_attach(&pdev->dev); drv = pdev->dev.driver; - if (drv) - pr_info("gsp-phys: released %s; rebound to %s (rc=%d)\n", - pci_name(pdev), drv->name, ret); - else - pr_warn("gsp-phys: released %s; no driver rebound " - "(rc=%d), run `modprobe nvidia`\n", - pci_name(pdev), ret); + if (!drv) { + pr_err("gsp-phys: %s did not rebind (rc=%d); power domain stays LOCKED; rebind the distribution driver manually (e.g. `modprobe nvidia`), then unload\n", + pci_name(pdev), ret); + pci_dev_put(pdev); + return; + } + + pr_info("gsp-phys: released %s; rebound to %s (rc=%d)\n", + pci_name(pdev), drv->name, ret); pci_dev_put(pdev); msleep(GSP_PHYS_POWER_SETTLE_MS); -- cgit v1.2.3