[PATCH v3 8/9] KVM: arm-vgic: Add GICD_SPENDSGIR and GICD_CPENDSGIR handlers
Marc Zyngier
marc.zyngier at arm.com
Mon Dec 9 11:49:45 EST 2013
On 2013-11-17 04:30, Christoffer Dall wrote:
> Handle MMIO accesses to the two registers which should support both
> the
> case where the VMs want to read/write either of these registers and
> the
> case where user space reads/writes these registers to do save/restore
> of
> the VGIC state.
>
> Note that the added complexity compared to simple set/clear enable
> registers stems from the bookkeping of source cpu ids. It may be
> possible to change the underlying data structure to simplify the
> complexity, but since this is not in the critical path at all, this
> will
> do.
>
> Also note that reading this register from a live guest will not be
> accurate compared to on hardware, because some state may be living on
> the CPU LRs and the only way to give a consistent read would be to
> force
> stop all the VCPUs and request them to unqueu the LR state onto the
> distributor. Until we have an actual user of live reading this
> register, we can live with the difference.
>
> Signed-off-by: Christoffer Dall <christoffer.dall at linaro.org>
Looks pretty good to me. Small note below, but otherwise:
Acked-by: Marc Zyngier <marc.zyngier at arm.com>
>
> Changelog[v3]:
> - Renamed read/write SGI set/clear functions
> - Rely on unqueuing of interrupts from LRs instead of reading LRs
> directly
> - Deduplicate code
>
> Changelog[v2]:
> - Use struct kvm_exit_mmio accessors for ->data field.
> ---
> virt/kvm/arm/vgic.c | 70
> ++++++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 66 insertions(+), 4 deletions(-)
>
> diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
> index 44c669b..16053eb 100644
> --- a/virt/kvm/arm/vgic.c
> +++ b/virt/kvm/arm/vgic.c
> @@ -655,18 +655,80 @@ static void vgic_unqueue_irqs(struct kvm_vcpu
> *vcpu)
> }
> }
>
> -static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu,
> - struct kvm_exit_mmio *mmio,
> - phys_addr_t offset)
> +/* Handle reads of GICD_CPENDSGIRn and GICD_SPENDSGIRn */
> +static bool read_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
> + struct kvm_exit_mmio *mmio,
> + phys_addr_t offset)
> {
> + struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
> + int sgi;
> + int min_sgi = (offset & ~0x3) * 4;
> + int max_sgi = min_sgi + 3;
> + int vcpu_id = vcpu->vcpu_id;
> + u32 reg = 0;
> +
> + /* Copy source SGIs from distributor side */
> + for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
> + int shift = 8 * (sgi - min_sgi);
> + reg |= (u32)dist->irq_sgi_sources[vcpu_id][sgi] << shift;
> + }
> +
> + mmio_data_write(mmio, ~0, reg);
> return false;
> }
>
> +static bool write_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
> + struct kvm_exit_mmio *mmio,
> + phys_addr_t offset, bool set)
> +{
> + struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
> + int sgi;
> + int min_sgi = (offset & ~0x3) * 4;
> + int max_sgi = min_sgi + 3;
> + int vcpu_id = vcpu->vcpu_id;
> + u32 reg;
> + bool updated = false;
> +
> + reg = mmio_data_read(mmio, ~0);
> +
> + /* Clear pending SGIs on the distributor */
> + for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
> + u8 mask = reg >> (8 * (sgi - min_sgi));
> + if (set) {
> + if ((dist->irq_sgi_sources[vcpu_id][sgi] & mask) != mask)
> + updated = true;
> + dist->irq_sgi_sources[vcpu_id][sgi] |= mask;
> + } else {
> + if (dist->irq_sgi_sources[vcpu_id][sgi] & mask)
> + updated = true;
> + dist->irq_sgi_sources[vcpu_id][sgi] &= ~mask;
> + }
> + }
> +
> + if (updated)
> + vgic_update_state(vcpu->kvm);
So I realize we have that construct everywhere. Surely it'd be worth it
moving the mmio calls to vgic_update_state into vgic_handle_mmio. Or
have I missed something?
> + return updated;
> +}
> +
> static bool handle_mmio_sgi_set(struct kvm_vcpu *vcpu,
> struct kvm_exit_mmio *mmio,
> phys_addr_t offset)
> {
> - return false;
> + if (!mmio->is_write)
> + return read_set_clear_sgi_pend_reg(vcpu, mmio, offset);
> + else
> + return write_set_clear_sgi_pend_reg(vcpu, mmio, offset, true);
> +}
> +
> +static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu,
> + struct kvm_exit_mmio *mmio,
> + phys_addr_t offset)
> +{
> + if (!mmio->is_write)
> + return read_set_clear_sgi_pend_reg(vcpu, mmio, offset);
> + else
> + return write_set_clear_sgi_pend_reg(vcpu, mmio, offset, false);
> }
>
> /*
--
Fast, cheap, reliable. Pick two.
More information about the linux-arm-kernel
mailing list