]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
nouveau/vmm: fix another SPT/LPT race
authorDave Airlie <airlied@redhat.com>
Mon, 15 Jun 2026 04:47:37 +0000 (14:47 +1000)
committerDave Airlie <airlied@redhat.com>
Fri, 10 Jul 2026 04:19:23 +0000 (14:19 +1000)
We've had an unknown Turing issue for a while with page faults since
large pages and compression.

I've got a patch series that syncs all our L2 handling with ogkm and it
made this fault happen more.

After writing a bunch of debugging patches, I spotted an invalid LPT
entry where there should have been a valid one.

A 64K MAP succeeds on a range, but a subsequent SPT put drops SPT refs
across multiple ranges,

We shouldn't assume all ranges where SPTEs go away will have the same
sparse/invalid/valid state, just iterate over each instead and do the
right thing.

Cc: stable@vger.kernel.org
Signed-off-by: Dave Airlie <airlied@redhat.com>
Fixes: d19512f5abb1 ("nouveau/vmm: start tracking if the LPT PTE is valid. (v6)")
Link: https://patch.msgid.link/20260615044737.3419585-1-airlied@gmail.com
[ Properly format commit message. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
(cherry picked from commit d008141ed4ce924167a03d46fbce9ad1fe4efa29)
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c

index 107bdb642f22924e19b7ddfe348ef1fcedc6cd38..190c082b12c8cfae89c2d3136816accc0c92f298 100644 (file)
@@ -231,29 +231,26 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt,
                 * covered by a number of LPTEs, the LPTEs once again take
                 * control over their address range.
                 *
-                * Determine how many LPTEs need to transition state.
+                * Transition each LPTE individually as each may have a
+                * different target state (sparse, invalid, or valid).
                 */
-               pgt->pte[ptei].s.spte_valid = false;
-               for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) {
+               for (ptei++; ptei < lpti; ptei++) {
                        if (pgt->pte[ptei].s.sptes)
                                break;
-                       pgt->pte[ptei].s.spte_valid = false;
                }
 
-               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 (!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);
+               while (pteb < ptei) {
+                       pgt->pte[pteb].s.spte_valid = false;
+                       if (pgt->pte[pteb].s.sparse) {
+                               TRA(it, "LPTE %05x: U -> S", pteb);
+                               pair->func->sparse(vmm, pgt->pt[0], pteb, 1);
+                       } else if (!pgt->pte[pteb].s.lpte_valid) {
+                               if (pair->func->invalid) {
+                                       TRA(it, "LPTE %05x: U -> I", pteb);
+                                       pair->func->invalid(vmm, pgt->pt[0], pteb, 1);
+                               }
                        }
-               } else {
-                       TRA(it, "LPTE %05x: V %d PTEs", pteb, ptes);
+                       pteb++;
                }
        }
 }