@samitouri / QOSamiQemu / commits / bb36be6fd7

target/arm/cpu: Introduce the infrastructure for cpreg migration tolerances

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: a) mismatch in cpreg indexes - ToleranceNotOnBothEnds (cpreg index is allowed to be only present on one end) - ToleranceOnlySrcTestValue (cpreg index is allowed to be only present in source if its value @mask field matches @value) b) mismatch in cpreg values - 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. Only registration of migration tolerances related to cpreg index mismatch is currently allowed. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260420140552.104369-2-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

Eric Auger committed Apr 20, 2026 at 16:03 UTC bb36be6fd799c4fdb2ac893cfab7f307c12527f2
3 files changed +137
target/arm/cpu.c
+82
@@ -181,6 +181,82 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
181 QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node);
182 }
183
184 +static ARMCPRegMigTolerance *find_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx)
185 +{
186 + ARMCPRegMigTolerance *t;
187 + QLIST_FOREACH(t, &cpu->cpreg_mig_tolerances, node) {
188 + if (t->kvmidx == kvmidx) {
189 + return t;
190 + }
191 + }
192 + return NULL;
193 +}
194 +
195 +void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
196 + uint64_t mask, uint64_t value,
197 + ARMCPRegMigToleranceType type)
198 +{
199 + ARMCPRegMigTolerance *entry;
200 +
201 + /* make sure the kvmidx has not tolerance already registered */
202 + assert(!find_mig_tolerance(cpu, kvmidx));
203 +
204 + assert(type == ToleranceNotOnBothEnds ||
205 + type == ToleranceOnlySrcTestValue);
206 +
207 + entry = g_new0(ARMCPRegMigTolerance, 1);
208 +
209 + entry->kvmidx = kvmidx;
210 + entry->mask = mask;
211 + entry->value = value;
212 + entry->type = type;
213 +
214 + QLIST_INSERT_HEAD(&cpu->cpreg_mig_tolerances, entry, node);
215 +}
216 +
217 +bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
218 + uint64_t vmstate_value, uint64_t local_value,
219 + ARMCPRegMigToleranceType type)
220 +{
221 + ARMCPRegMigTolerance *t = find_mig_tolerance(cpu, kvmidx);
222 + uint64_t diff, diff_outside_mask, field;
223 +
224 + if (!t || t->type != type) {
225 + return false;
226 + }
227 +
228 + if (type == ToleranceNotOnBothEnds) {
229 + return true;
230 + }
231 +
232 + if (type == ToleranceOnlySrcTestValue &&
233 + ((vmstate_value & t->mask) == t->value)) {
234 + return true;
235 + }
236 +
237 + /* Need to check the mask */
238 + diff = vmstate_value ^ local_value;
239 + diff_outside_mask = diff & ~t->mask;
240 +
241 + if (diff_outside_mask) {
242 + /* there are differences outside of the mask */
243 + return false;
244 + }
245 + if (type == ToleranceDiffInMask) {
246 + /* differences only in the field, tolerance matched */
247 + return true;
248 + }
249 + /* need to compare field value against authorized ones */
250 + field = vmstate_value & t->mask;
251 + if (type == ToleranceFieldLT && (field < t->value)) {
252 + return true;
253 + }
254 + if (type == ToleranceFieldGT && (field > t->value)) {
255 + return true;
256 + }
257 + return false;
258 +}
259 +
260 static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
261 {
262 /* Reset a single ARMCPRegInfo register */
@@ -1102,6 +1178,7 @@ static void arm_cpu_initfn(Object *obj)
1178
1179 QLIST_INIT(&cpu->pre_el_change_hooks);
1180 QLIST_INIT(&cpu->el_change_hooks);
1181 + QLIST_INIT(&cpu->cpreg_mig_tolerances);
1182
1183 #ifdef CONFIG_USER_ONLY
1184 # ifdef TARGET_AARCH64
@@ -1574,6 +1651,7 @@ static void arm_cpu_finalizefn(Object *obj)
1651 {
1652 ARMCPU *cpu = ARM_CPU(obj);
1653 ARMELChangeHook *hook, *next;
1654 + ARMCPRegMigTolerance *t, *n;
1655
1656 g_hash_table_destroy(cpu->cp_regs);
1657
@@ -1585,6 +1663,10 @@ static void arm_cpu_finalizefn(Object *obj)
1663 QLIST_REMOVE(hook, node);
1664 g_free(hook);
1665 }
1666 + QLIST_FOREACH_SAFE(t, &cpu->cpreg_mig_tolerances, node, n) {
1667 + QLIST_REMOVE(t, node);
1668 + g_free(t);
1669 + }
1670 #ifndef CONFIG_USER_ONLY
1671 if (cpu->pmu_timer) {
1672 timer_free(cpu->pmu_timer);
target/arm/cpu.h
+1
@@ -1140,6 +1140,7 @@ struct ArchCPU {
1140
1141 QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks;
1142 QLIST_HEAD(, ARMELChangeHook) el_change_hooks;
1143 + QLIST_HEAD(, ARMCPRegMigTolerance) cpreg_mig_tolerances;
1144
1145 int32_t node_id; /* NUMA node this CPU belongs to */
1146
target/arm/internals.h
+54
@@ -1943,4 +1943,58 @@ int compare_u64(const void *a, const void *b);
1943 /* Used in FEAT_MEC to set the MECIDWidthm1 field in the MECIDR_EL2 register. */
1944 #define MECID_WIDTH 16
1945
1946 +typedef enum {
1947 + ToleranceNotOnBothEnds,
1948 + ToleranceOnlySrcTestValue,
1949 + ToleranceDiffInMask,
1950 + ToleranceFieldLT,
1951 + ToleranceFieldGT,
1952 +} ARMCPRegMigToleranceType;
1953 +
1954 +typedef struct ARMCPRegMigTolerance {
1955 + uint64_t kvmidx;
1956 + uint64_t mask;
1957 + uint64_t value;
1958 + ARMCPRegMigToleranceType type;
1959 + QLIST_ENTRY(ARMCPRegMigTolerance) node;
1960 +} ARMCPRegMigTolerance;
1961 +
1962 +/**
1963 + * arm_register_cpreg_mig_tolerance:
1964 + * Register a migration tolerance wrt one given cpreg identified by its
1965 + * @kvmidx. Calling this function twice for the same @kvmidx is a
1966 + * programming error and will cause an assertion failure.
1967 + *
1968 + * @cpu: vcpu to apply the migration tolerance on
1969 + * @kvmidx: kvm index of the cpreg the tolerance applies to
1970 + * @mask: bitmask where a difference is tolerated
1971 + * (relevant with ToleranceDiffInMask)
1972 + * @value: value the bitmask field is compared with
1973 + * (relevant with ToleranceFieldLT and ToleranceFieldGT)
1974 + * @type: type of the migration tolerance:
1975 + * - ToleranceNotOnBothEnds (cpreg index is allowed to be only present
1976 + * on one end)
1977 + * - ToleranceOnlySrcTestValue (cpreg index is allowed to be only
1978 + * present in source if its value @mask field matches @value)
1979 + * - ToleranceDiffInMask (mismatch in cpreg values are only tolerated
1980 + * if differences are within @mask)
1981 + * - ToleranceFieldLT (mismatch in cpreg values are only tolerated
1982 + * if incoming @bitmask field value is less than @value)
1983 + * - ToleranceFieldGT (mismatch in cpreg values are only tolerated
1984 + * if incoming @bitmask field value is greater than @value)
1985 + */
1986 +void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
1987 + uint64_t mask, uint64_t value,
1988 + ARMCPRegMigToleranceType type);
1989 +
1990 +/**
1991 + * arm_cpu_match_cpreg_mig_tolerance:
1992 + * Check whether a tolerance of type @type exists for a given @kvmidx
1993 + * and the tolerance criterion is satisfied
1994 + */
1995 +bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
1996 + uint64_t vmstate_value, uint64_t local_value,
1997 + ARMCPRegMigToleranceType type);
1998 +
1999 +
2000 #endif