diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 00:52:08 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 00:52:08 -0400 |
| commit | 8100b152a8f36655fb50952fbac16c65b787d194 (patch) | |
| tree | 342dc6fc4253a6afeed0318f78d071ad4b8456a6 | |
| parent | 0cce44aea0c63aa55414d6e5147e6919d51118e5 (diff) | |
| download | open-iov-8100b152a8f36655fb50952fbac16c65b787d194.tar.gz open-iov-8100b152a8f36655fb50952fbac16c65b787d194.zip | |
gsp-phys: hold a GPU runtime-PM reference across the claim window (udev bind-event hardening)
| -rw-r--r-- | kernel/gsp_phys_gpu.c | 85 |
1 files changed, 79 insertions, 6 deletions
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c index 40759a1..a2ff631 100644 --- a/kernel/gsp_phys_gpu.c +++ b/kernel/gsp_phys_gpu.c @@ -30,10 +30,12 @@ * 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. + * itself consume the GPU's runtime-PM reference and a udev bind rule can + * re-allow GPU runtime PM; the port lock and no_d3cold are the real + * guarantees, backed by an extra runtime-PM usage reference taken once the + * device is ours (gsp_phys_runtime_ref_hold()). 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 @@ -137,6 +139,11 @@ struct gsp_phys_gpu { bool regions; bool drv_registered; bool claimed; + /* + * Extra runtime-PM usage reference held on the GPU for the duration + * of the claim; see gsp_phys_runtime_ref_hold(). + */ + bool runtime_ref_held; struct gsp_phys_boot boot; struct gsp_phys_power_lock locks[GSP_PHYS_MAX_POWER_LOCKS]; unsigned int nr_locks; @@ -156,6 +163,35 @@ void __iomem *gsp_phys_gpu_bar0(void) } EXPORT_SYMBOL_GPL(gsp_phys_gpu_bar0); +/* + * Defense in depth for the GPU side of the D0 hold. The distribution + * driver's udev rule writes power/control="auto" for the GPU on the bind + * event, which re-allows GPU runtime PM underneath us mid-claim and can + * defeat the GPU-side pm_runtime_forbid(). Take an extra usage reference + * with pm_runtime_get_noresume(): it bumps usage_count without resuming + * the device and without changing runtime_auto, so it cannot leave an + * `allow` mismatch behind and does not fight the udev policy. The + * root-port lock remains the load-bearing guarantee; this hold merely + * keeps the GPU itself from autosuspending while we own it. + */ +static void gsp_phys_runtime_ref_hold(struct pci_dev *pdev) +{ + pm_runtime_get_noresume(&pdev->dev); + phys.runtime_ref_held = true; + pr_debug("gsp-phys: runtime-PM reference held on %s\n", pci_name(pdev)); +} + +static void gsp_phys_runtime_ref_drop(struct pci_dev *pdev) +{ + if (!phys.runtime_ref_held) + return; + + pm_runtime_put(&pdev->dev); + phys.runtime_ref_held = false; + pr_debug("gsp-phys: runtime-PM reference dropped on %s\n", + pci_name(pdev)); +} + static void gsp_phys_fw_unload(void) { if (!phys.fw) @@ -644,6 +680,13 @@ int gsp_phys_gpu_claim(void) if (ret) goto err_board; + /* + * The device is ours and answers config reads: take the extra + * runtime-PM reference that keeps it from autosuspending under the + * udev bind-event policy for the rest of the claim window. + */ + gsp_phys_runtime_ref_hold(pdev); + pci_dev_put(pdev); pr_info("gsp-phys: physical GPU claimed (backend rung 1)\n"); @@ -666,6 +709,12 @@ err_board: phys.drv_registered = false; } phys.claimed = false; + /* + * Balance note: the runtime-PM reference is taken only after this + * board check passes, so a claim that fails here never held one and + * has nothing to drop. The D0 locks are intentionally left in + * place. + */ pr_err("gsp-phys: claim aborted with the power domain LOCKED and no driver on %s; not rebinding the distribution driver (do not retry in this boot)\n", pci_name(pdev)); pci_dev_put(pdev); @@ -710,6 +759,16 @@ void gsp_phys_gpu_release(void) if (gsp_phys_board_check(pdev, "before rebind")) { pr_err("gsp-phys: NOT rebinding %s: board unresponsive; power domain stays LOCKED (do not retry in this boot)\n", pci_name(pdev)); + /* + * Intentional imbalance: the claim's runtime-PM + * reference stays held together with the D0 locks so + * a board that no longer answers config reads cannot + * be power-gated again. A later successful release + * drops it; otherwise it stays held until reboot. + */ + if (phys.runtime_ref_held) + pr_debug("gsp-phys: keeping the runtime-PM reference on %s held (intentional abort)\n", + pci_name(pdev)); pci_dev_put(pdev); return; } @@ -719,15 +778,29 @@ void gsp_phys_gpu_release(void) 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); + /* + * Same intentional imbalance as the unresponsive-board + * abort: nvidia did not rebind, the D0 locks stay and + * the runtime-PM reference stays held with them. + */ + if (phys.runtime_ref_held) + pr_debug("gsp-phys: keeping the runtime-PM reference on %s held (no rebind)\n", + pci_name(pdev)); 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); + + /* + * The distribution driver has rebound and settled: drop the + * claim's runtime-PM reference exactly once, before the D0 + * locks are released. + */ + gsp_phys_runtime_ref_drop(pdev); + pci_dev_put(pdev); } gsp_phys_power_unlock(); |
