diff options
Diffstat (limited to 'virt/kvm/kvm_main.c')
| -rw-r--r-- | virt/kvm/kvm_main.c | 43 | 
1 files changed, 35 insertions, 8 deletions
| diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 6c07dd423458..bcef324ccbf2 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1103,6 +1103,14 @@ void __weak kvm_arch_create_vm_debugfs(struct kvm *kvm)  {  } +/* Called only on cleanup and destruction paths when there are no users. */ +static inline struct kvm_io_bus *kvm_get_bus_for_destruction(struct kvm *kvm, +							     enum kvm_bus idx) +{ +	return rcu_dereference_protected(kvm->buses[idx], +					 !refcount_read(&kvm->users_count)); +} +  static struct kvm *kvm_create_vm(unsigned long type, const char *fdname)  {  	struct kvm *kvm = kvm_arch_alloc_vm(); @@ -1228,7 +1236,7 @@ out_err_no_disable:  out_err_no_arch_destroy_vm:  	WARN_ON_ONCE(!refcount_dec_and_test(&kvm->users_count));  	for (i = 0; i < KVM_NR_BUSES; i++) -		kfree(kvm_get_bus(kvm, i)); +		kfree(kvm_get_bus_for_destruction(kvm, i));  	kvm_free_irq_routing(kvm);  out_err_no_irq_routing:  	cleanup_srcu_struct(&kvm->irq_srcu); @@ -1276,7 +1284,7 @@ static void kvm_destroy_vm(struct kvm *kvm)  	kvm_free_irq_routing(kvm);  	for (i = 0; i < KVM_NR_BUSES; i++) { -		struct kvm_io_bus *bus = kvm_get_bus(kvm, i); +		struct kvm_io_bus *bus = kvm_get_bus_for_destruction(kvm, i);  		if (bus)  			kvm_io_bus_destroy(bus); @@ -1312,6 +1320,7 @@ static void kvm_destroy_vm(struct kvm *kvm)  		kvm_free_memslots(kvm, &kvm->__memslots[i][1]);  	}  	cleanup_srcu_struct(&kvm->irq_srcu); +	srcu_barrier(&kvm->srcu);  	cleanup_srcu_struct(&kvm->srcu);  #ifdef CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES  	xa_destroy(&kvm->mem_attr_array); @@ -5843,6 +5852,18 @@ static int __kvm_io_bus_write(struct kvm_vcpu *vcpu, struct kvm_io_bus *bus,  	return -EOPNOTSUPP;  } +static struct kvm_io_bus *kvm_get_bus_srcu(struct kvm *kvm, enum kvm_bus idx) +{ +	/* +	 * Ensure that any updates to kvm_buses[] observed by the previous vCPU +	 * machine instruction are also visible to the vCPU machine instruction +	 * that triggered this call. +	 */ +	smp_mb__after_srcu_read_lock(); + +	return srcu_dereference(kvm->buses[idx], &kvm->srcu); +} +  int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,  		     int len, const void *val)  { @@ -5855,7 +5876,7 @@ int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,  		.len = len,  	}; -	bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu); +	bus = kvm_get_bus_srcu(vcpu->kvm, bus_idx);  	if (!bus)  		return -ENOMEM;  	r = __kvm_io_bus_write(vcpu, bus, &range, val); @@ -5874,7 +5895,7 @@ int kvm_io_bus_write_cookie(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx,  		.len = len,  	}; -	bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu); +	bus = kvm_get_bus_srcu(vcpu->kvm, bus_idx);  	if (!bus)  		return -ENOMEM; @@ -5924,7 +5945,7 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,  		.len = len,  	}; -	bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu); +	bus = kvm_get_bus_srcu(vcpu->kvm, bus_idx);  	if (!bus)  		return -ENOMEM;  	r = __kvm_io_bus_read(vcpu, bus, &range, val); @@ -5932,6 +5953,13 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,  }  EXPORT_SYMBOL_GPL(kvm_io_bus_read); +static void __free_bus(struct rcu_head *rcu) +{ +	struct kvm_io_bus *bus = container_of(rcu, struct kvm_io_bus, rcu); + +	kfree(bus); +} +  int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,  			    int len, struct kvm_io_device *dev)  { @@ -5970,8 +5998,7 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,  	memcpy(new_bus->range + i + 1, bus->range + i,  		(bus->dev_count - i) * sizeof(struct kvm_io_range));  	rcu_assign_pointer(kvm->buses[bus_idx], new_bus); -	synchronize_srcu_expedited(&kvm->srcu); -	kfree(bus); +	call_srcu(&kvm->srcu, &bus->rcu, __free_bus);  	return 0;  } @@ -6033,7 +6060,7 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,  	srcu_idx = srcu_read_lock(&kvm->srcu); -	bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); +	bus = kvm_get_bus_srcu(kvm, bus_idx);  	if (!bus)  		goto out_unlock; | 
