|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v2 03/17] xen/riscv: introduce guest domain's VMID allocation and manegement
On 10.06.2025 15:05, Oleksii Kurochko wrote:
> Implementation is based on Arm code with some minor changes:
> - Re-define INVALID_VMID.
> - Re-define MAX_VMID.
> - Add TLB flushing when VMID is re-used.
>
> Also, as a part of this path structure p2m_domain is introduced with
> vmid member inside it. It is necessary for VMID management functions.
>
> Add a bitmap-based allocator to manage VMID space, supporting up to 127
> VMIDs on RV32 and 16,383 on RV64 platforms, in accordance with the
> architecture's hgatp VMID field (RV32 - 7 bit long, others - 14 bit long).
>
> Reserve the highest VMID as INVALID_VMID to ensure it's not reused.
Why must that VMID not be (re)used? INVALID_VMID can be any value wider
than the hgatp.VMID field.
> --- a/xen/arch/riscv/Makefile
> +++ b/xen/arch/riscv/Makefile
> @@ -6,6 +6,7 @@ obj-y += intc.o
> obj-y += irq.o
> obj-y += mm.o
> obj-y += pt.o
> +obj-y += p2m.o
Nit: Numbers typically sort ahead of letters.
> --- /dev/null
> +++ b/xen/arch/riscv/p2m.c
> @@ -0,0 +1,115 @@
> +#include <xen/bitops.h>
> +#include <xen/lib.h>
> +#include <xen/sched.h>
> +#include <xen/spinlock.h>
> +#include <xen/xvmalloc.h>
> +
> +#include <asm/p2m.h>
> +#include <asm/sbi.h>
> +
> +static spinlock_t vmid_alloc_lock = SPIN_LOCK_UNLOCKED;
> +
> +/*
> + * hgatp's VMID field is 7 or 14 bits. RV64 may support 14-bit VMID.
> + * Using a bitmap here limits us to 127 (2^7 - 1) or 16383 (2^14 - 1)
> + * concurrent domains.
Which is pretty limiting especially in the RV32 case. Hence why we don't
assign a permanent ID to VMs on x86, but rather manage IDs per-CPU (note:
not per-vCPU).
> The bitmap space will be allocated dynamically
> + * based on whether 7 or 14 bit VMIDs are supported.
> + */
> +static unsigned long *vmid_mask;
> +static unsigned long *vmid_flushing_needed;
> +
> +/*
> + * -2 here because:
> + * - -1 is needed to get the maximal possible VMID
I don't follow this part.
> + * - -1 is reserved for beinng used as INVALID_VMID
Whereas for this part - see above.
> + */
> +#ifdef CONFIG_RISCV_32
> +#define MAX_VMID (BIT(7, U) - 2)
> +#else
Better "#elif defined(CONFIG_RISCV_64)"?
> +#define MAX_VMID (BIT(14, U) - 2)
> +#endif
> +
> +/* Reserve the max possible VMID to be INVALID. */
> +#define INVALID_VMID (MAX_VMID + 1)
> +
> +void p2m_vmid_allocator_init(void)
__init
> +{
> + /*
> + * Allocate space for vmid_mask and vmid_flushing_needed
> + * based on INVALID_VMID as it is the max possible VMID which just
> + * was reserved to be INVALID_VMID.
> + */
> + vmid_mask = xvzalloc_array(unsigned long, BITS_TO_LONGS(INVALID_VMID));
> + vmid_flushing_needed =
> + xvzalloc_array(unsigned long, BITS_TO_LONGS(INVALID_VMID));
These both want to use MAX_VMID + 1; there's no logical connection here to
INVALID_VMID.
Furthermore don't you first need to determine how many bits hgatp.VMID actually
implements? The 7 and 14 bits respectively are maximum values only, after all.
VMIDLEN being permitted to be 0, how would you run more than one VM (e.g. Dom0)
on such a system?
> + if ( !vmid_mask || !vmid_flushing_needed )
> + panic("Could not allocate VMID bitmap space or VMID flushing map\n");
> +
> + set_bit(INVALID_VMID, vmid_mask);
If (see above) this is really needed, __set_bit() please.
> +}
> +
> +int p2m_alloc_vmid(struct domain *d)
Looks like this can be static? (p2m_free_vmid() has no caller at all, so
it's not clear what use it is going to be.)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> +
> + int rc, nr;
No need for the blank line between the (few) declarations?
> + spin_lock(&vmid_alloc_lock);
> +
> + nr = find_first_zero_bit(vmid_mask, MAX_VMID);
As per this nr wants to be unsigned int.
> + ASSERT(nr != INVALID_VMID);
> +
> + if ( nr == MAX_VMID )
> + {
> + rc = -EBUSY;
> + printk(XENLOG_ERR "p2m.c: dom%d: VMID pool exhausted\n",
> d->domain_id);
Please use %pd.
> + goto out;
> + }
> +
> + set_bit(nr, vmid_mask);
Since you do this under lock, even here __set_bit() ought to be sufficient.
> + if ( test_bit(p2m->vmid, vmid_flushing_needed) )
> + {
> + clear_bit(p2m->vmid, vmid_flushing_needed);
And __clear_bit() here, or yet better use __test_and_clear_bit() in the if().
> + sbi_remote_hfence_gvma_vmid(d->dirty_cpumask, 0, 0, p2m->vmid);
You're creating d; it cannot possibly have run on any CPU yet. IOW
d->dirty_cpumask will be reliably empty here. I think it would be hard to
avoid issuing the flush to all CPUs here in this scheme.
> + }
> +
> + p2m->vmid = nr;
> +
> + rc = 0;
> +
> +out:
Nit: Style.
> + spin_unlock(&vmid_alloc_lock);
> + return rc;
> +}
> +
> +void p2m_free_vmid(struct domain *d)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> +
> + spin_lock(&vmid_alloc_lock);
> +
> + if ( p2m->vmid != INVALID_VMID )
> + {
> + clear_bit(p2m->vmid, vmid_mask);
> + set_bit(p2m->vmid, vmid_flushing_needed);
Does this scheme really avoid any flushes (except near when the system is
about to go down)?
As to choice of functions - see above.
> + }
> +
> + spin_unlock(&vmid_alloc_lock);
> +}
> +
> +int p2m_init(struct domain *d)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + int rc;
> +
> + p2m->vmid = INVALID_VMID;
Given the absence of callers of p2m_free_vmid() it's also not clear what use
this is.
Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |