From aa0e714f3e277ee91f7b3e27430db3a239149345 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Tue, 19 May 2026 16:09:55 +0300 Subject: [PATCH 1/8] chore(migration): check migration with HT Signed-off-by: Maksim Fedotov --- .../pkg/controller/kvbuilder/kvvm.go | 21 +++-- .../pkg/controller/kvbuilder/kvvm_test.go | 87 +++++++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 68cb1793a6..5d4bb2e7b7 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -50,6 +50,7 @@ const ( // GenericCPUModel specifies the base CPU model for Features and Discovery CPU model types. GenericCPUModel = "qemu64" + HTCPUFeature = "ht" MaxMemorySizeForHotplug = 256 * 1024 * 1024 * 1024 // 256 Gi (safely limit to not overlap somewhat conservative 38 bit physical address space) EnableMemoryHotplugThreshold = 1 * 1024 * 1024 * 1024 // 1 Gi (no hotplug for VMs with less than 1Gi) @@ -164,10 +165,11 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { cpu.Model = class.Spec.CPU.Model case v1alpha2.CPUTypeDiscovery, v1alpha2.CPUTypeFeatures: cpu.Model = GenericCPUModel - l := len(class.Status.CpuFeatures.Enabled) - features := make([]virtv1.CPUFeature, l, l+1) + features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+2) hasSvm := false - for i, feature := range class.Status.CpuFeatures.Enabled { + hasVMX := false + hasHT := false + for _, feature := range class.Status.CpuFeatures.Enabled { policy := "require" if feature == "invtsc" { policy = "optional" @@ -175,14 +177,23 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if feature == "svm" { hasSvm = true } - features[i] = virtv1.CPUFeature{ + if feature == "vmx" { + hasVMX = true + } + if feature == HTCPUFeature { + hasHT = true + } + features = append(features, virtv1.CPUFeature{ Name: feature, Policy: policy, - } + }) } if !hasSvm { features = append(features, virtv1.CPUFeature{Name: "svm", Policy: "optional"}) } + if hasVMX && !hasHT { + features = append(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) + } cpu.Features = features default: return fmt.Errorf("unexpected cpu type: %q", class.Spec.CPU.Type) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go index 8e18bf2031..d0c85b7653 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + virtv1 "kubevirt.io/api/core/v1" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) @@ -224,6 +225,92 @@ func TestApplyPVNodeAffinity(t *testing.T) { }) } +func TestSetCPUModel(t *testing.T) { + name := "test-name" + namespace := "test-namespace" + + t.Run("should add optional ht feature for intel x86 discovery cpu", func(t *testing.T) { + builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) + class := &v1alpha2.VirtualMachineClass{ + Spec: v1alpha2.VirtualMachineClassSpec{ + CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeDiscovery}, + }, + Status: v1alpha2.VirtualMachineClassStatus{ + CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"vmx", "aes"}}, + }, + } + + if err := builder.SetCPUModel(class); err != nil { + t.Fatalf("SetCPUModel() failed: %v", err) + } + + features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features + if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) { + t.Fatalf("expected optional ht feature to be added, got %#v", features) + } + }) + + t.Run("should not add ht feature for amd discovery cpu", func(t *testing.T) { + builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) + class := &v1alpha2.VirtualMachineClass{ + Spec: v1alpha2.VirtualMachineClassSpec{ + CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeDiscovery}, + }, + Status: v1alpha2.VirtualMachineClassStatus{ + CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"svm", "aes"}}, + }, + } + + if err := builder.SetCPUModel(class); err != nil { + t.Fatalf("SetCPUModel() failed: %v", err) + } + + features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features + if containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) { + t.Fatalf("did not expect optional ht feature for amd cpu, got %#v", features) + } + }) + + t.Run("should keep existing ht feature policy when already present", func(t *testing.T) { + builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) + class := &v1alpha2.VirtualMachineClass{ + Spec: v1alpha2.VirtualMachineClassSpec{ + CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeFeatures}, + }, + Status: v1alpha2.VirtualMachineClassStatus{ + CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"vmx", HTCPUFeature}}, + }, + } + + if err := builder.SetCPUModel(class); err != nil { + t.Fatalf("SetCPUModel() failed: %v", err) + } + + features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features + htCount := 0 + for _, feature := range features { + if feature.Name == HTCPUFeature { + htCount++ + if feature.Policy != "require" { + t.Fatalf("expected existing ht policy to stay require, got %#v", feature) + } + } + } + if htCount != 1 { + t.Fatalf("expected exactly one ht feature, got %#v", features) + } + }) +} + +func containsCPUFeature(features []virtv1.CPUFeature, expected virtv1.CPUFeature) bool { + for _, feature := range features { + if feature == expected { + return true + } + } + return false +} + func TestSetOsType(t *testing.T) { name := "test-name" namespace := "test-namespace" From 39f057aeefa2338251f653ec86506d87d2cc8b47 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Tue, 19 May 2026 17:17:39 +0300 Subject: [PATCH 2/8] drop vmx Signed-off-by: Maksim Fedotov --- .../pkg/controller/kvbuilder/kvvm.go | 6 +---- .../pkg/controller/kvbuilder/kvvm_test.go | 25 ++----------------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 5d4bb2e7b7..a9ba7182a2 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -167,7 +167,6 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { cpu.Model = GenericCPUModel features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+2) hasSvm := false - hasVMX := false hasHT := false for _, feature := range class.Status.CpuFeatures.Enabled { policy := "require" @@ -177,9 +176,6 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if feature == "svm" { hasSvm = true } - if feature == "vmx" { - hasVMX = true - } if feature == HTCPUFeature { hasHT = true } @@ -191,7 +187,7 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if !hasSvm { features = append(features, virtv1.CPUFeature{Name: "svm", Policy: "optional"}) } - if hasVMX && !hasHT { + if !hasHT { features = append(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) } cpu.Features = features diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go index d0c85b7653..a0b1c3470a 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -229,14 +229,14 @@ func TestSetCPUModel(t *testing.T) { name := "test-name" namespace := "test-namespace" - t.Run("should add optional ht feature for intel x86 discovery cpu", func(t *testing.T) { + t.Run("should add optional ht feature for discovery cpu", func(t *testing.T) { builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) class := &v1alpha2.VirtualMachineClass{ Spec: v1alpha2.VirtualMachineClassSpec{ CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeDiscovery}, }, Status: v1alpha2.VirtualMachineClassStatus{ - CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"vmx", "aes"}}, + CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"aes"}}, }, } @@ -250,27 +250,6 @@ func TestSetCPUModel(t *testing.T) { } }) - t.Run("should not add ht feature for amd discovery cpu", func(t *testing.T) { - builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) - class := &v1alpha2.VirtualMachineClass{ - Spec: v1alpha2.VirtualMachineClassSpec{ - CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeDiscovery}, - }, - Status: v1alpha2.VirtualMachineClassStatus{ - CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"svm", "aes"}}, - }, - } - - if err := builder.SetCPUModel(class); err != nil { - t.Fatalf("SetCPUModel() failed: %v", err) - } - - features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features - if containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) { - t.Fatalf("did not expect optional ht feature for amd cpu, got %#v", features) - } - }) - t.Run("should keep existing ht feature policy when already present", func(t *testing.T) { builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) class := &v1alpha2.VirtualMachineClass{ From 07a2bcdd062c3d96efb412dccc84bea160d3f871 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Tue, 19 May 2026 17:40:02 +0300 Subject: [PATCH 3/8] require ht feature Signed-off-by: Maksim Fedotov --- .../pkg/controller/kvbuilder/kvvm.go | 2 +- .../pkg/controller/kvbuilder/kvvm_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index a9ba7182a2..5e2be4ddf7 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -188,7 +188,7 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { features = append(features, virtv1.CPUFeature{Name: "svm", Policy: "optional"}) } if !hasHT { - features = append(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) + features = append(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "require"}) } cpu.Features = features default: diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go index a0b1c3470a..4004913abe 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -229,7 +229,7 @@ func TestSetCPUModel(t *testing.T) { name := "test-name" namespace := "test-namespace" - t.Run("should add optional ht feature for discovery cpu", func(t *testing.T) { + t.Run("should add required ht feature for discovery cpu", func(t *testing.T) { builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) class := &v1alpha2.VirtualMachineClass{ Spec: v1alpha2.VirtualMachineClassSpec{ @@ -245,8 +245,8 @@ func TestSetCPUModel(t *testing.T) { } features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features - if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) { - t.Fatalf("expected optional ht feature to be added, got %#v", features) + if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "require"}) { + t.Fatalf("expected required ht feature to be added, got %#v", features) } }) From 075d7490fc8b1d998a8f399b52ceb837a1924614 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Tue, 19 May 2026 18:03:00 +0300 Subject: [PATCH 4/8] add ht to cpu type model Signed-off-by: Maksim Fedotov --- .../pkg/controller/kvbuilder/kvvm.go | 1 + .../pkg/controller/kvbuilder/kvvm_test.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 5e2be4ddf7..481b3a7b13 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -163,6 +163,7 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { cpu.Model = virtv1.CPUModeHostPassthrough case v1alpha2.CPUTypeModel: cpu.Model = class.Spec.CPU.Model + cpu.Features = []virtv1.CPUFeature{{Name: HTCPUFeature, Policy: "require"}} case v1alpha2.CPUTypeDiscovery, v1alpha2.CPUTypeFeatures: cpu.Model = GenericCPUModel features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+2) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go index 4004913abe..58416d8bca 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -229,6 +229,24 @@ func TestSetCPUModel(t *testing.T) { name := "test-name" namespace := "test-namespace" + t.Run("should add required ht feature for model cpu", func(t *testing.T) { + builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) + class := &v1alpha2.VirtualMachineClass{ + Spec: v1alpha2.VirtualMachineClassSpec{ + CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeModel, Model: "Nehalem"}, + }, + } + + if err := builder.SetCPUModel(class); err != nil { + t.Fatalf("SetCPUModel() failed: %v", err) + } + + features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features + if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "require"}) { + t.Fatalf("expected required ht feature to be added for model cpu, got %#v", features) + } + }) + t.Run("should add required ht feature for discovery cpu", func(t *testing.T) { builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) class := &v1alpha2.VirtualMachineClass{ From 2a37b41084e30726b65a3cfd5d0d0d39ca9edad3 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 10:56:52 +0300 Subject: [PATCH 5/8] fix(core, kubevirt): remove ht from discovery Signed-off-by: Maksim Fedotov --- .../pkg/controller/kvbuilder/kvvm.go | 9 +-------- .../pkg/controller/kvbuilder/kvvm_test.go | 20 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 481b3a7b13..5521c3fb2f 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -166,9 +166,8 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { cpu.Features = []virtv1.CPUFeature{{Name: HTCPUFeature, Policy: "require"}} case v1alpha2.CPUTypeDiscovery, v1alpha2.CPUTypeFeatures: cpu.Model = GenericCPUModel - features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+2) + features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+1) hasSvm := false - hasHT := false for _, feature := range class.Status.CpuFeatures.Enabled { policy := "require" if feature == "invtsc" { @@ -177,9 +176,6 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if feature == "svm" { hasSvm = true } - if feature == HTCPUFeature { - hasHT = true - } features = append(features, virtv1.CPUFeature{ Name: feature, Policy: policy, @@ -188,9 +184,6 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if !hasSvm { features = append(features, virtv1.CPUFeature{Name: "svm", Policy: "optional"}) } - if !hasHT { - features = append(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "require"}) - } cpu.Features = features default: return fmt.Errorf("unexpected cpu type: %q", class.Spec.CPU.Type) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go index 58416d8bca..e9cb04098c 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -247,26 +247,6 @@ func TestSetCPUModel(t *testing.T) { } }) - t.Run("should add required ht feature for discovery cpu", func(t *testing.T) { - builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) - class := &v1alpha2.VirtualMachineClass{ - Spec: v1alpha2.VirtualMachineClassSpec{ - CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeDiscovery}, - }, - Status: v1alpha2.VirtualMachineClassStatus{ - CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"aes"}}, - }, - } - - if err := builder.SetCPUModel(class); err != nil { - t.Fatalf("SetCPUModel() failed: %v", err) - } - - features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features - if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "require"}) { - t.Fatalf("expected required ht feature to be added, got %#v", features) - } - }) t.Run("should keep existing ht feature policy when already present", func(t *testing.T) { builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) From 561d406d25a65f1c6f0532d254d4bcd530529d77 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 10:57:51 +0300 Subject: [PATCH 6/8] make ht optional Signed-off-by: Maksim Fedotov --- .../virtualization-artifact/pkg/controller/kvbuilder/kvvm.go | 2 +- .../pkg/controller/kvbuilder/kvvm_test.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 5521c3fb2f..ecb6d4bf6b 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -163,7 +163,7 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { cpu.Model = virtv1.CPUModeHostPassthrough case v1alpha2.CPUTypeModel: cpu.Model = class.Spec.CPU.Model - cpu.Features = []virtv1.CPUFeature{{Name: HTCPUFeature, Policy: "require"}} + cpu.Features = []virtv1.CPUFeature{{Name: HTCPUFeature, Policy: "optional"}} case v1alpha2.CPUTypeDiscovery, v1alpha2.CPUTypeFeatures: cpu.Model = GenericCPUModel features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+1) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go index e9cb04098c..3f4548be6a 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -242,12 +242,11 @@ func TestSetCPUModel(t *testing.T) { } features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features - if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "require"}) { - t.Fatalf("expected required ht feature to be added for model cpu, got %#v", features) + if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) { + t.Fatalf("expected optional ht feature to be added for model cpu, got %#v", features) } }) - t.Run("should keep existing ht feature policy when already present", func(t *testing.T) { builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) class := &v1alpha2.VirtualMachineClass{ From 72f14924a1063fc555f2e10c256b5b405666d1e4 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 11:30:23 +0300 Subject: [PATCH 7/8] Revert "fix(core, kubevirt): remove ht from discovery" and add optional Signed-off-by: Maksim Fedotov --- .../qemu/patches/remove-nehalem-ht-summary.md | 94 +++++++++++++++++++ .../pkg/controller/kvbuilder/kvvm.go | 9 +- .../pkg/controller/kvbuilder/kvvm_test.go | 21 +++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 images/qemu/patches/remove-nehalem-ht-summary.md diff --git a/images/qemu/patches/remove-nehalem-ht-summary.md b/images/qemu/patches/remove-nehalem-ht-summary.md new file mode 100644 index 0000000000..01b8efb57f --- /dev/null +++ b/images/qemu/patches/remove-nehalem-ht-summary.md @@ -0,0 +1,94 @@ +# Nehalem HT removal summary + +## Problem + +Need to check whether the QEMU `Nehalem` CPU model exposes the HT/Hyper-Threading CPUID feature and remove it. + +## Findings + +Relevant file: +- `target/i386/cpu.c` + +What was found: +- The `Nehalem` CPU model is defined in `target/i386/cpu.c`. +- `CPUID_HT` is not explicitly present in the `Nehalem` model's base `.features[FEAT_1_EDX]` initializer. +- But QEMU later auto-enables `CPUID_HT` when topology indicates more than one thread per package: + +```c +if (x86_threads_per_pkg(&env->topo_info) > 1) { + env->features[FEAT_1_EDX] |= CPUID_HT; +} +``` + +So in practice `Nehalem` can still advertise HT. + +## First attempt and issue + +Initial change was to add this directly to the `Nehalem` definition: + +```c +.filtered_features[FEAT_1_EDX] = CPUID_HT, +``` + +That did not compile on this QEMU version because `X86CPUDefinition` in this tree did not contain a `filtered_features` field. + +Build error: +- `X86CPUDefinition has no member named filtered_features` + +## What was changed + +Changes were made in `target/i386/cpu.c`: + +### 1. Extend `X86CPUDefinition` +Added a model-level filtered feature array: + +```c +FeatureWordArray filtered_features; +``` + +### 2. Apply model-level filtered features when loading the CPU model +Updated `x86_cpu_load_model()` from: + +```c +for (w = 0; w < FEATURE_WORDS; w++) { + env->features[w] = def->features[w]; +} +``` + +to: + +```c +for (w = 0; w < FEATURE_WORDS; w++) { + env->features[w] = def->features[w]; + cpu->filtered_features[w] |= def->filtered_features[w]; +} +``` + +### 3. Mask HT for `Nehalem` +Added to the `Nehalem` CPU definition: + +```c +.filtered_features[FEAT_1_EDX] = CPUID_HT, +``` + +## Expected result + +`Nehalem` should no longer expose the HT CPUID feature, even if topology-based logic would otherwise auto-enable it. + +## Patch + +Patch file written to: +- `/Users/max/kode/virtualization/images/qemu/patches/remove-nehalem-ht.patch` + +## Validation + +Tried to run a local compile check: + +```bash +ninja -C build libqemu-x86_64-softmmu.a.p/target_i386_cpu.c.o +``` + +But it failed because there is no local build directory in this checkout: +- `/Users/max/kode/qemu` + +So the patch and source changes were updated, but compilation was not verified locally here. diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index ecb6d4bf6b..dca2a49b7f 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -166,8 +166,9 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { cpu.Features = []virtv1.CPUFeature{{Name: HTCPUFeature, Policy: "optional"}} case v1alpha2.CPUTypeDiscovery, v1alpha2.CPUTypeFeatures: cpu.Model = GenericCPUModel - features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+1) + features := make([]virtv1.CPUFeature, 0, len(class.Status.CpuFeatures.Enabled)+2) hasSvm := false + hasHT := false for _, feature := range class.Status.CpuFeatures.Enabled { policy := "require" if feature == "invtsc" { @@ -176,6 +177,9 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if feature == "svm" { hasSvm = true } + if feature == HTCPUFeature { + hasHT = true + } features = append(features, virtv1.CPUFeature{ Name: feature, Policy: policy, @@ -184,6 +188,9 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if !hasSvm { features = append(features, virtv1.CPUFeature{Name: "svm", Policy: "optional"}) } + if !hasHT { + features = append(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "optional"}) + } cpu.Features = features default: return fmt.Errorf("unexpected cpu type: %q", class.Spec.CPU.Type) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go index 3f4548be6a..17ede1cc9d 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -247,6 +247,27 @@ func TestSetCPUModel(t *testing.T) { } }) + t.Run("should add required ht feature for discovery cpu", func(t *testing.T) { + builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) + class := &v1alpha2.VirtualMachineClass{ + Spec: v1alpha2.VirtualMachineClassSpec{ + CPU: v1alpha2.CPU{Type: v1alpha2.CPUTypeDiscovery}, + }, + Status: v1alpha2.VirtualMachineClassStatus{ + CpuFeatures: v1alpha2.CpuFeatures{Enabled: []string{"aes"}}, + }, + } + + if err := builder.SetCPUModel(class); err != nil { + t.Fatalf("SetCPUModel() failed: %v", err) + } + + features := builder.Resource.Spec.Template.Spec.Domain.CPU.Features + if !containsCPUFeature(features, virtv1.CPUFeature{Name: HTCPUFeature, Policy: "require"}) { + t.Fatalf("expected required ht feature to be added, got %#v", features) + } + }) + t.Run("should keep existing ht feature policy when already present", func(t *testing.T) { builder := NewEmptyKVVM(types.NamespacedName{Name: name, Namespace: namespace}, KVVMOptions{}) class := &v1alpha2.VirtualMachineClass{ From 00fb3dd6d8e9e13bdcc8f901ab1fc207f41a7e1d Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 11:32:11 +0300 Subject: [PATCH 8/8] remove md file Signed-off-by: Maksim Fedotov --- .../qemu/patches/remove-nehalem-ht-summary.md | 94 ------------------- 1 file changed, 94 deletions(-) delete mode 100644 images/qemu/patches/remove-nehalem-ht-summary.md diff --git a/images/qemu/patches/remove-nehalem-ht-summary.md b/images/qemu/patches/remove-nehalem-ht-summary.md deleted file mode 100644 index 01b8efb57f..0000000000 --- a/images/qemu/patches/remove-nehalem-ht-summary.md +++ /dev/null @@ -1,94 +0,0 @@ -# Nehalem HT removal summary - -## Problem - -Need to check whether the QEMU `Nehalem` CPU model exposes the HT/Hyper-Threading CPUID feature and remove it. - -## Findings - -Relevant file: -- `target/i386/cpu.c` - -What was found: -- The `Nehalem` CPU model is defined in `target/i386/cpu.c`. -- `CPUID_HT` is not explicitly present in the `Nehalem` model's base `.features[FEAT_1_EDX]` initializer. -- But QEMU later auto-enables `CPUID_HT` when topology indicates more than one thread per package: - -```c -if (x86_threads_per_pkg(&env->topo_info) > 1) { - env->features[FEAT_1_EDX] |= CPUID_HT; -} -``` - -So in practice `Nehalem` can still advertise HT. - -## First attempt and issue - -Initial change was to add this directly to the `Nehalem` definition: - -```c -.filtered_features[FEAT_1_EDX] = CPUID_HT, -``` - -That did not compile on this QEMU version because `X86CPUDefinition` in this tree did not contain a `filtered_features` field. - -Build error: -- `X86CPUDefinition has no member named filtered_features` - -## What was changed - -Changes were made in `target/i386/cpu.c`: - -### 1. Extend `X86CPUDefinition` -Added a model-level filtered feature array: - -```c -FeatureWordArray filtered_features; -``` - -### 2. Apply model-level filtered features when loading the CPU model -Updated `x86_cpu_load_model()` from: - -```c -for (w = 0; w < FEATURE_WORDS; w++) { - env->features[w] = def->features[w]; -} -``` - -to: - -```c -for (w = 0; w < FEATURE_WORDS; w++) { - env->features[w] = def->features[w]; - cpu->filtered_features[w] |= def->filtered_features[w]; -} -``` - -### 3. Mask HT for `Nehalem` -Added to the `Nehalem` CPU definition: - -```c -.filtered_features[FEAT_1_EDX] = CPUID_HT, -``` - -## Expected result - -`Nehalem` should no longer expose the HT CPUID feature, even if topology-based logic would otherwise auto-enable it. - -## Patch - -Patch file written to: -- `/Users/max/kode/virtualization/images/qemu/patches/remove-nehalem-ht.patch` - -## Validation - -Tried to run a local compile check: - -```bash -ninja -C build libqemu-x86_64-softmmu.a.p/target_i386_cpu.c.o -``` - -But it failed because there is no local build directory in this checkout: -- `/Users/max/kode/qemu` - -So the patch and source changes were updated, but compilation was not verified locally here.