Defrag doesn't need to check presence.
The concept of presence is captured in the attribues of a PTE so
blocks with the same attribues all have the same state of presence.
Change-Id: I579ae2b77ddebbe308cffa9bca39931d74f75d63
diff --git a/src/mm.c b/src/mm.c
index fc4adde..d31da51 100644
--- a/src/mm.c
+++ b/src/mm.c
@@ -515,35 +515,15 @@
}
/**
- * Given that `entry` is a subtable but its entries are all absent, return the
- * absent entry with which it can be replaced. Note that `entry` will no longer
- * be valid after calling this function as the subtable will have been freed.
+ * Given the table PTE entries are all have identical attributes, return the
+ * single entry with which it can be replaced. Note that the table PTE will no
+ * longer be valid after calling this function as the table may have been freed.
+ *
+ * If the table is freed, the memory is freed directly rather than calling
+ * `mm_free_page_pte()` as it is known to not have subtables.
*/
-static pte_t mm_table_pte_to_absent(pte_t entry, uint8_t level,
- struct mpool *ppool)
-{
- struct mm_page_table *table =
- mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
-
- /*
- * Free the subtable. This is safe to do directly (rather than
- * using mm_free_page_pte) because we know by this point that it
- * doesn't have any subtables of its own.
- */
- mpool_free(ppool, table);
-
- /* Replace subtable with a single absent entry. */
- return arch_mm_absent_pte(level);
-}
-
-/**
- * Given that `entry` is a subtable and its entries are all identical, return
- * the single block entry with which it can be replaced if possible. Note that
- * `entry` will no longer be valid after calling this function as the subtable
- * may have been freed.
- */
-static pte_t mm_table_pte_to_block(pte_t entry, uint8_t level,
- struct mpool *ppool)
+static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level,
+ struct mpool *ppool)
{
struct mm_page_table *table;
uint64_t block_attrs;
@@ -551,33 +531,34 @@
uint64_t combined_attrs;
paddr_t block_address;
- if (!arch_mm_is_block_allowed(level)) {
- return entry;
+ table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
+
+ if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
+ /* Free the table and return an absent entry. */
+ mpool_free(ppool, table);
+ return arch_mm_absent_pte(level);
}
- table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
+ /* Might not be possible to merge the table into a single block. */
+ if (!arch_mm_is_block_allowed(level)) {
+ return table_pte;
+ }
- /* Replace subtable with a single block, with equivalent attributes. */
+ /* Replace table with a single block, with equivalent attributes. */
block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
- table_attrs = arch_mm_pte_attrs(entry, level);
+ table_attrs = arch_mm_pte_attrs(table_pte, level);
combined_attrs =
arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
- /* Free the subtable. */
+ /* Free the table and return a block. */
mpool_free(ppool, table);
-
- /*
- * We can assume that the block is aligned properly because all virtual
- * addresses are aligned by definition, and we have a 1-1 mapping from
- * virtual to physical addresses.
- */
return arch_mm_block_pte(level, block_address, combined_attrs);
}
/**
- * Defragment the given ptable entry by recursively replacing any tables with
- * block or absent entries where possible.
+ * Defragment the given PTE by recursively replacing any tables with blocks or
+ * absent entries where possible.
*/
static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
struct mpool *ppool)
@@ -585,8 +566,6 @@
struct mm_page_table *table;
uint64_t i;
uint64_t attrs;
- bool identical_blocks_so_far = true;
- bool all_absent_so_far = true;
if (!arch_mm_pte_is_table(entry, level)) {
return entry;
@@ -596,36 +575,25 @@
/*
* Check if all entries are blocks with the same flags or are all
- * absent.
+ * absent. It assumes addresses are contiguous due to identity mapping.
*/
attrs = arch_mm_pte_attrs(table->entries[0], level);
for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
- /*
- * First try to defrag the entry, in case it is a subtable.
- */
+ /* First try to defrag the entry, in case it is a subtable. */
table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
level - 1, ppool);
- if (arch_mm_pte_is_present(table->entries[i], level - 1)) {
- all_absent_so_far = false;
- }
-
/*
- * If the entry is a block, check that the flags are the same as
- * what we have so far.
+ * If the entry isn't a block or has different attributes then
+ * it isn't possible to defragment it.
*/
if (!arch_mm_pte_is_block(table->entries[i], level - 1) ||
arch_mm_pte_attrs(table->entries[i], level) != attrs) {
- identical_blocks_so_far = false;
+ return entry;
}
}
- if (identical_blocks_so_far) {
- return mm_table_pte_to_block(entry, level, ppool);
- }
- if (all_absent_so_far) {
- return mm_table_pte_to_absent(entry, level, ppool);
- }
- return entry;
+
+ return mm_merge_table_pte(entry, level, ppool);
}
/**