diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 03ab35a58836..5dcac8df0a85 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -799,10 +799,13 @@ long kvm_arch_vm_ioctl(struct file *filp, switch (ioctl) { case KVM_CREATE_IRQCHIP: { - if (vgic_present) - return kvm_vgic_create(kvm); - else + int ret; + if (!vgic_present) return -ENXIO; + mutex_lock(&kvm->lock); + ret = kvm_vgic_create(kvm); + mutex_unlock(&kvm->lock); + return ret; } case KVM_ARM_SET_DEVICE_ADDR: { struct kvm_arm_device_addr dev_addr; diff --git a/arch/powerpc/kvm/book3s_xics.c b/arch/powerpc/kvm/book3s_xics.c index 6d35d7baa8e1..6be2830c8753 100644 --- a/arch/powerpc/kvm/book3s_xics.c +++ b/arch/powerpc/kvm/book3s_xics.c @@ -1239,12 +1239,10 @@ static int kvmppc_xics_create(struct kvm_device *dev, u32 type) xics->kvm = kvm; /* Already there ? */ - mutex_lock(&kvm->lock); if (kvm->arch.xics) ret = -EEXIST; else kvm->arch.xics = xics; - mutex_unlock(&kvm->lock); if (ret) { kfree(xics); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 95e87f8ab351..4f10dcf3e76b 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1059,6 +1059,12 @@ struct kvm_device { /* create, destroy, and name are mandatory */ struct kvm_device_ops { const char *name; + + /* + * create is called holding kvm->lock and any operations not suitable + * to do while holding the lock should be deferred to init (see + * below). + */ int (*create)(struct kvm_device *dev, u32 type); /* diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 24ac123f4ad8..7b490485712c 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1624,12 +1624,8 @@ int kvm_vgic_create(struct kvm *kvm) int i, vcpu_lock_idx = -1, ret; struct kvm_vcpu *vcpu; - mutex_lock(&kvm->lock); - - if (kvm->arch.vgic.vctrl_base) { - ret = -EEXIST; - goto out; - } + if (kvm->arch.vgic.vctrl_base) + return -EEXIST; /* * Any time a vcpu is run, vcpu_load is called which tries to grab the @@ -1659,9 +1655,6 @@ int kvm_vgic_create(struct kvm *kvm) vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx); mutex_unlock(&vcpu->mutex); } - -out: - mutex_unlock(&kvm->lock); return ret; } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index d98e6cf1e781..f8cbf5ba6136 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -587,6 +587,11 @@ static void kvm_destroy_devices(struct kvm *kvm) { struct list_head *node, *tmp; + /* + * We do not need to take the kvm->lock here, because nobody else + * has a reference to the struct kvm at this point and therefore + * cannot access the devices list anyhow. + */ list_for_each_safe(node, tmp, &kvm->devices) { struct kvm_device *dev = list_entry(node, struct kvm_device, vm_node); @@ -2322,11 +2327,15 @@ static int kvm_ioctl_create_device(struct kvm *kvm, dev->ops = ops; dev->kvm = kvm; + mutex_lock(&kvm->lock); ret = ops->create(dev, cd->type); if (ret < 0) { + mutex_unlock(&kvm->lock); kfree(dev); return ret; } + list_add(&dev->vm_node, &kvm->devices); + mutex_unlock(&kvm->lock); if (ops->init) ops->init(dev); @@ -2334,10 +2343,12 @@ static int kvm_ioctl_create_device(struct kvm *kvm, ret = anon_inode_getfd(ops->name, &kvm_device_fops, dev, O_RDWR | O_CLOEXEC); if (ret < 0) { ops->destroy(dev); + mutex_lock(&kvm->lock); + list_del(&dev->vm_node); + mutex_unlock(&kvm->lock); return ret; } - list_add(&dev->vm_node, &kvm->devices); kvm_get_kvm(kvm); cd->fd = ret; return 0;