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 <[email protected]> Signed-off-by: Oleksii Kurochko <[email protected]> Acked-by: Jan Beulich <[email protected]> --- Changes in V9: - Add Acked-by: Jan Beulich <[email protected]>. --- Changes in V8: - 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 ab32311568ac..4f64f9757058 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 48162f5d65cd..a005d0247a6f 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 24dd07165bd1..a6e4c01b873d 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; @@ -450,16 +462,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); } /* @@ -626,18 +649,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
