]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
nouveau/vmm: start tracking if the LPT PTE is valid. (v6)
authorDave Airlie <airlied@redhat.com>
Wed, 4 Feb 2026 03:00:07 +0000 (13:00 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 4 Feb 2026 20:05:09 +0000 (06:05 +1000)
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 <mary@mary.zone>
Tested-by: Mary Guillemard <mary@mary.zone>
Tested-by: Mel Henning <mhenning@darkrefraction.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Link: https://patch.msgid.link/20260204030208.2313241-4-airlied@gmail.com
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h

index 0e409414f44db79a1a567647342d997b962b084a..1c2523e2f92ef32b3dab34f806bb372337739636 100644 (file)
@@ -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 <linux/notifier.h>
index 44daeec0aa6d24a2bf7b6d35b9d8f385a7d30f90..19a7407cf702e0607d0c512db894243e82c98887 100644 (file)
@@ -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;
 }
 
index a8b08126e8dc6126b15171432e89e870b11ed8c1..4ec0a3a211690f732f3c47cf569e7361a08d113b 100644 (file)
@@ -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;
 };