cryptospore / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone

Blame SOURCES/kvm-i386-Add-ratelimit-for-bus-locks-acquired-in-guest.patch

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