This series is a joint effort to re-implement KVM's GIC emulation.
While the current implementation is centered around providing
efficient MMIO emulation, the hot path for most guests is actually
the guest entry and exit, which currently is rather costly.
Also the existing emulation has a global distributor lock, which
quickly becomes a bottleneck once the number of VCPUs increases.
Additionally the emulation was originally designed for GICv2, adding
GICv3 ITS emulation support to this proved to be rather painful.
Last, but not least the existing code became less and less
maintainable, with many special cases handled explicitly.
The new implementation is build around a struct vgic_irq data data
structure, which holds all information about a virtual interrupt.
Interruts which should be injected are hold in a per-VCPU list, this
make the entry/exit path much more efficient. Also the new structure
allows to have more fine grained locking - per IRQ and per VCPU -
getting rid of the global distributor lock.
As a result of the new design ITS emulation fits in more nicely, the
respective code will be provided as a follow-up series.
This series implements the same feature set as the existing emulation,
as a goodie we now implement priorities correctly.
To allow an easy transition with good test coverage, but still maintain
stability, both implementations live side by side, selectable via a
Kconfig option. The default is the new implementation.
If this code proves to be reliable, we will later remove the current
implementation with an extra patch set.
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
Cheers,
Andre.
Andre Przywara (26):
KVM: arm/arm64: add missing MMIO data write-back
KVM: arm/arm64: pmu: abstract access to number of SPIs
KVM: arm/arm64: arch_timer: rework VGIC <-> timer interface
KVM: arm/arm64: vgic-new: Add MMIO handling framework
KVM: arm/arm64: vgic-new: Export register access interface
KVM: arm/arm64: vgic-new: Add CTLR, TYPER and IIDR handlers
KVM: arm/arm64: vgic-new: Add ENABLE registers handlers
KVM: arm/arm64: vgic-new: Add PENDING registers handlers
KVM: arm/arm64: vgic-new: Add PRIORITY registers handlers
KVM: arm/arm64: vgic-new: Add ACTIVE registers handlers
KVM: arm/arm64: vgic-new: Add CONFIG registers handlers
KVM: arm/arm64: vgic-new: Add TARGET registers handlers
KVM: arm/arm64: vgic-new: Add SGIR register handler
KVM: arm/arm64: vgic-new: Add SGIPENDR register handlers
KVM: arm/arm64: vgic-new: Add GICv3 emulation framework
KVM: arm/arm64: vgic-new: Add GICv3 CTLR, IIDR, TYPER handlers
KVM: arm/arm64: vgic-new: Add GICv3 redistributor TYPER handler
KVM: arm/arm64: vgic-new: Add GICv3 IDREGS register handler
KVM: arm/arm64: vgic-new: Add GICv3 IROUTER register handlers
KVM: arm/arm64: vgic-new: Add GICv3 SGI system register trap handler
KVM: arm/arm64: vgic-new: Add userland access to VGIC dist registers
KVM: arm/arm64: vgic-new: Add GICH_VMCR accessors
KVM: arm/arm64: vgic-new: Add userland GIC CPU interface access
KVM: arm/arm64: vgic-new: implement mapped IRQ handling
KVM: arm/arm64: vgic-new: Add dummy MSI implementation
KVM: arm/arm64: vgic-new: enable build
Christoffer Dall (5):
KVM: arm/arm64: vgic-new: Add data structure definitions
KVM: arm/arm64: vgic-new: Add acccessor to new struct vgic_irq
instance
KVM: arm/arm64: vgic-new: Implement virtual IRQ injection
KVM: arm/arm64: vgic-new: Add vgic GICv2 change_affinity
KVM: arm/arm64: vgic-new: Add IRQ sorting
Eric Auger (12):
KVM: arm/arm64: vgic-new: Implement kvm_vgic_vcpu_pending_irq
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM device ops registration
KVM: arm/arm64: vgic-new: vgic_kvm_device:
KVM_DEV_ARM_VGIC_GRP_NR_IRQS
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM_DEV_ARM_VGIC_GRP_CTRL
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM_DEV_ARM_VGIC_GRP_ADDR
KVM: arm/arm64: vgic-new: vgic_kvm_device: access to VGIC registers
KVM: arm/arm64: vgic-new: vgic_kvm_device: implement kvm_vgic_addr
KVM: arm/arm64: vgic-new: vgic_init: implement kvm_vgic_hyp_init
KVM: arm/arm64: vgic-new: vgic_init: implement vgic_create
KVM: arm/arm64: vgic-new: vgic_init: implement vgic_init
KVM: arm/arm64: vgic-new: vgic_init: implement map_resources
KVM: arm/arm64: vgic-new: Add vgic_v2/v3_enable
Marc Zyngier (2):
KVM: arm/arm64: vgic-new: Add GICv2 IRQ sync/flush
KVM: arm/arm64: vgic-new: Add GICv3 world switch backend
arch/arm/kvm/Kconfig | 7 +
arch/arm/kvm/Makefile | 10 +
arch/arm/kvm/mmio.c | 2 +-
arch/arm64/kvm/Kconfig | 7 +
arch/arm64/kvm/Makefile | 10 +
include/kvm/arm_vgic.h | 14 +-
include/kvm/vgic/vgic.h | 256 +++++++
virt/kvm/arm/arch_timer.c | 11 +-
virt/kvm/arm/pmu.c | 2 +-
virt/kvm/arm/vgic.c | 18 +-
virt/kvm/arm/vgic/vgic-v2.c | 381 +++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 357 ++++++++++
virt/kvm/arm/vgic/vgic.c | 614 +++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 136 ++++
virt/kvm/arm/vgic/vgic_init.c | 447 ++++++++++++
virt/kvm/arm/vgic/vgic_irqfd.c | 51 ++
virt/kvm/arm/vgic/vgic_kvm_device.c | 522 ++++++++++++++
virt/kvm/arm/vgic/vgic_mmio.c | 1277 +++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic_mmio.h | 47 ++
19 files changed, 4149 insertions(+), 20 deletions(-)
create mode 100644 include/kvm/vgic/vgic.h
create mode 100644 virt/kvm/arm/vgic/vgic-v2.c
create mode 100644 virt/kvm/arm/vgic/vgic-v3.c
create mode 100644 virt/kvm/arm/vgic/vgic.c
create mode 100644 virt/kvm/arm/vgic/vgic.h
create mode 100644 virt/kvm/arm/vgic/vgic_init.c
create mode 100644 virt/kvm/arm/vgic/vgic_irqfd.c
create mode 100644 virt/kvm/arm/vgic/vgic_kvm_device.c
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.c
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.h
--
2.7.3
When the kernel was handling a guest MMIO access internally, we need
to copy the emulation result into the run->mmio structure in order
for the kvm_handle_mmio_return() function to pick it up and inject
the result back into the guest.
Currently the only user of kvm_io_bus for ARM is the VGIC, which did
this copying itself, so this was not causing issues so far.
But with upcoming kvm_io_bus users we need to do the copying here.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
arch/arm/kvm/mmio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Currently the PMU uses a member of the struct vgic_dist directly,
which not only breaks abstraction, but will fail with the new VGIC.
Abstract this access in the VGIC header file.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/arm_vgic.h | 2 ++
virt/kvm/arm/pmu.c | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
Adapt the interface between the virtualized arch timer and the
emulated VGIC to avoid the phys_map when possible.
This prepares the arch timer to go with both the existing VGIC
implementation and the new version later without too many code
changes.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/arm_vgic.h | 7 ++++---
virt/kvm/arm/arch_timer.c | 11 +++++------
virt/kvm/arm/vgic.c | 18 +++++++++---------
3 files changed, 18 insertions(+), 18 deletions(-)
@@ -1522,7 +1522,6 @@ static int vgic_validate_injection(struct kvm_vcpu *vcpu, int irq, int level)}staticintvgic_update_irq_pending(structkvm*kvm,intcpuid,-structirq_phys_map*map,unsignedintirq_num,boollevel){structvgic_dist*dist=&kvm->arch.vgic;
@@ -1661,14 +1660,14 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,if(map)return-EINVAL;-returnvgic_update_irq_pending(kvm,cpuid,NULL,irq_num,level);+returnvgic_update_irq_pending(kvm,cpuid,irq_num,level);}/***kvm_vgic_inject_mapped_irq-InjectaphysicallymappedIRQtothevgic*@kvm:TheVMstructurepointer*@cpuid:TheCPUforPPIs-*@map:Pointertoairq_phys_mapstructuredescribingthemapping+*@virt_irq:ThevirtualIRQtobeinjected*@level:Edge-triggered:true:totriggertheinterrupt*false:toignorethecall*Level-sensitivetrue:raisetheinputsignal
@@ -1679,7 +1678,7 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,*beingHIGHand0beingLOWandalldevicesbeingactive-HIGH.*/intkvm_vgic_inject_mapped_irq(structkvm*kvm,intcpuid,-structirq_phys_map*map,boollevel)+intvirt_irq,boollevel){intret;
@@ -1687,7 +1686,7 @@ int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid,if(ret)returnret;-returnvgic_update_irq_pending(kvm,cpuid,map,map->virt_irq,level);+returnvgic_update_irq_pending(kvm,cpuid,virt_irq,level);}staticirqreturn_tvgic_maintenance_handler(intirq,void*data)
From: Christoffer Dall <redacted>
Add a new header file for the new and improved GIC implementation.
The big change is that we now have a struct vgic_irq per IRQ instead
of spreading all the information over various bitmaps.
We include this new header conditionally from within the old header
file for the time being to avoid touching all the users.
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/arm_vgic.h | 5 ++
include/kvm/vgic/vgic.h | 198 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 203 insertions(+)
create mode 100644 include/kvm/vgic/vgic.h
@@ -0,0 +1,198 @@+/*+*Copyright(C)2015,2016ARMLtd.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram.Ifnot,see<http://www.gnu.org/licenses/>.+*/+#ifndef __ASM_ARM_KVM_VGIC_VGIC_H+#define __ASM_ARM_KVM_VGIC_VGIC_H++#include<linux/kernel.h>+#include<linux/kvm.h>+#include<linux/irqreturn.h>+#include<linux/spinlock.h>+#include<linux/types.h>+#include<kvm/iodev.h>++#define VGIC_V3_MAX_CPUS 255+#define VGIC_V2_MAX_CPUS 8+#define VGIC_NR_IRQS_LEGACY 256+#define VGIC_NR_SGIS 16+#define VGIC_NR_PPIS 16+#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS)+#define VGIC_MAX_PRIVATE (VGIC_NR_PRIVATE_IRQS - 1)+#define VGIC_MAX_SPI 1019+#define VGIC_MAX_RESERVED 1023+#define VGIC_MIN_LPI 8192++enumvgic_type{+VGIC_V2,/* Good ol' GICv2 */+VGIC_V3,/* New fancy GICv3 */+};++/* same for all guests, as depending only on the _host's_ GIC model */+structvgic_global{+/* type of the host GIC */+enumvgic_typetype;++/* Physical address of vgic virtual cpu interface */+phys_addr_tvcpu_base;++/* virtual control interface mapping */+void__iomem*vctrl_base;++/* Number of implemented list registers */+intnr_lr;++/* Maintenance IRQ number */+unsignedintmaint_irq;++/* maximum number of VCPUs allowed (GICv2 limits us to 8) */+intmax_gic_vcpus;++/* Only needed for the legacy KVM_CREATE_IRQCHIP */+boolcan_emulate_gicv2;+};++externstructvgic_globalkvm_vgic_global_state;++#define VGIC_V2_MAX_LRS (1 << 6)+#define VGIC_V3_MAX_LRS 16+#define VGIC_V3_LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)++enumvgic_irq_config{+VGIC_CONFIG_EDGE=0,+VGIC_CONFIG_LEVEL+};++structvgic_irq{+spinlock_tirq_lock;/* Protects the content of the struct */+structlist_headap_list;++structkvm_vcpu*vcpu;/* SGIs and PPIs: The VCPU+*SPIsandLPIs:TheVCPUwhoseap_list+*onwhichthisisqueued.+*/++structkvm_vcpu*target_vcpu;/* The VCPU that this interrupt should+*besendto,asaresultofthe+*targetsreg(v2)orthe+*affinityreg(v3).+*/++u32intid;/* Guest visible INTID */+boolpending;+boolline_level;/* Level only */+boolsoft_pending;/* Level only */+boolactive;/* not used for LPIs */+boolenabled;+boolhw;/* Tied to HW IRQ */+u32hwintid;/* HW INTID number */+union{+u8targets;/* GICv2 target VCPUs mask */+u32mpidr;/* GICv3 target VCPU */+};+u8source;/* GICv2 SGIs only */+u8priority;+enumvgic_irq_configconfig;/* Level or edge */+};++structvgic_dist{+boolin_kernel;+boolready;++/* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */+u32vgic_model;++intnr_spis;++/* TODO: Consider moving to global state */+/* Virtual control interface mapping */+void__iomem*vctrl_base;++/* base addresses in guest physical address space: */+gpa_tvgic_dist_base;/* distributor */+union{+/* either a GICv2 CPU interface */+gpa_tvgic_cpu_base;+/* or a number of GICv3 redistributor regions */+gpa_tvgic_redist_base;+};++/* distributor enabled */+u32enabled;++structvgic_irq*spis;+};++structvgic_v2_cpu_if{+u32vgic_hcr;+u32vgic_vmcr;+u32vgic_misr;/* Saved only */+u64vgic_eisr;/* Saved only */+u64vgic_elrsr;/* Saved only */+u32vgic_apr;+u32vgic_lr[VGIC_V2_MAX_LRS];+};++structvgic_v3_cpu_if{+#ifdef CONFIG_KVM_ARM_VGIC_V3+u32vgic_hcr;+u32vgic_vmcr;+u32vgic_sre;/* Restored only, change ignored */+u32vgic_misr;/* Saved only */+u32vgic_eisr;/* Saved only */+u32vgic_elrsr;/* Saved only */+u32vgic_ap0r[4];+u32vgic_ap1r[4];+u64vgic_lr[VGIC_V3_MAX_LRS];+#endif+};++structvgic_cpu{+/* CPU vif control registers for world switch */+union{+structvgic_v2_cpu_ifvgic_v2;+structvgic_v3_cpu_ifvgic_v3;+};++/* TODO: Move nr_lr to a global state */+/* Number of list registers on this CPU */+intnr_lr;++unsignedintused_lrs;+structvgic_irqprivate_irqs[VGIC_NR_PRIVATE_IRQS];++spinlock_tap_list_lock;/* Protects the ap_list */++/* list of IRQs for this VCPU to consider */+structlist_headap_list_head;+};++#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))+#define vgic_initialized(k) (false)+#define vgic_ready(k) ((k)->arch.vgic.ready)+#define vgic_valid_spi(k,i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \+((i)<(k)->arch.vgic.nr_spis+VGIC_NR_PRIVATE_IRQS))++/**+*kvm_vgic_get_max_vcpus-GetthemaximumnumberofVCPUsallowedbyHW+*+*Thehost'sGICnaturallylimitsthemaximumamountofVCPUsaguest+*canuse.+*/+staticinlineintkvm_vgic_get_max_vcpus(void)+{+returnkvm_vgic_global_state.max_gic_vcpus;+}++#endif /* __ASM_ARM_KVM_VGIC_VGIC_H */
From: Christoffer Dall <redacted>
The new VGIC implementation centers around a struct vgic_irq instance
per virtual IRQ.
Provide a function to retrieve the right instance for a given IRQ
number and (in case of private interrupts) the right VCPU.
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic.c | 41 +++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 22 ++++++++++++++++++++++
2 files changed, 63 insertions(+)
create mode 100644 virt/kvm/arm/vgic/vgic.c
create mode 100644 virt/kvm/arm/vgic/vgic.h
@@ -0,0 +1,41 @@+/*+*Copyright(C)2015,2016ARMLtd.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram.Ifnot,see<http://www.gnu.org/licenses/>.+*/++#include<linux/kvm.h>+#include<linux/kvm_host.h>++#include"vgic.h"++structvgic_globalkvm_vgic_global_state;++structvgic_irq*vgic_get_irq(structkvm*kvm,structkvm_vcpu*vcpu,+u32intid)+{+/* SGIs and PPIs */+if(intid<=VGIC_MAX_PRIVATE)+return&vcpu->arch.vgic_cpu.private_irqs[intid];++/* SPIs */+if(intid<=VGIC_MAX_SPI)+return&kvm->arch.vgic.spis[intid-VGIC_NR_PRIVATE_IRQS];++/* LPIs are not yet covered */+if(intid>=VGIC_MIN_LPI)+returnNULL;++WARN(1,"Looking up struct vgic_irq for reserved INTID");+returnNULL;+}
From: Christoffer Dall <redacted>
Provide a vgic_queue_irq() function which decides whether a given
IRQ needs to be queued to a VCPU's ap_list.
This should be called whenever an IRQ became pending or got enabled,
either as a result of userspace injection, from in-kernel emulated
devices like the architected timer or from MMIO accesses to the
distributor emulation.
Also provides the necessary functions to allow userland to inject an
IRQ to a guest.
[Andre: refactor out vgic_queue_irq()]
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 3 +
virt/kvm/arm/vgic/vgic.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 1 +
3 files changed, 185 insertions(+)
@@ -39,3 +56,167 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,WARN(1,"Looking up struct vgic_irq for reserved INTID");returnNULL;}++/**+*kvm_vgic_target_oracle-computethetargetvcpuforanirq+*+*@irq:Theirqtoroute.Mustbealreadylocked.+*+*Basedonthecurrentstateoftheinterrupt(enabled,pending,+*active,vcpuandtarget_vcpu),computethenextvcputhisshouldbe+*givento.ReturnNULLifthisshouldn'tbeinjectedatall.+*/+staticstructkvm_vcpu*vgic_target_oracle(structvgic_irq*irq)+{+/* If the interrupt is active, it must stay on the current vcpu */+if(irq->active)+returnirq->vcpu;++/* If enabled and pending, it can migrate to a new one */+if(irq->enabled&&irq->pending)+returnirq->target_vcpu;++/* Otherwise, it is considered idle */+returnNULL;+}++/*+*Onlyvalidinjectionifchanginglevelforlevel-triggeredIRQsorfora+*risingedge.+*/+staticboolvgic_validate_injection(structvgic_irq*irq,boollevel)+{+switch(irq->config){+caseVGIC_CONFIG_LEVEL:+returnirq->line_level!=level;+caseVGIC_CONFIG_EDGE:+returnlevel;+default:+BUG();+}+}++/*+*CheckwhetheranIRQneedsto(andcan)bequeuedtoaVCPU'saplist.+*Dothequeuingifnecessary,takingtherightlocksintherightorder.+*ReturnstruewhentheIRQwasqueued,falseotherwise.+*+*NeedstobeenteredwiththeIRQlockalreadyheld,butwillreturn+*withalllocksdropped.+*/+boolvgic_queue_irq(structkvm*kvm,structvgic_irq*irq)+{+structkvm_vcpu*vcpu=vgic_target_oracle(irq);++if(irq->vcpu||!(irq->pending&&irq->enabled)||!vcpu){+/*+*IfthisIRQisalreadyonaVCPU'sap_list,thenit+*cannotbemovedormodifiedandthereisnomoreworkfor+*ustodo.+*+*Otherwise,iftheirqisnotpendingandenabled,itdoes+*notneedtobeinsertedintoanap_listandthereisalso+*nomoreworkforustodo.+*/+spin_unlock(&irq->irq_lock);+returnfalse;+}++/*+*Wemustunlocktheirqlocktotaketheap_list_lockwhere+*wearegoingtoinsertthisnewpendinginterrupt.+*/+spin_unlock(&irq->irq_lock);++/* someone can do stuff here, which we re-check below */+retry:+spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);+spin_lock(&irq->irq_lock);++/*+*Didsomethingchangebehindourbacks?+*+*Therearetwocases:+*1)Theirqbecamependingoractivebehindourbacksand/or+*theirq->vcpufieldwassetcorrespondinglywhenputting+*theirqonanap_list.Thendropthelocksandreturn.+*2)Someonechangedtheaffinityonthisirqbehindour+*backsandwearenowholdingthewrongap_list_lock.+*ThendropthelocksandtrythenewVCPU.+*/+if(irq->vcpu||!(irq->pending&&irq->enabled)){+spin_unlock(&irq->irq_lock);+spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);+returnfalse;+}++if(irq->target_vcpu!=vcpu){+spin_unlock(&irq->irq_lock);+spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);++vcpu=irq->target_vcpu;+gotoretry;+}++list_add_tail(&irq->ap_list,&vcpu->arch.vgic_cpu.ap_list_head);+irq->vcpu=vcpu;++spin_unlock(&irq->irq_lock);+spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);++kvm_vcpu_kick(vcpu);++returntrue;+}++staticvoidvgic_update_irq_pending(structkvm*kvm,structkvm_vcpu*vcpu,+u32intid,boollevel)+{+structvgic_irq*irq=vgic_get_irq(kvm,vcpu,intid);++trace_vgic_update_irq_pending(vcpu->vcpu_id,intid,level);++BUG_ON(in_interrupt());++spin_lock(&irq->irq_lock);++if(!vgic_validate_injection(irq,level)){+/* Nothing to see here, move along... */+spin_unlock(&irq->irq_lock);+return;+}++if(irq->config==VGIC_CONFIG_LEVEL){+irq->line_level=level;+irq->pending=level||irq->soft_pending;+}else{+irq->pending=true;+}++vgic_queue_irq(kvm,irq);+}++/**+*kvm_vgic_inject_irq-InjectanIRQfromadevicetothevgic+*@kvm:TheVMstructurepointer+*@cpuid:TheCPUforPPIs+*@intid:TheINTIDtoinjectanewstateto.+*mustnotbemappedtoaHWinterrupt.+*@level:Edge-triggered:true:totriggertheinterrupt+*false:toignorethecall+*Level-sensitivetrue:raisetheinputsignal+*false:lowertheinputsignal+*+*TheGICisnotconcernedwithdevicesbeingactive-LOWoractive-HIGHfor+*level-sensitiveinterrupts.Youcanthinkofthelevelparameteras1+*beingHIGHand0beingLOWandalldevicesbeingactive-HIGH.+*/+intkvm_vgic_inject_irq(structkvm*kvm,intcpuid,unsignedintintid,+boollevel)+{+structkvm_vcpu*vcpu;++vcpu=kvm_get_vcpu(kvm,cpuid);+vgic_update_irq_pending(kvm,vcpu,intid,level);+return0;+}
From: Christoffer Dall <redacted>
Adds the sorting function to cover the case where you have more IRQs
to consider than you have LRs. We now consider priorities.
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
@@ -81,6 +82,58 @@ static struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq)}/*+*Theorderofitemsintheap_listsdefineshowwe'llpackthingsinLRsas+*well,thefirstitemsinthelistbeingthefirstthingspopulatedinthe+*LRs.+*+*AhardruleisthatactiveinterruptscanneverbepushedoutoftheLRs+*(andthereforetakepriority)sincewecannotreliablytrapondeactivation+*ofIRQsandthereforetheyhavetobepresentintheLRs.+*+*OtherwisethingsshouldbesortedbythepriorityfieldandtheGIC+*hardwaresupportwilltakecareofpreemptionofprioritygroupsetc.+*+*Returnnegativeif"a"sortsbefore"b",0topreserveorder,andpositive+*tosort"b"before"a".+*/+staticintvgic_irq_cmp(void*priv,structlist_head*a,structlist_head*b)+{+structvgic_irq*irqa=container_of(a,structvgic_irq,ap_list);+structvgic_irq*irqb=container_of(b,structvgic_irq,ap_list);+boolpenda,pendb;+intret;++spin_lock(&irqa->irq_lock);+spin_lock(&irqb->irq_lock);++if(irqa->active||irqb->active){+ret=(int)irqb->active-(int)irqa->active;+gotoout;+}++penda=irqa->enabled&&irqa->pending;+pendb=irqb->enabled&&irqb->pending;++if(!penda||!pendb){+ret=(int)pendb-(int)penda;+gotoout;+}++/* Both pending and enabled, sort by priority */+ret=irqa->priority-irqb->priority;+out:+spin_unlock(&irqb->irq_lock);+spin_unlock(&irqa->irq_lock);+returnret;+}++/* Must be called with the ap_list_lock held */+staticvoidvgic_sort_ap_list(structkvm_vcpu*vcpu)+{+list_sort(NULL,&vcpu->arch.vgic_cpu.ap_list_head,vgic_irq_cmp);+}++/**Onlyvalidinjectionifchanginglevelforlevel-triggeredIRQsorfora*risingedge.*/
From: Marc Zyngier <redacted>
Implement the functionality for syncing IRQs between our emulation
and the list registers, which represent the guest's view of IRQs.
This is done in kvm_vgic_flush_hwstate and kvm_vgic_sync_hwstate,
which gets called on guest entry and exit.
Signed-off-by: Marc Zyngier <redacted>
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 4 +
virt/kvm/arm/vgic/vgic-v2.c | 161 ++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 204 ++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 4 +
4 files changed, 373 insertions(+)
@@ -14,11 +14,172 @@*alongwiththisprogram.Ifnot,see<http://www.gnu.org/licenses/>.*/+#include<linux/irqchip/arm-gic.h>#include<linux/kvm.h>#include<linux/kvm_host.h>#include"vgic.h"+/*+*Callthisfunctiontoconvertau64valuetoanunsignedlong*bitmask+*inawaythatworksonboth32-bitand64-bitLEandBEplatforms.+*+*Warning:Callingthisfunctionmaymodify*val.+*/+staticunsignedlong*u64_to_bitmask(u64*val)+{+#if defined(CONFIG_CPU_BIG_ENDIAN) && BITS_PER_LONG == 32+*val=(*val>>32)|(*val<<32);+#endif+return(unsignedlong*)val;+}++voidvgic_v2_process_maintenance(structkvm_vcpu*vcpu)+{+structvgic_v2_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v2;++if(cpuif->vgic_misr&GICH_MISR_EOI){+u64eisr=cpuif->vgic_eisr;+unsignedlong*eisr_bmap=u64_to_bitmask(&eisr);+intlr;++for_each_set_bit(lr,eisr_bmap,vcpu->arch.vgic_cpu.nr_lr){+structvgic_irq*irq;+u32intid=cpuif->vgic_lr[lr]&GICH_LR_VIRTUALID;++irq=vgic_get_irq(vcpu->kvm,vcpu,intid);++WARN_ON(irq->config==VGIC_CONFIG_EDGE);+WARN_ON(cpuif->vgic_lr[lr]&GICH_LR_STATE);++kvm_notify_acked_irq(vcpu->kvm,0,+intid-VGIC_NR_PRIVATE_IRQS);++cpuif->vgic_lr[lr]&=~GICH_LR_STATE;/* Useful?? */+cpuif->vgic_elrsr|=1ULL<<lr;+}+}++/* check and disable underflow maintenance IRQ */+cpuif->vgic_hcr&=~GICH_HCR_UIE;++/*+*Inthenextiterationsofthevcpuloop,ifwesyncthe+*vgicstateafterflushingit,butbeforeenteringtheguest+*(thishappensforpendingsignalsandvmidrollovers),then+*makesurewedon'tpickupanyoldmaintenanceinterrupts+*here.+*/+cpuif->vgic_eisr=0;+}++voidvgic_v2_set_underflow(structkvm_vcpu*vcpu)+{+structvgic_v2_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v2;++cpuif->vgic_hcr|=GICH_HCR_UIE;+}++/*+*transferthecontentoftheLRsbackintothecorrespondingap_list:+*-activebitistransferredasis+*-pendingbitis+*-transferredasisincaseofedgesensitiveIRQs+*-settotheline-level(resampletime)forlevelsensitiveIRQs+*/+voidvgic_v2_fold_lr_state(structkvm_vcpu*vcpu)+{+structvgic_v2_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v2;+intlr;++for(lr=0;lr<vcpu->arch.vgic_cpu.used_lrs;lr++){+u32val=cpuif->vgic_lr[lr];+u32intid=val&GICH_LR_VIRTUALID;+structvgic_irq*irq;++irq=vgic_get_irq(vcpu->kvm,vcpu,intid);++spin_lock(&irq->irq_lock);++/* Always preserve the active bit */+irq->active=!!(val&GICH_LR_ACTIVE_BIT);++/* Edge is the only case where we preserve the pending bit */+if(irq->config==VGIC_CONFIG_EDGE&&+(val&GICH_LR_PENDING_BIT)){+irq->pending=true;++if(intid<VGIC_NR_SGIS){+u32cpuid=val&GICH_LR_PHYSID_CPUID;++cpuid>>=GICH_LR_PHYSID_CPUID_SHIFT;+irq->source|=(1<<cpuid);+}+}++/* Clear soft pending state when level IRQs have been acked */+if(irq->config==VGIC_CONFIG_LEVEL&&+!(val&GICH_LR_PENDING_BIT)){+irq->soft_pending=false;+irq->pending=irq->line_level;+}++spin_unlock(&irq->irq_lock);+}+}++/*+*PopulatestheparticularLRwiththestateofagivenIRQ:+*-foranedgesensitiveIRQthependingstateisresetinthestruct+*-foralevelsensitiveIRQthependingstatevalueisunchanged;+*itwillberesampledondeactivation+*+*IfirqisnotNULL,theirq_lockmustbeholdalreadybythecaller.+*IfirqisNULL,therespectiveLRgetscleared.+*/+voidvgic_v2_populate_lr(structkvm_vcpu*vcpu,structvgic_irq*irq,intlr)+{+u32val;++if(!irq){+val=0;+gotoout;+}++val=irq->intid;++if(irq->pending){+val|=GICH_LR_PENDING_BIT;++if(irq->config==VGIC_CONFIG_EDGE)+irq->pending=false;++if(irq->intid<VGIC_NR_SGIS){+u32src=ffs(irq->source);++BUG_ON(!src);+val|=(src-1)<<GICH_LR_PHYSID_CPUID_SHIFT;+irq->source&=~(1<<(src-1));+if(irq->source)+irq->pending=true;+}+}++if(irq->active)+val|=GICH_LR_ACTIVE_BIT;++if(irq->hw){+val|=GICH_LR_HW;+val|=irq->hwintid<<GICH_LR_PHYSID_CPUID_SHIFT;+}else{+if(irq->config==VGIC_CONFIG_LEVEL)+val|=GICH_LR_EOI;+}++out:+vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr]=val;+}+voidvgic_v2_irq_change_affinity(structkvm*kvm,u32intid,u8new_targets){structvgic_dist*dist=&kvm->arch.vgic;
@@ -273,3 +273,207 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,vgic_update_irq_pending(kvm,vcpu,intid,level);return0;}++/**+*vgic_prune_ap_list-Removenon-relevantinterruptsfromthelist+*+*@vcpu:TheVCPUpointer+*+*Gooverthelistof"interesting"interrupts,andprunethosethatwe+*won'thavetoconsiderinthenearfuture.+*/+staticvoidvgic_prune_ap_list(structkvm_vcpu*vcpu)+{+structvgic_cpu*vgic_cpu=&vcpu->arch.vgic_cpu;+structvgic_irq*irq,*tmp;++retry:+spin_lock(&vgic_cpu->ap_list_lock);++list_for_each_entry_safe(irq,tmp,&vgic_cpu->ap_list_head,ap_list){+structkvm_vcpu*target_vcpu,*vcpuA,*vcpuB;++spin_lock(&irq->irq_lock);++BUG_ON(vcpu!=irq->vcpu);++target_vcpu=vgic_target_oracle(irq);++if(!target_vcpu){+/*+*Wedon'tneedtoprocessthisinterruptany+*further,moveitoffthelist.+*/+list_del_init(&irq->ap_list);+irq->vcpu=NULL;+spin_unlock(&irq->irq_lock);+continue;+}++if(target_vcpu==vcpu){+/* We're on the right CPU */+spin_unlock(&irq->irq_lock);+continue;+}++/* This interrupt looks like it has to be migrated. */++spin_unlock(&irq->irq_lock);+spin_unlock(&vgic_cpu->ap_list_lock);++/*+*Ensurelockingorderbyalwayslockingthesmallest+*IDfirst.+*/+if(vcpu->vcpu_id<target_vcpu->vcpu_id){+vcpuA=vcpu;+vcpuB=target_vcpu;+}else{+vcpuA=target_vcpu;+vcpuB=vcpu;+}++spin_lock(&vcpuA->arch.vgic_cpu.ap_list_lock);+spin_lock(&vcpuB->arch.vgic_cpu.ap_list_lock);+spin_lock(&irq->irq_lock);++/*+*Iftheaffinityhasbeenpreserved,movethe+*interruptaround.Otherwise,itmeansthingshave+*changedwhiletheinterruptwasunlocked,andwe+*needtoreplaythis.+*+*Inallcases,wecannottrustthelistnottohave+*changed,sowerestartfromthebeginning.+*/+if(target_vcpu==vgic_target_oracle(irq)){+structvgic_cpu*new_cpu=&target_vcpu->arch.vgic_cpu;++list_del_init(&irq->ap_list);+irq->vcpu=target_vcpu;+list_add_tail(&irq->ap_list,&new_cpu->ap_list_head);+}++spin_unlock(&irq->irq_lock);+spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock);+spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock);+gotoretry;+}++spin_unlock(&vgic_cpu->ap_list_lock);+}++staticinlinevoidvgic_process_maintenance_interrupt(structkvm_vcpu*vcpu)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_process_maintenance(vcpu);+else+WARN(1,"GICv3 Not Implemented\n");+}++staticinlinevoidvgic_fold_lr_state(structkvm_vcpu*vcpu)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_fold_lr_state(vcpu);+else+WARN(1,"GICv3 Not Implemented\n");+}++/*+*Requirestheap_locktobeheld.+*IfirqisnotNULL,requirestheIRQlocktobeheldaswell.+*IfirqisNULL,thelistregistergetscleared.+*/+staticinlinevoidvgic_populate_lr(structkvm_vcpu*vcpu,+structvgic_irq*irq,intlr)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_populate_lr(vcpu,irq,lr);+else+WARN(1,"GICv3 Not Implemented\n");+}++staticinlinevoidvgic_set_underflow(structkvm_vcpu*vcpu)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_set_underflow(vcpu);+else+WARN(1,"GICv3 Not Implemented\n");+}++staticintcompute_ap_list_depth(structkvm_vcpu*vcpu)+{+structvgic_cpu*vgic_cpu=&vcpu->arch.vgic_cpu;+structvgic_irq*irq;+intcount=0;++list_for_each_entry(irq,&vgic_cpu->ap_list_head,ap_list){+spin_lock(&irq->irq_lock);+/* GICv2 SGIs can count for more than one... */+if(irq->intid<VGIC_NR_SGIS&&irq->source)+count+=hweight8(irq->source);+else+count++;+spin_unlock(&irq->irq_lock);+}+returncount;+}++/* requires the vcpu ap_lock to be held */+staticvoidvgic_populate_lrs(structkvm_vcpu*vcpu)+{+structvgic_cpu*vgic_cpu=&vcpu->arch.vgic_cpu;+u32model=vcpu->kvm->arch.vgic.vgic_model;+structvgic_irq*irq;+intcount=0;++if(compute_ap_list_depth(vcpu)>vcpu->arch.vgic_cpu.nr_lr){+vgic_set_underflow(vcpu);+vgic_sort_ap_list(vcpu);+}++list_for_each_entry(irq,&vgic_cpu->ap_list_head,ap_list){+spin_lock(&irq->irq_lock);++if(unlikely(vgic_target_oracle(irq)!=vcpu))+gotonext;++/*+*IfwegetanSGIwithmultiplesources,trytoget+*theminallatonce.+*/+if(model==KVM_DEV_TYPE_ARM_VGIC_V2&&+irq->intid<VGIC_NR_SGIS){+while(irq->source&&count<vcpu->arch.vgic_cpu.nr_lr)+vgic_populate_lr(vcpu,irq,count++);+}else{+vgic_populate_lr(vcpu,irq,count++);+}++next:+spin_unlock(&irq->irq_lock);++if(count==vcpu->arch.vgic_cpu.nr_lr)+break;+}++vcpu->arch.vgic_cpu.used_lrs=count;++/* Nuke remaining LRs */+for(;count<vcpu->arch.vgic_cpu.nr_lr;count++)+vgic_populate_lr(vcpu,NULL,count);+}++voidkvm_vgic_sync_hwstate(structkvm_vcpu*vcpu)+{+vgic_process_maintenance_interrupt(vcpu);+vgic_fold_lr_state(vcpu);+vgic_prune_ap_list(vcpu);+}++voidkvm_vgic_flush_hwstate(structkvm_vcpu*vcpu)+{+spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);+vgic_populate_lrs(vcpu);+spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);+}
From: Marc Zyngier <redacted>
As the GICv3 virtual interface registers differ from their GICv2
siblings, we need different handlers for processing maintenance
interrupts and reading/writing to the LRs.
Also as we store an IRQ's affinity directly as a MPIDR, we need a
separate change_affinity() implementation too.
Implement the respective handler functions and connect them to
existing code to be called if the host is using a GICv3.
Signed-off-by: Marc Zyngier <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic-v3.c | 191 ++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 8 +-
virt/kvm/arm/vgic/vgic.h | 30 +++++++
3 files changed, 225 insertions(+), 4 deletions(-)
create mode 100644 virt/kvm/arm/vgic/vgic-v3.c
@@ -0,0 +1,191 @@+/*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram.Ifnot,see<http://www.gnu.org/licenses/>.+*/++#include<linux/irqchip/arm-gic-v3.h>+#include<linux/kvm.h>+#include<linux/kvm_host.h>+#include<linux/irqchip/arm-gic.h>++#include"vgic.h"++voidvgic_v3_process_maintenance(structkvm_vcpu*vcpu)+{+structvgic_v3_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v3;+u32model=vcpu->kvm->arch.vgic.vgic_model;++if(cpuif->vgic_misr&ICH_MISR_EOI){+unsignedlongeisr_bmap=cpuif->vgic_eisr;+intlr;++for_each_set_bit(lr,&eisr_bmap,vcpu->arch.vgic_cpu.nr_lr){+u32intid;+u64val=cpuif->vgic_lr[lr];++if(model==KVM_DEV_TYPE_ARM_VGIC_V3)+intid=val&ICH_LR_VIRTUAL_ID_MASK;+else+intid=val&GICH_LR_VIRTUALID;++/*+*kvm_notify_acked_irqcallskvm_set_irq()+*toresettheIRQlevel,whichgrabsthedist->lock+*sowecallthisbeforetakingthedist->lock.+*/+kvm_notify_acked_irq(vcpu->kvm,0,+intid-VGIC_NR_PRIVATE_IRQS);++cpuif->vgic_lr[lr]&=~ICH_LR_STATE;/* Useful?? */+cpuif->vgic_elrsr|=1ULL<<lr;+}++/*+*Inthenextiterationsofthevcpuloop,ifwesync+*thevgicstateafterflushingit,butbefore+*enteringtheguest(thishappensforpending+*signalsandvmidrollovers),thenmakesurewe+*don'tpickupanyoldmaintenanceinterruptshere.+*/+cpuif->vgic_eisr=0;+}++cpuif->vgic_hcr&=~ICH_HCR_UIE;+}++voidvgic_v3_set_underflow(structkvm_vcpu*vcpu)+{+structvgic_v3_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v3;++cpuif->vgic_hcr|=ICH_HCR_UIE;+}++voidvgic_v3_fold_lr_state(structkvm_vcpu*vcpu)+{+structvgic_v3_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v3;+u32model=vcpu->kvm->arch.vgic.vgic_model;+intlr;++/* Assumes ap_list_lock held */++for(lr=0;lr<vcpu->arch.vgic_cpu.used_lrs;lr++){+u64val=cpuif->vgic_lr[lr];+u32intid;+structvgic_irq*irq;++if(model==KVM_DEV_TYPE_ARM_VGIC_V3)+intid=val&ICH_LR_VIRTUAL_ID_MASK;+else+intid=val&GICH_LR_VIRTUALID;+irq=vgic_get_irq(vcpu->kvm,vcpu,intid);++spin_lock(&irq->irq_lock);++/* Always preserve the active bit */+irq->active=!!(val&ICH_LR_ACTIVE_BIT);++/* Edge is the only case where we preserve the pending bit */+if(irq->config==VGIC_CONFIG_EDGE&&+(val&ICH_LR_PENDING_BIT)){+irq->pending=true;++if(intid<VGIC_NR_SGIS&&+model==KVM_DEV_TYPE_ARM_VGIC_V2){+u32cpuid=val&GICH_LR_PHYSID_CPUID;++cpuid>>=GICH_LR_PHYSID_CPUID_SHIFT;+irq->source|=(1<<cpuid);+}+}++/* Clear soft pending state when level irqs have been acked */+if(irq->config==VGIC_CONFIG_LEVEL&&+!(val&ICH_LR_PENDING_BIT)){+irq->soft_pending=false;+irq->pending=irq->line_level;+}++spin_unlock(&irq->irq_lock);+}+}++/* Requires the irq to be locked already */+voidvgic_v3_populate_lr(structkvm_vcpu*vcpu,structvgic_irq*irq,intlr)+{+u32model=vcpu->kvm->arch.vgic.vgic_model;+u64val;++if(!irq){+val=0;+gotoout;+}++val=irq->intid;++if(irq->pending){+val|=ICH_LR_PENDING_BIT;++if(irq->config==VGIC_CONFIG_EDGE)+irq->pending=false;++if(irq->intid<VGIC_NR_SGIS&&+model==KVM_DEV_TYPE_ARM_VGIC_V2){+u32src=ffs(irq->source);++BUG_ON(!src);+val|=(src-1)<<GICH_LR_PHYSID_CPUID_SHIFT;+irq->source&=~(1<<(src-1));+if(irq->source)+irq->pending=true;+}+}++if(irq->active)+val|=ICH_LR_ACTIVE_BIT;++if(irq->hw){+val|=ICH_LR_HW;+val|=((u64)irq->hwintid)<<ICH_LR_PHYS_ID_SHIFT;+}else{+if(irq->config==VGIC_CONFIG_LEVEL)+val|=ICH_LR_EOI;+}++/*+*CurrentlyallguestIRQsareGroup1,asGroup0wouldresult+*inaFIQintheguest,whichitwouldn'texpect.+*Eventuallywewanttomakethisconfigurable,sowemay+*revisitthisinthefuture.+*/+if(model==KVM_DEV_TYPE_ARM_VGIC_V3)+val|=ICH_LR_GROUP;++out:+vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr]=val;+}++voidvgic_v3_irq_change_affinity(structkvm*kvm,u32intid,u64mpidr)+{+structvgic_dist*dist=&kvm->arch.vgic;+structvgic_irq*irq;+structkvm_vcpu*vcpu;++BUG_ON(intid<=VGIC_MAX_PRIVATE||intid>1019);+BUG_ON(dist->vgic_model!=KVM_DEV_TYPE_ARM_VGIC_V3);++irq=vgic_get_irq(kvm,NULL,intid);+vcpu=kvm_mpidr_to_vcpu(kvm,mpidr);++spin_lock(&irq->irq_lock);+irq->target_vcpu=vcpu;+spin_unlock(&irq->irq_lock);+}
From: Eric Auger <redacted>
Tell KVM whether a particular VCPU has an IRQ that needs handling
in the guest. This is used to decide whether a VCPU is runnable.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 2 ++
virt/kvm/arm/vgic/vgic.c | 22 ++++++++++++++++++++++
2 files changed, 24 insertions(+)
We register each register group of the distributor and redistributors
as separate regions of the kvm-io-bus framework. This way calls get
directly handed over to the actual handler.
This puts a lot more regions into kvm-io-bus than what we use at the
moment on other architectures, so we will probably need to revisit the
implementation of the framework later to be more efficient.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Eric Auger <redacted>
---
include/kvm/vgic/vgic.h | 9 ++
virt/kvm/arm/vgic/vgic_mmio.c | 194 ++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic_mmio.h | 47 ++++++++++
3 files changed, 250 insertions(+)
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.c
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.h
Userland can access the emulated GIC to save and restore its state
for initialization or migration purposes.
The kvm_io_bus API requires an absolute gpa, which does not fit the
KVM_DEV_ARM_VGIC_GRP_DIST_REGS user API, that only provides relative
offsets. So we explicitly iterate our register list to connect
userland to the VGIC.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Eric Auger <redacted>
---
virt/kvm/arm/vgic/vgic.h | 2 ++
virt/kvm/arm/vgic/vgic_mmio.c | 45 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
@@ -82,9 +82,56 @@ static int vgic_mmio_write_nyi(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_v2_misc(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32value;++switch((addr-iodev->base_addr)&~3){+case0x0:+value=vcpu->kvm->arch.vgic.enabled?GICD_ENABLE:0;+break;+case0x4:+value=vcpu->kvm->arch.vgic.nr_spis+VGIC_NR_PRIVATE_IRQS;+value=(value>>5)-1;+value|=(atomic_read(&vcpu->kvm->online_vcpus)-1)<<5;+break;+case0x8:+value=(PRODUCT_ID_KVM<<24)|(IMPLEMENTER_ARM<<0);+break;+default:+return0;+}++write_mask32(value,addr&3,len,val);+return0;+}++staticintvgic_mmio_write_v2_misc(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+/*+*GICD_TYPERandGICD_IIDRareread-only,theupperthreebytesof+*GICD_CTLRarereserved.+*/+if(addr-iodev->base_addr>=1)+return0;++vcpu->kvm->arch.vgic.enabled=(*(u32*)val)?true:false;+/* TODO: is there anything to trigger at this point? */++return0;+}+structvgic_register_regionvgic_v2_dist_registers[]={REGISTER_DESC_WITH_LENGTH(GIC_DIST_CTRL,-vgic_mmio_read_nyi,vgic_mmio_write_nyi,12),+vgic_mmio_read_v2_misc,vgic_mmio_write_v2_misc,12),REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_IGROUP,vgic_mmio_read_raz,vgic_mmio_write_wi,1),REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_SET,
@@ -129,15 +129,92 @@ static int vgic_mmio_write_v2_misc(struct kvm_vcpu *vcpu,return0;}+/*+*ReadaccessestobothGICD_ICENABLERandGICD_ISENABLERreturnthevalue+*oftheenabledbit,sothereisonlyonefunctionforbothhere.+*/+staticintvgic_mmio_read_enable(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+u32value=0;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++/* Loop over all IRQs affected by this read */+for(i=0;i<len*8;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++if(irq->enabled)+value|=(1U<<i);+}++write_mask32(value,addr&3,len,val);+return0;+}++staticintvgic_mmio_write_senable(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for_each_set_bit(i,val,len*8){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+irq->enabled=true;+vgic_queue_irq(vcpu->kvm,irq);+}++return0;+}++staticintvgic_mmio_write_cenable(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for_each_set_bit(i,val,len*8){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);++irq->enabled=false;+/* TODO: Does the exit/entry code take care of "unqueuing"? */++spin_unlock(&irq->irq_lock);+}+return0;+}+structvgic_register_regionvgic_v2_dist_registers[]={REGISTER_DESC_WITH_LENGTH(GIC_DIST_CTRL,vgic_mmio_read_v2_misc,vgic_mmio_write_v2_misc,12),REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_IGROUP,vgic_mmio_read_raz,vgic_mmio_write_wi,1),REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_SET,-vgic_mmio_read_nyi,vgic_mmio_write_nyi,1),+vgic_mmio_read_enable,vgic_mmio_write_senable,1),REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_CLEAR,-vgic_mmio_read_nyi,vgic_mmio_write_nyi,1),+vgic_mmio_read_enable,vgic_mmio_write_cenable,1),REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_SET,vgic_mmio_read_nyi,vgic_mmio_write_nyi,1),REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_CLEAR,
@@ -206,6 +206,89 @@ static int vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_pending(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+u32value=0;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++/* Loop over all IRQs affected by this read */+for(i=0;i<len*8;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+if(irq->pending)+value|=(1U<<i);+spin_unlock(&irq->irq_lock);+}++write_mask32(value,addr&3,len,val);+return0;+}++staticintvgic_mmio_write_spending(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for_each_set_bit(i,val,len*8){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+irq->pending=true;+if(irq->config==VGIC_CONFIG_LEVEL)+irq->soft_pending=true;++vgic_queue_irq(vcpu->kvm,irq);+}++return0;+}++staticintvgic_mmio_write_cpending(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for_each_set_bit(i,val,len*8){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);++if(irq->config==VGIC_CONFIG_LEVEL){+irq->soft_pending=false;+irq->pending=irq->line_level;+}else{+irq->pending=false;+}+/* TODO: Does the exit/entry code take care of "unqueuing"? */++spin_unlock(&irq->irq_lock);+}+return0;+}+structvgic_register_regionvgic_v2_dist_registers[]={REGISTER_DESC_WITH_LENGTH(GIC_DIST_CTRL,vgic_mmio_read_v2_misc,vgic_mmio_write_v2_misc,12),
@@ -289,6 +289,83 @@ static int vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_active(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+u32value=0;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++/* Loop over all IRQs affected by this read */+for(i=0;i<len*8;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+if(irq->active)+value|=(1U<<i);+spin_unlock(&irq->irq_lock);+}++write_mask32(value,addr&3,len,val);+return0;+}++staticintvgic_mmio_write_cactive(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for_each_set_bit(i,val,len*8){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);++irq->active=false;+/* TODO: Anything more to do? Does flush/sync cover this? */++spin_unlock(&irq->irq_lock);+}+return0;+}++staticintvgic_mmio_write_sactive(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for_each_set_bit(i,val,len*8){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);++irq->active=true;+/* TODO: Anything more to do? Does flush/sync cover this? */++spin_unlock(&irq->irq_lock);+}+return0;+}+staticintvgic_mmio_read_priority(structkvm_vcpu*vcpu,structkvm_io_device*this,gpa_taddr,intlen,void*val)
@@ -512,6 +512,50 @@ static int vgic_mmio_write_target(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_write_sgir(structkvm_vcpu*source_vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+intnr_vcpus=atomic_read(&source_vcpu->kvm->online_vcpus);+u32value=*(u32*)val;+intintid=value&0xf;+inttargets=(value>>16)&0xff;+intmode=(value>>24)&0x03;+intc;+structkvm_vcpu*vcpu;++switch(mode){+case0x0:/* as specified by targets */+break;+case0x1:+targets=(1U<<nr_vcpus)-1;/* all, ... */+targets&=~(1U<<source_vcpu->vcpu_id);/* but self */+break;+case0x2:/* this very vCPU only */+targets=(1U<<source_vcpu->vcpu_id);+break;+case0x3:/* reserved */+break;+}++kvm_for_each_vcpu(c,vcpu,source_vcpu->kvm){+structvgic_irq*irq;++if(!(targets&(1U<<c)))+continue;++irq=vgic_get_irq(source_vcpu->kvm,vcpu,intid);++spin_lock(&irq->irq_lock);+irq->pending=true;+irq->source|=1U<<source_vcpu->vcpu_id;++vgic_queue_irq(source_vcpu->kvm,irq);+}++return0;+}+structvgic_register_regionvgic_v2_dist_registers[]={REGISTER_DESC_WITH_LENGTH(GIC_DIST_CTRL,vgic_mmio_read_v2_misc,vgic_mmio_write_v2_misc,12),
Describe the GICv3 distributor and redistributor registers in our
structure. This adds a special macro to deal with the split of
SGI/PPI in the redistributor and SPIs in the distributor, which
allows us to reuse the existing GICv2 handlers for those registers
which are compatible.
Also we register the separate MMIO page for the redistributor
registers dealing with private interrupts.
GICv3 specific registers are only implemented as stubs at this time.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic.h | 16 +++
virt/kvm/arm/vgic/vgic_mmio.c | 246 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 262 insertions(+)
As in the GICv2 emulation we handle those three registers in one
function.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic.h | 2 ++
virt/kvm/arm/vgic/vgic_mmio.c | 38 ++++++++++++++++++++++++++++++++++++--
2 files changed, 38 insertions(+), 2 deletions(-)
@@ -614,7 +636,19 @@ static int vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu,structkvm_io_device*this,gpa_taddr,intlen,constvoid*val){-/* TODO: implement */+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+boolenabled;++/* These are not the bits you are looking for ... */+if(addr-iodev->base_addr>0)+return0;++/* We only care about the enable bit, all other bits are WI. */+enabled=*(u8*)val&GICD_CTLR_ENABLE_SS_G1;++vcpu->kvm->arch.vgic.enabled=enabled;+return0;}
The redistributor TYPER tells the OS about the associated MPIDR,
also the LAST bit is crucial.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic_mmio.c | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
Those set the affinity of a SPI interrupt.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic_mmio.c | 56 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
In contrast to GICv2 SGIs in a GICv3 implementation are not triggered
by a MMIO write, but with a system register write. KVM knows about
that register already, we just need to implement the handler and wire
it up to the core KVM/ARM code.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 8 ++++
virt/kvm/arm/vgic/vgic_mmio.c | 101 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 109 insertions(+)
@@ -1139,4 +1139,105 @@ int vgic_register_redist_regions(struct kvm *kvm, gpa_t redist_base_address)returnret;}++/*+*Compareagivenaffinity(level1-3andalevel0mask,fromtheSGI+*generationregisterICC_SGI1R_EL1)withagivenVCPU.+*IftheVCPU'sMPIDRmatches,returnthelevel0affinity,otherwise+*return-1.+*/+staticintmatch_mpidr(u64sgi_aff,u16sgi_cpu_mask,structkvm_vcpu*vcpu)+{+unsignedlongaffinity;+intlevel0;++/*+*SplitthecurrentVCPU'sMPIDRintoaffinitylevel0andthe+*restasthisiswhatwehavetocompareagainst.+*/+affinity=kvm_vcpu_get_mpidr_aff(vcpu);+level0=MPIDR_AFFINITY_LEVEL(affinity,0);+affinity&=~MPIDR_LEVEL_MASK;++/* bail out if the upper three levels don't match */+if(sgi_aff!=affinity)+return-1;++/* Is this VCPU's bit set in the mask ? */+if(!(sgi_cpu_mask&BIT(level0)))+return-1;++returnlevel0;+}++#define SGI_AFFINITY_LEVEL(reg, level) \+((((reg)&ICC_SGI1R_AFFINITY_##level##_MASK)\+>>ICC_SGI1R_AFFINITY_##level##_SHIFT)<<MPIDR_LEVEL_SHIFT(level))++/**+*vgic_v3_dispatch_sgi-handleSGIrequestsfromVCPUs+*@vcpu:TheVCPUrequestingaSGI+*@reg:ThevaluewrittenintotheICC_SGI1R_EL1registerbythatVCPU+*+*WithGICv3(andARE=1)CPUstriggerSGIsbywritingtoasystemregister.+*Thiswilltrapinsys_regs.candcallthisfunction.+*ThisICC_SGI1R_EL1registercontainstheupperthreeaffinitylevelsofthe+*targetprocessorsaswellasabitmaskof16Aff0CPUs.+*Iftheinterruptroutingmodebitisnotset,weiterateoverallVCPUsto+*checkformatchingones.Ifthisbitisset,wesignalall,butnotthe+*callingVCPU.+*/+voidvgic_v3_dispatch_sgi(structkvm_vcpu*vcpu,u64reg)+{+structkvm*kvm=vcpu->kvm;+structkvm_vcpu*c_vcpu;+u16target_cpus;+u64mpidr;+intsgi,c;+intvcpu_id=vcpu->vcpu_id;+boolbroadcast;++sgi=(reg&ICC_SGI1R_SGI_ID_MASK)>>ICC_SGI1R_SGI_ID_SHIFT;+broadcast=reg&BIT(ICC_SGI1R_IRQ_ROUTING_MODE_BIT);+target_cpus=(reg&ICC_SGI1R_TARGET_LIST_MASK)>>ICC_SGI1R_TARGET_LIST_SHIFT;+mpidr=SGI_AFFINITY_LEVEL(reg,3);+mpidr|=SGI_AFFINITY_LEVEL(reg,2);+mpidr|=SGI_AFFINITY_LEVEL(reg,1);++/*+*WeiterateoverallVCPUstofindtheMPIDRsmatchingtherequest.+*IfwehavehandledoneCPU,weclearit'sbittodetectearly+*ifwearealreadyfinished.Thisavoidsiteratingthroughall+*VCPUswhenmostofthetimeswejustsignalasingleVCPU.+*/+kvm_for_each_vcpu(c,c_vcpu,kvm){+structvgic_irq*irq;++/* Exit early if we have dealt with all requested CPUs */+if(!broadcast&&target_cpus==0)+break;++/* Don't signal the calling VCPU */+if(broadcast&&c==vcpu_id)+continue;++if(!broadcast){+intlevel0;++level0=match_mpidr(mpidr,target_cpus,c_vcpu);+if(level0==-1)+continue;++/* remove this matching VCPU from the mask */+target_cpus&=~BIT(level0);+}++irq=vgic_get_irq(vcpu->kvm,c_vcpu,sgi);++spin_lock(&irq->irq_lock);+irq->pending=true;++vgic_queue_irq(vcpu->kvm,irq);+}+}#endif
From: Eric Auger <redacted>
This patch introduces the skeleton for the KVM device operations
associated to KVM_DEV_TYPE_ARM_VGIC_V2 and KVM_DEV_TYPE_ARM_VGIC_V3.
At that stage kvm_vgic_create is stubbed.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic.h | 2 +
virt/kvm/arm/vgic/vgic_kvm_device.c | 108 ++++++++++++++++++++++++++++++++++++
2 files changed, 110 insertions(+)
create mode 100644 virt/kvm/arm/vgic/vgic_kvm_device.c
From: Eric Auger <redacted>
This patch implements the KVM_DEV_ARM_VGIC_GRP_NR_IRQS group. This
modality is supported by both VGIC V2 and V3 KVM device as will be
other groups, hence the introduction of common helpers.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic_kvm_device.c | 83 +++++++++++++++++++++++++++++++++++--
1 file changed, 79 insertions(+), 4 deletions(-)
From: Eric Auger <redacted>
This patch implements the KVM_DEV_ARM_VGIC_GRP_CTRL group API
featuring KVM_DEV_ARM_VGIC_CTRL_INIT attribute. The vgic_init
function is not yet implemented though.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic_kvm_device.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
From: Eric Auger <redacted>
This patch implements the KVM_DEV_ARM_VGIC_GRP_ADDR group which
enables to set the base address of GIC regions as seen by the guest.
The kvm_vgic_addr function whci eventually assigns the chosen address
to the internal structure still is stubbed.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic_kvm_device.c | 38 +++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
From: Eric Auger <redacted>
This patch implements the switches for KVM_DEV_ARM_VGIC_GRP_DIST_REGS
and KVM_DEV_ARM_VGIC_GRP_CPU_REGS API which allows the userspace to
access VGIC registers.
At that stage the interfaces with the MMIO API are not implemented:
- vgic_attr_regs_access
- vgic_v2_has_attr_regs
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic.h | 1 +
virt/kvm/arm/vgic/vgic_kvm_device.c | 53 +++++++++++++++++++++++++++++++++++--
virt/kvm/arm/vgic/vgic_mmio.c | 34 ++++++++++++++++++++++++
3 files changed, 86 insertions(+), 2 deletions(-)
@@ -140,6 +140,21 @@ void kvm_register_vgic_device(unsigned long type)}}+/** vgic_attr_regs_access: allows user space to read/write VGIC registers+*+*@dev:kvmdevicehandle+*@attr:kvmdeviceattribute+*@reg:addressthevalueisreadorwritten+*@is_write:writeflag+*+*/+staticintvgic_attr_regs_access(structkvm_device*dev,+structkvm_device_attr*attr,+u32*reg,boolis_write)+{+return-ENXIO;+}+/* V2 ops */staticintvgic_v2_set_attr(structkvm_device*dev,
@@ -148,8 +163,23 @@ static int vgic_v2_set_attr(struct kvm_device *dev,intret;ret=vgic_set_common_attr(dev,attr);-returnret;+if(ret!=-ENXIO)+returnret;++switch(attr->group){+caseKVM_DEV_ARM_VGIC_GRP_DIST_REGS:+caseKVM_DEV_ARM_VGIC_GRP_CPU_REGS:{+u32__user*uaddr=(u32__user*)(long)attr->addr;+u32reg;++if(get_user(reg,uaddr))+return-EFAULT;+returnvgic_attr_regs_access(dev,attr,®,true);+}+}++return-ENXIO;}staticintvgic_v2_get_attr(structkvm_device*dev,
@@ -158,7 +188,23 @@ static int vgic_v2_get_attr(struct kvm_device *dev,intret;ret=vgic_get_common_attr(dev,attr);-returnret;+if(ret!=-ENXIO)+returnret;++switch(attr->group){+caseKVM_DEV_ARM_VGIC_GRP_DIST_REGS:+caseKVM_DEV_ARM_VGIC_GRP_CPU_REGS:{+u32__user*uaddr=(u32__user*)(long)attr->addr;+u32reg=0;++ret=vgic_attr_regs_access(dev,attr,®,false);+if(ret)+returnret;+returnput_user(reg,uaddr);+}+}++return-ENXIO;}staticintvgic_v2_has_attr(structkvm_device*dev,
@@ -172,6 +218,9 @@ static int vgic_v2_has_attr(struct kvm_device *dev,return0;}break;+caseKVM_DEV_ARM_VGIC_GRP_DIST_REGS:+caseKVM_DEV_ARM_VGIC_GRP_CPU_REGS:+returnvgic_v2_has_attr_regs(dev,attr);caseKVM_DEV_ARM_VGIC_GRP_NR_IRQS:return0;caseKVM_DEV_ARM_VGIC_GRP_CTRL:
From: Eric Auger <redacted>
kvm_vgic_addr is used by the userspace to set the base address of
the following register regions, as seen by the guest:
- distributor(v2 and v3),
- re-distributors (v3),
- CPU interface (v2).
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 2 +
virt/kvm/arm/vgic/vgic.h | 3 +
virt/kvm/arm/vgic/vgic_kvm_device.c | 112 ++++++++++++++++++++++++++++++++++++
3 files changed, 117 insertions(+)
@@ -167,6 +167,13 @@ struct vgic_v3_cpu_if {#endif};+structvgic_vmcr{+u32ctlr;+u32abpr;+u32bpr;+u32pmr;+};+structvgic_cpu{/* CPU vif control registers for world switch */union{
@@ -17,8 +17,11 @@#include<kvm/vgic/vgic.h>#include<linux/uaccess.h>#include<asm/kvm_mmu.h>+#include<linux/irqchip/arm-gic.h>#include"vgic.h"+#define GICC_ARCH_VERSION_V2 0x2+/* common helpers */staticintvgic_ioaddr_overlap(structkvm*kvm)
@@ -252,6 +255,69 @@ void kvm_register_vgic_device(unsigned long type)}}+staticu32vgic_read_vcpuif(structkvm_vcpu*vcpu,intoffset)+{+structvgic_vmcrvmcr;+u32*field;++switch(offset){+caseGIC_CPU_CTRL:+field=&vmcr.ctlr;+break;+caseGIC_CPU_PRIMASK:+field=&vmcr.pmr;+break;+caseGIC_CPU_BINPOINT:+field=&vmcr.bpr;+break;+caseGIC_CPU_ALIAS_BINPOINT:+field=&vmcr.abpr;+break;+caseGIC_CPU_IDENT:+return(PRODUCT_ID_KVM<<20)|+(GICC_ARCH_VERSION_V2<<16)|+(IMPLEMENTER_ARM<<0);+default:+return0;+}++vgic_get_vmcr(vcpu,&vmcr);++return*field;+}++staticboolvgic_write_vcpuif(structkvm_vcpu*vcpu,intoffset,u32value)+{+structvgic_vmcrvmcr;+u32*field;++switch(offset){+caseGIC_CPU_CTRL:+field=&vmcr.ctlr;+break;+caseGIC_CPU_PRIMASK:+field=&vmcr.pmr;+break;+caseGIC_CPU_BINPOINT:+field=&vmcr.bpr;+break;+caseGIC_CPU_ALIAS_BINPOINT:+field=&vmcr.abpr;+break;+default:+returnfalse;+}++vgic_get_vmcr(vcpu,&vmcr);+if(*field==value)+returnfalse;++*field=value;+vgic_set_vmcr(vcpu,&vmcr);++returntrue;+}+/** vgic_attr_regs_access: allows user space to read/write VGIC registers**@dev:kvmdevicehandle
@@ -300,7 +366,11 @@ static int vgic_attr_regs_access(struct kvm_device *dev,switch(attr->group){caseKVM_DEV_ARM_VGIC_GRP_CPU_REGS:-ret=-EINVAL;+ret=0;+if(is_write)+vgic_write_vcpuif(vcpu,addr,*reg);+else+*reg=vgic_read_vcpuif(vcpu,addr);break;caseKVM_DEV_ARM_VGIC_GRP_DIST_REGS:ret=vgic_v2_dist_access(vcpu,is_write,addr,4,reg);
From: Eric Auger <redacted>
This patch implements the vgic_creation function which is
called on CREATE_IRQCHIP VM IOCTL (v2 only) or KVM_CREATE_DEVICE
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 1 +
virt/kvm/arm/vgic/vgic_init.c | 84 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 85 insertions(+)
From: Eric Auger <redacted>
This patch allocates and initializes the data structures used
to model the vgic distributor and virtual cpu interfaces. At that
stage the number of IRQs and number of virtual CPUs is frozen.
The following realy_init functions are kept since they are called from
arm.c. However they may disappear in subsequent patches since
they are void.
vgic_[v2|v3]_enable still is stubbed at this stage.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 7 +-
virt/kvm/arm/vgic/vgic-v2.c | 5 +
virt/kvm/arm/vgic/vgic-v3.c | 5 +
virt/kvm/arm/vgic/vgic.c | 5 +
virt/kvm/arm/vgic/vgic.h | 8 ++
virt/kvm/arm/vgic/vgic_init.c | 214 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 243 insertions(+), 1 deletion(-)
@@ -115,6 +115,7 @@ struct vgic_io_device {structvgic_dist{boolin_kernel;boolready;+boolinitialized;/* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */u32vgic_model;
@@ -268,6 +268,11 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,boollevel){structkvm_vcpu*vcpu;+intret;++ret=vgic_lazy_init(kvm);+if(ret)+returnret;vcpu=kvm_get_vcpu(kvm,cpuid);vgic_update_irq_pending(kvm,vcpu,intid,level);
From: Eric Auger <redacted>
map_resources is the last initialization step. It is executed on
1st VCPU run. At that stage the code checks the userspace has provided
the base addresses for the relevant VGIC regions, which depend on
the type of VGIC that is exposed to the guest.
The function also forces the vgic_init if it has not been executed yet
(only allowed for VGIC v2).
for GICv2, The VGIC CPU interface is mapped onto the GIC virtual CPU
interface.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 1 +
virt/kvm/arm/vgic/vgic-v2.c | 47 +++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 44 ++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 16 +++++++++++++++
virt/kvm/arm/vgic/vgic_init.c | 27 +++++++++++++++++++++++++
5 files changed, 135 insertions(+)
@@ -239,6 +239,53 @@ void vgic_v2_enable(struct kvm_vcpu *vcpu){}+intvgic_v2_map_resources(structkvm*kvm)+{+structvgic_dist*dist=&kvm->arch.vgic;+intret=0;++if(vgic_ready(kvm))+gotoout;++if(IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)||+IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)){+kvm_err("Need to set vgic cpu and dist addresses first\n");+ret=-ENXIO;+gotoout;+}++/*+*Initializethevgicifthishasn'talreadybeendoneondemandby+*accessingthevgicstatefromuserspace.+*/+ret=vgic_init(kvm);+if(ret){+kvm_err("Unable to initialize VGIC dynamic data structures\n");+gotoout;+}++ret=vgic_register_dist_regions(kvm,dist->vgic_dist_base,VGIC_V2);+if(ret){+kvm_err("Unable to register VGIC MMIO regions\n");+gotoout;+}++ret=kvm_phys_addr_ioremap(kvm,dist->vgic_cpu_base,+kvm_vgic_global_state.vcpu_base,+KVM_VGIC_V2_CPU_SIZE,true);+if(ret){+kvm_err("Unable to remap VGIC CPU to VCPU\n");+gotoout;+}++dist->ready=true;++out:+if(ret)+kvm_vgic_destroy(kvm);+returnret;+}+/***vgic_v2_probe-probeforaGICv2compatibleinterruptcontrollerinDT*@node:pointertotheDTnode
@@ -234,9 +234,18 @@ void vgic_v2_irq_change_affinity(struct kvm *kvm, u32 intid, u8 new_targets)spin_unlock(&irq->irq_lock);}-/* not yet implemented */voidvgic_v2_enable(structkvm_vcpu*vcpu){+/*+*ByforcingVMCRtozero,theGICwillrestorethebinary+*pointstotheirresetvalues.Anythingelseresetstozero+*anyway.+*/+vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr=0;+vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr=~0;++/* Get the show on the road... */+vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr=GICH_HCR_EN;}intvgic_v2_map_resources(structkvm*kvm)
@@ -220,9 +220,30 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)vmcrp->pmr=(vmcr&ICH_VMCR_PMR_MASK)>>ICH_VMCR_PMR_SHIFT;}-/* not yet implemented */voidvgic_v3_enable(structkvm_vcpu*vcpu){+structvgic_v3_cpu_if*vgic_v3=&vcpu->arch.vgic_cpu.vgic_v3;++/*+*ByforcingVMCRtozero,theGICwillrestorethebinary+*pointstotheirresetvalues.Anythingelseresetstozero+*anyway.+*/+vgic_v3->vgic_vmcr=0;+vgic_v3->vgic_elrsr=~0;++/*+*IfweareemulatingaGICv3,wedoitinannon-GICv2-compatible+*way,soweforceSREto1todemonstratethistotheguest.+*ThisgoeswiththespecallowingthevaluetobeRAO/WI.+*/+if(vcpu->kvm->arch.vgic.vgic_model==KVM_DEV_TYPE_ARM_VGIC_V3)+vgic_v3->vgic_sre=ICC_SRE_EL1_SRE;+else+vgic_v3->vgic_sre=0;++/* Get the show on the road... */+vgic_v3->vgic_hcr=ICH_HCR_EN;}intvgic_v3_map_resources(structkvm*kvm)
We now store the mapped hardware IRQ number in our struct, so we
don't need the irq_phys_map any longer for the new VGIC.
Implement the hardware IRQ mapping on top of the reworked arch
timer interface.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 15 ++++++++
virt/kvm/arm/vgic/vgic.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
@@ -276,6 +279,83 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,vcpu=kvm_get_vcpu(kvm,cpuid);vgic_update_irq_pending(kvm,vcpu,intid,level);++return0;+}++/**+*kvm_vgic_inject_mapped_irq-InjectahardwaremappedIRQtothevgic+*@kvm:TheVMstructurepointer+*@cpuid:TheCPUforPPIs+*@irq_num:TheINTIDtoinjectanewstateto.+*@level:Edge-triggered:true:totriggertheinterrupt+*false:toignorethecall+*Level-sensitivetrue:raisetheinputsignal+*false:lowertheinputsignal+*+*TheGICisnotconcernedwithdevicesbeingactive-LOWoractive-HIGHfor+*level-sensitiveinterrupts.Youcanthinkofthelevelparameteras1+*beingHIGHand0beingLOWandalldevicesbeingactive-HIGH.+*/+intkvm_vgic_inject_mapped_irq(structkvm*kvm,intcpuid,unsignedintintid,+boollevel)+{+structkvm_vcpu*vcpu;+intret;++ret=vgic_lazy_init(kvm);+if(ret)+returnret;++vcpu=kvm_get_vcpu(kvm,cpuid);+vgic_update_irq_pending(kvm,vcpu,intid,level);++return0;+}++structirq_phys_map*kvm_vgic_map_phys_irq(structkvm_vcpu*vcpu,+u32virt_irq,u32intid)+{+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,virt_irq);+structirq_desc*desc;+structirq_data*data;++BUG_ON(!irq);++desc=irq_to_desc(intid);+if(!desc){+kvm_err("%s: no interrupt descriptor\n",__func__);+returnNULL;+}++data=irq_desc_get_irq_data(desc);+while(data->parent_data)+data=data->parent_data;++spin_lock(&irq->irq_lock);++irq->hw=true;+irq->hwintid=data->hwirq;++spin_unlock(&irq->irq_lock);++returnNULL;+}++intkvm_vgic_unmap_phys_irq(structkvm_vcpu*vcpu,structirq_phys_map*map,+u32intid)+{+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid);++BUG_ON(!irq);++spin_lock(&irq->irq_lock);++irq->hw=false;+irq->hwintid=0;++spin_unlock(&irq->irq_lock);+return0;}
@@ -520,3 +600,15 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)returnpending;}++boolkvm_vgic_map_is_active(structkvm_vcpu*vcpu,u32intid)+{+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid);+boolmap_is_active;++spin_lock(&irq->irq_lock);+map_is_active=irq->hw&&irq->active;+spin_unlock(&irq->irq_lock);++returnmap_is_active;+}
Although we don't provide any virtual MSI functionality yet, we
need to implement the functions required by the KVM interface.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic_irqfd.c | 51 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 virt/kvm/arm/vgic/vgic_irqfd.c
Now that the new VGIC implementation has reached feature parity with
the old one, add the new files to the build system and add a Kconfig
option to switch between the two versions.
We set the default to the new version to get maximum test coverage,
in case people experience problems they can switch back to the old
behaviour if needed.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
arch/arm/kvm/Kconfig | 7 +++++++
arch/arm/kvm/Makefile | 10 ++++++++++
arch/arm64/kvm/Kconfig | 7 +++++++
arch/arm64/kvm/Makefile | 10 ++++++++++
4 files changed, 34 insertions(+)
Hi Andre,
On which kernel version should I apply these series ?
Thanks,
Diana
On 03/25/2016 04:05 AM, Andre Przywara wrote:
This series is a joint effort to re-implement KVM's GIC emulation.
While the current implementation is centered around providing
efficient MMIO emulation, the hot path for most guests is actually
the guest entry and exit, which currently is rather costly.
Also the existing emulation has a global distributor lock, which
quickly becomes a bottleneck once the number of VCPUs increases.
Additionally the emulation was originally designed for GICv2, adding
GICv3 ITS emulation support to this proved to be rather painful.
Last, but not least the existing code became less and less
maintainable, with many special cases handled explicitly.
The new implementation is build around a struct vgic_irq data data
structure, which holds all information about a virtual interrupt.
Interruts which should be injected are hold in a per-VCPU list, this
make the entry/exit path much more efficient. Also the new structure
allows to have more fine grained locking - per IRQ and per VCPU -
getting rid of the global distributor lock.
As a result of the new design ITS emulation fits in more nicely, the
respective code will be provided as a follow-up series.
This series implements the same feature set as the existing emulation,
as a goodie we now implement priorities correctly.
To allow an easy transition with good test coverage, but still maintain
stability, both implementations live side by side, selectable via a
Kconfig option. The default is the new implementation.
If this code proves to be reliable, we will later remove the current
implementation with an extra patch set.
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
Cheers,
Andre.
Andre Przywara (26):
KVM: arm/arm64: add missing MMIO data write-back
KVM: arm/arm64: pmu: abstract access to number of SPIs
KVM: arm/arm64: arch_timer: rework VGIC <-> timer interface
KVM: arm/arm64: vgic-new: Add MMIO handling framework
KVM: arm/arm64: vgic-new: Export register access interface
KVM: arm/arm64: vgic-new: Add CTLR, TYPER and IIDR handlers
KVM: arm/arm64: vgic-new: Add ENABLE registers handlers
KVM: arm/arm64: vgic-new: Add PENDING registers handlers
KVM: arm/arm64: vgic-new: Add PRIORITY registers handlers
KVM: arm/arm64: vgic-new: Add ACTIVE registers handlers
KVM: arm/arm64: vgic-new: Add CONFIG registers handlers
KVM: arm/arm64: vgic-new: Add TARGET registers handlers
KVM: arm/arm64: vgic-new: Add SGIR register handler
KVM: arm/arm64: vgic-new: Add SGIPENDR register handlers
KVM: arm/arm64: vgic-new: Add GICv3 emulation framework
KVM: arm/arm64: vgic-new: Add GICv3 CTLR, IIDR, TYPER handlers
KVM: arm/arm64: vgic-new: Add GICv3 redistributor TYPER handler
KVM: arm/arm64: vgic-new: Add GICv3 IDREGS register handler
KVM: arm/arm64: vgic-new: Add GICv3 IROUTER register handlers
KVM: arm/arm64: vgic-new: Add GICv3 SGI system register trap handler
KVM: arm/arm64: vgic-new: Add userland access to VGIC dist registers
KVM: arm/arm64: vgic-new: Add GICH_VMCR accessors
KVM: arm/arm64: vgic-new: Add userland GIC CPU interface access
KVM: arm/arm64: vgic-new: implement mapped IRQ handling
KVM: arm/arm64: vgic-new: Add dummy MSI implementation
KVM: arm/arm64: vgic-new: enable build
Christoffer Dall (5):
KVM: arm/arm64: vgic-new: Add data structure definitions
KVM: arm/arm64: vgic-new: Add acccessor to new struct vgic_irq
instance
KVM: arm/arm64: vgic-new: Implement virtual IRQ injection
KVM: arm/arm64: vgic-new: Add vgic GICv2 change_affinity
KVM: arm/arm64: vgic-new: Add IRQ sorting
Eric Auger (12):
KVM: arm/arm64: vgic-new: Implement kvm_vgic_vcpu_pending_irq
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM device ops registration
KVM: arm/arm64: vgic-new: vgic_kvm_device:
KVM_DEV_ARM_VGIC_GRP_NR_IRQS
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM_DEV_ARM_VGIC_GRP_CTRL
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM_DEV_ARM_VGIC_GRP_ADDR
KVM: arm/arm64: vgic-new: vgic_kvm_device: access to VGIC registers
KVM: arm/arm64: vgic-new: vgic_kvm_device: implement kvm_vgic_addr
KVM: arm/arm64: vgic-new: vgic_init: implement kvm_vgic_hyp_init
KVM: arm/arm64: vgic-new: vgic_init: implement vgic_create
KVM: arm/arm64: vgic-new: vgic_init: implement vgic_init
KVM: arm/arm64: vgic-new: vgic_init: implement map_resources
KVM: arm/arm64: vgic-new: Add vgic_v2/v3_enable
Marc Zyngier (2):
KVM: arm/arm64: vgic-new: Add GICv2 IRQ sync/flush
KVM: arm/arm64: vgic-new: Add GICv3 world switch backend
arch/arm/kvm/Kconfig | 7 +
arch/arm/kvm/Makefile | 10 +
arch/arm/kvm/mmio.c | 2 +-
arch/arm64/kvm/Kconfig | 7 +
arch/arm64/kvm/Makefile | 10 +
include/kvm/arm_vgic.h | 14 +-
include/kvm/vgic/vgic.h | 256 +++++++
virt/kvm/arm/arch_timer.c | 11 +-
virt/kvm/arm/pmu.c | 2 +-
virt/kvm/arm/vgic.c | 18 +-
virt/kvm/arm/vgic/vgic-v2.c | 381 +++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 357 ++++++++++
virt/kvm/arm/vgic/vgic.c | 614 +++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 136 ++++
virt/kvm/arm/vgic/vgic_init.c | 447 ++++++++++++
virt/kvm/arm/vgic/vgic_irqfd.c | 51 ++
virt/kvm/arm/vgic/vgic_kvm_device.c | 522 ++++++++++++++
virt/kvm/arm/vgic/vgic_mmio.c | 1277 +++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic_mmio.h | 47 ++
19 files changed, 4149 insertions(+), 20 deletions(-)
create mode 100644 include/kvm/vgic/vgic.h
create mode 100644 virt/kvm/arm/vgic/vgic-v2.c
create mode 100644 virt/kvm/arm/vgic/vgic-v3.c
create mode 100644 virt/kvm/arm/vgic/vgic.c
create mode 100644 virt/kvm/arm/vgic/vgic.h
create mode 100644 virt/kvm/arm/vgic/vgic_init.c
create mode 100644 virt/kvm/arm/vgic/vgic_irqfd.c
create mode 100644 virt/kvm/arm/vgic/vgic_kvm_device.c
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.c
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.h
This series is a joint effort to re-implement KVM's GIC emulation.
Forgot to mention:
This series is based on an older version of kvmarm.git/next, which is
roughly the pull request that got merged into 4.6-rc0. It applies to the
current master, so I suggest people use 4.6-rc1 as a base once this is
released.
A git tree containing this series and the prerequisites can be found on
linux-arm.org:
git://linux-arm.org/linux-ap.git
http://www.linux-arm.org/git?p=linux-ap.git;a=log;h=refs/heads/vgic-new/rfc
Cheers,
Andre.
While the current implementation is centered around providing
efficient MMIO emulation, the hot path for most guests is actually
the guest entry and exit, which currently is rather costly.
Also the existing emulation has a global distributor lock, which
quickly becomes a bottleneck once the number of VCPUs increases.
Additionally the emulation was originally designed for GICv2, adding
GICv3 ITS emulation support to this proved to be rather painful.
Last, but not least the existing code became less and less
maintainable, with many special cases handled explicitly.
The new implementation is build around a struct vgic_irq data data
structure, which holds all information about a virtual interrupt.
Interruts which should be injected are hold in a per-VCPU list, this
make the entry/exit path much more efficient. Also the new structure
allows to have more fine grained locking - per IRQ and per VCPU -
getting rid of the global distributor lock.
As a result of the new design ITS emulation fits in more nicely, the
respective code will be provided as a follow-up series.
This series implements the same feature set as the existing emulation,
as a goodie we now implement priorities correctly.
To allow an easy transition with good test coverage, but still maintain
stability, both implementations live side by side, selectable via a
Kconfig option. The default is the new implementation.
If this code proves to be reliable, we will later remove the current
implementation with an extra patch set.
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
Cheers,
Andre.
Andre Przywara (26):
KVM: arm/arm64: add missing MMIO data write-back
KVM: arm/arm64: pmu: abstract access to number of SPIs
KVM: arm/arm64: arch_timer: rework VGIC <-> timer interface
KVM: arm/arm64: vgic-new: Add MMIO handling framework
KVM: arm/arm64: vgic-new: Export register access interface
KVM: arm/arm64: vgic-new: Add CTLR, TYPER and IIDR handlers
KVM: arm/arm64: vgic-new: Add ENABLE registers handlers
KVM: arm/arm64: vgic-new: Add PENDING registers handlers
KVM: arm/arm64: vgic-new: Add PRIORITY registers handlers
KVM: arm/arm64: vgic-new: Add ACTIVE registers handlers
KVM: arm/arm64: vgic-new: Add CONFIG registers handlers
KVM: arm/arm64: vgic-new: Add TARGET registers handlers
KVM: arm/arm64: vgic-new: Add SGIR register handler
KVM: arm/arm64: vgic-new: Add SGIPENDR register handlers
KVM: arm/arm64: vgic-new: Add GICv3 emulation framework
KVM: arm/arm64: vgic-new: Add GICv3 CTLR, IIDR, TYPER handlers
KVM: arm/arm64: vgic-new: Add GICv3 redistributor TYPER handler
KVM: arm/arm64: vgic-new: Add GICv3 IDREGS register handler
KVM: arm/arm64: vgic-new: Add GICv3 IROUTER register handlers
KVM: arm/arm64: vgic-new: Add GICv3 SGI system register trap handler
KVM: arm/arm64: vgic-new: Add userland access to VGIC dist registers
KVM: arm/arm64: vgic-new: Add GICH_VMCR accessors
KVM: arm/arm64: vgic-new: Add userland GIC CPU interface access
KVM: arm/arm64: vgic-new: implement mapped IRQ handling
KVM: arm/arm64: vgic-new: Add dummy MSI implementation
KVM: arm/arm64: vgic-new: enable build
Christoffer Dall (5):
KVM: arm/arm64: vgic-new: Add data structure definitions
KVM: arm/arm64: vgic-new: Add acccessor to new struct vgic_irq
instance
KVM: arm/arm64: vgic-new: Implement virtual IRQ injection
KVM: arm/arm64: vgic-new: Add vgic GICv2 change_affinity
KVM: arm/arm64: vgic-new: Add IRQ sorting
Eric Auger (12):
KVM: arm/arm64: vgic-new: Implement kvm_vgic_vcpu_pending_irq
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM device ops registration
KVM: arm/arm64: vgic-new: vgic_kvm_device:
KVM_DEV_ARM_VGIC_GRP_NR_IRQS
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM_DEV_ARM_VGIC_GRP_CTRL
KVM: arm/arm64: vgic-new: vgic_kvm_device: KVM_DEV_ARM_VGIC_GRP_ADDR
KVM: arm/arm64: vgic-new: vgic_kvm_device: access to VGIC registers
KVM: arm/arm64: vgic-new: vgic_kvm_device: implement kvm_vgic_addr
KVM: arm/arm64: vgic-new: vgic_init: implement kvm_vgic_hyp_init
KVM: arm/arm64: vgic-new: vgic_init: implement vgic_create
KVM: arm/arm64: vgic-new: vgic_init: implement vgic_init
KVM: arm/arm64: vgic-new: vgic_init: implement map_resources
KVM: arm/arm64: vgic-new: Add vgic_v2/v3_enable
Marc Zyngier (2):
KVM: arm/arm64: vgic-new: Add GICv2 IRQ sync/flush
KVM: arm/arm64: vgic-new: Add GICv3 world switch backend
arch/arm/kvm/Kconfig | 7 +
arch/arm/kvm/Makefile | 10 +
arch/arm/kvm/mmio.c | 2 +-
arch/arm64/kvm/Kconfig | 7 +
arch/arm64/kvm/Makefile | 10 +
include/kvm/arm_vgic.h | 14 +-
include/kvm/vgic/vgic.h | 256 +++++++
virt/kvm/arm/arch_timer.c | 11 +-
virt/kvm/arm/pmu.c | 2 +-
virt/kvm/arm/vgic.c | 18 +-
virt/kvm/arm/vgic/vgic-v2.c | 381 +++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 357 ++++++++++
virt/kvm/arm/vgic/vgic.c | 614 +++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 136 ++++
virt/kvm/arm/vgic/vgic_init.c | 447 ++++++++++++
virt/kvm/arm/vgic/vgic_irqfd.c | 51 ++
virt/kvm/arm/vgic/vgic_kvm_device.c | 522 ++++++++++++++
virt/kvm/arm/vgic/vgic_mmio.c | 1277 +++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic_mmio.h | 47 ++
19 files changed, 4149 insertions(+), 20 deletions(-)
create mode 100644 include/kvm/vgic/vgic.h
create mode 100644 virt/kvm/arm/vgic/vgic-v2.c
create mode 100644 virt/kvm/arm/vgic/vgic-v3.c
create mode 100644 virt/kvm/arm/vgic/vgic.c
create mode 100644 virt/kvm/arm/vgic/vgic.h
create mode 100644 virt/kvm/arm/vgic/vgic_init.c
create mode 100644 virt/kvm/arm/vgic/vgic_irqfd.c
create mode 100644 virt/kvm/arm/vgic/vgic_kvm_device.c
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.c
create mode 100644 virt/kvm/arm/vgic/vgic_mmio.h
From: Christoffer Dall <hidden> Date: 2016-03-29 12:33:08
On Fri, Mar 25, 2016 at 02:04:24AM +0000, Andre Przywara wrote:
quoted hunk
When the kernel was handling a guest MMIO access internally, we need
to copy the emulation result into the run->mmio structure in order
for the kvm_handle_mmio_return() function to pick it up and inject
the result back into the guest.
Currently the only user of kvm_io_bus for ARM is the VGIC, which did
this copying itself, so this was not causing issues so far.
But with upcoming kvm_io_bus users we need to do the copying here.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
arch/arm/kvm/mmio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
@@ -87,11 +87,10 @@ static unsigned long mmio_read_buf(char *buf, unsigned int len)/***kvm_handle_mmio_return--HandleMMIOloadsafteruserspaceemulation+*orin-kernelIOemulation+**@vcpu:TheVCPUpointer*@run:TheVCPUrunstructcontainingthemmiodata-*-*ThisshouldonlybecalledafterreturningfromuserspaceforMMIOload-*emulation.*/intkvm_handle_mmio_return(structkvm_vcpu*vcpu,structkvm_run*run){
@@ -206,18 +205,19 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,run->mmio.is_write=is_write;run->mmio.phys_addr=fault_ipa;run->mmio.len=len;-if(is_write)-memcpy(run->mmio.data,data_buf,len);if(!ret){/* We handled the access successfully in the kernel. */+if(!is_write)+memcpy(run->mmio.data,data_buf,len);vcpu->stat.mmio_exit_kernel++;kvm_handle_mmio_return(vcpu,run);return1;-}else{-vcpu->stat.mmio_exit_user++;}+if(is_write)+memcpy(run->mmio.data,data_buf,len);+vcpu->stat.mmio_exit_user++;run->exit_reason=KVM_EXIT_MMIO;return0;}
From: Christoffer Dall <hidden> Date: 2016-03-29 13:01:35
On Fri, Mar 25, 2016 at 02:04:26AM +0000, Andre Przywara wrote:
quoted hunk
Adapt the interface between the virtualized arch timer and the
emulated VGIC to avoid the phys_map when possible.
This prepares the arch timer to go with both the existing VGIC
implementation and the new version later without too many code
changes.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/arm_vgic.h | 7 ++++---
virt/kvm/arm/arch_timer.c | 11 +++++------
virt/kvm/arm/vgic.c | 18 +++++++++---------
3 files changed, 18 insertions(+), 18 deletions(-)
I don't understand the benefit of this change. You're now passing the
virt_irq in both the struct and as a parameter.
If you want to get rid of the irq_phys_map structure, just replace all
occurences of it with two parameters/arguments instead, int virt_irq,
int phys_irq, both of which are INTIDs.
You don't need to pass the Linux IRQ number from outside the vgic to the
vgic anymore, and it's just there for historical reasons, so you can
remove that with a separate patch if you want.
If you want me to send a patch removing the IRQ field, let me know. It
looks something like this:
@@ -345,7 +344,7 @@ int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid,voidvgic_v3_dispatch_sgi(structkvm_vcpu*vcpu,u64reg);intkvm_vgic_vcpu_pending_irq(structkvm_vcpu*vcpu);structirq_phys_map*kvm_vgic_map_phys_irq(structkvm_vcpu*vcpu,-intvirt_irq,intirq);+intvirt_irq,intphys_irq);intkvm_vgic_unmap_phys_irq(structkvm_vcpu*vcpu,structirq_phys_map*map);boolkvm_vgic_map_is_active(structkvm_vcpu*vcpu,structirq_phys_map*map);
From: Christoffer Dall <hidden> Date: 2016-03-29 13:09:22
On Fri, Mar 25, 2016 at 02:04:27AM +0000, Andre Przywara wrote:
quoted hunk
From: Christoffer Dall <redacted>
Add a new header file for the new and improved GIC implementation.
The big change is that we now have a struct vgic_irq per IRQ instead
of spreading all the information over various bitmaps.
We include this new header conditionally from within the old header
file for the time being to avoid touching all the users.
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/arm_vgic.h | 5 ++
include/kvm/vgic/vgic.h | 198 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 203 insertions(+)
create mode 100644 include/kvm/vgic/vgic.h
@@ -0,0 +1,198 @@+/*+*Copyright(C)2015,2016ARMLtd.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram.Ifnot,see<http://www.gnu.org/licenses/>.+*/+#ifndef __ASM_ARM_KVM_VGIC_VGIC_H+#define __ASM_ARM_KVM_VGIC_VGIC_H++#include<linux/kernel.h>+#include<linux/kvm.h>+#include<linux/irqreturn.h>+#include<linux/spinlock.h>+#include<linux/types.h>+#include<kvm/iodev.h>++#define VGIC_V3_MAX_CPUS 255+#define VGIC_V2_MAX_CPUS 8+#define VGIC_NR_IRQS_LEGACY 256+#define VGIC_NR_SGIS 16+#define VGIC_NR_PPIS 16+#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS)+#define VGIC_MAX_PRIVATE (VGIC_NR_PRIVATE_IRQS - 1)+#define VGIC_MAX_SPI 1019+#define VGIC_MAX_RESERVED 1023+#define VGIC_MIN_LPI 8192++enumvgic_type{+VGIC_V2,/* Good ol' GICv2 */+VGIC_V3,/* New fancy GICv3 */+};++/* same for all guests, as depending only on the _host's_ GIC model */+structvgic_global{+/* type of the host GIC */+enumvgic_typetype;++/* Physical address of vgic virtual cpu interface */+phys_addr_tvcpu_base;++/* virtual control interface mapping */+void__iomem*vctrl_base;++/* Number of implemented list registers */+intnr_lr;++/* Maintenance IRQ number */+unsignedintmaint_irq;++/* maximum number of VCPUs allowed (GICv2 limits us to 8) */+intmax_gic_vcpus;++/* Only needed for the legacy KVM_CREATE_IRQCHIP */+boolcan_emulate_gicv2;+};++externstructvgic_globalkvm_vgic_global_state;++#define VGIC_V2_MAX_LRS (1 << 6)+#define VGIC_V3_MAX_LRS 16+#define VGIC_V3_LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)++enumvgic_irq_config{+VGIC_CONFIG_EDGE=0,+VGIC_CONFIG_LEVEL+};++structvgic_irq{+spinlock_tirq_lock;/* Protects the content of the struct */+structlist_headap_list;++structkvm_vcpu*vcpu;/* SGIs and PPIs: The VCPU+*SPIsandLPIs:TheVCPUwhoseap_list+*onwhichthisisqueued.+*/++structkvm_vcpu*target_vcpu;/* The VCPU that this interrupt should+*besendto,asaresultofthe+*targetsreg(v2)orthe+*affinityreg(v3).+*/++u32intid;/* Guest visible INTID */+boolpending;+boolline_level;/* Level only */+boolsoft_pending;/* Level only */+boolactive;/* not used for LPIs */+boolenabled;+boolhw;/* Tied to HW IRQ */+u32hwintid;/* HW INTID number */+union{+u8targets;/* GICv2 target VCPUs mask */+u32mpidr;/* GICv3 target VCPU */+};+u8source;/* GICv2 SGIs only */+u8priority;+enumvgic_irq_configconfig;/* Level or edge */+};++structvgic_dist{+boolin_kernel;+boolready;++/* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */+u32vgic_model;++intnr_spis;++/* TODO: Consider moving to global state */+/* Virtual control interface mapping */+void__iomem*vctrl_base;++/* base addresses in guest physical address space: */+gpa_tvgic_dist_base;/* distributor */+union{+/* either a GICv2 CPU interface */+gpa_tvgic_cpu_base;+/* or a number of GICv3 redistributor regions */+gpa_tvgic_redist_base;+};++/* distributor enabled */+u32enabled;++structvgic_irq*spis;+};++structvgic_v2_cpu_if{+u32vgic_hcr;+u32vgic_vmcr;+u32vgic_misr;/* Saved only */+u64vgic_eisr;/* Saved only */+u64vgic_elrsr;/* Saved only */+u32vgic_apr;+u32vgic_lr[VGIC_V2_MAX_LRS];+};++structvgic_v3_cpu_if{+#ifdef CONFIG_KVM_ARM_VGIC_V3+u32vgic_hcr;+u32vgic_vmcr;+u32vgic_sre;/* Restored only, change ignored */+u32vgic_misr;/* Saved only */+u32vgic_eisr;/* Saved only */+u32vgic_elrsr;/* Saved only */+u32vgic_ap0r[4];+u32vgic_ap1r[4];+u64vgic_lr[VGIC_V3_MAX_LRS];+#endif+};++structvgic_cpu{+/* CPU vif control registers for world switch */+union{+structvgic_v2_cpu_ifvgic_v2;+structvgic_v3_cpu_ifvgic_v3;+};++/* TODO: Move nr_lr to a global state */
what is our current plan and status about this TODO?
+ /* Number of list registers on this CPU */
+ int nr_lr;
+
+ unsigned int used_lrs;
+ struct vgic_irq private_irqs[VGIC_NR_PRIVATE_IRQS];
+
+ spinlock_t ap_list_lock; /* Protects the ap_list */
+
+ /* list of IRQs for this VCPU to consider */
/*
* List of IRQs that this VCPU should consider because they are either
* Active or Pending (hence the name; AP list), or because they recently
* were one of the two and need to be migrated off this list to another
* VCPU.
*/
+ struct list_head ap_list_head;
+};
+
+#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
+#define vgic_initialized(k) (false)
+#define vgic_ready(k) ((k)->arch.vgic.ready)
+#define vgic_valid_spi(k,i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \
+ ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS))
+
+/**
+ * kvm_vgic_get_max_vcpus - Get the maximum number of VCPUs allowed by HW
+ *
+ * The host's GIC naturally limits the maximum amount of VCPUs a guest
+ * can use.
+ */
+static inline int kvm_vgic_get_max_vcpus(void)
+{
+ return kvm_vgic_global_state.max_gic_vcpus;
+}
+
+#endif /* __ASM_ARM_KVM_VGIC_VGIC_H */
--
2.7.3
From: Vladimir Murzin <hidden> Date: 2016-03-29 13:12:09
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
for --cpus 58 and above, where with the old vgic it is quite happy with
up to 255 cpus.
Cheers
Vladimir
From: Christoffer Dall <hidden> Date: 2016-03-29 21:16:50
On Fri, Mar 25, 2016 at 02:04:29AM +0000, Andre Przywara wrote:
From: Christoffer Dall <redacted>
Provide a vgic_queue_irq() function which decides whether a given
IRQ needs to be queued to a VCPU's ap_list.
This should be called whenever an IRQ became pending or got enabled,
becomes pending or enabled,
either as a result of userspace injection, from in-kernel emulated
devices like the architected timer or from MMIO accesses to the
distributor emulation.
Also provides the necessary functions to allow userland to inject an
IRQ to a guest.
Since this is the first code that starts using our locking mechanism, we
add some (hopefully) clear documentation of our locking strategy and
requirements along with this patch.
quoted hunk
[Andre: refactor out vgic_queue_irq()]
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 3 +
virt/kvm/arm/vgic/vgic.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 1 +
3 files changed, 185 insertions(+)
@@ -39,3 +56,167 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,WARN(1,"Looking up struct vgic_irq for reserved INTID");returnNULL;}++/**+*kvm_vgic_target_oracle-computethetargetvcpuforanirq+*+*@irq:Theirqtoroute.Mustbealreadylocked.+*+*Basedonthecurrentstateoftheinterrupt(enabled,pending,+*active,vcpuandtarget_vcpu),computethenextvcputhisshouldbe+*givento.ReturnNULLifthisshouldn'tbeinjectedatall.+*/+staticstructkvm_vcpu*vgic_target_oracle(structvgic_irq*irq)+{+/* If the interrupt is active, it must stay on the current vcpu */+if(irq->active)+returnirq->vcpu;
we are not taking a lock here. What are the locking expectations? If
the expectarions are that the IRQ is locked when calling this function,
can we have a BIG FAT COMMENT saying that then?
It seems to me that we are somehow expecting irq->active and irq->vcpu
to be in sync, but that's not necessarily the case if the IRQ is not
locked.
+
+ /* If enabled and pending, it can migrate to a new one */
I think this comment should be rewritten to:
If the IRQ is not active but enabled and pending, we should direct it to
its configured target VCPU.
+ if (irq->enabled && irq->pending)
+ return irq->target_vcpu;
+
+ /* Otherwise, it is considered idle */
not sure what idle means here, I suggest something like:
If neither active nor pending and enabled, then this IRQ should not be
queued to any VCPU.
+ return NULL;
+}
+
+/*
+ * Only valid injection if changing level for level-triggered IRQs or for a
+ * rising edge.
+ */
+static bool vgic_validate_injection(struct vgic_irq *irq, bool level)
+{
+ switch (irq->config) {
+ case VGIC_CONFIG_LEVEL:
+ return irq->line_level != level;
+ case VGIC_CONFIG_EDGE:
+ return level;
+ default:
+ BUG();
is the default case there for making the compiler happy or can we just
get rid of it?
+ }
+}
+
+/*
+ * Check whether an IRQ needs to (and can) be queued to a VCPU's ap list.
+ * Do the queuing if necessary, taking the right locks in the right order.
+ * Returns true when the IRQ was queued, false otherwise.
+ *
+ * Needs to be entered with the IRQ lock already held, but will return
+ * with all locks dropped.
+ */
+bool vgic_queue_irq(struct kvm *kvm, struct vgic_irq *irq)
should we have something like BUG_ON(!spin_is_locked(irq->irq_lock));
here?
Not sure if there's some bug checking here which is only emitted if a
user select CONFIG_CHECK_SOME_LOCKING_THINGS that we could use...?
+
+ if (irq->vcpu || !(irq->pending && irq->enabled) || !vcpu) {
+ /*
+ * If this IRQ is already on a VCPU's ap_list, then it
+ * cannot be moved or modified and there is no more work for
+ * us to do.
+ *
+ * Otherwise, if the irq is not pending and enabled, it does
+ * not need to be inserted into an ap_list and there is also
+ * no more work for us to do.
+ */
is the !vcpu check here not redundant because if you ever get to
evaluating it, then irq->vcpu is null, and pending and enabled are set,
which means the oracle couldn't have returned null, could it?
that would also explain why we don't have to re-check the same
conditions below...
or am I getting this wrong, because you could also have someone
explicitly setting the IRQ to active via trapped MMIO, in which case we
should be able to queue it without it being pending && enabled, which
would indicate that it's the other way around, you should only evaluate
!vcpu and kup the !(pending && enabled) part....?
+ spin_unlock(&irq->irq_lock);
+ return false;
+ }
+
+ /*
+ * We must unlock the irq lock to take the ap_list_lock where
+ * we are going to insert this new pending interrupt.
+ */
+ spin_unlock(&irq->irq_lock);
+
+ /* someone can do stuff here, which we re-check below */
+retry:
+ spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
+ spin_lock(&irq->irq_lock);
+
+ /*
+ * Did something change behind our backs?
+ *
+ * There are two cases:
+ * 1) The irq became pending or active behind our backs and/or
+ * the irq->vcpu field was set correspondingly when putting
+ * the irq on an ap_list. Then drop the locks and return.
+ * 2) Someone changed the affinity on this irq behind our
+ * backs and we are now holding the wrong ap_list_lock.
+ * Then drop the locks and try the new VCPU.
+ */
+ if (irq->vcpu || !(irq->pending && irq->enabled)) {
here I'm concerned about the active state again.
I feel like something more similar to my initial version of this patch
is what we really want:
if (irq->vcpu || vcpu != vgic_target_oracle(irq))
goto real_retry;
and read_retry is then a label at the very top of this function, before
the initial call to vgic_target_oracle()....
I don't remember why we thought it was a good idea to have this BUG_ON()
anymore. Anyone?
+
+ spin_lock(&irq->irq_lock);
+
+ if (!vgic_validate_injection(irq, level)) {
+ /* Nothing to see here, move along... */
+ spin_unlock(&irq->irq_lock);
+ return;
+ }
+
+ if (irq->config == VGIC_CONFIG_LEVEL) {
+ irq->line_level = level;
+ irq->pending = level || irq->soft_pending;
+ } else {
+ irq->pending = true;
+ }
+
+ vgic_queue_irq(kvm, irq);
+}
+
+/**
+ * kvm_vgic_inject_irq - Inject an IRQ from a device to the vgic
+ * @kvm: The VM structure pointer
+ * @cpuid: The CPU for PPIs
+ * @intid: The INTID to inject a new state to.
+ * must not be mapped to a HW interrupt.
stray line here? I don't understand this bit about 'must not be mapped'
and I think that should be moved to the explanation below with some
rationale, and if important, perhaps guarded with a BUG_ON() ?
+ * @level: Edge-triggered: true: to trigger the interrupt
+ * false: to ignore the call
+ * Level-sensitive true: raise the input signal
+ * false: lower the input signal
+ *
+ * The GIC is not concerned with devices being active-LOW or active-HIGH for
We should probably write VGIC here instead of GIC, just to avoid
confusion.
quoted hunk
+ * level-sensitive interrupts. You can think of the level parameter as 1
+ * being HIGH and 0 being LOW and all devices being active-HIGH.
+ */
+int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
+ bool level)
+{
+ struct kvm_vcpu *vcpu;
+
+ vcpu = kvm_get_vcpu(kvm, cpuid);
+ vgic_update_irq_pending(kvm, vcpu, intid, level);
+ return 0;
+}
Otherwise the split between update/queue looks reasonable here.
Btw., anywhere where I write 'you' in this mail, I mean 'we' and take
partial blame for any bugs here :)
Thanks,
-Christoffer
From: Vladimir Murzin <hidden> Date: 2016-03-30 11:42:58
On 29/03/16 14:12, Vladimir Murzin wrote:
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
quoted
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
It comes from kvm_io_bus_register_dev()
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
with bus->dev_count being 1000
[<ffffffc00009f93c>] kvm_io_bus_register_dev+0x130/0x148
[<ffffffc0000ad274>] kvm_vgic_register_mmio_region+0xac/0xdc
[<ffffffc0000ad4d4>] vgic_register_redist_regions+0xb8/0x158
[<ffffffc0000abb90>] vgic_v3_map_resources+0x5c/0xf0
[<ffffffc0000aae04>] kvm_vgic_map_resources+0x40/0x84
[<ffffffc0000a23d0>] kvm_arch_vcpu_ioctl_run+0x3f0/0x400
[<ffffffc00009d484>] kvm_vcpu_ioctl+0x2d4/0x6ec
[<ffffffc0001c4eec>] do_vfs_ioctl+0xb4/0x760
[<ffffffc0001c561c>] SyS_ioctl+0x84/0x98
[<ffffffc000085d30>] el0_svc_naked+0x24/0x28
Hope this helps...
Vladimir
for --cpus 58 and above, where with the old vgic it is quite happy with
up to 255 cpus.
Cheers
Vladimir
_______________________________________________
kvmarm mailing list
kvmarm at lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
From: Vladimir Murzin <hidden> Date: 2016-03-30 11:52:27
On 30/03/16 12:42, Vladimir Murzin wrote:
On 29/03/16 14:12, Vladimir Murzin wrote:
quoted
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
quoted
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
It comes from kvm_io_bus_register_dev()
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
with bus->dev_count being 1000
[<ffffffc00009f93c>] kvm_io_bus_register_dev+0x130/0x148
[<ffffffc0000ad274>] kvm_vgic_register_mmio_region+0xac/0xdc
[<ffffffc0000ad4d4>] vgic_register_redist_regions+0xb8/0x158
[<ffffffc0000abb90>] vgic_v3_map_resources+0x5c/0xf0
[<ffffffc0000aae04>] kvm_vgic_map_resources+0x40/0x84
[<ffffffc0000a23d0>] kvm_arch_vcpu_ioctl_run+0x3f0/0x400
[<ffffffc00009d484>] kvm_vcpu_ioctl+0x2d4/0x6ec
[<ffffffc0001c4eec>] do_vfs_ioctl+0xb4/0x760
[<ffffffc0001c561c>] SyS_ioctl+0x84/0x98
[<ffffffc000085d30>] el0_svc_naked+0x24/0x28
and one more thing I've forgotten to mention... something odd happens on
destroy path
INFO: task kvm-vcpu-0:1123 blocked for more than 120 seconds.
Tainted: G W 4.5.0-rc6+ #432
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
kvm-vcpu-0 D ffffffc000086bcc 0 1123 1114 0x00000008
Call trace:
[<ffffffc000086bcc>] __switch_to+0x90/0xa4
[<ffffffc0006c4390>] __schedule+0x188/0x59c
[<ffffffc0006c47e0>] schedule+0x3c/0xa0
[<ffffffc0006c4bf4>] schedule_preempt_disabled+0x20/0x38
[<ffffffc0006c618c>] __mutex_lock_slowpath+0xc4/0x148
[<ffffffc0006c6254>] mutex_lock+0x44/0x5c
[<ffffffc0000aacb8>] kvm_vgic_destroy+0x20/0xb0
[<ffffffc0000abbdc>] vgic_v3_map_resources+0xa8/0xf0
[<ffffffc0000aae04>] kvm_vgic_map_resources+0x40/0x84
[<ffffffc0000a23d0>] kvm_arch_vcpu_ioctl_run+0x3f0/0x400
[<ffffffc00009d484>] kvm_vcpu_ioctl+0x2d4/0x6ec
[<ffffffc0001c4eec>] do_vfs_ioctl+0xb4/0x760
[<ffffffc0001c561c>] SyS_ioctl+0x84/0x98
[<ffffffc000085d30>] el0_svc_naked+0x24/0x28
Vladimir
Hope this helps...
Vladimir
quoted
for --cpus 58 and above, where with the old vgic it is quite happy with
up to 255 cpus.
Cheers
Vladimir
_______________________________________________
kvmarm mailing list
kvmarm at lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
From: Marc Zyngier <hidden> Date: 2016-03-30 12:07:06
On 30/03/16 12:42, Vladimir Murzin wrote:
On 29/03/16 14:12, Vladimir Murzin wrote:
quoted
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
quoted
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
It comes from kvm_io_bus_register_dev()
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
with bus->dev_count being 1000
Ouch. That's a consequence of having one "device" per actual register
set, I believe. So either we bump this limit up by a factor 10 (at
least), or we switch back to the old way of handling access to the GIC
regions (big "catch-all", and further demuxing).
Thoughts?
Thanks,
M.
--
Jazz is not dead. It just smells funny...
From: Christoffer Dall <hidden> Date: 2016-03-30 13:53:30
On Fri, Mar 25, 2016 at 02:04:32AM +0000, Andre Przywara wrote:
From: Marc Zyngier <redacted>
Implement the functionality for syncing IRQs between our emulation
and the list registers, which represent the guest's view of IRQs.
This is done in kvm_vgic_flush_hwstate and kvm_vgic_sync_hwstate,
which gets called on guest entry and exit.
I thought we agreed to split this up in a generic part and then GICv2
and GICv3 parts following, but ok, I'll look through this code, but I
strongly suggest splitting it up for the next posting.
quoted hunk
Signed-off-by: Marc Zyngier <redacted>
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 4 +
virt/kvm/arm/vgic/vgic-v2.c | 161 ++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 204 ++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 4 +
4 files changed, 373 insertions(+)
we just had a warning above if the LR state was set, so how do we
expect this to be modified in the mean time?
The following line caters for the famous hardware race, right? If so, I think it deserved a comment:
/*
* The hardware doesn't guarantee that the LR's bit is set in the ELRSR
* despite the virtual interrupt being EOIed and generating a
* maintenance interrupt. Force the bit to be set.
*/
I think the above should be moved to fold_lr_state
+
+ /*
+ * In the next iterations of the vcpu loop, if we sync the
+ * vgic state after flushing it, but before entering the guest
+ * (this happens for pending signals and vmid rollovers), then
+ * make sure we don't pick up any old maintenance interrupts
+ * here.
+ */
+ cpuif->vgic_eisr = 0;
+}
+
+void vgic_v2_set_underflow(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
+
+ cpuif->vgic_hcr |= GICH_HCR_UIE;
+}
+
+/*
+ * transfer the content of the LRs back into the corresponding ap_list:
+ * - active bit is transferred as is
+ * - pending bit is
+ * - transferred as is in case of edge sensitive IRQs
+ * - set to the line-level (resample time) for level sensitive IRQs
+ */
+void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
+ int lr;
+
+ for (lr = 0; lr < vcpu->arch.vgic_cpu.used_lrs; lr++) {
+ u32 val = cpuif->vgic_lr[lr];
+ u32 intid = val & GICH_LR_VIRTUALID;
+ struct vgic_irq *irq;
+
+ irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
+
+ spin_lock(&irq->irq_lock);
+
+ /* Always preserve the active bit */
+ irq->active = !!(val & GICH_LR_ACTIVE_BIT);
+
+ /* Edge is the only case where we preserve the pending bit */
+ if (irq->config == VGIC_CONFIG_EDGE &&
+ (val & GICH_LR_PENDING_BIT)) {
+ irq->pending = true;
+
+ if (intid < VGIC_NR_SGIS) {
+ u32 cpuid = val & GICH_LR_PHYSID_CPUID;
+
+ cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;
Are we happy with relying on all the remaining bits being 0 here or
should we define a proper CPUID mask?
+ irq->source |= (1 << cpuid);
+ }
+ }
+
+ /* Clear soft pending state when level IRQs have been acked */
+ if (irq->config == VGIC_CONFIG_LEVEL &&
+ !(val & GICH_LR_PENDING_BIT)) {
+ irq->soft_pending = false;
+ irq->pending = irq->line_level;
+ }
+
+ spin_unlock(&irq->irq_lock);
+ }
+}
+
+/*
+ * Populates the particular LR with the state of a given IRQ:
+ * - for an edge sensitive IRQ the pending state is reset in the struct
@@ -273,3 +273,207 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,vgic_update_irq_pending(kvm,vcpu,intid,level);return0;}++/**+*vgic_prune_ap_list-Removenon-relevantinterruptsfromthelist+*+*@vcpu:TheVCPUpointer+*+*Gooverthelistof"interesting"interrupts,andprunethosethatwe+*won'thavetoconsiderinthenearfuture.+*/+staticvoidvgic_prune_ap_list(structkvm_vcpu*vcpu)+{+structvgic_cpu*vgic_cpu=&vcpu->arch.vgic_cpu;+structvgic_irq*irq,*tmp;++retry:+spin_lock(&vgic_cpu->ap_list_lock);++list_for_each_entry_safe(irq,tmp,&vgic_cpu->ap_list_head,ap_list){+structkvm_vcpu*target_vcpu,*vcpuA,*vcpuB;++spin_lock(&irq->irq_lock);++BUG_ON(vcpu!=irq->vcpu);++target_vcpu=vgic_target_oracle(irq);++if(!target_vcpu){+/*+*Wedon'tneedtoprocessthisinterruptany+*further,moveitoffthelist.+*/+list_del_init(&irq->ap_list);
why list_del_init and not list_del here? do we ever do list_empty() on
the &irq->ap_list ?
+ irq->vcpu = NULL;
+ spin_unlock(&irq->irq_lock);
+ continue;
+ }
+
+ if (target_vcpu == vcpu) {
+ /* We're on the right CPU */
+ spin_unlock(&irq->irq_lock);
+ continue;
+ }
+
+ /* This interrupt looks like it has to be migrated. */
+
+ spin_unlock(&irq->irq_lock);
+ spin_unlock(&vgic_cpu->ap_list_lock);
+
+ /*
+ * Ensure locking order by always locking the smallest
+ * ID first.
+ */
+ if (vcpu->vcpu_id < target_vcpu->vcpu_id) {
+ vcpuA = vcpu;
+ vcpuB = target_vcpu;
+ } else {
+ vcpuA = target_vcpu;
+ vcpuB = vcpu;
+ }
+
+ spin_lock(&vcpuA->arch.vgic_cpu.ap_list_lock);
+ spin_lock(&vcpuB->arch.vgic_cpu.ap_list_lock);
+ spin_lock(&irq->irq_lock);
+
+ /*
+ * If the affinity has been preserved, move the
+ * interrupt around. Otherwise, it means things have
+ * changed while the interrupt was unlocked, and we
+ * need to replay this.
+ *
+ * In all cases, we cannot trust the list not to have
+ * changed, so we restart from the beginning.
+ */
+ if (target_vcpu == vgic_target_oracle(irq)) {
+ struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu;
+
+ list_del_init(&irq->ap_list);
again, why list_del_init and not just list_del ?
+ irq->vcpu = target_vcpu;
+ list_add_tail(&irq->ap_list, &new_cpu->ap_list_head);
+ }
+
+ spin_unlock(&irq->irq_lock);
+ spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock);
+ spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock);
+ goto retry;
+ }
+
+ spin_unlock(&vgic_cpu->ap_list_lock);
+}
+
+static inline void vgic_process_maintenance_interrupt(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vgic_global_state.type == VGIC_V2)
+ vgic_v2_process_maintenance(vcpu);
+ else
+ WARN(1, "GICv3 Not Implemented\n");
+}
+
+static inline void vgic_fold_lr_state(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vgic_global_state.type == VGIC_V2)
+ vgic_v2_fold_lr_state(vcpu);
+ else
+ WARN(1, "GICv3 Not Implemented\n");
+}
+
+/*
+ * Requires the ap_lock to be held.
+ * If irq is not NULL, requires the IRQ lock to be held as well.
+ * If irq is NULL, the list register gets cleared.
+ */
+static inline void vgic_populate_lr(struct kvm_vcpu *vcpu,
+ struct vgic_irq *irq, int lr)
+{
+ if (kvm_vgic_global_state.type == VGIC_V2)
+ vgic_v2_populate_lr(vcpu, irq, lr);
+ else
+ WARN(1, "GICv3 Not Implemented\n");
+}
+
+static inline void vgic_set_underflow(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vgic_global_state.type == VGIC_V2)
+ vgic_v2_set_underflow(vcpu);
+ else
+ WARN(1, "GICv3 Not Implemented\n");
+}
+
+static int compute_ap_list_depth(struct kvm_vcpu *vcpu)
+{
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ struct vgic_irq *irq;
+ int count = 0;
+
+ list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
+ spin_lock(&irq->irq_lock);
+ /* GICv2 SGIs can count for more than one... */
+ if (irq->intid < VGIC_NR_SGIS && irq->source)
how can we have an SGI on an AP list without the irq->source field set?
Is the irq->source check to cater for GICv3?
this does feel like an awful lot of code on each entry. I'm wondering
if we should have a count on each vgic_cpu containing the length of the
AP list which is then adjusted via the queue and removal functions?
I'm not in love with the fact that we have two separate functions named:
vgic_populate_lrs and
vgic_populate_lr
perhaps this could be changed to 'vgic_flush_ap_list' ?
+{
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ u32 model = vcpu->kvm->arch.vgic.vgic_model;
+ struct vgic_irq *irq;
+ int count = 0;
+
+ if (compute_ap_list_depth(vcpu) > vcpu->arch.vgic_cpu.nr_lr) {
+ vgic_set_underflow(vcpu);
+ vgic_sort_ap_list(vcpu);
+ }
+
+ list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
+ spin_lock(&irq->irq_lock);
+
+ if (unlikely(vgic_target_oracle(irq) != vcpu))
+ goto next;
+
+ /*
+ * If we get an SGI with multiple sources, try to get
+ * them in all at once.
+ */
+ if (model == KVM_DEV_TYPE_ARM_VGIC_V2 &&
+ irq->intid < VGIC_NR_SGIS) {
I wonder if a lot of this code would be more readable if we added and
used the following primitives:
vgic_irq_is_sgi()
vgic_irq_is_ppi()
vgic_irq_is_spi()
this stuff about the SGIs is really dense, so I'm wondering if it's more
clean to make it even more dense and rewrite the whole if-statement to:
do {
vgic_populate(vcpu, irq, count++);
} while (irq->source && count < vgic.nr_lr);
(btw. I believe all these places referencing the nr_lr in the vgic_cpu
struct could use the global state structure instead).
should we not change this to adhere to the optimizations Marc
implemented for the current VGIC (i.e. clear the LRs on sync+init, and
only write what you need here)?
From: Vladimir Murzin <hidden> Date: 2016-03-30 14:13:20
On 30/03/16 14:56, Christoffer Dall wrote:
Hi Vladimir,
On Wed, Mar 30, 2016 at 12:52:27PM +0100, Vladimir Murzin wrote:
quoted
On 30/03/16 12:42, Vladimir Murzin wrote:
quoted
On 29/03/16 14:12, Vladimir Murzin wrote:
quoted
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
quoted
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
It comes from kvm_io_bus_register_dev()
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
with bus->dev_count being 1000
[<ffffffc00009f93c>] kvm_io_bus_register_dev+0x130/0x148
[<ffffffc0000ad274>] kvm_vgic_register_mmio_region+0xac/0xdc
[<ffffffc0000ad4d4>] vgic_register_redist_regions+0xb8/0x158
[<ffffffc0000abb90>] vgic_v3_map_resources+0x5c/0xf0
[<ffffffc0000aae04>] kvm_vgic_map_resources+0x40/0x84
[<ffffffc0000a23d0>] kvm_arch_vcpu_ioctl_run+0x3f0/0x400
[<ffffffc00009d484>] kvm_vcpu_ioctl+0x2d4/0x6ec
[<ffffffc0001c4eec>] do_vfs_ioctl+0xb4/0x760
[<ffffffc0001c561c>] SyS_ioctl+0x84/0x98
[<ffffffc000085d30>] el0_svc_naked+0x24/0x28
and one more thing I've forgotten to mention... something odd happens on
destroy path
INFO: task kvm-vcpu-0:1123 blocked for more than 120 seconds.
Tainted: G W 4.5.0-rc6+ #432
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
kvm-vcpu-0 D ffffffc000086bcc 0 1123 1114 0x00000008
Call trace:
[<ffffffc000086bcc>] __switch_to+0x90/0xa4
[<ffffffc0006c4390>] __schedule+0x188/0x59c
[<ffffffc0006c47e0>] schedule+0x3c/0xa0
[<ffffffc0006c4bf4>] schedule_preempt_disabled+0x20/0x38
[<ffffffc0006c618c>] __mutex_lock_slowpath+0xc4/0x148
[<ffffffc0006c6254>] mutex_lock+0x44/0x5c
[<ffffffc0000aacb8>] kvm_vgic_destroy+0x20/0xb0
[<ffffffc0000abbdc>] vgic_v3_map_resources+0xa8/0xf0
[<ffffffc0000aae04>] kvm_vgic_map_resources+0x40/0x84
[<ffffffc0000a23d0>] kvm_arch_vcpu_ioctl_run+0x3f0/0x400
[<ffffffc00009d484>] kvm_vcpu_ioctl+0x2d4/0x6ec
[<ffffffc0001c4eec>] do_vfs_ioctl+0xb4/0x760
[<ffffffc0001c561c>] SyS_ioctl+0x84/0x98
[<ffffffc000085d30>] el0_svc_naked+0x24/0x28
Is this also with many many VCPUs or in general?
It's seen with many many VCPUs. Basically, I see
kvm [1273]: Unable to register VGICv3 redist MMIO regions
and then (if I'm patient enough) I see that backtrace. So the flow looks
like:
el0_svc_naked
...
vgic_v3_map_resources
vgic_register_redist_regions
kvm_vgic_register_mmio_region
kvm_io_bus_register_dev // return -ENOSPC
kvm_vgic_destroy
mutex_lock
Cheers
Vladimir
From: Christoffer Dall <hidden> Date: 2016-03-30 19:53:11
On Wed, Mar 30, 2016 at 03:13:20PM +0100, Vladimir Murzin wrote:
On 30/03/16 14:56, Christoffer Dall wrote:
quoted
Hi Vladimir,
On Wed, Mar 30, 2016 at 12:52:27PM +0100, Vladimir Murzin wrote:
quoted
On 30/03/16 12:42, Vladimir Murzin wrote:
quoted
On 29/03/16 14:12, Vladimir Murzin wrote:
quoted
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
quoted
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
It comes from kvm_io_bus_register_dev()
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
with bus->dev_count being 1000
[<ffffffc00009f93c>] kvm_io_bus_register_dev+0x130/0x148
[<ffffffc0000ad274>] kvm_vgic_register_mmio_region+0xac/0xdc
[<ffffffc0000ad4d4>] vgic_register_redist_regions+0xb8/0x158
[<ffffffc0000abb90>] vgic_v3_map_resources+0x5c/0xf0
[<ffffffc0000aae04>] kvm_vgic_map_resources+0x40/0x84
[<ffffffc0000a23d0>] kvm_arch_vcpu_ioctl_run+0x3f0/0x400
[<ffffffc00009d484>] kvm_vcpu_ioctl+0x2d4/0x6ec
[<ffffffc0001c4eec>] do_vfs_ioctl+0xb4/0x760
[<ffffffc0001c561c>] SyS_ioctl+0x84/0x98
[<ffffffc000085d30>] el0_svc_naked+0x24/0x28
and one more thing I've forgotten to mention... something odd happens on
destroy path
INFO: task kvm-vcpu-0:1123 blocked for more than 120 seconds.
Tainted: G W 4.5.0-rc6+ #432
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
kvm-vcpu-0 D ffffffc000086bcc 0 1123 1114 0x00000008
Call trace:
[<ffffffc000086bcc>] __switch_to+0x90/0xa4
[<ffffffc0006c4390>] __schedule+0x188/0x59c
[<ffffffc0006c47e0>] schedule+0x3c/0xa0
[<ffffffc0006c4bf4>] schedule_preempt_disabled+0x20/0x38
[<ffffffc0006c618c>] __mutex_lock_slowpath+0xc4/0x148
[<ffffffc0006c6254>] mutex_lock+0x44/0x5c
[<ffffffc0000aacb8>] kvm_vgic_destroy+0x20/0xb0
[<ffffffc0000abbdc>] vgic_v3_map_resources+0xa8/0xf0
[<ffffffc0000aae04>] kvm_vgic_map_resources+0x40/0x84
[<ffffffc0000a23d0>] kvm_arch_vcpu_ioctl_run+0x3f0/0x400
[<ffffffc00009d484>] kvm_vcpu_ioctl+0x2d4/0x6ec
[<ffffffc0001c4eec>] do_vfs_ioctl+0xb4/0x760
[<ffffffc0001c561c>] SyS_ioctl+0x84/0x98
[<ffffffc000085d30>] el0_svc_naked+0x24/0x28
Is this also with many many VCPUs or in general?
It's seen with many many VCPUs. Basically, I see
kvm [1273]: Unable to register VGICv3 redist MMIO regions
and then (if I'm patient enough) I see that backtrace. So the flow looks
like:
el0_svc_naked
...
vgic_v3_map_resources
vgic_register_redist_regions
kvm_vgic_register_mmio_region
kvm_io_bus_register_dev // return -ENOSPC
kvm_vgic_destroy
mutex_lock
From: Christoffer Dall <hidden> Date: 2016-03-30 19:55:55
On Wed, Mar 30, 2016 at 01:07:06PM +0100, Marc Zyngier wrote:
On 30/03/16 12:42, Vladimir Murzin wrote:
quoted
On 29/03/16 14:12, Vladimir Murzin wrote:
quoted
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
quoted
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
It comes from kvm_io_bus_register_dev()
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
with bus->dev_count being 1000
Ouch. That's a consequence of having one "device" per actual register
set, I believe. So either we bump this limit up by a factor 10 (at
least), or we switch back to the old way of handling access to the GIC
regions (big "catch-all", and further demuxing).
Thoughts?
Since we have to have code to iterate through the individual register
regions for the userland MMIO accesses anyway, I think we should just
revert back to the old way.
-Christoffer
From: Christoffer Dall <hidden> Date: 2016-03-30 20:40:12
On Fri, Mar 25, 2016 at 02:04:33AM +0000, Andre Przywara wrote:
From: Marc Zyngier <redacted>
As the GICv3 virtual interface registers differ from their GICv2
siblings, we need different handlers for processing maintenance
interrupts and reading/writing to the LRs.
Also as we store an IRQ's affinity directly as a MPIDR, we need a
separate change_affinity() implementation too.
not sure why we embed the vgic_v3_irq_change_affinity in this patch here
and had a stand-alone patch for v2?
I think it would be better to split them up and again have one patch to
introduce the infrastructure for some piece of functionality, followed
by 2 patches, one plugging in the v2 part, the other plugging in the v3
part.
quoted hunk
Implement the respective handler functions and connect them to
existing code to be called if the host is using a GICv3.
Signed-off-by: Marc Zyngier <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic-v3.c | 191 ++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 8 +-
virt/kvm/arm/vgic/vgic.h | 30 +++++++
3 files changed, 225 insertions(+), 4 deletions(-)
create mode 100644 virt/kvm/arm/vgic/vgic-v3.c
this comment clearly doesn't apply anymore.
also, looking at the similarities here with the v2 code, we should
probably have another look at sharing some more code between v2 and v3.
here you don't have the WARN that you had in v2, so does this mean we
can actually come here with the LR state field having some value?
+ cpuif->vgic_elrsr |= 1ULL << lr;
+ }
+
+ /*
+ * In the next iterations of the vcpu loop, if we sync
+ * the vgic state after flushing it, but before
+ * entering the guest (this happens for pending
+ * signals and vmid rollovers), then make sure we
+ * don't pick up any old maintenance interrupts here.
+ */
+ cpuif->vgic_eisr = 0;
+ }
+
+ cpuif->vgic_hcr &= ~ICH_HCR_UIE;
like in the v2 case, I think this should be moved out of the process
maintenance function.
+}
+
+void vgic_v3_set_underflow(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
+
+ cpuif->vgic_hcr |= ICH_HCR_UIE;
+}
+
+void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
+ u32 model = vcpu->kvm->arch.vgic.vgic_model;
+ int lr;
+
+ /* Assumes ap_list_lock held */
+
+ for (lr = 0; lr < vcpu->arch.vgic_cpu.used_lrs; lr++) {
+ u64 val = cpuif->vgic_lr[lr];
+ u32 intid;
+ struct vgic_irq *irq;
+
+ if (model == KVM_DEV_TYPE_ARM_VGIC_V3)
+ intid = val & ICH_LR_VIRTUAL_ID_MASK;
+ else
+ intid = val & GICH_LR_VIRTUALID;
+ irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
+
+ spin_lock(&irq->irq_lock);
+
+ /* Always preserve the active bit */
+ irq->active = !!(val & ICH_LR_ACTIVE_BIT);
+
+ /* Edge is the only case where we preserve the pending bit */
+ if (irq->config == VGIC_CONFIG_EDGE &&
+ (val & ICH_LR_PENDING_BIT)) {
+ irq->pending = true;
+
+ if (intid < VGIC_NR_SGIS &&
+ model == KVM_DEV_TYPE_ARM_VGIC_V2) {
+ u32 cpuid = val & GICH_LR_PHYSID_CPUID;
+
+ cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;
+ irq->source |= (1 << cpuid);
+ }
+ }
+
+ /* Clear soft pending state when level irqs have been acked */
+ if (irq->config == VGIC_CONFIG_LEVEL &&
+ !(val & ICH_LR_PENDING_BIT)) {
+ irq->soft_pending = false;
+ irq->pending = irq->line_level;
+ }
+
+ spin_unlock(&irq->irq_lock);
+ }
+}
+
+/* Requires the irq to be locked already */
+void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
+{
+ u32 model = vcpu->kvm->arch.vgic.vgic_model;
+ u64 val;
+
+ if (!irq) {
+ val = 0;
+ goto out;
+ }
+
+ val = irq->intid;
+
+ if (irq->pending) {
+ val |= ICH_LR_PENDING_BIT;
+
+ if (irq->config == VGIC_CONFIG_EDGE)
+ irq->pending = false;
+
+ if (irq->intid < VGIC_NR_SGIS &&
+ model == KVM_DEV_TYPE_ARM_VGIC_V2) {
+ u32 src = ffs(irq->source);
+
+ BUG_ON(!src);
+ val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
+ irq->source &= ~(1 << (src - 1));
+ if (irq->source)
+ irq->pending = true;
+ }
+ }
+
+ if (irq->active)
+ val |= ICH_LR_ACTIVE_BIT;
+
+ if (irq->hw) {
+ val |= ICH_LR_HW;
+ val |= ((u64)irq->hwintid) << ICH_LR_PHYS_ID_SHIFT;
+ } else {
+ if (irq->config == VGIC_CONFIG_LEVEL)
+ val |= ICH_LR_EOI;
+ }
indeed this code looks very much like the v2 code, so maybe Marc had a
point when he argued for this being more shared between v2 and v3. Is
there a nice way to do that without an intermediate LR representation?
+
+ /*
+ * Currently all guest IRQs are Group1, as Group0 would result
+ * in a FIQ in the guest, which it wouldn't expect.
+ * Eventually we want to make this configurable, so we may
+ * revisit this in the future.
I know we have something similar in the current code, but I actually
don't understand this. If the IRQ is programmed to be group0, then the
guest *would* expect an FIQ, or?
Why is this not a matter of reading which group this IRQ is configured
to be?
From: Christoffer Dall <hidden> Date: 2016-03-31 08:54:32
On Fri, Mar 25, 2016 at 02:04:34AM +0000, Andre Przywara wrote:
quoted hunk
From: Eric Auger <redacted>
Tell KVM whether a particular VCPU has an IRQ that needs handling
in the guest. This is used to decide whether a VCPU is runnable.
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 2 ++
virt/kvm/arm/vgic/vgic.c | 22 ++++++++++++++++++++++
2 files changed, 24 insertions(+)
as long as we only call this in the wfi/wfe path, then it's fine. If we
start calling this in the critical path, I think we should check the
common case of list_empty(&vgic_cpu->ap_list) first.
Thanks,
-Christoffer
From: Marc Zyngier <hidden> Date: 2016-03-31 09:06:01
On 30/03/16 20:55, Christoffer Dall wrote:
On Wed, Mar 30, 2016 at 01:07:06PM +0100, Marc Zyngier wrote:
quoted
On 30/03/16 12:42, Vladimir Murzin wrote:
quoted
On 29/03/16 14:12, Vladimir Murzin wrote:
quoted
Hi Andre,
On 25/03/16 02:04, Andre Przywara wrote:
quoted
Please have a look at the series, review it and give the code some
serious testing (and possibly debugging). All feedback is appreciated.
I see that with the new vgic implementation kvmtool starts throwing:
kvm [1273]: Unable to register VGICv3 redist MMIO regions
It comes from kvm_io_bus_register_dev()
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
with bus->dev_count being 1000
Ouch. That's a consequence of having one "device" per actual register
set, I believe. So either we bump this limit up by a factor 10 (at
least), or we switch back to the old way of handling access to the GIC
regions (big "catch-all", and further demuxing).
Thoughts?
Since we have to have code to iterate through the individual register
regions for the userland MMIO accesses anyway, I think we should just
revert back to the old way.
That's what I was thinking as well. We could use a binary search to
speed-up the "in-region" search, just like the kvm_io_bus stuff does.
Thanks,
M.
--
Jazz is not dead. It just smells funny...
From: Christoffer Dall <hidden> Date: 2016-03-31 09:08:24
Hi Andre,
[cc'ing Paolo here for his thoughts on the KVM IO bus framework]
On Fri, Mar 25, 2016 at 02:04:35AM +0000, Andre Przywara wrote:
We register each register group of the distributor and redistributors
as separate regions of the kvm-io-bus framework. This way calls get
directly handed over to the actual handler.
This puts a lot more regions into kvm-io-bus than what we use at the
moment on other architectures, so we will probably need to revisit the
implementation of the framework later to be more efficient.
Looking more carefully at the KVM IO bus stuff, it looks like it is
indeed designed to be a *per device* thing you register, not a *per
register* thing.
My comments to Vladimir's bug report notwithstanding, there's still a
choice here to:
1) Expand/modify the KVM IO bus framework to take an arbitrary number of devices
2) Build a KVM architectureal generic framework on top of the IO bus
framework to handle individual register regions.
3) Stick with what we had before, do not modify the KVM IO bus stuff,
and handle the individual register region business locally within the
arm/vgic code.
@@ -0,0 +1,194 @@+/*+*VGICMMIOhandlingfunctions+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*/++#include<linux/kvm.h>+#include<linux/kvm_host.h>+#include<kvm/iodev.h>+#include<kvm/vgic/vgic.h>+#include<linux/bitops.h>+#include<linux/irqchip/arm-gic.h>++#include"vgic.h"+#include"vgic_mmio.h"++voidwrite_mask32(u32value,intoffset,intlen,void*val)+{+value=cpu_to_le32(value)>>(offset*8);+memcpy(val,&value,len);+}++u32mask32(u32origvalue,intoffset,intlen,constvoid*val)+{+origvalue&=~((BIT_ULL(len)-1)<<(offset*8));+memcpy((char*)&origvalue+(offset*8),val,len);+returnorigvalue;+}++#ifdef CONFIG_KVM_ARM_VGIC_V3+voidwrite_mask64(u64value,intoffset,intlen,void*val)+{+value=cpu_to_le64(value)>>(offset*8);+memcpy(val,&value,len);+}++/* FIXME: I am clearly misguided here, there must be some saner way ... */
I'm confuses in general. Can you explain what these mask functions do
overall at some higher level?
I also keep having a feeling that mixing endianness stuff into the
emulation code itself is the wrong way to go about it. The emulation
code should just deal with register values of varying length and the
interface to the VGIC should abstract all endianness nonsense for us,
but I also think I've lost this argument some time in the past. Sigh.
But, is the maximum read/write unit for any MMIO access not a 64-bit
value? So why can't we let the VGIC emulation code simply take/return a
u64 which is then masked off/morphed into the right endianness outside
the VGIC code?
From: Christoffer Dall <hidden> Date: 2016-03-31 09:09:25
[really cc'ing Paolo this time]
On Thu, Mar 31, 2016 at 11:08 AM, Christoffer Dall
[off-list ref] wrote:
Hi Andre,
[cc'ing Paolo here for his thoughts on the KVM IO bus framework]
On Fri, Mar 25, 2016 at 02:04:35AM +0000, Andre Przywara wrote:
quoted
We register each register group of the distributor and redistributors
as separate regions of the kvm-io-bus framework. This way calls get
directly handed over to the actual handler.
This puts a lot more regions into kvm-io-bus than what we use at the
moment on other architectures, so we will probably need to revisit the
implementation of the framework later to be more efficient.
Looking more carefully at the KVM IO bus stuff, it looks like it is
indeed designed to be a *per device* thing you register, not a *per
register* thing.
My comments to Vladimir's bug report notwithstanding, there's still a
choice here to:
1) Expand/modify the KVM IO bus framework to take an arbitrary number of devices
2) Build a KVM architectureal generic framework on top of the IO bus
framework to handle individual register regions.
3) Stick with what we had before, do not modify the KVM IO bus stuff,
and handle the individual register region business locally within the
arm/vgic code.
@@ -0,0 +1,194 @@+/*+*VGICMMIOhandlingfunctions+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*/++#include<linux/kvm.h>+#include<linux/kvm_host.h>+#include<kvm/iodev.h>+#include<kvm/vgic/vgic.h>+#include<linux/bitops.h>+#include<linux/irqchip/arm-gic.h>++#include"vgic.h"+#include"vgic_mmio.h"++voidwrite_mask32(u32value,intoffset,intlen,void*val)+{+value=cpu_to_le32(value)>>(offset*8);+memcpy(val,&value,len);+}++u32mask32(u32origvalue,intoffset,intlen,constvoid*val)+{+origvalue&=~((BIT_ULL(len)-1)<<(offset*8));+memcpy((char*)&origvalue+(offset*8),val,len);+returnorigvalue;+}++#ifdef CONFIG_KVM_ARM_VGIC_V3+voidwrite_mask64(u64value,intoffset,intlen,void*val)+{+value=cpu_to_le64(value)>>(offset*8);+memcpy(val,&value,len);+}++/* FIXME: I am clearly misguided here, there must be some saner way ... */
I'm confuses in general. Can you explain what these mask functions do
overall at some higher level?
I also keep having a feeling that mixing endianness stuff into the
emulation code itself is the wrong way to go about it. The emulation
code should just deal with register values of varying length and the
interface to the VGIC should abstract all endianness nonsense for us,
but I also think I've lost this argument some time in the past. Sigh.
But, is the maximum read/write unit for any MMIO access not a 64-bit
value? So why can't we let the VGIC emulation code simply take/return a
u64 which is then masked off/morphed into the right endianness outside
the VGIC code?
From: Christoffer Dall <hidden> Date: 2016-03-31 09:24:46
On Fri, Mar 25, 2016 at 02:04:36AM +0000, Andre Przywara wrote:
quoted hunk
Userland can access the emulated GIC to save and restore its state
for initialization or migration purposes.
The kvm_io_bus API requires an absolute gpa, which does not fit the
KVM_DEV_ARM_VGIC_GRP_DIST_REGS user API, that only provides relative
offsets. So we explicitly iterate our register list to connect
userland to the VGIC.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Eric Auger <redacted>
---
virt/kvm/arm/vgic/vgic.h | 2 ++
virt/kvm/arm/vgic/vgic_mmio.c | 45 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
@@ -82,9 +82,56 @@ static int vgic_mmio_write_nyi(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_v2_misc(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32value;++switch((addr-iodev->base_addr)&~3){+case0x0:+value=vcpu->kvm->arch.vgic.enabled?GICD_ENABLE:0;+break;+case0x4:+value=vcpu->kvm->arch.vgic.nr_spis+VGIC_NR_PRIVATE_IRQS;+value=(value>>5)-1;+value|=(atomic_read(&vcpu->kvm->online_vcpus)-1)<<5;+break;+case0x8:+value=(PRODUCT_ID_KVM<<24)|(IMPLEMENTER_ARM<<0);+break;+default:+return0;+}++write_mask32(value,addr&3,len,val);+return0;+}++staticintvgic_mmio_write_v2_misc(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+/*+*GICD_TYPERandGICD_IIDRareread-only,theupperthreebytesof+*GICD_CTLRarereserved.+*/+if(addr-iodev->base_addr>=1)+return0;++vcpu->kvm->arch.vgic.enabled=(*(u32*)val)?true:false;+/* TODO: is there anything to trigger at this point? */
I guess we should actually check if the vgic is enabled in PATH 11, and
we should kick all VCPUs (or at least those with something pending) if
we went from disabled to enabled?
We should probably also check this in the sync function...
Or do we enforce this enabled bool somewhere else that I missed?
@@ -129,15 +129,92 @@ static int vgic_mmio_write_v2_misc(struct kvm_vcpu *vcpu,return0;}+/*+*ReadaccessestobothGICD_ICENABLERandGICD_ISENABLERreturnthevalue+*oftheenabledbit,sothereisonlyonefunctionforbothhere.+*/+staticintvgic_mmio_read_enable(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+u32value=0;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++/* Loop over all IRQs affected by this read */+for(i=0;i<len*8;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++if(irq->enabled)+value|=(1U<<i);+}++write_mask32(value,addr&3,len,val);+return0;+}++staticintvgic_mmio_write_senable(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for_each_set_bit(i,val,len*8){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+irq->enabled=true;+vgic_queue_irq(vcpu->kvm,irq);
this makes me feel like we should rename this to vgic_queue_irq_unlock
+ }
+
+ return 0;
+}
+
+static int vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
+ struct kvm_io_device *this,
+ gpa_t addr, int len, const void *val)
+{
+ struct vgic_io_device *iodev = container_of(this,
+ struct vgic_io_device, dev);
+ u32 intid = (addr - iodev->base_addr) * 8;
+ int i;
+
+ if (iodev->redist_vcpu)
+ vcpu = iodev->redist_vcpu;
+
+ for_each_set_bit(i, val, len * 8) {
+ struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
+
+ spin_lock(&irq->irq_lock);
+
+ irq->enabled = false;
+ /* TODO: Does the exit/entry code take care of "unqueuing"? */
yes it does, the sync calls prune_ap_list, which calls the Oracle, which
returns null if the IRQ is no longer enabled, so the prune function
removes the IRQ from the list.
@@ -206,6 +206,89 @@ static int vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_pending(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+u32value=0;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++/* Loop over all IRQs affected by this read */+for(i=0;i<len*8;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+if(irq->pending)+value|=(1U<<i);+spin_unlock(&irq->irq_lock);
here there clearly is no need to take the lock (because a bool read is
atomic), but that should be explained in a one-line comment.
From: Christoffer Dall <hidden> Date: 2016-03-31 09:47:15
On Fri, Mar 25, 2016 at 02:04:32AM +0000, Andre Przywara wrote:
quoted hunk
From: Marc Zyngier <redacted>
Implement the functionality for syncing IRQs between our emulation
and the list registers, which represent the guest's view of IRQs.
This is done in kvm_vgic_flush_hwstate and kvm_vgic_sync_hwstate,
which gets called on guest entry and exit.
Signed-off-by: Marc Zyngier <redacted>
Signed-off-by: Christoffer Dall <redacted>
Signed-off-by: Eric Auger <redacted>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
include/kvm/vgic/vgic.h | 4 +
virt/kvm/arm/vgic/vgic-v2.c | 161 ++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 204 ++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 4 +
4 files changed, 373 insertions(+)
@@ -14,11 +14,172 @@*alongwiththisprogram.Ifnot,see<http://www.gnu.org/licenses/>.*/+#include<linux/irqchip/arm-gic.h>#include<linux/kvm.h>#include<linux/kvm_host.h>#include"vgic.h"+/*+*Callthisfunctiontoconvertau64valuetoanunsignedlong*bitmask+*inawaythatworksonboth32-bitand64-bitLEandBEplatforms.+*+*Warning:Callingthisfunctionmaymodify*val.+*/+staticunsignedlong*u64_to_bitmask(u64*val)+{+#if defined(CONFIG_CPU_BIG_ENDIAN) && BITS_PER_LONG == 32+*val=(*val>>32)|(*val<<32);+#endif+return(unsignedlong*)val;+}++voidvgic_v2_process_maintenance(structkvm_vcpu*vcpu)+{+structvgic_v2_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v2;++if(cpuif->vgic_misr&GICH_MISR_EOI){+u64eisr=cpuif->vgic_eisr;+unsignedlong*eisr_bmap=u64_to_bitmask(&eisr);+intlr;++for_each_set_bit(lr,eisr_bmap,vcpu->arch.vgic_cpu.nr_lr){+structvgic_irq*irq;+u32intid=cpuif->vgic_lr[lr]&GICH_LR_VIRTUALID;++irq=vgic_get_irq(vcpu->kvm,vcpu,intid);++WARN_ON(irq->config==VGIC_CONFIG_EDGE);+WARN_ON(cpuif->vgic_lr[lr]&GICH_LR_STATE);++kvm_notify_acked_irq(vcpu->kvm,0,+intid-VGIC_NR_PRIVATE_IRQS);++cpuif->vgic_lr[lr]&=~GICH_LR_STATE;/* Useful?? */+cpuif->vgic_elrsr|=1ULL<<lr;+}+}++/* check and disable underflow maintenance IRQ */+cpuif->vgic_hcr&=~GICH_HCR_UIE;++/*+*Inthenextiterationsofthevcpuloop,ifwesyncthe+*vgicstateafterflushingit,butbeforeenteringtheguest+*(thishappensforpendingsignalsandvmidrollovers),then+*makesurewedon'tpickupanyoldmaintenanceinterrupts+*here.+*/+cpuif->vgic_eisr=0;+}++voidvgic_v2_set_underflow(structkvm_vcpu*vcpu)+{+structvgic_v2_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v2;++cpuif->vgic_hcr|=GICH_HCR_UIE;+}++/*+*transferthecontentoftheLRsbackintothecorrespondingap_list:+*-activebitistransferredasis+*-pendingbitis+*-transferredasisincaseofedgesensitiveIRQs+*-settotheline-level(resampletime)forlevelsensitiveIRQs+*/+voidvgic_v2_fold_lr_state(structkvm_vcpu*vcpu)+{+structvgic_v2_cpu_if*cpuif=&vcpu->arch.vgic_cpu.vgic_v2;+intlr;++for(lr=0;lr<vcpu->arch.vgic_cpu.used_lrs;lr++){+u32val=cpuif->vgic_lr[lr];+u32intid=val&GICH_LR_VIRTUALID;+structvgic_irq*irq;++irq=vgic_get_irq(vcpu->kvm,vcpu,intid);++spin_lock(&irq->irq_lock);++/* Always preserve the active bit */+irq->active=!!(val&GICH_LR_ACTIVE_BIT);++/* Edge is the only case where we preserve the pending bit */+if(irq->config==VGIC_CONFIG_EDGE&&+(val&GICH_LR_PENDING_BIT)){+irq->pending=true;++if(intid<VGIC_NR_SGIS){+u32cpuid=val&GICH_LR_PHYSID_CPUID;++cpuid>>=GICH_LR_PHYSID_CPUID_SHIFT;+irq->source|=(1<<cpuid);+}+}++/* Clear soft pending state when level IRQs have been acked */+if(irq->config==VGIC_CONFIG_LEVEL&&+!(val&GICH_LR_PENDING_BIT)){+irq->soft_pending=false;+irq->pending=irq->line_level;+}++spin_unlock(&irq->irq_lock);+}+}++/*+*PopulatestheparticularLRwiththestateofagivenIRQ:+*-foranedgesensitiveIRQthependingstateisresetinthestruct+*-foralevelsensitiveIRQthependingstatevalueisunchanged;+*itwillberesampledondeactivation+*+*IfirqisnotNULL,theirq_lockmustbeholdalreadybythecaller.+*IfirqisNULL,therespectiveLRgetscleared.+*/+voidvgic_v2_populate_lr(structkvm_vcpu*vcpu,structvgic_irq*irq,intlr)+{+u32val;++if(!irq){+val=0;+gotoout;+}++val=irq->intid;++if(irq->pending){+val|=GICH_LR_PENDING_BIT;++if(irq->config==VGIC_CONFIG_EDGE)+irq->pending=false;++if(irq->intid<VGIC_NR_SGIS){+u32src=ffs(irq->source);++BUG_ON(!src);+val|=(src-1)<<GICH_LR_PHYSID_CPUID_SHIFT;+irq->source&=~(1<<(src-1));+if(irq->source)+irq->pending=true;+}+}++if(irq->active)+val|=GICH_LR_ACTIVE_BIT;++if(irq->hw){+val|=GICH_LR_HW;+val|=irq->hwintid<<GICH_LR_PHYSID_CPUID_SHIFT;+}else{+if(irq->config==VGIC_CONFIG_LEVEL)+val|=GICH_LR_EOI;+}
shouldn't we start writing the priority here (and in the GICv3 version)?
(which has the fun consequence of having to compare priorities against
the virtual priority filter in PATCH 11).
@@ -273,3 +273,207 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,vgic_update_irq_pending(kvm,vcpu,intid,level);return0;}++/**+*vgic_prune_ap_list-Removenon-relevantinterruptsfromthelist+*+*@vcpu:TheVCPUpointer+*+*Gooverthelistof"interesting"interrupts,andprunethosethatwe+*won'thavetoconsiderinthenearfuture.+*/+staticvoidvgic_prune_ap_list(structkvm_vcpu*vcpu)+{+structvgic_cpu*vgic_cpu=&vcpu->arch.vgic_cpu;+structvgic_irq*irq,*tmp;++retry:+spin_lock(&vgic_cpu->ap_list_lock);++list_for_each_entry_safe(irq,tmp,&vgic_cpu->ap_list_head,ap_list){+structkvm_vcpu*target_vcpu,*vcpuA,*vcpuB;++spin_lock(&irq->irq_lock);++BUG_ON(vcpu!=irq->vcpu);++target_vcpu=vgic_target_oracle(irq);++if(!target_vcpu){+/*+*Wedon'tneedtoprocessthisinterruptany+*further,moveitoffthelist.+*/+list_del_init(&irq->ap_list);+irq->vcpu=NULL;+spin_unlock(&irq->irq_lock);+continue;+}++if(target_vcpu==vcpu){+/* We're on the right CPU */+spin_unlock(&irq->irq_lock);+continue;+}++/* This interrupt looks like it has to be migrated. */++spin_unlock(&irq->irq_lock);+spin_unlock(&vgic_cpu->ap_list_lock);++/*+*Ensurelockingorderbyalwayslockingthesmallest+*IDfirst.+*/+if(vcpu->vcpu_id<target_vcpu->vcpu_id){+vcpuA=vcpu;+vcpuB=target_vcpu;+}else{+vcpuA=target_vcpu;+vcpuB=vcpu;+}++spin_lock(&vcpuA->arch.vgic_cpu.ap_list_lock);+spin_lock(&vcpuB->arch.vgic_cpu.ap_list_lock);+spin_lock(&irq->irq_lock);++/*+*Iftheaffinityhasbeenpreserved,movethe+*interruptaround.Otherwise,itmeansthingshave+*changedwhiletheinterruptwasunlocked,andwe+*needtoreplaythis.+*+*Inallcases,wecannottrustthelistnottohave+*changed,sowerestartfromthebeginning.+*/+if(target_vcpu==vgic_target_oracle(irq)){+structvgic_cpu*new_cpu=&target_vcpu->arch.vgic_cpu;++list_del_init(&irq->ap_list);+irq->vcpu=target_vcpu;+list_add_tail(&irq->ap_list,&new_cpu->ap_list_head);+}++spin_unlock(&irq->irq_lock);+spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock);+spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock);+gotoretry;+}++spin_unlock(&vgic_cpu->ap_list_lock);+}++staticinlinevoidvgic_process_maintenance_interrupt(structkvm_vcpu*vcpu)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_process_maintenance(vcpu);+else+WARN(1,"GICv3 Not Implemented\n");+}++staticinlinevoidvgic_fold_lr_state(structkvm_vcpu*vcpu)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_fold_lr_state(vcpu);+else+WARN(1,"GICv3 Not Implemented\n");+}++/*+*Requirestheap_locktobeheld.+*IfirqisnotNULL,requirestheIRQlocktobeheldaswell.+*IfirqisNULL,thelistregistergetscleared.+*/+staticinlinevoidvgic_populate_lr(structkvm_vcpu*vcpu,+structvgic_irq*irq,intlr)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_populate_lr(vcpu,irq,lr);+else+WARN(1,"GICv3 Not Implemented\n");+}++staticinlinevoidvgic_set_underflow(structkvm_vcpu*vcpu)+{+if(kvm_vgic_global_state.type==VGIC_V2)+vgic_v2_set_underflow(vcpu);+else+WARN(1,"GICv3 Not Implemented\n");+}++staticintcompute_ap_list_depth(structkvm_vcpu*vcpu)+{+structvgic_cpu*vgic_cpu=&vcpu->arch.vgic_cpu;+structvgic_irq*irq;+intcount=0;++list_for_each_entry(irq,&vgic_cpu->ap_list_head,ap_list){+spin_lock(&irq->irq_lock);+/* GICv2 SGIs can count for more than one... */+if(irq->intid<VGIC_NR_SGIS&&irq->source)+count+=hweight8(irq->source);+else+count++;+spin_unlock(&irq->irq_lock);+}+returncount;+}++/* requires the vcpu ap_lock to be held */+staticvoidvgic_populate_lrs(structkvm_vcpu*vcpu)+{+structvgic_cpu*vgic_cpu=&vcpu->arch.vgic_cpu;+u32model=vcpu->kvm->arch.vgic.vgic_model;+structvgic_irq*irq;+intcount=0;++if(compute_ap_list_depth(vcpu)>vcpu->arch.vgic_cpu.nr_lr){+vgic_set_underflow(vcpu);+vgic_sort_ap_list(vcpu);+}++list_for_each_entry(irq,&vgic_cpu->ap_list_head,ap_list){+spin_lock(&irq->irq_lock);++if(unlikely(vgic_target_oracle(irq)!=vcpu))+gotonext;++/*+*IfwegetanSGIwithmultiplesources,trytoget+*theminallatonce.+*/+if(model==KVM_DEV_TYPE_ARM_VGIC_V2&&+irq->intid<VGIC_NR_SGIS){+while(irq->source&&count<vcpu->arch.vgic_cpu.nr_lr)+vgic_populate_lr(vcpu,irq,count++);+}else{+vgic_populate_lr(vcpu,irq,count++);+}++next:+spin_unlock(&irq->irq_lock);++if(count==vcpu->arch.vgic_cpu.nr_lr)+break;+}++vcpu->arch.vgic_cpu.used_lrs=count;++/* Nuke remaining LRs */+for(;count<vcpu->arch.vgic_cpu.nr_lr;count++)+vgic_populate_lr(vcpu,NULL,count);+}++voidkvm_vgic_sync_hwstate(structkvm_vcpu*vcpu)+{+vgic_process_maintenance_interrupt(vcpu);+vgic_fold_lr_state(vcpu);+vgic_prune_ap_list(vcpu);+}++voidkvm_vgic_flush_hwstate(structkvm_vcpu*vcpu)+{+spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);+vgic_populate_lrs(vcpu);+spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);+}
@@ -289,6 +289,50 @@ static int vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_priority(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr);+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for(i=0;i<len;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++((u8*)val)[i]=irq->priority;+}++return0;+}++staticintvgic_mmio_write_priority(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr);+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for(i=0;i<len;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+irq->priority=((u8*)val)[i];+spin_unlock(&irq->irq_lock);
if we add the priority check in PATCH 11 you have to kick the vcpu here
irq->vcpu is set. (and possibly even without a change to PATCH 11 in
case the VCPU is running with a bund of now lower-priority interrupts in
the LRs).
@@ -289,6 +289,83 @@ static int vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_active(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*8;+u32value=0;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++/* Loop over all IRQs affected by this read */+for(i=0;i<len*8;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++spin_lock(&irq->irq_lock);+if(irq->active)+value|=(1U<<i);+spin_unlock(&irq->irq_lock);
you don't need the spinlock here (for the same reason you didn't grab it
in the last patch, consistency above all on this matter, please).
so prune will remove this from the AP list if it's no longer
pending/enabled as well.
the question is what to do if the vcpu for this irq is running and the
LR there has the active bit set, then we'll overwrite this change when
we fold the LR state back into the vgic_irq struct.
since I expect this to be extremely rare, one option is to force
irq->vcpu to exit (if non-null) and then do you thing here after you've
confirm it has exited while holding some lock preventing it from
re-entering again. Slightly crazy.
The alternative is to put a big fat comment nothing that this is
non-supported bad race, and wait until someone submits a bug report
relating to this...
you need to add it to the ap_list of irq->target_vcpu if irq->vcpu is
not already set.
an alternative is to expand the queue function to handle the active
case, but I'm not sure which one is cleaner. I think we have to try it
to be able to tell, but my gut feeling is that implementing it here is
better, because it's a one-off.
@@ -410,6 +410,67 @@ static int vgic_mmio_write_priority(struct kvm_vcpu *vcpu,return0;}+staticintvgic_mmio_read_config(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,void*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*4;+u32value=0;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for(i=0;i<len*4;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++if(irq->config==VGIC_CONFIG_EDGE)+value|=(2U<<(i*2));+}++write_mask32(value,addr&3,len,val);+return0;+}++staticintvgic_mmio_write_config(structkvm_vcpu*vcpu,+structkvm_io_device*this,+gpa_taddr,intlen,constvoid*val)+{+structvgic_io_device*iodev=container_of(this,+structvgic_io_device,dev);+u32intid=(addr-iodev->base_addr)*4;+inti;++if(iodev->redist_vcpu)+vcpu=iodev->redist_vcpu;++for(i=0;i<len*4;i++){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,intid+i);++if(intid+i<16)+continue;++/*+*Thespecsaysthatinterruptsmustbedisabledbefore+*changingtheconfigurationtoavoidUNDEFINEDbehaviour.+*Isthissufficientinourcase?Dowequicklyenoughremove+*theIRQfromtheap_listtosafelydotheconfigchange?
I don't understand the question about 'quickly enough' here.
+ * Will even a disabled interrupt in an ap_list cause us
+ * headaches if we change the configuration?
+ */
I don't think there's any particular problem here, except for my
comments below:
(there may be some super weirdness if a VCPU is running with some bits
of this IRQ in a LR while changing it, but I think we can ignore this
case for now - or we can do like with the active state and force an
exit).