|
|
a83cc2 |
From e92a6c64cb4b1437c5b75f25a638dbb6eb041383 Mon Sep 17 00:00:00 2001
|
|
|
a83cc2 |
From: "plai@redhat.com" <plai@redhat.com>
|
|
|
a83cc2 |
Date: Thu, 29 Jul 2021 07:42:27 -0400
|
|
|
a83cc2 |
Subject: [PATCH 16/39] i386: Add ratelimit for bus locks acquired in guest
|
|
|
a83cc2 |
|
|
|
a83cc2 |
RH-Author: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
a83cc2 |
RH-MergeRequest: 32: Synchronize with RHEL-AV 8.5 release 27 to RHEL 9
|
|
|
a83cc2 |
RH-Commit: [8/15] 2b8f01e05e44388c2f90d5281a9fe5537ab2433d (mrezanin/centos-src-qemu-kvm)
|
|
|
a83cc2 |
RH-Bugzilla: 1957194
|
|
|
a83cc2 |
RH-Acked-by: Stefano Garzarella <sgarzare@redhat.com>
|
|
|
a83cc2 |
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
|
|
a83cc2 |
RH-Acked-by: Igor Mammedov <imammedo@redhat.com>
|
|
|
a83cc2 |
RH-Acked-by: Andrew Jones <drjones@redhat.com>
|
|
|
a83cc2 |
|
|
|
a83cc2 |
A bus lock is acquired through either split locked access to writeback
|
|
|
a83cc2 |
(WB) memory or any locked access to non-WB memory. It is typically >1000
|
|
|
a83cc2 |
cycles slower than an atomic operation within a cache and can also
|
|
|
a83cc2 |
disrupts performance on other cores.
|
|
|
a83cc2 |
|
|
|
a83cc2 |
Virtual Machines can exploit bus locks to degrade the performance of
|
|
|
a83cc2 |
system. To address this kind of performance DOS attack coming from the
|
|
|
a83cc2 |
VMs, bus lock VM exit is introduced in KVM and it can report the bus
|
|
|
a83cc2 |
locks detected in guest. If enabled in KVM, it would exit to the
|
|
|
a83cc2 |
userspace to let the user enforce throttling policies once bus locks
|
|
|
a83cc2 |
acquired in VMs.
|
|
|
a83cc2 |
|
|
|
a83cc2 |
The availability of bus lock VM exit can be detected through the
|
|
|
a83cc2 |
KVM_CAP_X86_BUS_LOCK_EXIT. The returned bitmap contains the potential
|
|
|
a83cc2 |
policies supported by KVM. The field KVM_BUS_LOCK_DETECTION_EXIT in
|
|
|
a83cc2 |
bitmap is the only supported strategy at present. It indicates that KVM
|
|
|
a83cc2 |
will exit to userspace to handle the bus locks.
|
|
|
a83cc2 |
|
|
|
a83cc2 |
This patch adds a ratelimit on the bus locks acquired in guest as a
|
|
|
a83cc2 |
mitigation policy.
|
|
|
a83cc2 |
|
|
|
a83cc2 |
Introduce a new field "bus_lock_ratelimit" to record the limited speed
|
|
|
a83cc2 |
of bus locks in the target VM. The user can specify it through the
|
|
|
a83cc2 |
"bus-lock-ratelimit" as a machine property. In current implementation,
|
|
|
a83cc2 |
the default value of the speed is 0 per second, which means no
|
|
|
a83cc2 |
restrictions on the bus locks.
|
|
|
a83cc2 |
|
|
|
a83cc2 |
As for ratelimit on detected bus locks, simply set the ratelimit
|
|
|
a83cc2 |
interval to 1s and restrict the quota of bus lock occurence to the value
|
|
|
a83cc2 |
of "bus_lock_ratelimit". A potential alternative is to introduce the
|
|
|
a83cc2 |
time slice as a property which can help the user achieve more precise
|
|
|
a83cc2 |
control.
|
|
|
a83cc2 |
|
|
|
a83cc2 |
The detail of bus lock VM exit can be found in spec:
|
|
|
a83cc2 |
https://software.intel.com/content/www/us/en/develop/download/intel-architecture-instruction-set-extensions-programming-reference.html
|
|
|
a83cc2 |
|
|
|
a83cc2 |
Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
|
|
|
a83cc2 |
Message-Id: <20210521043820.29678-1-chenyi.qiang@intel.com>
|
|
|
a83cc2 |
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
|
|
|
a83cc2 |
(cherry picked from commit 035d1ef26565f8f8eae058c37f5731a9ae304b96)
|
|
|
a83cc2 |
Signed-off-by: Paul Lai <plai@redhat.com>
|
|
|
a83cc2 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
a83cc2 |
---
|
|
|
a83cc2 |
hw/i386/x86.c | 24 ++++++++++++++++++++++++
|
|
|
a83cc2 |
include/hw/i386/x86.h | 8 ++++++++
|
|
|
a83cc2 |
target/i386/kvm/kvm.c | 41 +++++++++++++++++++++++++++++++++++++++++
|
|
|
a83cc2 |
3 files changed, 73 insertions(+)
|
|
|
a83cc2 |
|
|
|
a83cc2 |
diff --git a/hw/i386/x86.c b/hw/i386/x86.c
|
|
|
a83cc2 |
index ed796fe6ba..d30cf27e29 100644
|
|
|
a83cc2 |
--- a/hw/i386/x86.c
|
|
|
a83cc2 |
+++ b/hw/i386/x86.c
|
|
|
a83cc2 |
@@ -1246,6 +1246,23 @@ static void x86_machine_set_oem_table_id(Object *obj, const char *value,
|
|
|
a83cc2 |
strncpy(x86ms->oem_table_id, value, 8);
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
|
|
|
a83cc2 |
+static void x86_machine_get_bus_lock_ratelimit(Object *obj, Visitor *v,
|
|
|
a83cc2 |
+ const char *name, void *opaque, Error **errp)
|
|
|
a83cc2 |
+{
|
|
|
a83cc2 |
+ X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
a83cc2 |
+ uint64_t bus_lock_ratelimit = x86ms->bus_lock_ratelimit;
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
+ visit_type_uint64(v, name, &bus_lock_ratelimit, errp);
|
|
|
a83cc2 |
+}
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
+static void x86_machine_set_bus_lock_ratelimit(Object *obj, Visitor *v,
|
|
|
a83cc2 |
+ const char *name, void *opaque, Error **errp)
|
|
|
a83cc2 |
+{
|
|
|
a83cc2 |
+ X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
+ visit_type_uint64(v, name, &x86ms->bus_lock_ratelimit, errp);
|
|
|
a83cc2 |
+}
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
static void x86_machine_initfn(Object *obj)
|
|
|
a83cc2 |
{
|
|
|
a83cc2 |
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
a83cc2 |
@@ -1256,6 +1273,7 @@ static void x86_machine_initfn(Object *obj)
|
|
|
a83cc2 |
x86ms->pci_irq_mask = ACPI_BUILD_PCI_IRQS;
|
|
|
a83cc2 |
x86ms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
|
|
|
a83cc2 |
x86ms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
|
|
|
a83cc2 |
+ x86ms->bus_lock_ratelimit = 0;
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
|
|
|
a83cc2 |
static void x86_machine_class_init(ObjectClass *oc, void *data)
|
|
|
a83cc2 |
@@ -1299,6 +1317,12 @@ static void x86_machine_class_init(ObjectClass *oc, void *data)
|
|
|
a83cc2 |
"Override the default value of field OEM Table ID "
|
|
|
a83cc2 |
"in ACPI table header."
|
|
|
a83cc2 |
"The string may be up to 8 bytes in size");
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
+ object_class_property_add(oc, X86_MACHINE_BUS_LOCK_RATELIMIT, "uint64_t",
|
|
|
a83cc2 |
+ x86_machine_get_bus_lock_ratelimit,
|
|
|
a83cc2 |
+ x86_machine_set_bus_lock_ratelimit, NULL, NULL);
|
|
|
a83cc2 |
+ object_class_property_set_description(oc, X86_MACHINE_BUS_LOCK_RATELIMIT,
|
|
|
a83cc2 |
+ "Set the ratelimit for the bus locks acquired in VMs");
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
|
|
|
a83cc2 |
static const TypeInfo x86_machine_info = {
|
|
|
a83cc2 |
diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h
|
|
|
a83cc2 |
index c09b648dff..25a1f16f01 100644
|
|
|
a83cc2 |
--- a/include/hw/i386/x86.h
|
|
|
a83cc2 |
+++ b/include/hw/i386/x86.h
|
|
|
a83cc2 |
@@ -74,12 +74,20 @@ struct X86MachineState {
|
|
|
a83cc2 |
* will be translated to MSI messages in the address space.
|
|
|
a83cc2 |
*/
|
|
|
a83cc2 |
AddressSpace *ioapic_as;
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
+ /*
|
|
|
a83cc2 |
+ * Ratelimit enforced on detected bus locks in guest.
|
|
|
a83cc2 |
+ * The default value of the bus_lock_ratelimit is 0 per second,
|
|
|
a83cc2 |
+ * which means no limitation on the guest's bus locks.
|
|
|
a83cc2 |
+ */
|
|
|
a83cc2 |
+ uint64_t bus_lock_ratelimit;
|
|
|
a83cc2 |
};
|
|
|
a83cc2 |
|
|
|
a83cc2 |
#define X86_MACHINE_SMM "smm"
|
|
|
a83cc2 |
#define X86_MACHINE_ACPI "acpi"
|
|
|
a83cc2 |
#define X86_MACHINE_OEM_ID "x-oem-id"
|
|
|
a83cc2 |
#define X86_MACHINE_OEM_TABLE_ID "x-oem-table-id"
|
|
|
a83cc2 |
+#define X86_MACHINE_BUS_LOCK_RATELIMIT "bus-lock-ratelimit"
|
|
|
a83cc2 |
|
|
|
a83cc2 |
#define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86")
|
|
|
a83cc2 |
OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, X86_MACHINE)
|
|
|
a83cc2 |
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
|
|
|
a83cc2 |
index 4c69c2cb4b..af030af116 100644
|
|
|
a83cc2 |
--- a/target/i386/kvm/kvm.c
|
|
|
a83cc2 |
+++ b/target/i386/kvm/kvm.c
|
|
|
a83cc2 |
@@ -130,6 +130,9 @@ static bool has_msr_mcg_ext_ctl;
|
|
|
a83cc2 |
static struct kvm_cpuid2 *cpuid_cache;
|
|
|
a83cc2 |
static struct kvm_msr_list *kvm_feature_msrs;
|
|
|
a83cc2 |
|
|
|
a83cc2 |
+#define BUS_LOCK_SLICE_TIME 1000000000ULL /* ns */
|
|
|
a83cc2 |
+static RateLimit bus_lock_ratelimit_ctrl;
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
int kvm_has_pit_state2(void)
|
|
|
a83cc2 |
{
|
|
|
a83cc2 |
return has_pit_state2;
|
|
|
a83cc2 |
@@ -2267,6 +2270,28 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
|
|
|
a83cc2 |
+ if (object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) {
|
|
|
a83cc2 |
+ X86MachineState *x86ms = X86_MACHINE(ms);
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
+ if (x86ms->bus_lock_ratelimit > 0) {
|
|
|
a83cc2 |
+ ret = kvm_check_extension(s, KVM_CAP_X86_BUS_LOCK_EXIT);
|
|
|
a83cc2 |
+ if (!(ret & KVM_BUS_LOCK_DETECTION_EXIT)) {
|
|
|
a83cc2 |
+ error_report("kvm: bus lock detection unsupported");
|
|
|
a83cc2 |
+ return -ENOTSUP;
|
|
|
a83cc2 |
+ }
|
|
|
a83cc2 |
+ ret = kvm_vm_enable_cap(s, KVM_CAP_X86_BUS_LOCK_EXIT, 0,
|
|
|
a83cc2 |
+ KVM_BUS_LOCK_DETECTION_EXIT);
|
|
|
a83cc2 |
+ if (ret < 0) {
|
|
|
a83cc2 |
+ error_report("kvm: Failed to enable bus lock detection cap: %s",
|
|
|
a83cc2 |
+ strerror(-ret));
|
|
|
a83cc2 |
+ return ret;
|
|
|
a83cc2 |
+ }
|
|
|
a83cc2 |
+ ratelimit_init(&bus_lock_ratelimit_ctrl);
|
|
|
a83cc2 |
+ ratelimit_set_speed(&bus_lock_ratelimit_ctrl,
|
|
|
a83cc2 |
+ x86ms->bus_lock_ratelimit, BUS_LOCK_SLICE_TIME);
|
|
|
a83cc2 |
+ }
|
|
|
a83cc2 |
+ }
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
return 0;
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
|
|
|
a83cc2 |
@@ -4225,6 +4250,15 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run)
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
|
|
|
a83cc2 |
+static void kvm_rate_limit_on_bus_lock(void)
|
|
|
a83cc2 |
+{
|
|
|
a83cc2 |
+ uint64_t delay_ns = ratelimit_calculate_delay(&bus_lock_ratelimit_ctrl, 1);
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
+ if (delay_ns) {
|
|
|
a83cc2 |
+ g_usleep(delay_ns / SCALE_US);
|
|
|
a83cc2 |
+ }
|
|
|
a83cc2 |
+}
|
|
|
a83cc2 |
+
|
|
|
a83cc2 |
MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
|
|
|
a83cc2 |
{
|
|
|
a83cc2 |
X86CPU *x86_cpu = X86_CPU(cpu);
|
|
|
a83cc2 |
@@ -4240,6 +4274,9 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
|
|
|
a83cc2 |
} else {
|
|
|
a83cc2 |
env->eflags &= ~IF_MASK;
|
|
|
a83cc2 |
}
|
|
|
a83cc2 |
+ if (run->flags & KVM_RUN_X86_BUS_LOCK) {
|
|
|
a83cc2 |
+ kvm_rate_limit_on_bus_lock();
|
|
|
a83cc2 |
+ }
|
|
|
a83cc2 |
|
|
|
a83cc2 |
/* We need to protect the apic state against concurrent accesses from
|
|
|
a83cc2 |
* different threads in case the userspace irqchip is used. */
|
|
|
a83cc2 |
@@ -4598,6 +4635,10 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
|
|
a83cc2 |
ioapic_eoi_broadcast(run->eoi.vector);
|
|
|
a83cc2 |
ret = 0;
|
|
|
a83cc2 |
break;
|
|
|
a83cc2 |
+ case KVM_EXIT_X86_BUS_LOCK:
|
|
|
a83cc2 |
+ /* already handled in kvm_arch_post_run */
|
|
|
a83cc2 |
+ ret = 0;
|
|
|
a83cc2 |
+ break;
|
|
|
a83cc2 |
default:
|
|
|
a83cc2 |
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
|
|
|
a83cc2 |
ret = -1;
|
|
|
a83cc2 |
--
|
|
|
a83cc2 |
2.27.0
|
|
|
a83cc2 |
|