nouveau/vmm: fix another SPT/LPT race

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: d19512f5ab ("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>
This commit is contained in:
Dave Airlie 2026-06-15 14:47:37 +10:00
parent 695255c57f
commit 6763a0aea6

View 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++;
}
}
}