@@ -74,12 +74,20 @@ struct X86MachineState {
* will be translated to MSI messages in the address space.
*/
AddressSpace *ioapic_as;
+
+ /*
+ * Ratelimit enforced on detected bus locks in guest.
+ * The default value of the bus_lock_ratelimit is 0 per second,
+ * which means no limitation on the guest's bus locks.
+ */
+ uint64_t bus_lock_ratelimit;
};
#define X86_MACHINE_SMM "smm"
#define X86_MACHINE_ACPI "acpi"
#define X86_MACHINE_OEM_ID "x-oem-id"
#define X86_MACHINE_OEM_TABLE_ID "x-oem-table-id"
+#define X86_MACHINE_BUS_LOCK_RATELIMIT "bus-lock-ratelimit"
#define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86")
OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, X86_MACHINE)
@@ -1246,6 +1246,23 @@ static void x86_machine_set_oem_table_id(Object *obj, const char *value,
strncpy(x86ms->oem_table_id, value, 8);
}
+static void x86_machine_get_bus_lock_ratelimit(Object *obj, Visitor *v,
+ const char *name, void *opaque, Error **errp)
+{
+ X86MachineState *x86ms = X86_MACHINE(obj);
+ uint64_t bus_lock_ratelimit = x86ms->bus_lock_ratelimit;
+
+ visit_type_uint64(v, name, &bus_lock_ratelimit, errp);
+}
+
+static void x86_machine_set_bus_lock_ratelimit(Object *obj, Visitor *v,
+ const char *name, void *opaque, Error **errp)
+{
+ X86MachineState *x86ms = X86_MACHINE(obj);
+
+ visit_type_uint64(v, name, &x86ms->bus_lock_ratelimit, errp);
+}
+
static void x86_machine_initfn(Object *obj)
{
X86MachineState *x86ms = X86_MACHINE(obj);
@@ -1256,6 +1273,7 @@ static void x86_machine_initfn(Object *obj)
x86ms->pci_irq_mask = ACPI_BUILD_PCI_IRQS;
x86ms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
x86ms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
+ x86ms->bus_lock_ratelimit = 0;
}
static void x86_machine_class_init(ObjectClass *oc, void *data)
@@ -1299,6 +1317,12 @@ static void x86_machine_class_init(ObjectClass *oc, void *data)
"Override the default value of field OEM Table ID "
"in ACPI table header."
"The string may be up to 8 bytes in size");
+
+ object_class_property_add(oc, X86_MACHINE_BUS_LOCK_RATELIMIT, "uint64_t",
+ x86_machine_get_bus_lock_ratelimit,
+ x86_machine_set_bus_lock_ratelimit, NULL, NULL);
+ object_class_property_set_description(oc, X86_MACHINE_BUS_LOCK_RATELIMIT,
+ "Set the ratelimit for the bus locks acquired in VMs");
}
static const TypeInfo x86_machine_info = {
@@ -132,6 +132,9 @@ static struct kvm_cpuid2 *cpuid_cache;
static struct kvm_cpuid2 *hv_cpuid_cache;
static struct kvm_msr_list *kvm_feature_msrs;
+#define BUS_LOCK_SLICE_TIME 1000000000ULL /* ns */
+static RateLimit bus_lock_ratelimit_ctrl;
+
int kvm_has_pit_state2(void)
{
return has_pit_state2;
@@ -2312,6 +2315,28 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
}
}
+ if (object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) {
+ X86MachineState *x86ms = X86_MACHINE(ms);
+
+ if (x86ms->bus_lock_ratelimit > 0) {
+ ret = kvm_check_extension(s, KVM_CAP_X86_BUS_LOCK_EXIT);
+ if (!(ret & KVM_BUS_LOCK_DETECTION_EXIT)) {
+ error_report("kvm: bus lock detection unsupported");
+ return -ENOTSUP;
+ }
+ ret = kvm_vm_enable_cap(s, KVM_CAP_X86_BUS_LOCK_EXIT, 0,
+ KVM_BUS_LOCK_DETECTION_EXIT);
+ if (ret < 0) {
+ error_report("kvm: Failed to enable bus lock detection cap: %s",
+ strerror(-ret));
+ return ret;
+ }
+ ratelimit_init(&bus_lock_ratelimit_ctrl);
+ ratelimit_set_speed(&bus_lock_ratelimit_ctrl,
+ x86ms->bus_lock_ratelimit, BUS_LOCK_SLICE_TIME);
+ }
+ }
+
return 0;
}
@@ -4266,6 +4291,15 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run)
}
}
+static void kvm_rate_limit_on_bus_lock(void)
+{
+ uint64_t delay_ns = ratelimit_calculate_delay(&bus_lock_ratelimit_ctrl, 1);
+
+ if (delay_ns) {
+ g_usleep(delay_ns / SCALE_US);
+ }
+}
+
MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
{
X86CPU *x86_cpu = X86_CPU(cpu);
@@ -4281,6 +4315,9 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
} else {
env->eflags &= ~IF_MASK;
}
+ if (run->flags & KVM_RUN_X86_BUS_LOCK) {
+ kvm_rate_limit_on_bus_lock();
+ }
/* We need to protect the apic state against concurrent accesses from
* different threads in case the userspace irqchip is used. */
@@ -4639,6 +4676,10 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
ioapic_eoi_broadcast(run->eoi.vector);
ret = 0;
break;
+ case KVM_EXIT_X86_BUS_LOCK:
+ /* already handled in kvm_arch_post_run */
+ ret = 0;
+ break;
default:
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
ret = -1;