Commit d19512f5 authored by Dave Airlie's avatar Dave Airlie
Browse files

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: default avatarMary Guillemard <mary@mary.zone>
Tested-by: default avatarMary Guillemard <mary@mary.zone>
Tested-by: default avatarMel Henning <mhenning@darkrefraction.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
Link: https://patch.msgid.link/20260204030208.2313241-4-airlied@gmail.com
parent 9dc983a8
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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>
+31 −8
Original line number Diff line number Diff line
@@ -242,7 +242,7 @@ 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
		} 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
@@ -251,6 +251,9 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt,
				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;
}

+2 −1
Original line number Diff line number Diff line
@@ -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;
};