KVM_SET_PMU_EVENT_FILTER of x86 KVM supports masked events mode, which
accepts masked entry format event to flexibly represent a group of PMU
events.
Support masked entry format in kvm-pmu-filter object and handle this in
i386 kvm codes.
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
---
accel/kvm/kvm-pmu.c | 91 +++++++++++++++++++++++++++++++++++++++++++
qapi/kvm.json | 68 ++++++++++++++++++++++++++++++--
target/i386/kvm/kvm.c | 39 +++++++++++++++++++
3 files changed, 195 insertions(+), 3 deletions(-)
diff --git a/accel/kvm/kvm-pmu.c b/accel/kvm/kvm-pmu.c
index 51d3fba5a72a..7a1720c68f8f 100644
--- a/accel/kvm/kvm-pmu.c
+++ b/accel/kvm/kvm-pmu.c
@@ -46,6 +46,16 @@ static void kvm_pmu_filter_get_event(Object *obj, Visitor *v, const char *name,
str_event->u.x86_default.umask =
g_strdup_printf("0x%x", event->u.x86_default.umask);
break;
+ case KVM_PMU_EVENT_FMT_X86_MASKED_ENTRY:
+ str_event->u.x86_masked_entry.select =
+ g_strdup_printf("0x%x", event->u.x86_masked_entry.select);
+ str_event->u.x86_masked_entry.match =
+ g_strdup_printf("0x%x", event->u.x86_masked_entry.match);
+ str_event->u.x86_masked_entry.mask =
+ g_strdup_printf("0x%x", event->u.x86_masked_entry.mask);
+ str_event->u.x86_masked_entry.exclude =
+ event->u.x86_masked_entry.exclude;
+ break;
default:
g_assert_not_reached();
}
@@ -145,6 +155,87 @@ static void kvm_pmu_filter_set_event(Object *obj, Visitor *v, const char *name,
event->u.x86_default.umask = umask;
break;
}
+ case KVM_PMU_EVENT_FMT_X86_MASKED_ENTRY: {
+ uint64_t select, match, mask;
+
+ ret = qemu_strtou64(str_event->u.x86_masked_entry.select,
+ NULL, 0, &select);
+ if (ret < 0) {
+ error_setg(errp,
+ "Invalid %s PMU event (select: %s): %s. "
+ "The select must be a "
+ "12-bit unsigned number string.",
+ KVMPMUEventEncodeFmt_str(str_event->format),
+ str_event->u.x86_masked_entry.select,
+ strerror(-ret));
+ g_free(event);
+ goto fail;
+ }
+ if (select > UINT12_MAX) {
+ error_setg(errp,
+ "Invalid %s PMU event (select: %s): "
+ "Numerical result out of range. "
+ "The select must be a "
+ "12-bit unsigned number string.",
+ KVMPMUEventEncodeFmt_str(str_event->format),
+ str_event->u.x86_masked_entry.select);
+ g_free(event);
+ goto fail;
+ }
+ event->u.x86_masked_entry.select = select;
+
+ ret = qemu_strtou64(str_event->u.x86_masked_entry.match,
+ NULL, 0, &match);
+ if (ret < 0) {
+ error_setg(errp,
+ "Invalid %s PMU event (match: %s): %s. "
+ "The match must be a uint8 string.",
+ KVMPMUEventEncodeFmt_str(str_event->format),
+ str_event->u.x86_masked_entry.match,
+ strerror(-ret));
+ g_free(event);
+ goto fail;
+ }
+ if (match > UINT8_MAX) {
+ error_setg(errp,
+ "Invalid %s PMU event (match: %s): "
+ "Numerical result out of range. "
+ "The match must be a uint8 string.",
+ KVMPMUEventEncodeFmt_str(str_event->format),
+ str_event->u.x86_masked_entry.match);
+ g_free(event);
+ goto fail;
+ }
+ event->u.x86_masked_entry.match = match;
+
+ ret = qemu_strtou64(str_event->u.x86_masked_entry.mask,
+ NULL, 0, &mask);
+ if (ret < 0) {
+ error_setg(errp,
+ "Invalid %s PMU event (mask: %s): %s. "
+ "The mask must be a uint8 string.",
+ KVMPMUEventEncodeFmt_str(str_event->format),
+ str_event->u.x86_masked_entry.mask,
+ strerror(-ret));
+ g_free(event);
+ goto fail;
+ }
+ if (mask > UINT8_MAX) {
+ error_setg(errp,
+ "Invalid %s PMU event (mask: %s): "
+ "Numerical result out of range. "
+ "The mask must be a uint8 string.",
+ KVMPMUEventEncodeFmt_str(str_event->format),
+ str_event->u.x86_masked_entry.mask);
+ g_free(event);
+ goto fail;
+ }
+ event->u.x86_masked_entry.mask = mask;
+
+ event->u.x86_masked_entry.exclude =
+ str_event->u.x86_masked_entry.exclude;
+ break;
+ }
default:
g_assert_not_reached();
}
diff --git a/qapi/kvm.json b/qapi/kvm.json
index 0d759884c229..f4e8854fa6c6 100644
--- a/qapi/kvm.json
+++ b/qapi/kvm.json
@@ -29,11 +29,14 @@
#
# @x86-default: standard x86 encoding format with select and umask.
#
+# @x86-masked-entry: KVM's masked entry format for x86, which could
+# mask bunch of events.
+#
# Since 9.1
##
{ 'enum': 'KVMPMUEventEncodeFmt',
'prefix': 'KVM_PMU_EVENT_FMT',
- 'data': ['raw', 'x86-default'] }
+ 'data': ['raw', 'x86-default', 'x86-masked-entry'] }
##
# @KVMPMURawEvent:
@@ -67,6 +70,40 @@
'data': { 'select': 'uint16',
'umask': 'uint8' } }
+##
+# @KVMPMUX86MaskedEntry:
+#
+# x86 PMU events encoding in KVM masked entry format.
+#
+# Encoding layout:
+# Bits Description
+# ---- -----------
+# 7:0 event select (low bits)
+# 15:8 (umask) match
+# 31:16 unused
+# 35:32 event select (high bits)
+# 36:54 unused
+# 55 exclude bit
+# 63:56 (umask) mask
+#
+# Events are selected by (umask & mask == match)
+#
+# @select: x86 PMU event select, which is a 12-bit unsigned number.
+#
+# @match: umask match.
+#
+# @mask: umask mask
+#
+# @exclude: Whether the matched events are excluded.
+#
+# Since 9.1
+##
+{ 'struct': 'KVMPMUX86MaskedEntry',
+ 'data': { 'select': 'uint16',
+ 'match': 'uint8',
+ 'mask': 'uint8',
+ 'exclude': 'bool' } }
+
##
# @KVMPMUFilterEvent:
#
@@ -83,7 +120,8 @@
'format': 'KVMPMUEventEncodeFmt' },
'discriminator': 'format',
'data': { 'raw': 'KVMPMURawEvent',
- 'x86-default': 'KVMPMUX86DefalutEvent' } }
+ 'x86-default': 'KVMPMUX86DefalutEvent',
+ 'x86-masked-entry': 'KVMPMUX86MaskedEntry' } }
##
# @KVMPMUFilterProperty:
@@ -128,6 +166,29 @@
'data': { 'select': 'str',
'umask': 'str' } }
+##
+# @KVMPMUX86MaskedEntryVariant:
+#
+# The variant of KVMPMUX86MaskedEntry with the string, rather than
+# the numeric value.
+#
+# @select: x86 PMU event select. This field is a 12-bit unsigned
+# number string.
+#
+# @match: umask match. This field is a uint8 string.
+#
+# @mask: umask mask. This field is a uint8 string.
+#
+# @exclude: Whether the matched events are excluded.
+#
+# Since 9.1
+##
+{ 'struct': 'KVMPMUX86MaskedEntryVariant',
+ 'data': { 'select': 'str',
+ 'match': 'str',
+ 'mask': 'str',
+ 'exclude': 'bool' } }
+
##
# @KVMPMUFilterEventVariant:
#
@@ -144,7 +205,8 @@
'format': 'KVMPMUEventEncodeFmt' },
'discriminator': 'format',
'data': { 'raw': 'KVMPMURawEventVariant',
- 'x86-default': 'KVMPMUX86DefalutEventVariant' } }
+ 'x86-default': 'KVMPMUX86DefalutEventVariant',
+ 'x86-masked-entry': 'KVMPMUX86MaskedEntryVariant' } }
##
# @KVMPMUFilterPropertyVariant:
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 391531c036a6..396a93efe745 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -5397,6 +5397,13 @@ kvm_config_pmu_event(KVMPMUFilter *filter,
code = X86_PMU_RAW_EVENT(event->u.x86_default.select,
event->u.x86_default.umask);
break;
+ case KVM_PMU_EVENT_FMT_X86_MASKED_ENTRY:
+ code = KVM_PMU_ENCODE_MASKED_ENTRY(
+ event->u.x86_masked_entry.select,
+ event->u.x86_masked_entry.mask,
+ event->u.x86_masked_entry.match,
+ event->u.x86_masked_entry.exclude ? 1 : 0);
+ break;
default:
g_assert_not_reached();
}
@@ -5432,6 +5439,21 @@ static bool kvm_install_pmu_event_filter(KVMState *s)
g_assert_not_reached();
}
+ /*
+ * The check in kvm_arch_check_pmu_filter() ensures masked entry
+ * format won't be mixed with other formats.
+ */
+ kvm_filter->flags = filter->events->value->format ==
+ KVM_PMU_EVENT_FMT_X86_MASKED_ENTRY ?
+ KVM_PMU_EVENT_FLAG_MASKED_EVENTS : 0;
+
+ if (kvm_filter->flags == KVM_PMU_EVENT_FLAG_MASKED_EVENTS &&
+ !kvm_vm_check_extension(s, KVM_CAP_PMU_EVENT_MASKED_EVENTS)) {
+ error_report("Masked entry format of PMU event "
+ "is not supported by Host.");
+ goto fail;
+ }
+
if (!kvm_config_pmu_event(filter, kvm_filter)) {
goto fail;
}
@@ -6064,6 +6086,7 @@ static void kvm_arch_check_pmu_filter(const Object *obj, const char *name,
KVMPMUFilter *filter = KVM_PMU_FILTER(child);
KVMPMUFilterEventList *events = filter->events;
KVMPMUFilterAction action;
+ uint32_t base_flag;
if (!filter->nevents) {
error_setg(errp,
@@ -6071,13 +6094,22 @@ static void kvm_arch_check_pmu_filter(const Object *obj, const char *name,
return;
}
+ /* Pick the first event's flag as the base one. */
+ base_flag = events->value->format ==
+ KVM_PMU_EVENT_FMT_X86_MASKED_ENTRY ?
+ KVM_PMU_EVENT_FLAG_MASKED_EVENTS : 0;
action = KVM_PMU_FILTER_ACTION__MAX;
while (events) {
KVMPMUFilterEvent *event = events->value;
+ uint32_t flag;
switch (event->format) {
case KVM_PMU_EVENT_FMT_RAW:
case KVM_PMU_EVENT_FMT_X86_DEFAULT:
+ flag = 0;
+ break;
+ case KVM_PMU_EVENT_FMT_X86_MASKED_ENTRY:
+ flag = KVM_PMU_EVENT_FLAG_MASKED_EVENTS;
break;
default:
error_setg(errp,
@@ -6086,6 +6118,13 @@ static void kvm_arch_check_pmu_filter(const Object *obj, const char *name,
return;
}
+ if (flag != base_flag) {
+ error_setg(errp,
+ "Masked entry format cannot be mixed with "
+ "other formats.");
+ return;
+ }
+
if (action == KVM_PMU_FILTER_ACTION__MAX) {
action = event->action;
} else if (action != event->action) {
--
2.34.1
© 2016 - 2024 Red Hat, Inc.