Skip to content

Commit f7e1164

Browse files
Nicolas Pitrenashif
authored andcommitted
arch/arm64/mmu: fix page table reference counting
Existing code confused table usage and table reference counts together. This obviously doesn't work. A table with one reference to it and one populated PTE is not the same as a table with 2 references to it and no PTe in use. So split the two concepts and adjust the code accordingly. A page needs to have its PTE usage count drop to zero before the last reference is released. When both counts are 0 then the page is free. Signed-off-by: Nicolas Pitre <[email protected]>
1 parent 0a2e4d1 commit f7e1164

File tree

1 file changed

+40
-25
lines changed

1 file changed

+40
-25
lines changed

arch/arm64/core/mmu.c

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
2828

2929
static uint64_t xlat_tables[CONFIG_MAX_XLAT_TABLES * Ln_XLAT_NUM_ENTRIES]
3030
__aligned(Ln_XLAT_NUM_ENTRIES * sizeof(uint64_t));
31-
static uint16_t xlat_use_count[CONFIG_MAX_XLAT_TABLES];
31+
static int xlat_use_count[CONFIG_MAX_XLAT_TABLES];
3232
static struct k_spinlock xlat_lock;
3333

34+
/* Usage count value range */
35+
#define XLAT_PTE_COUNT_MASK GENMASK(15, 0)
36+
#define XLAT_REF_COUNT_UNIT BIT(16)
37+
3438
/* Returns a reference to a free table */
3539
static uint64_t *new_table(void)
3640
{
@@ -39,9 +43,9 @@ static uint64_t *new_table(void)
3943

4044
/* Look for a free table. */
4145
for (i = 0U; i < CONFIG_MAX_XLAT_TABLES; i++) {
42-
if (xlat_use_count[i] == 0U) {
46+
if (xlat_use_count[i] == 0) {
4347
table = &xlat_tables[i * Ln_XLAT_NUM_ENTRIES];
44-
xlat_use_count[i] = 1U;
48+
xlat_use_count[i] = XLAT_REF_COUNT_UNIT;
4549
MMU_DEBUG("allocating table [%d]%p\n", i, table);
4650
return table;
4751
}
@@ -59,29 +63,43 @@ static inline unsigned int table_index(uint64_t *pte)
5963
return i;
6064
}
6165

62-
/* Makes a table free for reuse. */
63-
static void free_table(uint64_t *table)
66+
/* Adjusts usage count and returns current count. */
67+
static int table_usage(uint64_t *table, int adjustment)
6468
{
6569
unsigned int i = table_index(table);
70+
int prev_count = xlat_use_count[i];
71+
int new_count = prev_count + adjustment;
6672

67-
MMU_DEBUG("freeing table [%d]%p\n", i, table);
68-
__ASSERT(xlat_use_count[i] == 1U, "table still in use");
69-
xlat_use_count[i] = 0U;
73+
if (IS_ENABLED(DUMP_PTE) || new_count == 0) {
74+
MMU_DEBUG("table [%d]%p: usage %#x -> %#x\n", i, table, prev_count, new_count);
75+
}
76+
77+
__ASSERT(new_count >= 0,
78+
"table use count underflow");
79+
__ASSERT(new_count == 0 || new_count >= XLAT_REF_COUNT_UNIT,
80+
"table in use with no reference to it");
81+
__ASSERT((new_count & XLAT_PTE_COUNT_MASK) <= Ln_XLAT_NUM_ENTRIES,
82+
"table PTE count overflow");
83+
84+
xlat_use_count[i] = new_count;
85+
return new_count;
7086
}
7187

72-
/* Adjusts usage count and returns current count. */
73-
static int table_usage(uint64_t *table, int adjustment)
88+
static inline void inc_table_ref(uint64_t *table)
7489
{
75-
unsigned int i = table_index(table);
90+
table_usage(table, XLAT_REF_COUNT_UNIT);
91+
}
7692

77-
xlat_use_count[i] += adjustment;
78-
__ASSERT(xlat_use_count[i] > 0, "usage count underflow");
79-
return xlat_use_count[i];
93+
static inline void dec_table_ref(uint64_t *table)
94+
{
95+
int ref_unit = XLAT_REF_COUNT_UNIT;
96+
97+
table_usage(table, -ref_unit);
8098
}
8199

82100
static inline bool is_table_unused(uint64_t *table)
83101
{
84-
return table_usage(table, 0) == 1;
102+
return (table_usage(table, 0) & XLAT_PTE_COUNT_MASK) == 0;
85103
}
86104

87105
static inline bool is_free_desc(uint64_t desc)
@@ -225,7 +243,6 @@ static uint64_t *expand_to_table(uint64_t *pte, unsigned int level)
225243

226244
/* Link the new table in place of the pte it replaces */
227245
set_pte_table_desc(pte, table, level);
228-
table_usage(table, 1);
229246

230247
return table;
231248
}
@@ -300,7 +317,7 @@ static int set_mapping(struct arm_mmu_ptables *ptables,
300317
/* recursively free unused tables if any */
301318
while (level != BASE_XLAT_LEVEL &&
302319
is_table_unused(pte)) {
303-
free_table(pte);
320+
dec_table_ref(pte);
304321
pte = ptes[--level];
305322
set_pte_block_desc(pte, 0, level);
306323
table_usage(pte, -1);
@@ -347,8 +364,8 @@ static uint64_t *dup_table(uint64_t *src_table, unsigned int level)
347364
}
348365

349366
dst_table[i] = src_table[i];
350-
if (is_table_desc(src_table[i], level)) {
351-
table_usage(pte_desc_table(src_table[i]), 1);
367+
if (is_table_desc(dst_table[i], level)) {
368+
inc_table_ref(pte_desc_table(dst_table[i]));
352369
}
353370
if (!is_free_desc(dst_table[i])) {
354371
table_usage(dst_table, 1);
@@ -388,8 +405,7 @@ static int privatize_table(uint64_t *dst_table, uint64_t *src_table,
388405
return -ENOMEM;
389406
}
390407
set_pte_table_desc(&dst_table[i], dst_subtable, level);
391-
table_usage(dst_subtable, 1);
392-
table_usage(src_subtable, -1);
408+
dec_table_ref(src_subtable);
393409
}
394410

395411
ret = privatize_table(dst_subtable, src_subtable,
@@ -436,15 +452,14 @@ static void discard_table(uint64_t *table, unsigned int level)
436452

437453
for (i = 0U; i < Ln_XLAT_NUM_ENTRIES; i++) {
438454
if (is_table_desc(table[i], level)) {
439-
table_usage(pte_desc_table(table[i]), -1);
440455
discard_table(pte_desc_table(table[i]), level + 1);
456+
dec_table_ref(pte_desc_table(table[i]));
441457
}
442458
if (!is_free_desc(table[i])) {
443459
table[i] = 0U;
444460
table_usage(table, -1);
445461
}
446462
}
447-
free_table(table);
448463
}
449464

450465
static int globalize_table(uint64_t *dst_table, uint64_t *src_table,
@@ -497,15 +512,15 @@ static int globalize_table(uint64_t *dst_table, uint64_t *src_table,
497512
table_usage(dst_table, -1);
498513
}
499514
if (is_table_desc(src_table[i], level)) {
500-
table_usage(pte_desc_table(src_table[i]), 1);
515+
inc_table_ref(pte_desc_table(src_table[i]));
501516
}
502517
dst_table[i] = src_table[i];
503518
debug_show_pte(&dst_table[i], level);
504519

505520
if (old_table) {
506521
/* we can discard the whole branch */
507-
table_usage(old_table, -1);
508522
discard_table(old_table, level + 1);
523+
dec_table_ref(old_table);
509524
}
510525
}
511526

0 commit comments

Comments
 (0)