summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_phys_gpu.c85
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();