KVM: arm-vgic: Support KVM_CREATE_DEVICE for VGIC
[firefly-linux-kernel-4.4.55.git] / virt / kvm / arm / vgic.c
index 5e9df47778fb3caee11eac298b54f8dad76eecf1..b15d6c17a0905609db65d419f4c9877099a0dae3 100644 (file)
@@ -1433,20 +1433,45 @@ out:
 
 int kvm_vgic_create(struct kvm *kvm)
 {
-       int ret = 0;
+       int i, vcpu_lock_idx = -1, ret = 0;
+       struct kvm_vcpu *vcpu;
 
        mutex_lock(&kvm->lock);
 
-       if (atomic_read(&kvm->online_vcpus) || kvm->arch.vgic.vctrl_base) {
+       if (kvm->arch.vgic.vctrl_base) {
                ret = -EEXIST;
                goto out;
        }
 
+       /*
+        * Any time a vcpu is run, vcpu_load is called which tries to grab the
+        * vcpu->mutex.  By grabbing the vcpu->mutex of all VCPUs we ensure
+        * that no other VCPUs are run while we create the vgic.
+        */
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               if (!mutex_trylock(&vcpu->mutex))
+                       goto out_unlock;
+               vcpu_lock_idx = i;
+       }
+
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               if (vcpu->arch.has_run_once) {
+                       ret = -EBUSY;
+                       goto out_unlock;
+               }
+       }
+
        spin_lock_init(&kvm->arch.vgic.lock);
        kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
        kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
        kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
 
+out_unlock:
+       for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
+               vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);
+               mutex_unlock(&vcpu->mutex);
+       }
+
 out:
        mutex_unlock(&kvm->lock);
        return ret;
@@ -1510,3 +1535,37 @@ int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
        mutex_unlock(&kvm->lock);
        return r;
 }
+
+static int vgic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+       return -ENXIO;
+}
+
+static int vgic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+       return -ENXIO;
+}
+
+static int vgic_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+       return -ENXIO;
+}
+
+static void vgic_destroy(struct kvm_device *dev)
+{
+       kfree(dev);
+}
+
+static int vgic_create(struct kvm_device *dev, u32 type)
+{
+       return kvm_vgic_create(dev->kvm);
+}
+
+struct kvm_device_ops kvm_arm_vgic_v2_ops = {
+       .name = "kvm-arm-vgic",
+       .create = vgic_create,
+       .destroy = vgic_destroy,
+       .set_attr = vgic_set_attr,
+       .get_attr = vgic_get_attr,
+       .has_attr = vgic_has_attr,
+};