On Thu, Jul 5, 2012 at 11:28 AM, Marc Zyngier <marc.zyngier@xxxxxxx> wrote: > Plug the interrupt injection code. Interrupts can now be generated > from user space. > > Signed-off-by: Marc Zyngier <marc.zyngier@xxxxxxx> > --- > arch/arm/include/asm/kvm_vgic.h | 1 + > arch/arm/kvm/arm.c | 43 ++++++++++++++++++++++++++++-- > arch/arm/kvm/vgic.c | 56 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 98 insertions(+), 2 deletions(-) > > diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h > index 481f4a9..48c27da 100644 > --- a/arch/arm/include/asm/kvm_vgic.h > +++ b/arch/arm/include/asm/kvm_vgic.h > @@ -220,6 +220,7 @@ struct kvm_exit_mmio; > #ifdef CONFIG_KVM_ARM_VGIC > void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu); > void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu); > +int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq); > int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); > int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, > struct kvm_exit_mmio *mmio); > diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c > index bdf0f86..7b27a4e 100644 > --- a/arch/arm/kvm/arm.c > +++ b/arch/arm/kvm/arm.c > @@ -654,6 +654,12 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level) > bool set; > int bit_nr; > > + if (irqchip_in_kernel(kvm)) { > + if (irq_level->irq < 32) > + return -EINVAL; > + return kvm_vgic_inject_irq(kvm, 0, irq_level); > + } > + > vcpu_idx = irq_level->irq >> 1; > if (vcpu_idx >= KVM_MAX_VCPUS) > return -EINVAL; > @@ -696,7 +702,28 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level) > long kvm_arch_vcpu_ioctl(struct file *filp, > unsigned int ioctl, unsigned long arg) > { > - return -EINVAL; > + struct kvm_vcpu *vcpu = filp->private_data; > + void __user *argp = (void __user *)arg; > + > + switch (ioctl) { > +#ifdef CONFIG_KVM_ARM_VGIC > + case KVM_IRQ_LINE: { > + struct kvm_irq_level irq_event; > + > + if (copy_from_user(&irq_event, argp, sizeof irq_event)) > + return -EFAULT; > + > + if (!irqchip_in_kernel(vcpu->kvm)) > + return -EINVAL; > + > + if (irq_event.irq < 16 || irq_event.irq >= 32) > + return -EINVAL; > + return kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, &irq_event); > + } We need to rework this scheme, see the discussion here: https://patchwork.kernel.org/patch/1149581/ Having an ioctl that can both be a vcpu and a vm ioctl is certainly ugly. I don't think we should have a vcpu ioctl for this purpose at all for two reasons 1) this happens asynchronously to the vcpu thread and 2) even a PPI is semantically not a vcpu operation but an operation on the gic distributor, which belongs to the VM. Can't we simply build on what we have and use this scheme: | 31 30 29 ... 18 17 16 | 15 | 14 13 12 ... 3 2 1 | 0 | | vcpu index | 0 | SPI / PPI | I=0/F=1 | For user space gic and PPI: vcpu_idx = val >> 16; if (vcpu_idx > online_vcpus) return -EINVAL; For user space gic: bool irq = !(val & 1); bool fiq = val & 1' For in-kernel gic: irq = (val >> 1) & 0x7fff; if (irq < 16) return -EINVAL; > +#endif > + default: > + return -EINVAL; > + } > } > > int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) > @@ -707,7 +734,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) > long kvm_arch_vm_ioctl(struct file *filp, > unsigned int ioctl, unsigned long arg) > { > - return -EINVAL; > + struct kvm *kvm = filp->private_data; > + > + switch (ioctl) { > +#ifdef CONFIG_KVM_ARM_VGIC > + case KVM_CREATE_IRQCHIP: > + if (vgic_present) > + return kvm_vgic_init(kvm); > + else > + return -EINVAL; > +#endif > + default: > + return -EINVAL; > + } > } > > static void cpu_set_vector(void *vector) > diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c > index 8722efc..4d5d23a 100644 > --- a/arch/arm/kvm/vgic.c > +++ b/arch/arm/kvm/vgic.c > @@ -69,6 +69,7 @@ > #define ACCESS_WRITE_MASK(x) ((x) & (3 << 1)) > > static void vgic_update_state(struct kvm *kvm); > +static void vgic_kick_vcpus(struct kvm *kvm); > static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); > > static inline int vgic_irq_is_edge(struct vgic_dist *dist, int irq) > @@ -500,6 +501,8 @@ int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit > kvm_handle_mmio_return(vcpu, run); > spin_unlock(&vcpu->kvm->arch.vgic.lock); > > + vgic_kick_vcpus(vcpu->kvm); > + > return KVM_EXIT_UNKNOWN; > } > > @@ -795,3 +798,56 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) > > return test_bit(1 << vcpu->vcpu_id, &dist->irq_pending_on_cpu); > } > + > +static void vgic_kick_vcpus(struct kvm *kvm) > +{ > + struct kvm_vcpu *vcpu; > + int c; > + > + /* > + * We've injected an interrupt, time to find out who deserves > + * a good kick... > + */ > + kvm_for_each_vcpu(c, vcpu, kvm) { > + if (kvm_vgic_vcpu_pending_irq(vcpu)) { > + vcpu->arch.wait_for_interrupts = 0; > + kvm_vcpu_kick(vcpu); > + } > + } > +} > + > +int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq) > +{ > + struct vgic_dist *dist = &kvm->arch.vgic; > + int nrcpus = atomic_read(&kvm->online_vcpus); > + int is_edge, state; > + > + if (cpuid >= nrcpus) > + return -EINVAL; > + > + /* Only PPIs or SPIs */ > + if (irq->irq >= VGIC_NR_IRQS || irq->irq < 16) > + return -EINVAL; > + > + kvm_debug("Inject IRQ%d\n", irq->irq); > + spin_lock(&dist->lock); > + is_edge = vgic_irq_is_edge(dist, irq->irq); > + state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq->irq); > + > + /* > + * Inject an interrupt if: > + * - level triggered and we change level > + * - edge triggered and we have a rising edge > + */ > + if ((!is_edge && (state ^ !!irq->level)) || > + (is_edge && !state && irq->level)) { > + vgic_bitmap_set_irq_val(&dist->irq_state, cpuid, > + irq->irq, !!irq->level); > + vgic_update_state(kvm); > + } > + spin_unlock(&dist->lock); > + > + vgic_kick_vcpus(kvm); do we need this if we don't call vgic_update_state? > + > + return 0; > +} > -- > 1.7.10.3 > > _______________________________________________ kvmarm mailing list kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm