From: Dave Airlie <[email protected]>

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
Signed-off-by: Dave Airlie <[email protected]>
---
 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 39 +++++++++++++++----
 drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h |  3 +-
 2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c 
b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
index 8b8f4b85e315..ea1191386c6e 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 a8b08126e8dc..4ec0a3a21169 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.52.0

Reply via email to