We introduce a datatype for a tolerance with respect to a given
cpreg migration issue. The tolerance applies to a given cpreg kvm index,
and can be of different types:
- ToleranceNotOnBothEnds (cpreg index is allowed to be only present
on one end)
- ToleranceDiffInMask (value differences are allowed only within a mask)
- ToleranceFieldLT (incoming field value must be less than a given value)
- ToleranceFieldGT (incoming field value must be greater than a given value)
A QLIST of such tolerances can be populated using a new helper:
arm_register_cpreg_mig_tolerance() and arm_cpu_match_cpreg_mig_tolerance()
allows to check whether a tolerance exists for a given kvm index and its
criterion is matched.
callers for those helpers will be introduced in subsequent patches.
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Sebastian Ott <sebott@redhat.com>
---
v8 -> v9
- fix the tolerance type checking
- rename arm_cpu_cpreg_has_mig_tolerance into arm_cpu_match_cpreg_mig_tolerance
---
target/arm/cpu.h | 33 +++++++++++++++++++++
target/arm/cpu.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 657ff4ab20b..2b1d3af34f0 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -843,6 +843,21 @@ struct ARMELChangeHook {
QLIST_ENTRY(ARMELChangeHook) node;
};
+typedef enum {
+ ToleranceNotOnBothEnds,
+ ToleranceDiffInMask,
+ ToleranceFieldLT,
+ ToleranceFieldGT,
+} ARMCPUCPREGMigToleranceType;
+
+typedef struct ARMCPUCPREGMigTolerance {
+ uint64_t kvmidx;
+ uint64_t mask;
+ uint64_t value;
+ ARMCPUCPREGMigToleranceType type;
+ QLIST_ENTRY(ARMCPUCPREGMigTolerance) node;
+} ARMCPUCPREGMigTolerance;
+
/* These values map onto the return values for
* QEMU_PSCI_0_2_FN_AFFINITY_INFO */
typedef enum ARMPSCIState {
@@ -1139,6 +1154,7 @@ struct ArchCPU {
QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks;
QLIST_HEAD(, ARMELChangeHook) el_change_hooks;
+ QLIST_HEAD(, ARMCPUCPREGMigTolerance) cpreg_mig_tolerances;
int32_t node_id; /* NUMA node this CPU belongs to */
@@ -2632,6 +2648,23 @@ void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void
*opaque);
+/**
+ * arm_register_cpreg_mig_tolerance:
+ * Register a migration tolerance wrt one given cpreg identified by its
+ * @kvmidx. Only one tolerance can be registered by kvm reg idx.
+ */
+void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t mask, uint64_t value,
+ ARMCPUCPREGMigToleranceType type);
+
+/**
+ * arm_cpu_match_cpreg_mig_tolerance:
+ * Check whether a tolerance of type @type exists for a given @kvmidx
+ * and the tolerance criterion is satified
+ */
+bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t vmstate_value, uint64_t local_value,
+ ARMCPUCPREGMigToleranceType type);
/**
* arm_rebuild_hflags:
* Rebuild the cached TBFLAGS for arbitrary changed processor state.
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 7e3e84b4bbb..79e328c2888 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -181,6 +181,75 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node);
}
+void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t mask, uint64_t value,
+ ARMCPUCPREGMigToleranceType type)
+{
+ ARMCPUCPREGMigTolerance *t, *entry;
+
+ /* make sure the kvmidx has not tolerance already registered */
+ QLIST_FOREACH(t, &cpu->cpreg_mig_tolerances, node) {
+ if (t->kvmidx == kvmidx) {
+ g_assert_not_reached();
+ }
+ }
+ entry = g_new0(ARMCPUCPREGMigTolerance, 1);
+
+ entry->kvmidx = kvmidx;
+ entry->mask = mask;
+ entry->value = value;
+ entry->type = type;
+
+ QLIST_INSERT_HEAD(&cpu->cpreg_mig_tolerances, entry, node);
+}
+
+bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t vmstate_value, uint64_t local_value,
+ ARMCPUCPREGMigToleranceType type)
+{
+ ARMCPUCPREGMigTolerance *t;
+ uint64_t diff, diff_outside_mask, field;
+ bool found = false;
+
+ QLIST_FOREACH(t, &cpu->cpreg_mig_tolerances, node) {
+ if (t->kvmidx == kvmidx && t->type == type) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+
+ /* we found one tolerance woth @type associated to the @kvmidx */
+
+ if (type == ToleranceNotOnBothEnds) {
+ return true;
+ }
+
+ /* Need to check the mask */
+ diff = vmstate_value ^ local_value;
+ diff_outside_mask = diff & ~t->mask;
+
+ if (diff_outside_mask) {
+ /* there are differences outside of the mask */
+ return false;
+ }
+ if (type == ToleranceDiffInMask) {
+ /* differences only in the field, tolerance matched */
+ return true;
+ }
+ /* need to compare field value against authorized ones */
+ field = vmstate_value & t->mask;
+ if (type == ToleranceFieldLT && (field < t->value)) {
+ return true;
+ }
+ if (type == ToleranceFieldGT && (field > t->value)) {
+ return true;
+ }
+ return false;
+}
+
static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
{
/* Reset a single ARMCPRegInfo register */
@@ -1106,6 +1175,7 @@ static void arm_cpu_initfn(Object *obj)
QLIST_INIT(&cpu->pre_el_change_hooks);
QLIST_INIT(&cpu->el_change_hooks);
+ QLIST_INIT(&cpu->cpreg_mig_tolerances);
#ifdef CONFIG_USER_ONLY
# ifdef TARGET_AARCH64
@@ -1550,6 +1620,7 @@ static void arm_cpu_finalizefn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
ARMELChangeHook *hook, *next;
+ ARMCPUCPREGMigTolerance *t, *n;
g_hash_table_destroy(cpu->cp_regs);
@@ -1561,6 +1632,10 @@ static void arm_cpu_finalizefn(Object *obj)
QLIST_REMOVE(hook, node);
g_free(hook);
}
+ QLIST_FOREACH_SAFE(t, &cpu->cpreg_mig_tolerances, node, n) {
+ QLIST_REMOVE(t, node);
+ g_free(t);
+ }
#ifndef CONFIG_USER_ONLY
if (cpu->pmu_timer) {
timer_free(cpu->pmu_timer);
--
2.53.0