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

Re: [PATCH v2 39/39] xen/riscv: introduce IMSIC h/w interrupt file attaching to vcpu



> Introduce imsic_vsfile_attach() to initialize the AIA-related state needed
> for a vCPU to have a working guest interrupt file.
> 
> A guest (VS) interrupt file must be mapped to one of a pCPU's
> hardware interrupt files (if they exist), so the pCPU a vCPU will actually
> run on needs to be known first. arch_vcpu_create() is therefore not a
> suitable place to call vcpu_aia_init(), since the pCPU assigned to a
> vCPU can still change before it is first scheduled. To avoid
> reassigning the VS interrupt file id and remapping it to a different
> pCPU's hardware interrupt file, imsic_vsfile_attach() is called from a
> later point in the scheduling path (e.g. continue_new_vcpu()). Since
> it will end up being called from a non-__init context, it is not
> itself marked __init.
> 
> Introduce imsic_update_state() to update a vCPU's guest IMSIC state
> (the guest interrupt file id and the pCPU whose hardware interrupt
> file it is mapped to) as a single consistent unit. This state can be
> read concurrently, e.g. by a future helper that checks whether a
> vCPU has a pending IMSIC interrupt, though no such consumer exists
> yet at this stage, so it is protected by a lock.
I have difficulties to clearly understand the aim of this function. In
fact, if we want to know if a vCPU has a pending interrupts, wouldn't be
enough to check hgeip[guest_file_id]?

In any case, why does updating vCPU's guest IMSIC state would be
required?
> 
> Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
>
> diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c
> index 0782148b72..15b6bfffa9 100644
> --- a/xen/arch/riscv/domain.c
> +++ b/xen/arch/riscv/domain.c
> @@ -155,6 +155,8 @@ static void continue_new_vcpu(struct vcpu *prev)
>          reset_stack_and_jump(idle_loop);
>      else
>      {
> +        imsic_vsfile_attach(current);
> +
>          /*
>           * During a context switch to a new vCPU, interrupts must be disabled
>           * to guarantee that the vCPU's CSR state can be safely restored into
> diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c
> index 374a21ace1..ad638d7485 100644
> --- a/xen/arch/riscv/imsic.c
> +++ b/xen/arch/riscv/imsic.c
> @@ -212,7 +212,14 @@ unsigned int vcpu_guest_file_id(const struct vcpu *v)
>  
>  void imsic_update_state(struct vcpu *v, unsigned int guest_file_id)
>  {
> -    BUG_ON("unimplemented\n");
> +    unsigned long flags;
> +    struct vimsic_state *vimsic_state = v->arch.vimsic_state;
> +    unsigned int cpu = guest_file_id ? v->processor : NR_CPUS;
> +
> +    write_lock_irqsave(&vimsic_state->vsfile_lock, flags);
> +    vimsic_state->guest_file_id = guest_file_id;
> +    vimsic_state->vsfile_cpu = cpu;
> +    write_unlock_irqrestore(&vimsic_state->vsfile_lock, flags);
>  }
>  
>  void __init imsic_ids_local_delivery(bool enable)
> @@ -640,6 +647,16 @@ struct imsic_vsfile_data {
>      struct imsic_mrif *mrif;
>  };
>  
> +/*
> + * Number of 64-bit EIx groups needed to cover all the interrupt identities 
> an
> + * IMSIC interrupt file provides, which are 0 (never valid, but it still
> + * occupies a bit) up to and including imsic_cfg.nr_ids.
> + */
> +static unsigned int imsic_nr_eix(void)
> +{
> +    return DIV_ROUND_UP(imsic_cfg.nr_ids + 1, BITS_PER_TYPE(uint64_t));
> +}
> +
>  /*
>   * Execute func() on the pCPU which owns the IMSIC interrupt file func() is
>   * going to work with.
> @@ -1138,20 +1155,90 @@ static void cf_check imsic_vsfile_local_update(void 
> *data)
>      csr_write(CSR_VSISELECT, old_vsiselect);
>  }
>  
> +/*
> + * Point the vCPU's HSTATUS.VGEIN at the guest interrupt file it has been
> + * given. It is applied to the hart when the vCPU's context is restored.
> + */
> +static void vcpu_set_vgein(struct vcpu *v, unsigned int vsfile_id)
> +{
> +    unsigned long hstatus = vcpu_guest_cpu_user_regs(v)->hstatus;
> +
> +    hstatus &= ~HSTATUS_VGEIN;
> +    hstatus |= MASK_INSR(vsfile_id, HSTATUS_VGEIN);
> +
> +    vcpu_guest_cpu_user_regs(v)->hstatus = hstatus;
> +}
> +
> +/*
> + * Take a h/w guest interrupt file of 'cpu' for the vCPU: zero the file out,
> + * map it into the domain's G-stage at the vCPU's virtual IMSIC page and
> + * record the new location in the per-vCPU IMSIC state.
> + *
> + * HSTATUS.VGEIN is deliberately left alone: the vCPU may be pointed at the
> + * file only when the file already holds the vCPU's interrupt state, which in
> + * the case of imsic_migrate_vcpu() happens only after the old file has been
> + * moved to the new one. Thereby it is up to the caller to call
> + * vcpu_set_vgein() at the right moment.
Nit: would clarify the paragraph here:
  HSTATUS.VGEIN is deliberately left alone: it must only point at a file
  which already holds the vCPU's interrupt state. The file taken here is
  zeroed, so for imsic_migrate_vcpu() VGEIN can only be updated once the
  state of the old file has been copied into it. Thereby it is up to the
  caller to call vcpu_set_vgein() at the right moment.
> + *
> + * Returns the id of the taken interrupt file, or 0 if none could be taken, 
> in
> + * which case the domain is crashed.
> + */
> +static unsigned int imsic_vsfile_acquire(struct vcpu *v, unsigned int cpu)
> +{
> +    struct imsic_vsfile_data vsfile_data = { .nr_eix = imsic_nr_eix() };
> +    unsigned int vsfile_id;
> +    int rc;
> +
> +    vsfile_id = vgein_assign(v);
> +    if ( !vsfile_id )
> +    {
> +        /*
> +         * vgein_assign() returns 0 when no free h/w guest interrupt file is
> +         * available. s/w guest interrupt files aren't supported yet, so such
> +         * a vCPU can't be run.
> +         */
> +        domain_crash(v->domain,
> +                     "%pv: no free h/w guest interrupt file on CPU%u\n",
> +                     v, cpu);
> +        return 0;
> +    }
> +
> +    vsfile_data.hgei = vsfile_id;
> +
> +    /* The file could still hold the state of its previous owner */
> +    imsic_call_on_cpu(cpu, imsic_vsfile_local_clear, &vsfile_data);
> +
> +    rc = imsic_map_guest_file(v, vsfile_id);
> +    if ( rc )
> +    {
> +        vgein_release(v, vsfile_id, cpu);
> +
> +        /* Can't continue w/o correctly mapped IMSIC interrupt file */
> +        domain_crash(v->domain,
> +                     "%pv: failed to map h/w guest interrupt file %u: %d\n",
> +                     v, vsfile_id, rc);
> +        return 0;
> +    }
> +
> +    imsic_update_state(v, vsfile_id);
> +
> +    return vsfile_id;
> +}
> +
>  void imsic_migrate_vcpu(struct vcpu *v)
>  {
> -    unsigned int new_vsfile_hgei;
> +    unsigned int new_vsfile_id;
>      unsigned int new_vsfile_cpu;
> -    unsigned int nr_hw_eix = DIV_ROUND_UP(imsic_cfg.nr_ids + 1,
> -                                          BITS_PER_TYPE(uint64_t));
> -    struct imsic_vsfile_data vsfile_data = {
> -        .nr_eix = nr_hw_eix,
> -    };
> +    unsigned int nr_hw_eix = imsic_nr_eix();
>      struct vimsic_state *imsic_state = v->arch.vimsic_state;
>      unsigned long flags;
>      unsigned int old_vsfile_id;
>      unsigned int old_vsfile_cpu;
>      struct imsic_mrif tmrif = { };
> +    struct imsic_vsfile_data vsfile_data = {
> +        .nr_eix = nr_hw_eix,
> +        .mrif = &tmrif,
> +    };
>  
>      /*
>       * The scheduler can mark a freshly created vCPU's unit as migrated and
> @@ -1189,25 +1276,10 @@ void imsic_migrate_vcpu(struct vcpu *v)
>       */
>      new_vsfile_cpu = v->processor;
>  
> -    new_vsfile_hgei = vgein_assign(v);
> -
> -    /* We don't support SW interrupt files at the moment. */
> -    BUG_ON(!new_vsfile_hgei);
> -
> -    vsfile_data.hgei = new_vsfile_hgei;
> -
> -    /* Zero-out new IMSIC VS-file */
> -    imsic_call_on_cpu(new_vsfile_cpu, imsic_vsfile_local_clear, 
> &vsfile_data);
> -
> -    /* Update G-stage mapping for the new IMSIC VS-file */
> -    if ( imsic_map_guest_file(v, new_vsfile_hgei) )
> -    {
> -        domain_crash(v->domain, "Migration to hw interrupt file failed\n");
> -
> +    /* Zero-out, map and start to use the new IMSIC VS-file */
> +    new_vsfile_id = imsic_vsfile_acquire(v, new_vsfile_cpu);
> +    if ( !new_vsfile_id )
>          return;
> -    }
> -
> -    imsic_update_state(v, new_vsfile_hgei);
>  
>      /*
>       * TODO: Modify the relevant translation tables at all IOMMUs so that 
> MSIs
> @@ -1245,7 +1317,7 @@ void imsic_migrate_vcpu(struct vcpu *v)
>      vgein_release(v, old_vsfile_id, old_vsfile_cpu);
>  
>      /* Restore register state in the new IMSIC VS-file */
> -    vsfile_data.mrif = &tmrif;
> +    vsfile_data.hgei = new_vsfile_id;
Isn't this already done by imsic_vsfile_acquire()? Couldn't we pass a
&vsfile_data in their arg so it could fill it instead of allocating one
in the stack?

-- 
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®.