Skip to content

Commit 59112e9

Browse files
jpbruckerMarc Zyngier
authored andcommitted
KVM: arm64: vgic: Fix a circular locking issue
Lockdep reports a circular lock dependency between the srcu and the config_lock: [ 262.179917] -> #1 (&kvm->srcu){.+.+}-{0:0}: [ 262.182010] __synchronize_srcu+0xb0/0x224 [ 262.183422] synchronize_srcu_expedited+0x24/0x34 [ 262.184554] kvm_io_bus_register_dev+0x324/0x50c [ 262.185650] vgic_register_redist_iodev+0x254/0x398 [ 262.186740] vgic_v3_set_redist_base+0x3b0/0x724 [ 262.188087] kvm_vgic_addr+0x364/0x600 [ 262.189189] vgic_set_common_attr+0x90/0x544 [ 262.190278] vgic_v3_set_attr+0x74/0x9c [ 262.191432] kvm_device_ioctl+0x2a0/0x4e4 [ 262.192515] __arm64_sys_ioctl+0x7ac/0x1ba8 [ 262.193612] invoke_syscall.constprop.0+0x70/0x1e0 [ 262.195006] do_el0_svc+0xe4/0x2d4 [ 262.195929] el0_svc+0x44/0x8c [ 262.196917] el0t_64_sync_handler+0xf4/0x120 [ 262.198238] el0t_64_sync+0x190/0x194 [ 262.199224] [ 262.199224] -> #0 (&kvm->arch.config_lock){+.+.}-{3:3}: [ 262.201094] __lock_acquire+0x2b70/0x626c [ 262.202245] lock_acquire+0x454/0x778 [ 262.203132] __mutex_lock+0x190/0x8b4 [ 262.204023] mutex_lock_nested+0x24/0x30 [ 262.205100] vgic_mmio_write_v3_misc+0x5c/0x2a0 [ 262.206178] dispatch_mmio_write+0xd8/0x258 [ 262.207498] __kvm_io_bus_write+0x1e0/0x350 [ 262.208582] kvm_io_bus_write+0xe0/0x1cc [ 262.209653] io_mem_abort+0x2ac/0x6d8 [ 262.210569] kvm_handle_guest_abort+0x9b8/0x1f88 [ 262.211937] handle_exit+0xc4/0x39c [ 262.212971] kvm_arch_vcpu_ioctl_run+0x90c/0x1c04 [ 262.214154] kvm_vcpu_ioctl+0x450/0x12f8 [ 262.215233] __arm64_sys_ioctl+0x7ac/0x1ba8 [ 262.216402] invoke_syscall.constprop.0+0x70/0x1e0 [ 262.217774] do_el0_svc+0xe4/0x2d4 [ 262.218758] el0_svc+0x44/0x8c [ 262.219941] el0t_64_sync_handler+0xf4/0x120 [ 262.221110] el0t_64_sync+0x190/0x194 Note that the current report, which can be triggered by the vgic_irq kselftest, is a triple chain that includes slots_lock, but after inverting the slots_lock/config_lock dependency, the actual problem reported above remains. In several places, the vgic code calls kvm_io_bus_register_dev(), which synchronizes the srcu, while holding config_lock (#1). And the MMIO handler takes the config_lock while holding the srcu read lock (#0). Break dependency #1, by registering the distributor and redistributors without holding config_lock. The ITS also uses kvm_io_bus_register_dev() but already relies on slots_lock to serialize calls. The distributor iodev is created on the first KVM_RUN call. Multiple threads will race for vgic initialization, and only the first one will see !vgic_ready() under the lock. To serialize those threads, rely on slots_lock rather than config_lock. Redistributors are created earlier, through KVM_DEV_ARM_VGIC_GRP_ADDR ioctls and vCPU creation. Similarly, serialize the iodev creation with slots_lock, and the rest with config_lock. Fixes: f003277 ("KVM: arm64: Use config_lock to protect vgic state") Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20230518100914.2837292-2-jean-philippe@linaro.org
1 parent c3a62df commit 59112e9

File tree

6 files changed

+51
-37
lines changed

6 files changed

+51
-37
lines changed

arch/arm64/kvm/vgic/vgic-init.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,9 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
235235
* KVM io device for the redistributor that belongs to this VCPU.
236236
*/
237237
if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
238-
mutex_lock(&vcpu->kvm->arch.config_lock);
238+
mutex_lock(&vcpu->kvm->slots_lock);
239239
ret = vgic_register_redist_iodev(vcpu);
240-
mutex_unlock(&vcpu->kvm->arch.config_lock);
240+
mutex_unlock(&vcpu->kvm->slots_lock);
241241
}
242242
return ret;
243243
}
@@ -446,11 +446,13 @@ int vgic_lazy_init(struct kvm *kvm)
446446
int kvm_vgic_map_resources(struct kvm *kvm)
447447
{
448448
struct vgic_dist *dist = &kvm->arch.vgic;
449+
gpa_t dist_base;
449450
int ret = 0;
450451

451452
if (likely(vgic_ready(kvm)))
452453
return 0;
453454

455+
mutex_lock(&kvm->slots_lock);
454456
mutex_lock(&kvm->arch.config_lock);
455457
if (vgic_ready(kvm))
456458
goto out;
@@ -463,13 +465,26 @@ int kvm_vgic_map_resources(struct kvm *kvm)
463465
else
464466
ret = vgic_v3_map_resources(kvm);
465467

466-
if (ret)
468+
if (ret) {
467469
__kvm_vgic_destroy(kvm);
468-
else
469-
dist->ready = true;
470+
goto out;
471+
}
472+
dist->ready = true;
473+
dist_base = dist->vgic_dist_base;
474+
mutex_unlock(&kvm->arch.config_lock);
475+
476+
ret = vgic_register_dist_iodev(kvm, dist_base,
477+
kvm_vgic_global_state.type);
478+
if (ret) {
479+
kvm_err("Unable to register VGIC dist MMIO regions\n");
480+
kvm_vgic_destroy(kvm);
481+
}
482+
mutex_unlock(&kvm->slots_lock);
483+
return ret;
470484

471485
out:
472486
mutex_unlock(&kvm->arch.config_lock);
487+
mutex_unlock(&kvm->slots_lock);
473488
return ret;
474489
}
475490

arch/arm64/kvm/vgic/vgic-kvm-device.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ static int kvm_vgic_addr(struct kvm *kvm, struct kvm_device_attr *attr, bool wri
102102
if (get_user(addr, uaddr))
103103
return -EFAULT;
104104

105-
mutex_lock(&kvm->arch.config_lock);
105+
/*
106+
* Since we can't hold config_lock while registering the redistributor
107+
* iodevs, take the slots_lock immediately.
108+
*/
109+
mutex_lock(&kvm->slots_lock);
106110
switch (attr->attr) {
107111
case KVM_VGIC_V2_ADDR_TYPE_DIST:
108112
r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
@@ -182,16 +186,18 @@ static int kvm_vgic_addr(struct kvm *kvm, struct kvm_device_attr *attr, bool wri
182186
if (r)
183187
goto out;
184188

189+
mutex_lock(&kvm->arch.config_lock);
185190
if (write) {
186191
r = vgic_check_iorange(kvm, *addr_ptr, addr, alignment, size);
187192
if (!r)
188193
*addr_ptr = addr;
189194
} else {
190195
addr = *addr_ptr;
191196
}
197+
mutex_unlock(&kvm->arch.config_lock);
192198

193199
out:
194-
mutex_unlock(&kvm->arch.config_lock);
200+
mutex_unlock(&kvm->slots_lock);
195201

196202
if (!r && !write)
197203
r = put_user(addr, uaddr);

arch/arm64/kvm/vgic/vgic-mmio-v3.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -769,10 +769,13 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu)
769769
struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev;
770770
struct vgic_redist_region *rdreg;
771771
gpa_t rd_base;
772-
int ret;
772+
int ret = 0;
773+
774+
lockdep_assert_held(&kvm->slots_lock);
775+
mutex_lock(&kvm->arch.config_lock);
773776

774777
if (!IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr))
775-
return 0;
778+
goto out_unlock;
776779

777780
/*
778781
* We may be creating VCPUs before having set the base address for the
@@ -782,10 +785,12 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu)
782785
*/
783786
rdreg = vgic_v3_rdist_free_slot(&vgic->rd_regions);
784787
if (!rdreg)
785-
return 0;
788+
goto out_unlock;
786789

787-
if (!vgic_v3_check_base(kvm))
788-
return -EINVAL;
790+
if (!vgic_v3_check_base(kvm)) {
791+
ret = -EINVAL;
792+
goto out_unlock;
793+
}
789794

790795
vgic_cpu->rdreg = rdreg;
791796
vgic_cpu->rdreg_index = rdreg->free_index;
@@ -799,16 +804,20 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu)
799804
rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rd_registers);
800805
rd_dev->redist_vcpu = vcpu;
801806

802-
mutex_lock(&kvm->slots_lock);
807+
mutex_unlock(&kvm->arch.config_lock);
808+
803809
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, rd_base,
804810
2 * SZ_64K, &rd_dev->dev);
805-
mutex_unlock(&kvm->slots_lock);
806-
807811
if (ret)
808812
return ret;
809813

814+
/* Protected by slots_lock */
810815
rdreg->free_index++;
811816
return 0;
817+
818+
out_unlock:
819+
mutex_unlock(&kvm->arch.config_lock);
820+
return ret;
812821
}
813822

814823
static void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu)
@@ -834,12 +843,10 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm)
834843
/* The current c failed, so iterate over the previous ones. */
835844
int i;
836845

837-
mutex_lock(&kvm->slots_lock);
838846
for (i = 0; i < c; i++) {
839847
vcpu = kvm_get_vcpu(kvm, i);
840848
vgic_unregister_redist_iodev(vcpu);
841849
}
842-
mutex_unlock(&kvm->slots_lock);
843850
}
844851

845852
return ret;
@@ -938,7 +945,9 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
938945
{
939946
int ret;
940947

948+
mutex_lock(&kvm->arch.config_lock);
941949
ret = vgic_v3_alloc_redist_region(kvm, index, addr, count);
950+
mutex_unlock(&kvm->arch.config_lock);
942951
if (ret)
943952
return ret;
944953

@@ -950,8 +959,10 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
950959
if (ret) {
951960
struct vgic_redist_region *rdreg;
952961

962+
mutex_lock(&kvm->arch.config_lock);
953963
rdreg = vgic_v3_rdist_region_from_index(kvm, index);
954964
vgic_v3_free_redist_region(rdreg);
965+
mutex_unlock(&kvm->arch.config_lock);
955966
return ret;
956967
}
957968

arch/arm64/kvm/vgic/vgic-mmio.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,6 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
10961096
enum vgic_type type)
10971097
{
10981098
struct vgic_io_device *io_device = &kvm->arch.vgic.dist_iodev;
1099-
int ret = 0;
11001099
unsigned int len;
11011100

11021101
switch (type) {
@@ -1114,10 +1113,6 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
11141113
io_device->iodev_type = IODEV_DIST;
11151114
io_device->redist_vcpu = NULL;
11161115

1117-
mutex_lock(&kvm->slots_lock);
1118-
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist_base_address,
1119-
len, &io_device->dev);
1120-
mutex_unlock(&kvm->slots_lock);
1121-
1122-
return ret;
1116+
return kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist_base_address,
1117+
len, &io_device->dev);
11231118
}

arch/arm64/kvm/vgic/vgic-v2.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,12 +312,6 @@ int vgic_v2_map_resources(struct kvm *kvm)
312312
return ret;
313313
}
314314

315-
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2);
316-
if (ret) {
317-
kvm_err("Unable to register VGIC MMIO regions\n");
318-
return ret;
319-
}
320-
321315
if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
322316
ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
323317
kvm_vgic_global_state.vcpu_base,

arch/arm64/kvm/vgic/vgic-v3.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,6 @@ int vgic_v3_map_resources(struct kvm *kvm)
539539
{
540540
struct vgic_dist *dist = &kvm->arch.vgic;
541541
struct kvm_vcpu *vcpu;
542-
int ret = 0;
543542
unsigned long c;
544543

545544
kvm_for_each_vcpu(c, vcpu, kvm) {
@@ -569,12 +568,6 @@ int vgic_v3_map_resources(struct kvm *kvm)
569568
return -EBUSY;
570569
}
571570

572-
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3);
573-
if (ret) {
574-
kvm_err("Unable to register VGICv3 dist MMIO regions\n");
575-
return ret;
576-
}
577-
578571
if (kvm_vgic_global_state.has_gicv4_1)
579572
vgic_v4_configure_vsgis(kvm);
580573

0 commit comments

Comments
 (0)