[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v3 11/39] xen/riscv: implement vCPU context switching



> Implement context_switch() and the helpers it needs: save/restore of
> H/VS CSRs, virtual timer and P2M context, and __context_switch() in
> assembly, which switches Xen's own callee-saved state (and thereby the
> stack) from prev to next. The virtual interrupt controller state isn't
> switched here.
> 
> Add offsets of struct arch_vcpu's xen_saved_context to asm-offsets.c for
> use by __context_switch().
> 
> henvcfg, htimedelta and vsie are 64-bit on both RV32 and RV64, so store
> them as uint64_t and access them with csr_{read,write}64(). For that
> purpose introduce csr_read64().
> 
> The VMID has to be claimed on the context switch rather than on guest
> entry: once p2m_ctxt_switch_to() writes HGATP, speculation can populate
> G-stage entries under whatever VMID is written there. This is done by
> p2m_vmid_switch_to(), called right before the HGATP write:
> 
>  - VMIDs are a per-hart resource, so the (generation, vmid) pair of a
>    vCPU which last ran on another hart is invalidated before a VMID is
>    claimed for it. The local flush for a wrapped generation moves along
>    with the claim.
>  - Nothing is switched in for the idle vCPU, so HGATP keeps pointing at
>    the p2m of the domain which ran on the hart last. Track that domain
>    per hart (hgatp_owner) and keep the hart in its dirty_cpumask until a
>    vCPU of another domain is switched in: p2m_tlb_flush() then goes on
>    reaching the hart, and a domain which idles between two runs on the
>    same hart keeps its VMIDs.
>  - When the owner changes, the hart leaves the old owner's dirty_cpumask
>    while its TLB may still hold that domain's G-stage translations, so it
>    is moved to a new VMID generation, which makes none of them reachable
>    again. It joins the new owner's dirty_cpumask before HGATP is written;
>    a full barrier there, paired with one in p2m_tlb_flush(), guarantees
>    that a concurrent P2M change either reaches the hart with an
>    HFENCE.GVMA or is visible to it before it walks the p2m.
> 
> That leaves p2m_handle_vmenter() with nothing to do, so drop it together
> with its call from check_for_pcpu_work(): a VMID is only ever invalidated
> while its vCPU isn't running, and p2m_tlb_flush() drops stale entries
> with a remote HFENCE.GVMA rather than by retiring VMIDs. Unlike
> p2m_handle_vmenter(), HGATP is written unconditionally, as it holds the
> G-stage root too, which on a context switch belongs to another domain.
> 
> While at it, fix the inclusion order of headers in asm-offsets.c: Xen's
> headers go first, then arch specific ones.
> 
> Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
> ---
> Changes in v3:
>  - Update the commit message.
>  - Introduce csr_read64(), as vsie is 64-bit on RV32 too, i.e. has to be
>    accessed as the vsie/vsieh pair there.
>  - Make vsie declared as uint64_t instead of register_t.
>  - Rename the parameter of save_csr_regs() to p and of restore_csr_regs()
>    to n, to match their callers ctxt_switch_from()/ctxt_switch_to() and the
>    rest of the context switch helpers (p2m_ctxt_switch_{from,to}(),
>    vtimer_ctxt_switch_{from,to}()).
>  - s/save_csr_regs/csr_regs_ctxt_switch_from/
>  - s/restore_csr_regs/csr_regs_ctxt_switch_to/
>  - Drop the forward declaration of struct vcpu in <asm/system.h>: the
>    return type of __context_switch() already declares the tag at file
>    scope.
>  - Make the next parameter of __context_switch() pointer-to-const, as
>    next's saved context is only read.
>  - Extend the comment above __context_switch(): as ra is switched too, it
>    doesn't return to its caller, but to where next last called it from
>    (next's own context_switch(), or continue_new_vcpu() for a vCPU which
>    has never run).
>  - Call vtimer_ctxt_switch_to() after csr_regs_ctxt_switch_to(), so that
>    ctxt_switch_to() restores state in the reverse order of
>    ctxt_switch_from() saving it.
>  - Rework p2m's VMID handling in the context switch: track the domain
>    whose p2m HGATP points at in a per-CPU hgatp_owner, so a pass through
>    idle no longer drops the hart from that domain's dirty_cpumask nor
>    burns a VMID generation; move the dirty_cpumask update and the
>    invalidation of a VMID brought from another hart from schedule_tail()
>    and ctxt_switch_to() to p2m_ctxt_switch_to(), next to the VMID claim;
>    add the barriers ordering the dirty_cpumask update against
>    p2m_tlb_flush().
>  - Initialise arch_vcpu.last_cpu to CPU_NONE instead of NR_CPUS.
>  - Mark prev's dirty_cpu clean before ctxt_switch_to() and set current's
>    after it.
> ---
> Changes in v2:
>  - New patch.
> ---
> ---
>  xen/arch/riscv/domain.c              | 127 ++++++++++++++++++++++
>  xen/arch/riscv/entry.S               |  48 ++++++++
>  xen/arch/riscv/include/asm/csr.h     |  13 +++
>  xen/arch/riscv/include/asm/domain.h  |  15 ++-
>  xen/arch/riscv/include/asm/p2m.h     |   1 -
>  xen/arch/riscv/include/asm/system.h  |   2 +
>  xen/arch/riscv/p2m.c                 | 157 ++++++++++++++++++---------
>  xen/arch/riscv/riscv64/asm-offsets.c |  19 +++-
>  xen/arch/riscv/stubs.c               |   5 -
>  xen/arch/riscv/traps.c               |   2 -
>  10 files changed, 330 insertions(+), 59 deletions(-)
>
> diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c
> index fd62296356..f80643e2d8 100644
> --- a/xen/arch/riscv/domain.c
> +++ b/xen/arch/riscv/domain.c
> @@ -11,6 +11,7 @@
>  #include <asm/bitops.h>
>  #include <asm/cpufeature.h>
>  #include <asm/csr.h>
> +#include <asm/current.h>
>  #include <asm/intc.h>
>  #include <asm/mmio.h>
>  #include <asm/riscv_encoding.h>
> @@ -152,6 +153,8 @@ int arch_vcpu_create(struct vcpu *v)
>      if ( is_idle_vcpu(v) )
>          return 0;
>  
> +    v->arch.last_cpu = CPU_NONE;
> +
>      vcpu_csr_init(v);
>  
>      if ( (rc = vcpu_vtimer_init(v)) )
> @@ -323,6 +326,130 @@ int arch_domain_create(struct domain *d,
>      return rc;
>  }
>  
> +static void csr_regs_ctxt_switch_from(struct vcpu *p)
> +{
> +    /*
> +     * There is no need to save these CSRs as only hypervisor writes them in
> +     * csr_regs_ctxt_switch_to() and guest can't access them so they 
> shouldn't
> +     * be stored here. Keep them commented here just for symmetry with the
> +     * restore CSRs register part.
> +     *
> +     * p->arch.hedeleg = csr_read(CSR_HEDELEG);
> +     * p->arch.hideleg = csr_read(CSR_HIDELEG);
> +     * p->arch.henvcfg = csr_read64(CSR_HENVCFG);
> +     * p->arch.hcounteren = csr_read(CSR_HCOUNTEREN);
> +     * p->arch.htimedelta = csr_read64(CSR_HTIMEDELTA);
> +     *
> +     * if ( riscv_isa_extension_available(NULL, RISCV_ISA_EXT_smstateen) )
> +     *     p->arch.hstateen0 = csr_read(CSR_HSTATEEN0);
> +     */
> +
> +    p->arch.hvip = csr_read(CSR_HVIP);
> +
> +    p->arch.vsstatus = csr_read(CSR_VSSTATUS);
> +    p->arch.vsie = csr_read64(CSR_VSIE);
> +    p->arch.vstvec = csr_read(CSR_VSTVEC);
> +    p->arch.vsscratch = csr_read(CSR_VSSCRATCH);
> +    p->arch.vscause = csr_read(CSR_VSCAUSE);
> +    p->arch.vstval = csr_read(CSR_VSTVAL);
> +    p->arch.vsepc = csr_read(CSR_VSEPC);
> +}
> +
> +static void csr_regs_ctxt_switch_to(struct vcpu *n)
> +{
> +    csr_write(CSR_HEDELEG, n->arch.hedeleg);
> +    csr_write(CSR_HIDELEG, n->arch.hideleg);
> +    csr_write(CSR_HVIP, n->arch.hvip);
> +    csr_write64(CSR_HENVCFG, n->arch.henvcfg);
> +    csr_write(CSR_HCOUNTEREN, n->arch.hcounteren);
> +    csr_write64(CSR_HTIMEDELTA, n->arch.htimedelta);
> +
> +    if ( riscv_isa_extension_available(NULL, RISCV_ISA_EXT_smstateen) )
> +        csr_write(CSR_HSTATEEN0, n->arch.hstateen0);
> +
> +    csr_write(CSR_VSSTATUS, n->arch.vsstatus);
> +    csr_write64(CSR_VSIE, n->arch.vsie);
> +    csr_write(CSR_VSTVEC, n->arch.vstvec);
> +    csr_write(CSR_VSSCRATCH, n->arch.vsscratch);
> +    csr_write(CSR_VSCAUSE, n->arch.vscause);
> +    csr_write(CSR_VSTVAL, n->arch.vstval);
> +    csr_write(CSR_VSEPC, n->arch.vsepc);
> +}
> +
> +static void ctxt_switch_from(struct vcpu *p)
> +{
> +    /*
> +     * When the idle VCPU is running, Xen will always stay in hypervisor
> +     * mode.
> +     * Therefore we don't need to save the context of an idle VCPU.
> +     */
> +    if ( is_idle_vcpu(p) )
> +        return;
> +
> +    p2m_ctxt_switch_from(p);
> +
> +    vtimer_ctxt_switch_from(p);
> +
> +    csr_regs_ctxt_switch_from(p);
> +}
> +
> +static void ctxt_switch_to(struct vcpu *n)
> +{
> +    /*
> +     * When the idle VCPU is running, Xen will always stay in hypervisor
> +     * mode.
> +     * Therefore we don't need to restore the context of an idle VCPU.
> +     */
> +    if ( is_idle_vcpu(n) )
> +        return;
> +
> +    csr_regs_ctxt_switch_to(n);
> +
> +    vtimer_ctxt_switch_to(n);
> +
> +    p2m_ctxt_switch_to(n);
> +}
> +
> +static void schedule_tail(struct vcpu *prev)
> +{
> +    unsigned int cpu = smp_processor_id();
> +
> +    ASSERT(prev != current);
> +
> +    ctxt_switch_from(prev);
> +
> +    write_atomic(&prev->dirty_cpu, VCPU_CPU_CLEAN);
> +
> +    ctxt_switch_to(current);
> +
> +    write_atomic(&current->dirty_cpu, cpu);
> +
> +    current->arch.last_cpu = cpu;
> +
> +    /*
> +     * sched_context_switched() internally uses a spinlock,
> +     * which requires interrupts to be enabled.
> +     */
> +    local_irq_enable();
> +
> +    sched_context_switched(prev, current);
> +}
> +
> +void context_switch(struct vcpu *prev, struct vcpu *next)
> +{
> +    ASSERT(local_irq_is_enabled());
> +    ASSERT(prev != next);
> +    ASSERT(!vcpu_cpu_dirty(next));
> +
> +    local_irq_disable();
> +
> +    set_current(next);
> +
> +    prev = __context_switch(prev, next);
> +
> +    schedule_tail(prev);
> +}
> +
>  static void __init __maybe_unused build_assertions(void)
>  {
>      /*
> diff --git a/xen/arch/riscv/entry.S b/xen/arch/riscv/entry.S
> index 202a35fb03..017fbb0626 100644
> --- a/xen/arch/riscv/entry.S
> +++ b/xen/arch/riscv/entry.S
> @@ -99,3 +99,51 @@ restore_registers:
>  
>          sret
>  END(handle_trap)
> +
> +/*
> + * struct vcpu *__context_switch(struct vcpu *prev, const struct vcpu *next)
> + *
> + * This is called on prev's stack, and returns on next's. As ra is
> + * switched too, it doesn't return to its caller: it returns to where
> + * next last called it from, i.e. into next's own context_switch(), or,
> + * for a vCPU which has never run, to continue_new_vcpu() with an empty
> + * stack.
> + *
> + * a0 - prev
> + * a1 - next
> + *
> + * Returns prev in a0
> + */
> +FUNC(__context_switch)
> +        REG_S   s0, VCPU_XEN_SAVED_CONTEXT_S0(a0)
> +        REG_S   s1, VCPU_XEN_SAVED_CONTEXT_S1(a0)
> +        REG_S   s2, VCPU_XEN_SAVED_CONTEXT_S2(a0)
> +        REG_S   s3, VCPU_XEN_SAVED_CONTEXT_S3(a0)
> +        REG_S   s4, VCPU_XEN_SAVED_CONTEXT_S4(a0)
> +        REG_S   s5, VCPU_XEN_SAVED_CONTEXT_S5(a0)
> +        REG_S   s6, VCPU_XEN_SAVED_CONTEXT_S6(a0)
> +        REG_S   s7, VCPU_XEN_SAVED_CONTEXT_S7(a0)
> +        REG_S   s8, VCPU_XEN_SAVED_CONTEXT_S8(a0)
> +        REG_S   s9, VCPU_XEN_SAVED_CONTEXT_S9(a0)
> +        REG_S   s10, VCPU_XEN_SAVED_CONTEXT_S10(a0)
> +        REG_S   s11, VCPU_XEN_SAVED_CONTEXT_S11(a0)
> +        REG_S   sp, VCPU_XEN_SAVED_CONTEXT_SP(a0)
> +        REG_S   ra, VCPU_XEN_SAVED_CONTEXT_RA(a0)
> +
> +        REG_L   s0, VCPU_XEN_SAVED_CONTEXT_S0(a1)
> +        REG_L   s1, VCPU_XEN_SAVED_CONTEXT_S1(a1)
> +        REG_L   s2, VCPU_XEN_SAVED_CONTEXT_S2(a1)
> +        REG_L   s3, VCPU_XEN_SAVED_CONTEXT_S3(a1)
> +        REG_L   s4, VCPU_XEN_SAVED_CONTEXT_S4(a1)
> +        REG_L   s5, VCPU_XEN_SAVED_CONTEXT_S5(a1)
> +        REG_L   s6, VCPU_XEN_SAVED_CONTEXT_S6(a1)
> +        REG_L   s7, VCPU_XEN_SAVED_CONTEXT_S7(a1)
> +        REG_L   s8, VCPU_XEN_SAVED_CONTEXT_S8(a1)
> +        REG_L   s9, VCPU_XEN_SAVED_CONTEXT_S9(a1)
> +        REG_L   s10, VCPU_XEN_SAVED_CONTEXT_S10(a1)
> +        REG_L   s11, VCPU_XEN_SAVED_CONTEXT_S11(a1)
> +        REG_L   sp, VCPU_XEN_SAVED_CONTEXT_SP(a1)
> +        REG_L   ra, VCPU_XEN_SAVED_CONTEXT_RA(a1)
> +
> +        ret
> +END(__context_switch)
> diff --git a/xen/arch/riscv/include/asm/csr.h 
> b/xen/arch/riscv/include/asm/csr.h
> index a5cb24c863..aad82c6a6d 100644
> --- a/xen/arch/riscv/include/asm/csr.h
> +++ b/xen/arch/riscv/include/asm/csr.h
> @@ -40,6 +40,13 @@
>      csr_write(csr ## H, v_ >> 32);  \
>  })
>  
> +#define csr_read64(csr)                 \
> +({                                      \
> +    uint64_t v_ = csr_read(csr ## H);   \
This violates Misra 20.12 rule (docs/misra/rules.rst) as we use macro
parameter `csr` as an operand of ##, while it could be expanded as a
macro (e.g. csr_read64(CSR_HENVCFG)).
> +                                        \
> +    (v_ << 32) | csr_read(csr);         \
> +})
> +
>  /*
>   * The two halves are read by separate instructions, so a CSR which hardware
>   * increments can carry from the low half into the high one in between,
> @@ -72,6 +79,12 @@
>      (void)csr ## H;                 \
>      csr_read(csr);                  \
>  })
> +
> +#define csr_read64(csr)             \
> +({                                  \
> +    (void)csr ## H;                 \
Same as above.
> +    csr_read(csr);                  \
> +})
>  #endif
>  
>  #define csr_swap(csr, val)                                      \
> diff --git a/xen/arch/riscv/include/asm/domain.h 
> b/xen/arch/riscv/include/asm/domain.h
> index 15e8fa1968..77ad888a2d 100644
> --- a/xen/arch/riscv/include/asm/domain.h
> +++ b/xen/arch/riscv/include/asm/domain.h
> @@ -29,6 +29,11 @@ struct arch_vcpu_io {
>  struct arch_vcpu {
>      struct vcpu_vmid vmid;
>  
> +    /*
> +     * The last CPU this vCPU ran on. Initialised to CPU_NONE.
> +     */
> +    unsigned int last_cpu;
> +
>      /*
>       * Callee saved registers for Xen's state used to switch from
>       * prev's stack to the next's stack during context switch.
> @@ -60,11 +65,19 @@ struct arch_vcpu {
>      register_t hcounteren;
>      register_t hedeleg;
>      register_t hideleg;
> -    register_t henvcfg;
> +    uint64_t   henvcfg;
>      register_t hstateen0;
> +    uint64_t   htimedelta;
>      register_t hvip;
>  
>      register_t vsatp;
> +    register_t vscause;
> +    register_t vsepc;
> +    uint64_t   vsie;
> +    register_t vsscratch;
> +    register_t vsstatus;
> +    register_t vstval;
> +    register_t vstvec;
>  
>      /*
>       * VCPU interrupts
> diff --git a/xen/arch/riscv/include/asm/p2m.h 
> b/xen/arch/riscv/include/asm/p2m.h
> index 0d1dace1a0..9edf78377e 100644
> --- a/xen/arch/riscv/include/asm/p2m.h
> +++ b/xen/arch/riscv/include/asm/p2m.h
> @@ -262,7 +262,6 @@ struct page_info *p2m_get_page_from_gfn(struct p2m_domain 
> *p2m, gfn_t gfn,
>  
>  void p2m_ctxt_switch_from(struct vcpu *p);
>  void p2m_ctxt_switch_to(struct vcpu *n);
> -void p2m_handle_vmenter(void);
>  
>  #endif /* ASM__RISCV__P2M_H */
>  
> diff --git a/xen/arch/riscv/include/asm/system.h 
> b/xen/arch/riscv/include/asm/system.h
> index f33af64fd2..d350b9c959 100644
> --- a/xen/arch/riscv/include/asm/system.h
> +++ b/xen/arch/riscv/include/asm/system.h
> @@ -76,6 +76,8 @@ static inline bool local_irq_is_enabled(void)
>  
>  #define arch_fetch_and_add(x, v) __sync_fetch_and_add(x, v)
>  
> +struct vcpu *__context_switch(struct vcpu *prev, const struct vcpu *next);
> +
>  #endif /* __ASSEMBLER__ */
>  
>  #endif /* ASM__RISCV__SYSTEM_H */
> diff --git a/xen/arch/riscv/p2m.c b/xen/arch/riscv/p2m.c
> index de25607247..02e7f6364f 100644
> --- a/xen/arch/riscv/p2m.c
> +++ b/xen/arch/riscv/p2m.c
> @@ -243,6 +243,15 @@ static void p2m_tlb_flush(struct p2m_domain *p2m)
>  
>      p2m->need_flush = false;
>  
> +    /*
> +     * Order the p2m updates above against the read of dirty_cpumask below,
> +     * pairing with the barrier in p2m_vmid_switch_to(). Either that hart is
> +     * seen here and gets an HFENCE.GVMA, or it adds itself to the mask
> +     * afterwards, in which case it starts walking this p2m only once the
> +     * updates are visible to it.
> +     */
> +    smp_mb();
> +
>      sbi_remote_hfence_gvma(d->dirty_cpumask, 0, 0);
>  }
>  
> @@ -1504,16 +1513,110 @@ void p2m_ctxt_switch_from(struct vcpu *p)
>       * VMID, world-switch code should zero vsatp, then swap hgatp, then
>       * finally write the new vsatp value what will be done in
>       * p2m_ctxt_switch_to().
> -     * Note, that also HGATP update could happen in p2m_handle_vmenter().
>       */
>      p->arch.vsatp = csr_swap(CSR_VSATP, 0);
>  
>      /*
> -     * Nothing to do with HGATP as it will be update in p2m_ctxt_switch_to()
> -     * or/and in p2m_handle_vmenter().
> +     * Nothing to do with HGATP as it will be updated in
> +     * p2m_ctxt_switch_to().
>       */
>  }
>  
> +/*
> + * Domain whose p2m this hart's HGATP points at. ctxt_switch_to() bails out
> + * early for the idle vCPU, so HGATP survives a pass through idle and keeps
> + * pointing at the domain which ran here last. That domain, rather than the
> + * one the scheduler switched away from, is what owns this hart's G-stage
> + * translations. The hart also keeps its place in the owner's dirty_cpumask:
> + * p2m_tlb_flush() goes on reaching it, and a domain which idles between two
> + * runs on the same hart keeps its VMIDs.
> + *
> + * TODO: nothing resets hgatp_owner when its domain is destroyed, so a hart
> + *       which stays idle from the domain's last run until it is freed is
> + *       left with a dangling pointer. Once domain_relinquish_resources()
> + *       tears down the p2m, it has to reset hgatp_owner of every hart still
> + *       pointing at the domain (cmpxchg() against d), i.e. before the RCU
> + *       grace period of domain_destroy() and so before the domain is freed.
> + */
> +static DEFINE_PER_CPU(struct domain *, hgatp_owner);
> +
> +/*
> + * Update this hart's place in the dirty_cpumask of the domains involved and
> + * claim a VMID for n. Must be called before HGATP is pointed at n's p2m, as
> + * it is written with the VMID claimed here.
> + */
> +static void p2m_vmid_switch_to(struct vcpu *n)
> +{
> +    unsigned int cpu = smp_processor_id();
> +    struct domain *owner = this_cpu(hgatp_owner);
> +    bool need_flush;
> +
> +    ASSERT(!is_idle_vcpu(n));
> +
> +    if ( owner != n->domain )
> +    {
> +        /*
> +         * Once this hart drops out of the owner's dirty_cpumask it stops
> +         * being a target of p2m_tlb_flush(), while its TLB may still hold
> +         * G-stage translations of that domain: none of the vCPUs of that
> +         * domain which ran here has had its VMID invalidated. Move the hart
> +         * to a new VMID generation so that none of them can be reached
> +         * again.
> +         *
> +         * This has to precede the VMID claim below, so that n gets a VMID of
> +         * the new generation.
> +         */
> +        if ( owner )
> +        {
> +            vmid_flush_hart();
> +
> +            cpumask_clear_cpu(cpu, owner->dirty_cpumask);
> +        }
> +
> +        /*
> +         * Mark this hart in the incoming domain's dirty_cpumask before HGATP
> +         * is pointed at its p2m: from that write on the hart may cache the
> +         * p2m's translations, so p2m_tlb_flush() must already reach it.
> +         */
> +        cpumask_set_cpu(cpu, n->domain->dirty_cpumask);
> +
> +        /*
> +         * Pairs with the barrier in p2m_tlb_flush(). cpumask_set_cpu() is an
> +         * unordered AMO on RISC-V, so without this a concurrent flusher 
> could
> +         * read the mask without this hart in it while this hart is already
> +         * walking the p2m it is about to be pointed at.
> +         */
> +        smp_mb();
> +
> +        this_cpu(hgatp_owner) = n->domain;
> +    }
> +
> +    /*
> +     * A VMID is meaningful only on the hart whose pool issued it: 
> generations
> +     * are per-hart counters which all start at 1 and advance independently,
> +     * so the pair a vCPU brings from another hart may match this hart's
> +     * generation by coincidence, leaving the vCPU under a VMID which is live
> +     * here for someone else.
> +     */
So if I understand correctly, we are setting generation to 0 to mark the
vCPU that it is coming from other hart right?
Nit: If it's the case I may simplify the comment to:
      /*
       * VMIDs and generations are per hart, so a pair issued on another hart
       * may match this hart's generation by coincidence. Reset the generation
       * to 0, which no hart ever uses, so that a fresh VMID is claimed from
       * this hart's pool.
       */

> +    if ( n->arch.last_cpu != cpu )
> +        vmid_flush_vcpu(n);
>
Nit: I had to read this a couple of times to get why the guest entry is too
late. "makes HGATP live" doesn't say what happens once it is: if it's
because of speculation filling the TLB with G-stage entries tagged with
the VMID in HGATP, like you explained in the commit message then it may
worth spell out:

      /*
       * Claim the VMID before the caller writes HGATP rather than on guest
       * entry: from that write on, speculation may fill the TLB with this
       * domain's G-stage entries tagged with that VMID. A stale VMID may
       * already have been re-issued to a vCPU of another domain, which would
       * then hit these entries.
       */

-- 
Baptiste Le Duc <baptiste.le-duc@xxxxxxxxxx>



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.