Module Name:    src
Committed By:   rin
Date:           Fri Dec 15 09:33:30 UTC 2023

Modified Files:
        src/sys/arch/powerpc/oea: pmap.c

Log Message:
powerpc/oea: pmap: Rework pmap_pte_spill()

It was broken in many ways... Now, it gets working stable both for
OEA and OEA64_BRIDGE, as far as I can see.

Part of PR kern/57621


To generate a diff of this commit:
cvs rdiff -u -r1.117 -r1.118 src/sys/arch/powerpc/oea/pmap.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/powerpc/oea/pmap.c
diff -u src/sys/arch/powerpc/oea/pmap.c:1.117 src/sys/arch/powerpc/oea/pmap.c:1.118
--- src/sys/arch/powerpc/oea/pmap.c:1.117	Fri Dec 15 09:32:05 2023
+++ src/sys/arch/powerpc/oea/pmap.c	Fri Dec 15 09:33:29 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: pmap.c,v 1.117 2023/12/15 09:32:05 rin Exp $	*/
+/*	$NetBSD: pmap.c,v 1.118 2023/12/15 09:33:29 rin Exp $	*/
 /*-
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -63,7 +63,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.117 2023/12/15 09:32:05 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.118 2023/12/15 09:33:29 rin Exp $");
 
 #define	PMAP_NOOPNAMES
 
@@ -879,48 +879,35 @@ pmap_pte_insert(int ptegidx, struct pte 
  * Tries to spill a page table entry from the overflow area.
  * This runs in either real mode (if dealing with a exception spill)
  * or virtual mode when dealing with manually spilling one of the
- * kernel's pte entries.  In either case, interrupts are already
- * disabled.
+ * kernel's pte entries.
  */
 
 int
-pmap_pte_spill(struct pmap *pm, vaddr_t addr, bool exec)
+pmap_pte_spill(struct pmap *pm, vaddr_t addr, bool isi_p)
 {
-	struct pvo_entry *source_pvo, *victim_pvo, *next_pvo;
-	struct pvo_entry *pvo;
-	/* XXX: gcc -- vpvoh is always set at either *1* or *2* */
-	struct pvo_tqhead *pvoh, *vpvoh = NULL;
-	int ptegidx, i, j;
+	struct pvo_tqhead *spvoh, *vpvoh;
+	struct pvo_entry *pvo, *source_pvo, *victim_pvo;
 	volatile struct pteg *pteg;
 	volatile struct pte *pt;
+	register_t msr, vsid, hash;
+	int ptegidx, hid, i, j;
+	int done = 0;
 
 	PMAP_LOCK();
+	msr = pmap_interrupts_off();
+
+	/* XXXRO paranoid? */
+	if (pm->pm_evictions == 0)
+		goto out;
 
 	ptegidx = va_to_pteg(pm, addr);
 
 	/*
-	 * Have to substitute some entry. Use the primary hash for this.
-	 * Use low bits of timebase as random generator.  Make sure we are
-	 * not picking a kernel pte for replacement.
+	 * Find source pvo.
 	 */
-	pteg = &pmap_pteg_table[ptegidx];
-	i = MFTB() & 7;
-	for (j = 0; j < 8; j++) {
-		pt = &pteg->pt[i];
-		if ((pt->pte_hi & PTE_VALID) == 0)
-			break;
-		if (VSID_TO_HASH((pt->pte_hi & PTE_VSID) >> PTE_VSID_SHFT)
-				< PHYSMAP_VSIDBITS)
-			break;
-		i = (i + 1) & 7;
-	}
-	KASSERT(j < 8);
-
+	spvoh = &pmap_pvo_table[ptegidx];
 	source_pvo = NULL;
-	victim_pvo = NULL;
-	pvoh = &pmap_pvo_table[ptegidx];
-	TAILQ_FOREACH(pvo, pvoh, pvo_olink) {
-
+	TAILQ_FOREACH(pvo, spvoh, pvo_olink) {
 		/*
 		 * We need to find pvo entry for this address...
 		 */
@@ -931,101 +918,105 @@ pmap_pte_spill(struct pmap *pm, vaddr_t 
 		 * a valid PTE, then we know we can't find it because all
 		 * evicted PVOs always are first in the list.
 		 */
-		if (source_pvo == NULL && (pvo->pvo_pte.pte_hi & PTE_VALID))
+		if ((pvo->pvo_pte.pte_hi & PTE_VALID) != 0)
 			break;
-		if (source_pvo == NULL && pm == pvo->pvo_pmap &&
-		    addr == PVO_VADDR(pvo)) {
 
-			/*
-			 * Now we have found the entry to be spilled into the
-			 * pteg.  Attempt to insert it into the page table.
-			 */
-			j = pmap_pte_insert(ptegidx, &pvo->pvo_pte);
-			if (j >= 0) {
-				PVO_PTEGIDX_SET(pvo, j);
-				PMAP_PVO_CHECK(pvo);	/* sanity check */
-				PVO_WHERE(pvo, SPILL_INSERT);
-				pvo->pvo_pmap->pm_evictions--;
-				PMAPCOUNT(ptes_spilled);
-				PMAPCOUNT2(((pvo->pvo_pte.pte_hi & PTE_HID)
-				    ? pmap_evcnt_ptes_secondary
-				    : pmap_evcnt_ptes_primary)[j]);
-
-				/*
-				 * Since we keep the evicted entries at the
-				 * from of the PVO list, we need move this
-				 * (now resident) PVO after the evicted
-				 * entries.
-				 */
-				next_pvo = TAILQ_NEXT(pvo, pvo_olink);
-
-				/*
-				 * If we don't have to move (either we were the
-				 * last entry or the next entry was valid),
-				 * don't change our position.  Otherwise 
-				 * move ourselves to the tail of the queue.
-				 */
-				if (next_pvo != NULL &&
-				    !(next_pvo->pvo_pte.pte_hi & PTE_VALID)) {
-					TAILQ_REMOVE(pvoh, pvo, pvo_olink);
-					TAILQ_INSERT_TAIL(pvoh, pvo, pvo_olink);
-				}
-				PMAP_UNLOCK();
-				return 1;
+		if (pm == pvo->pvo_pmap && addr == PVO_VADDR(pvo)) {
+			if (isi_p) {
+				if (!PVO_EXECUTABLE_P(pvo))
+					goto out;
+#if defined(PMAP_OEA) || defined(PMAP_OEA64_BRIDGE)
+				int sr __diagused =
+				    PVO_VADDR(pvo) >> ADDR_SR_SHFT;
+				KASSERT((pm->pm_sr[sr] & SR_NOEXEC) == 0);
+#endif
 			}
+			KASSERT(!PVO_PTEGIDX_ISSET(pvo));
+			/* XXXRO where check */
 			source_pvo = pvo;
-			if (exec && !PVO_EXECUTABLE_P(source_pvo)) {
-				PMAP_UNLOCK();
-				return 0;
-			}
-			if (victim_pvo != NULL)
-				break;
-		}
-
-		/*
-		 * We also need the pvo entry of the victim we are replacing
-		 * so save the R & C bits of the PTE.
-		 */
-		if ((pt->pte_hi & PTE_HID) == 0 && victim_pvo == NULL &&
-		    pmap_pte_compare(pt, &pvo->pvo_pte)) {
-			vpvoh = pvoh;			/* *1* */
-			victim_pvo = pvo;
-			if (source_pvo != NULL)
-				break;
+			break;
 		}
 	}
-
 	if (source_pvo == NULL) {
 		PMAPCOUNT(ptes_unspilled);
-		PMAP_UNLOCK();
-		return 0;
+		goto out;
 	}
 
-	if (victim_pvo == NULL) {
-		if ((pt->pte_hi & PTE_HID) == 0)
-			panic("pmap_pte_spill: victim p-pte (%p) has "
-			    "no pvo entry!", pt);
+	/*
+	 * Now we have found the entry to be spilled into the
+	 * pteg.  Attempt to insert it into the page table.
+	 */
+	i = pmap_pte_insert(ptegidx, &pvo->pvo_pte);
+	if (i >= 0) {
+		PVO_PTEGIDX_SET(pvo, i);
+		PMAP_PVO_CHECK(pvo);	/* sanity check */
+		PVO_WHERE(pvo, SPILL_INSERT);
+		pvo->pvo_pmap->pm_evictions--;
+		PMAPCOUNT(ptes_spilled);
+		PMAPCOUNT2(((pvo->pvo_pte.pte_hi & PTE_HID) != 0
+		    ? pmap_evcnt_ptes_secondary
+		    : pmap_evcnt_ptes_primary)[i]);
 
-		/*
-		 * If this is a secondary PTE, we need to search
-		 * its primary pvo bucket for the matching PVO.
-		 */
-		vpvoh = &pmap_pvo_table[ptegidx ^ pmap_pteg_mask]; /* *2* */
-		TAILQ_FOREACH(pvo, vpvoh, pvo_olink) {
-			PMAP_PVO_CHECK(pvo);		/* sanity check */
+		TAILQ_REMOVE(spvoh, pvo, pvo_olink);
+		TAILQ_INSERT_TAIL(spvoh, pvo, pvo_olink);
 
-			/*
-			 * We also need the pvo entry of the victim we are
-			 * replacing so save the R & C bits of the PTE.
-			 */
-			if (pmap_pte_compare(pt, &pvo->pvo_pte)) {
-				victim_pvo = pvo;
-				break;
-			}
+		done = 1;
+		goto out;
+	}
+
+	/*
+	 * Have to substitute some entry. Use the primary hash for this.
+	 * Use low bits of timebase as random generator.
+	 *
+	 * XXX:
+	 * Make sure we are not picking a kernel pte for replacement.
+	 */
+	hid = 0;
+	i = MFTB() & 7;
+	pteg = &pmap_pteg_table[ptegidx];
+ retry:
+	for (j = 0; j < 8; j++, i = (i + 1) & 7) {
+		pt = &pteg->pt[i];
+
+		if ((pt->pte_hi & PTE_VALID) == 0)
+			break;
+
+		vsid = (pt->pte_hi & PTE_VSID) >> PTE_VSID_SHFT;
+		hash = VSID_TO_HASH(vsid);
+		if (hash < PHYSMAP_VSIDBITS)
+			break;
+	}
+	if (j == 8) {
+		if (hid != 0)
+			panic("%s: no victim\n", __func__);
+		hid = PTE_HID;
+		pteg = &pmap_pteg_table[ptegidx ^ pmap_pteg_mask];
+		goto retry;
+	}
+
+	/*
+	 * We also need the pvo entry of the victim we are replacing
+	 * so save the R & C bits of the PTE.
+	 */
+	if ((pt->pte_hi & PTE_HID) == hid)
+		vpvoh = spvoh;
+	else
+		vpvoh = &pmap_pvo_table[ptegidx ^ pmap_pteg_mask];
+	victim_pvo = NULL;
+	TAILQ_FOREACH(pvo, vpvoh, pvo_olink) {
+		PMAP_PVO_CHECK(pvo);		/* sanity check */
+
+		if ((pvo->pvo_pte.pte_hi & PTE_VALID) == 0)
+			continue;
+
+		if (pmap_pte_compare(pt, &pvo->pvo_pte)) {
+			victim_pvo = pvo;
+			break;
 		}
-		if (victim_pvo == NULL)
-			panic("pmap_pte_spill: victim s-pte (%p) has "
-			    "no pvo entry!", pt);
+	}
+	if (victim_pvo == NULL) {
+		panic("%s: victim p-pte (%p) has no pvo entry!",
+		    __func__, pt);
 	}
 
 	/*
@@ -1041,7 +1032,10 @@ pmap_pte_spill(struct pmap *pm, vaddr_t 
 	 * we lose any ref/chg bit changes contained in the TLB
 	 * entry.
 	 */
-	source_pvo->pvo_pte.pte_hi &= ~PTE_HID;
+	if (hid == 0)
+		source_pvo->pvo_pte.pte_hi &= ~PTE_HID;
+	else
+		source_pvo->pvo_pte.pte_hi |= PTE_HID;
 
 	/*
 	 * To enforce the PVO list ordering constraint that all
@@ -1050,8 +1044,8 @@ pmap_pte_spill(struct pmap *pm, vaddr_t 
 	 * victim PVO to the head of its list (which might not be
 	 * the same list, if the victim was using the secondary hash).
 	 */
-	TAILQ_REMOVE(pvoh, source_pvo, pvo_olink);
-	TAILQ_INSERT_TAIL(pvoh, source_pvo, pvo_olink);
+	TAILQ_REMOVE(spvoh, source_pvo, pvo_olink);
+	TAILQ_INSERT_TAIL(spvoh, source_pvo, pvo_olink);
 	TAILQ_REMOVE(vpvoh, victim_pvo, pvo_olink);
 	TAILQ_INSERT_HEAD(vpvoh, victim_pvo, pvo_olink);
 	pmap_pte_unset(pt, &victim_pvo->pvo_pte, victim_pvo->pvo_vaddr);
@@ -1071,8 +1065,12 @@ pmap_pte_spill(struct pmap *pm, vaddr_t 
 	PMAP_PVO_CHECK(victim_pvo);
 	PMAP_PVO_CHECK(source_pvo);
 
+	done = 1;
+
+ out:
+	pmap_interrupts_restore(msr);
 	PMAP_UNLOCK();
-	return 1;
+	return done;
 }
 
 /*

Reply via email to