Introduce tracking of metadata page entries usage and if all of them are
p2m_invalid then free them.
Intermediate P2M page tables are allocated with MEMF_no_owner, so we are free
to repurpose struct page_info fields for them. Since page_info.u.* is not
used for such pages, introduce a used_entries counter in struct page_info
to track how many metadata entries are in use for a given intermediate P2M
page table.
The counter is updated in p2m_set_type() when metadata entries transition
between p2m_invalid and a valid external type. When the last metadata entry
is cleared (used_entries == 0), the associated metadata page is freed and
returned to the P2M pool.
Refactor metadata page freeing into a new helper, p2m_free_metadata_page(),
as the same logic is needed both when tearing down a P2M table and when
all metadata entries become p2m_invalid in p2m_set_type(). As part of this
refactoring, move the declaration of p2m_free_page() earlier to satisfy the
new helper.
Additionally, implement page_set_tlbflush_timestamp() for RISC-V instead of
BUGing, as it is invoked when returning memory to the domheap.
Suggested-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
---
Changes in V7:
- New patch.
---
xen/arch/riscv/include/asm/flushtlb.h | 2 +-
xen/arch/riscv/include/asm/mm.h | 12 ++++++++++
xen/arch/riscv/p2m.c | 32 +++++++++++++++++++++------
3 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/xen/arch/riscv/include/asm/flushtlb.h b/xen/arch/riscv/include/asm/flushtlb.h
index ab32311568..4f64f97570 100644
--- a/xen/arch/riscv/include/asm/flushtlb.h
+++ b/xen/arch/riscv/include/asm/flushtlb.h
@@ -38,7 +38,7 @@ static inline void tlbflush_filter(cpumask_t *mask, uint32_t page_timestamp) {}
static inline void page_set_tlbflush_timestamp(struct page_info *page)
{
- BUG_ON("unimplemented");
+ page->tlbflush_timestamp = tlbflush_current_time();
}
static inline void arch_flush_tlb_mask(const cpumask_t *mask)
diff --git a/xen/arch/riscv/include/asm/mm.h b/xen/arch/riscv/include/asm/mm.h
index 48162f5d65..a005d0247a 100644
--- a/xen/arch/riscv/include/asm/mm.h
+++ b/xen/arch/riscv/include/asm/mm.h
@@ -113,6 +113,18 @@ struct page_info
unsigned long type_info;
} inuse;
+ /* Page is used as an intermediate P2M page table: count_info == 0 */
+ struct {
+ /*
+ * Tracks the number of used entries in the metadata page table.
+ *
+ * If used_entries == 0, then `page_info.v.md.pg` can be freed and
+ * returned to the P2M pool.
+ */
+ unsigned long used_entries;
+ } md;
+
+
/* Page is on a free list: ((count_info & PGC_count_mask) == 0). */
union {
struct {
diff --git a/xen/arch/riscv/p2m.c b/xen/arch/riscv/p2m.c
index bbc3d53d01..496e8c394f 100644
--- a/xen/arch/riscv/p2m.c
+++ b/xen/arch/riscv/p2m.c
@@ -51,6 +51,18 @@ static struct gstage_mode_desc __ro_after_init max_gstage_mode = {
.name = "Bare",
};
+static void p2m_free_page(struct p2m_domain *p2m, struct page_info *pg);
+
+static inline void p2m_free_metadata_page(struct p2m_domain *p2m,
+ struct page_info **md_pg)
+{
+ if ( *md_pg )
+ {
+ p2m_free_page(p2m, *md_pg);
+ *md_pg = NULL;
+ }
+}
+
unsigned char get_max_supported_mode(void)
{
return max_gstage_mode.mode;
@@ -455,16 +467,27 @@ static void p2m_set_type(pte_t *pte, p2m_type_t t,
if ( t >= p2m_first_external )
{
+ if ( metadata[ctx->index].type == p2m_invalid )
+ ctx->pt_page->u.md.used_entries++;
+
metadata[ctx->index].type = t;
t = p2m_ext_storage;
}
else if ( metadata )
+ {
+ if ( metadata[ctx->index].type != p2m_invalid )
+ ctx->pt_page->u.md.used_entries--;
+
metadata[ctx->index].type = p2m_invalid;
+ }
pte->pte |= MASK_INSR(t, P2M_TYPE_PTE_BITS_MASK);
unmap_domain_page(metadata);
+
+ if ( *md_pg && !ctx->pt_page->u.md.used_entries )
+ p2m_free_metadata_page(ctx->p2m, md_pg);
}
/*
@@ -631,18 +654,13 @@ static pte_t page_to_p2m_table(const struct page_info *page)
return p2m_pte_from_mfn(page_to_mfn(page), p2m_invalid, NULL);
}
-static void p2m_free_page(struct p2m_domain *p2m, struct page_info *pg);
-
/*
* Free page table's page and metadata page linked to page table's page.
*/
static void p2m_free_table(struct p2m_domain *p2m, struct page_info *tbl_pg)
{
- if ( tbl_pg->v.md.pg )
- {
- p2m_free_page(p2m, tbl_pg->v.md.pg);
- tbl_pg->v.md.pg = NULL;
- }
+ p2m_free_metadata_page(p2m, &tbl_pg->v.md.pg);
+
p2m_free_page(p2m, tbl_pg);
}
--
2.52.0