mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/riscv: Implement p2m_free_subtree() and related helpers
This patch introduces a working implementation of p2m_free_subtree() for RISC-V
based on ARM's implementation of p2m_free_entry(), enabling proper cleanup
of page table entries in the P2M (physical-to-machine) mapping.
Only few things are changed:
- Introduce and use p2m_get_type() to get a type of p2m entry as
RISC-V's PTE doesn't have enough space to store all necessary types so
a type is stored outside PTE. But, at the moment, handle only types
which fit into PTE's bits.
Key additions include:
- p2m_free_subtree(): Recursively frees page table entries at all levels. It
handles both regular and superpage mappings and ensures that TLB entries
are flushed before freeing intermediate tables.
- p2m_put_page() and helpers:
- p2m_put_4k_page(): Clears GFN from xenheap pages if applicable.
- p2m_put_2m_superpage(): Releases foreign page references in a 2MB
superpage.
- p2m_get_type(): Extracts the stored p2m_type from the PTE bits.
- p2m_free_page(): Returns a page to a domain's freelist.
- Introduce p2m_is_foreign() and connected to it things.
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
fcd6755606
commit
6f7881e331
6 files changed
+176
-8
No files matched your search
@@ -41,8 +41,10 @@ static inline void page_set_tlbflush_timestamp(struct page_info *page)
|
||||
BUG_ON("unimplemented");
|
||||
}
|
||||
|
||||
/* Flush specified CPUs' TLBs */
|
||||
void arch_flush_tlb_mask(const cpumask_t *mask);
|
||||
static inline void arch_flush_tlb_mask(const cpumask_t *mask)
|
||||
{
|
||||
sbi_remote_hfence_gvma(mask, 0, 0);
|
||||
}
|
||||
|
||||
#endif /* ASM__RISCV__FLUSHTLB_H */
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ typedef enum {
|
||||
p2m_mmio_direct_io, /* Read/write mapping of genuine Device MMIO area,
|
||||
PTE_PBMT_IO will be used for such mappings */
|
||||
p2m_ext_storage, /* Following types'll be stored outsude PTE bits: */
|
||||
p2m_map_foreign_rw, /* Read/write RAM pages from foreign domain */
|
||||
p2m_map_foreign_ro, /* Read-only RAM pages from foreign domain */
|
||||
|
||||
/* Sentinel — not a real type, just a marker for comparison */
|
||||
p2m_first_external = p2m_ext_storage,
|
||||
@@ -121,15 +123,28 @@ static inline p2m_type_t arch_dt_passthrough_p2m_type(void)
|
||||
return p2m_mmio_direct_io;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bits 8 and 9 are reserved for use by supervisor software;
|
||||
* the implementation shall ignore this field.
|
||||
* We are going to use to save in these bits frequently used types to avoid
|
||||
* get/set of a type from radix tree.
|
||||
*/
|
||||
#define P2M_TYPE_PTE_BITS_MASK PTE_RSW
|
||||
|
||||
/* We use bitmaps and mask to handle groups of types */
|
||||
#define p2m_to_mask(t) BIT(t, UL)
|
||||
|
||||
/* RAM types, which map to real machine frames */
|
||||
#define P2M_RAM_TYPES (p2m_to_mask(p2m_ram_rw))
|
||||
|
||||
/* Foreign mappings types */
|
||||
#define P2M_FOREIGN_TYPES (p2m_to_mask(p2m_map_foreign_rw) | \
|
||||
p2m_to_mask(p2m_map_foreign_ro))
|
||||
|
||||
/* Useful predicates */
|
||||
#define p2m_is_ram(t) (p2m_to_mask(t) & P2M_RAM_TYPES)
|
||||
#define p2m_is_any_ram(t) (p2m_to_mask(t) & P2M_RAM_TYPES)
|
||||
#define p2m_is_foreign(t) (p2m_to_mask(t) & P2M_FOREIGN_TYPES)
|
||||
|
||||
#include <xen/p2m-common.h>
|
||||
|
||||
|
||||
@@ -13,4 +13,6 @@ int paging_freelist_adjust(struct domain *d, unsigned long pages,
|
||||
int paging_ret_to_domheap(struct domain *d, unsigned int nr_pages);
|
||||
int paging_refill_from_domheap(struct domain *d, unsigned int nr_pages);
|
||||
|
||||
void paging_free_page(struct domain *d, struct page_info *pg);
|
||||
|
||||
#endif /* ASM_RISCV_PAGING_H */
|
||||
+147
-1
@@ -356,6 +356,16 @@ int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static p2m_type_t p2m_get_type(const pte_t pte)
|
||||
{
|
||||
p2m_type_t type = MASK_EXTR(pte.pte, P2M_TYPE_PTE_BITS_MASK);
|
||||
|
||||
if ( type == p2m_ext_storage )
|
||||
panic("unimplemented\n");
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static inline void p2m_write_pte(pte_t *p, pte_t pte, bool clean_cache)
|
||||
{
|
||||
write_pte(p, pte);
|
||||
@@ -412,11 +422,147 @@ static int p2m_next_level(struct p2m_domain *p2m, bool alloc_tbl,
|
||||
return P2M_TABLE_MAP_NONE;
|
||||
}
|
||||
|
||||
static void p2m_put_foreign_page(struct page_info *pg)
|
||||
{
|
||||
/*
|
||||
* It’s safe to call put_page() here because arch_flush_tlb_mask()
|
||||
* will be invoked if the page is reallocated, which will trigger a
|
||||
* flush of the guest TLBs.
|
||||
*/
|
||||
put_page(pg);
|
||||
}
|
||||
|
||||
static void p2m_put_4k_page(mfn_t mfn, p2m_type_t type)
|
||||
{
|
||||
/* TODO: Handle other p2m types */
|
||||
|
||||
if ( p2m_is_foreign(type) )
|
||||
{
|
||||
ASSERT(mfn_valid(mfn));
|
||||
p2m_put_foreign_page(mfn_to_page(mfn));
|
||||
}
|
||||
}
|
||||
|
||||
static void p2m_put_2m_superpage(mfn_t mfn, p2m_type_t type)
|
||||
{
|
||||
struct page_info *pg;
|
||||
unsigned int i;
|
||||
|
||||
/* TODO: Handle other p2m types */
|
||||
if ( !p2m_is_foreign(type) )
|
||||
return;
|
||||
|
||||
ASSERT(mfn_valid(mfn));
|
||||
|
||||
pg = mfn_to_page(mfn);
|
||||
|
||||
/*
|
||||
* PAGETABLE_ENTRIES is used instead of P2M_PAGETABLE_ENTRIES(1) because
|
||||
* they are expected to be identical (this is verified in calc_offset()).
|
||||
* This avoids having to pass p2m_domain here and throughout the call stack
|
||||
* above solely for the sake of one macro.
|
||||
*/
|
||||
for ( i = 0; i < PAGETABLE_ENTRIES; i++, pg++ )
|
||||
p2m_put_foreign_page(pg);
|
||||
}
|
||||
|
||||
static void p2m_put_page(const pte_t pte, unsigned int level, p2m_type_t p2mt)
|
||||
{
|
||||
mfn_t mfn = pte_get_mfn(pte);
|
||||
|
||||
ASSERT(pte_is_valid(pte));
|
||||
|
||||
/*
|
||||
* TODO: Currently we don't handle level 2 super-page, Xen is not
|
||||
* preemptible and therefore some work is needed to handle such
|
||||
* superpages, for which at some point Xen might end up freeing memory
|
||||
* and therefore for such a big mapping it could end up in a very long
|
||||
* operation.
|
||||
*/
|
||||
switch ( level )
|
||||
{
|
||||
case 1:
|
||||
return p2m_put_2m_superpage(mfn, p2mt);
|
||||
|
||||
case 0:
|
||||
return p2m_put_4k_page(mfn, p2mt);
|
||||
|
||||
default:
|
||||
ASSERT_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void p2m_free_page(struct p2m_domain *p2m, struct page_info *pg)
|
||||
{
|
||||
page_list_del(pg, &p2m->pages);
|
||||
|
||||
paging_free_page(p2m->domain, pg);
|
||||
}
|
||||
|
||||
/* Free pte sub-tree behind an entry */
|
||||
static void p2m_free_subtree(struct p2m_domain *p2m,
|
||||
pte_t entry, unsigned int level)
|
||||
{
|
||||
panic("%s: hasn't been implemented yet\n", __func__);
|
||||
unsigned int i;
|
||||
pte_t *table;
|
||||
mfn_t mfn;
|
||||
struct page_info *pg;
|
||||
|
||||
/*
|
||||
* Check if the level is valid: only 4K - 2M - 1G mappings are supported.
|
||||
* To support levels > 2, the implementation of p2m_free_subtree() would
|
||||
* need to be updated, as the current recursive approach could consume
|
||||
* excessive time and memory.
|
||||
*/
|
||||
ASSERT(level <= P2M_MAX_SUPPORTED_LEVEL_MAPPING);
|
||||
|
||||
/* Nothing to do if the entry is invalid. */
|
||||
if ( !pte_is_valid(entry) )
|
||||
return;
|
||||
|
||||
if ( pte_is_mapping(entry) )
|
||||
{
|
||||
p2m_type_t p2mt = p2m_get_type(entry);
|
||||
|
||||
#ifdef CONFIG_IOREQ_SERVER
|
||||
/*
|
||||
* If this gets called then either the entry was replaced by an entry
|
||||
* with a different base (valid case) or the shattering of a superpage
|
||||
* has failed (error case).
|
||||
* So, at worst, the spurious mapcache invalidation might be sent.
|
||||
*/
|
||||
if ( p2m_is_ram(p2mt) &&
|
||||
domain_has_ioreq_server(p2m->domain) )
|
||||
ioreq_request_mapcache_invalidate(p2m->domain);
|
||||
#endif
|
||||
|
||||
p2m_put_page(entry, level, p2mt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
table = map_domain_page(pte_get_mfn(entry));
|
||||
|
||||
for ( i = 0; i < P2M_PAGETABLE_ENTRIES(p2m, level); i++ )
|
||||
p2m_free_subtree(p2m, table[i], level - 1);
|
||||
|
||||
unmap_domain_page(table);
|
||||
|
||||
/*
|
||||
* Make sure all the references in the TLB have been removed before
|
||||
* freing the intermediate page table.
|
||||
* XXX: Should we defer the free of the page table to avoid the
|
||||
* flush?
|
||||
*/
|
||||
p2m_tlb_flush_sync(p2m);
|
||||
|
||||
mfn = pte_get_mfn(entry);
|
||||
ASSERT(mfn_valid(mfn));
|
||||
|
||||
pg = mfn_to_page(mfn);
|
||||
|
||||
p2m_free_page(p2m, pg);
|
||||
}
|
||||
|
||||
/* Insert an entry in the p2m */
|
||||
|
||||
@@ -109,6 +109,14 @@ int paging_ret_to_domheap(struct domain *d, unsigned int nr_pages)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void paging_free_page(struct domain *d, struct page_info *pg)
|
||||
{
|
||||
spin_lock(&d->arch.paging.lock);
|
||||
page_list_add_tail(pg, &d->arch.paging.freelist);
|
||||
d->arch.paging.total_pages++;
|
||||
spin_unlock(&d->arch.paging.lock);
|
||||
}
|
||||
|
||||
/* Domain paging struct initialization. */
|
||||
int paging_domain_init(struct domain *d)
|
||||
{
|
||||
|
||||
@@ -65,11 +65,6 @@ int arch_monitor_domctl_event(struct domain *d,
|
||||
|
||||
/* smp.c */
|
||||
|
||||
void arch_flush_tlb_mask(const cpumask_t *mask)
|
||||
{
|
||||
BUG_ON("unimplemented");
|
||||
}
|
||||
|
||||
void smp_send_event_check_mask(const cpumask_t *mask)
|
||||
{
|
||||
BUG_ON("unimplemented");
|
||||
|
||||