[coreboot-gerrit] Change in coreboot[master]: [WIP] cpu/x86/mtrr: Optimize hole carving strategy

Nico Huber (Code Review) gerrit at coreboot.org
Sat Oct 7 16:49:31 CEST 2017


Nico Huber has uploaded this change for review. ( https://review.coreboot.org/21915


Change subject: [WIP] cpu/x86/mtrr: Optimize hole carving strategy
......................................................................

[WIP] cpu/x86/mtrr: Optimize hole carving strategy

For WB ranges with unaligned end, we try to align the range up and
carve a hole out of it which might reduce MTRR usage. Instead of
trying an arbitrary alignment, we choose an optimal one.

Change-Id: Iefb064ce8c4f293490a19dd46054b966c63bde44
Signed-off-by: Nico Huber <nico.h at gmx.de>
---
M src/cpu/x86/mtrr/mtrr.c
1 file changed, 84 insertions(+), 25 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/15/21915/1

diff --git a/src/cpu/x86/mtrr/mtrr.c b/src/cpu/x86/mtrr/mtrr.c
index c2c629c..82666fc 100644
--- a/src/cpu/x86/mtrr/mtrr.c
+++ b/src/cpu/x86/mtrr/mtrr.c
@@ -105,8 +105,6 @@
 #define RANGE_TO_PHYS_ADDR(x) (((resource_t)(x)) << RANGE_SHIFT)
 #define NUM_FIXED_MTRRS (NUM_FIXED_RANGES / RANGES_PER_FIXED_MTRR)
 
-/* The minimum alignment while handling variable MTRR ranges is 64MiB. */
-#define MTRR_MIN_ALIGN PHYS_TO_RANGE_ADDR(64 << 20)
 /* Helpful constants. */
 #define RANGE_1MB PHYS_TO_RANGE_ADDR(1 << 20)
 #define RANGE_4GB (1 << (ADDR_SHIFT_TO_RANGE_SHIFT(32)))
@@ -471,10 +469,63 @@
 	}
 }
 
+static uint32_t optimize_var_mtrr_hole(const uint32_t base,
+				       const uint32_t limit)
+{
+	/*
+	 * With default type UC, we can potentially optimize a WB
+	 * range with unaligned upper end, by aligning it up and
+	 * carving the added "hole" out again.
+	 *
+	 * To optimize the upper end of the hole, we will predict
+	 * how many MTRRs calc_var_mtrr_range() will spend for the
+	 * hole and how many it will save us for the original range.
+	 *
+	 * We take two parameters, the upper end of the WB range as
+	 * `base` of the hole and a `limit` how far we may align it
+	 * up. As long as `base` is unaligned, calc_var_mtrr_range()
+	 * would add MTRRs of the size of lowest significant bit set
+	 * in `base` and add that size to `base`.
+	 *
+	 * We don't have to exercise all these additions. As we know
+	 * the next MTRR to set will be at the position the carry of
+	 * last addition takes. That is the closest 0 in a more sig-
+	 * nificant position. So we account one MTRR for the least
+	 * signifcant bit set in `base` plus one for each unset bit
+	 * up to the new alignment.
+	 *
+	 * OTOH, for every bit that is flipped to zero in `base` due
+	 * to the alignment, we save one MTRR for the original WB
+	 * range. Minus one for the final carry bit.
+	 */
+
+	int gain = -2; /* Minus one for the first addition and
+			  minus one for the final carry bit. */
+	int best_gain = 0;
+	uint32_t bit, best_bit = 1;
+	const uint32_t last_bit = 1 << fms(base);
+
+	/* Count 1s that will flip as gain, 0s we have to force as loss. */
+	for (bit = 1 << fls(base); bit <= last_bit; bit <<= 1) {
+		if (ALIGN_UP(base, bit) > limit)
+			break;
+		if (base & bit) {
+			if (++gain > best_gain) {
+				best_gain = gain;
+				best_bit = bit;
+			}
+		} else {
+			--gain;
+		}
+	}
+
+	return ALIGN_UP(base, best_bit);
+}
+
 static void calc_var_mtrrs_with_hole(struct var_mtrr_state *var_state,
 				     struct range_entry *r)
 {
-	uint32_t a1, a2, b1, b2;
+	uint32_t a1, a2, b1, b2, b2_limit;
 	int mtrr_type;
 	struct range_entry *next;
 
@@ -482,11 +533,11 @@
 	 * Determine MTRRs based on the following algorithm for the given entry:
 	 * +------------------+ b2 = ALIGN_UP(end)
 	 * |  0 or more bytes | <-- hole is carved out between b1 and b2
-	 * +------------------+ a2 = b1 = end
+	 * +------------------+ a2 = b1 = original end
 	 * |                  |
 	 * +------------------+ a1 = begin
 	 *
-	 * Thus, there are 3 sub-ranges to configure variable MTRRs for.
+	 * Thus, there are up to 2 sub-ranges to configure variable MTRRs for.
 	 */
 	mtrr_type = range_entry_mtrr_type(r);
 
@@ -513,38 +564,46 @@
 	if (!var_state->above4gb && a2 > RANGE_4GB)
 		a2 = RANGE_4GB;
 
+	/* May carve out hole if type is WB, otherwise we are done. */
+	if (mtrr_type != MTRR_TYPE_WRBACK) {
+		calc_var_mtrr_range(var_state, a1, a2 - a1, mtrr_type);
+		return;
+	}
+
 	next = memranges_next_entry(var_state->addr_space, r);
 
 	b1 = a2;
 
 	/* First check if a1 is >= 4GiB and the current entry is the last
 	 * entry. If so perform an optimization of covering a larger range
-	 * defined by the base address' alignment. */
+	 * by aligning up, at most to the next power of 2. */
 	if (a1 >= RANGE_4GB && next == NULL) {
-		uint32_t addr_lsb;
+		uint32_t align;
 
-		addr_lsb = fls(a1);
-		b2 = (1 << addr_lsb) + a1;
-		if (b2 >= a2) {
-			calc_var_mtrr_range(var_state, a1, b2 - a1, mtrr_type);
-			return;
-		}
-	}
+		/* Find first alignment that doesn't overflow. */
+		align = 1 << fms(a2);
+		do {
+			b2 = ALIGN_UP(a2, align);
+			align >>= 1;
+		} while (b2 < a2);
 
-	/* Handle the min alignment roundup case. */
-	b2 = ALIGN_UP(a2, MTRR_MIN_ALIGN);
-
-	/* Check against the next range. If the current range_entry is the
-	 * last entry then carving a hole is no problem. If the current entry
-	 * isn't the last entry then check that the last entry covers the
-	 * entire hole range with the default mtrr type. */
-	if (next != NULL &&
-	    (range_entry_mtrr_type(next) != var_state->def_mtrr_type ||
-	     range_entry_end_mtrr_addr(next) < b2)) {
-		calc_var_mtrr_range(var_state, a1, a2 - a1, mtrr_type);
+		calc_var_mtrr_range(var_state, a1, b2 - a1, mtrr_type);
 		return;
 	}
 
+	/* Find the limit how far we may align up. */
+	if (next == NULL) {
+		b2_limit = ALIGN_UP(a2, 1 << fms(a2));
+	} else if (range_entry_mtrr_type(next) == var_state->def_mtrr_type) {
+		b2_limit = range_entry_end_mtrr_addr(next);
+	} else {
+		b2_limit = a2;
+	}
+	/* No bits above a1's alignment may change, it would double the cost. */
+	if (a1)
+		b2_limit = MIN(ALIGN_UP(a2 + 1, 1 << fls(a1)) - 1, b2_limit);
+	b2 = optimize_var_mtrr_hole(a2, b2_limit);
+
 	calc_var_mtrr_range(var_state, a1, b2 - a1, mtrr_type);
 	calc_var_mtrr_range(var_state, b1, b2 - b1, var_state->def_mtrr_type);
 }

-- 
To view, visit https://review.coreboot.org/21915
To unsubscribe, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iefb064ce8c4f293490a19dd46054b966c63bde44
Gerrit-Change-Number: 21915
Gerrit-PatchSet: 1
Gerrit-Owner: Nico Huber <nico.h at gmx.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20171007/229b213d/attachment.html>


More information about the coreboot-gerrit mailing list