From d19512f5abb198daf29da877f6a02c667a95c03d Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 4 Feb 2026 13:00:07 +1000 Subject: [PATCH] nouveau/vmm: start tracking if the LPT PTE is valid. (v6) When NVK enabled large pages userspace tests were seeing fault reports at a valid address. There was a case where an address moving from 64k page to 4k pages could expose a race between unmapping the 4k page, mapping the 64k page and unref the 4k pages. Unref 4k pages would cause the dual-page table handling to always set the LPTE entry to SPARSE or INVALID, but if we'd mapped a valid LPTE in the meantime, it would get trashed. Keep track of when a valid LPTE has been referenced, and don't reset in that case. This adds an lpte valid tracker and lpte reference count. Whenever an lpte is referenced, it gets made valid and the ref count increases, whenever it gets unreference the refcount is tracked. Link: https://gitlab.freedesktop.org/mesa/mesa/-/issues/14610 Reviewed-by: Mary Guillemard Tested-by: Mary Guillemard Tested-by: Mel Henning Signed-off-by: Dave Airlie Link: https://patch.msgid.link/20260204030208.2313241-4-airlied@gmail.com --- drivers/gpu/drm/nouveau/nouveau_drv.h | 4 +- drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 39 +++++++++++++++---- drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h | 3 +- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 0e409414f44db..1c2523e2f92ef 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -10,7 +10,7 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 4 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_PATCHLEVEL 2 /* * 1.1.1: @@ -37,6 +37,8 @@ * - implemented limited ABI16/NVIF interop * 1.4.1: * - add variable page sizes and compression for Turing+ + * 1.4.2: + * - tell userspace LPTE/SPTE races are fixed. */ #include diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c index 44daeec0aa6d2..19a7407cf702e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -242,14 +242,17 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt, if (pgt->pte[pteb].s.sparse) { TRA(it, "LPTE %05x: U -> S %d PTEs", pteb, ptes); pair->func->sparse(vmm, pgt->pt[0], pteb, ptes); - } else - if (pair->func->invalid) { - /* If the MMU supports it, restore the LPTE to the - * INVALID state to tell the MMU there is no point - * trying to fetch the corresponding SPTEs. - */ - TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes); - pair->func->invalid(vmm, pgt->pt[0], pteb, ptes); + } else if (!pgt->pte[pteb].s.lpte_valid) { + if (pair->func->invalid) { + /* If the MMU supports it, restore the LPTE to the + * INVALID state to tell the MMU there is no point + * trying to fetch the corresponding SPTEs. + */ + TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes); + pair->func->invalid(vmm, pgt->pt[0], pteb, ptes); + } + } else { + TRA(it, "LPTE %05x: V %d PTEs", pteb, ptes); } } } @@ -280,6 +283,15 @@ nvkm_vmm_unref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) if (desc->type == SPT && (pgt->refs[0] || pgt->refs[1])) nvkm_vmm_unref_sptes(it, pgt, desc, ptei, ptes); + if (desc->type == LPT && (pgt->refs[0] || pgt->refs[1])) { + for (u32 lpti = ptei; ptes; lpti++) { + pgt->pte[lpti].s.lptes--; + if (pgt->pte[lpti].s.lptes == 0) + pgt->pte[lpti].s.lpte_valid = false; + ptes--; + } + } + /* PT no longer needed? Destroy it. */ if (!pgt->refs[type]) { it->lvl++; @@ -332,10 +344,12 @@ nvkm_vmm_ref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt, * Determine how many LPTEs need to transition state. */ pgt->pte[ptei].s.spte_valid = true; + pgt->pte[ptei].s.lpte_valid = false; for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { if (pgt->pte[ptei].s.spte_valid) break; pgt->pte[ptei].s.spte_valid = true; + pgt->pte[ptei].s.lpte_valid = false; } if (pgt->pte[pteb].s.sparse) { @@ -374,6 +388,15 @@ nvkm_vmm_ref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) if (desc->type == SPT) nvkm_vmm_ref_sptes(it, pgt, desc, ptei, ptes); + if (desc->type == LPT) { + for (u32 lpti = ptei; ptes; lpti++) { + pgt->pte[lpti].s.spte_valid = false; + pgt->pte[lpti].s.lpte_valid = true; + pgt->pte[lpti].s.lptes++; + ptes--; + } + } + return true; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h index a8b08126e8dc6..4ec0a3a211690 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h @@ -9,7 +9,8 @@ union nvkm_pte_tracker { struct { u32 sparse:1; u32 spte_valid:1; - u32 padding:14; + u32 lpte_valid:1; + u32 lptes:13; u32 sptes:16; } s; }; -- 2.47.3