From: Jiri Denemark <jdene...@redhat.com>

This patch is effectively a NOP, but it fixes a logic bug and makes the
heuristics more visible and easier to change should there be a need to
do so in the future.

We decide which CPU model is the best match for given CPU data by
comparing lists of features that need to be enabled/disabled on top of
the selected CPU model. Since the original approach of using just the
total number of features was not working well enough, commit
v8.3.0-42-g48341b025a implemented a penalty for disabled features which
would increase for each additional disabled features. Apparently the
intention was weighting disabled features as

                      disabled * (disabled + 3)
    weightDisabled =  -------------------------
                                2

and complete CPU model as

    weight = enabled + weightDisabled

But there was a bug in the code which caused it to ignore some of the
features and counted as enabled regardless on their policy. Instead of
going through all features the code used the number of "enabled"
features (the variable was not really counting number of enabled
features though) which was initialized to the total number of features
and decremented each time a disabled features was found. Thus depending
on the number of disabled features, some features at the end of the list
were ignored. Luckily we know all the ignored features had to be
disabled because the CPU definitions were created by x86DataToCPU which
constructs a list of enabled features followed by disabled features.

So to fix the bug while providing the same results we can come up with
an equivalent formula using properly counted features in the CPU
definition.

The number of disabled features counted by the buggy code is

    half = (disabled + 1) div 2

and the weight of all disabled features is

                     half * (half + 3)
    weightDisabled = -----------------
                            2

When computing the total weight, we can't no longer use number of
enabled features because the original code counted some of the disabled
features as enabled. So to match the old behavior, we count the total
weight as

    weight = features - half + weightDisabled

The weight of enabled features now differs from the value computed by
the old code, but we don't need to worry about it as it's not really
used anywhere except for logging.

Fixes: https://gitlab.com/libvirt/libvirt/-/issues/759
Signed-off-by: Jiri Denemark <jdene...@redhat.com>
---
 src/cpu/cpu_x86.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c
index b0fe2bed4c..213af67ea4 100644
--- a/src/cpu/cpu_x86.c
+++ b/src/cpu/cpu_x86.c
@@ -2087,9 +2087,6 @@ virCPUx86Compare(virCPUDef *host,
 }
 
 
-/* Base penalty for disabled features. */
-#define BASE_PENALTY 2
-
 struct virCPUx86Weight {
     size_t total;
     size_t enabled;
@@ -2100,26 +2097,25 @@ static void
 virCPUx86WeightFeatures(const virCPUDef *cpu,
                         struct virCPUx86Weight *weight)
 {
-    int penalty = BASE_PENALTY;
     size_t i;
+    size_t half; /* half of disabled features rounded up */
 
     weight->enabled = cpu->nfeatures;
-    weight->disabled = 0;
 
     if (cpu->type == VIR_CPU_TYPE_HOST) {
+        weight->disabled = 0;
         weight->total = cpu->nfeatures;
         return;
     }
 
-    for (i = 0; i < weight->enabled; i++) {
-        if (cpu->features[i].policy == VIR_CPU_FEATURE_DISABLE) {
+    for (i = 0; i < cpu->nfeatures; i++) {
+        if (cpu->features[i].policy == VIR_CPU_FEATURE_DISABLE)
             weight->enabled--;
-            weight->disabled += penalty;
-            penalty++;
-        }
     }
 
-    weight->total = weight->enabled + weight->disabled;
+    half = (cpu->nfeatures - weight->enabled + 1) / 2;
+    weight->disabled = half * (half + 3) / 2;
+    weight->total = cpu->nfeatures - half + weight->disabled;
 }
 
 
-- 
2.49.0

Reply via email to