From 898fd6c2616bb985ea5a781d54d64c7c891de396 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Tue, 12 May 2026 16:43:51 +0300 Subject: [PATCH 01/13] chore(ci): update qemu build Signed-off-by: Maksim Fedotov --- build/components/versions.yml | 2 +- images/qemu/werf.inc.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/components/versions.yml b/build/components/versions.yml index a22d26fe03..b9ea908d83 100644 --- a/build/components/versions.yml +++ b/build/components/versions.yml @@ -1,5 +1,5 @@ firmware: - qemu: v9.2.0 + qemu: v10.2.2 libvirt: v10.9.0 edk2: stable202411 core: diff --git a/images/qemu/werf.inc.yaml b/images/qemu/werf.inc.yaml index fd46e099e5..795490c486 100644 --- a/images/qemu/werf.inc.yaml +++ b/images/qemu/werf.inc.yaml @@ -226,7 +226,7 @@ shell: --block-drv-ro-whitelist="vdi,vmdk,vhdx,vpc,https" \ --disable-alsa \ --disable-auth-pam \ - --disable-avx2 \ + --enable-avx2 \ --disable-avx512bw \ --disable-block-drv-whitelist-in-tools \ --disable-bochs \ @@ -267,7 +267,7 @@ shell: --disable-linux-user \ --disable-lto \ --disable-lzfse \ - --disable-membarrier \ + --enable-membarrier \ --disable-module-upgrades \ --disable-multiprocess \ --disable-netmap \ @@ -332,7 +332,7 @@ shell: --enable-pie \ --enable-rbd \ --enable-rdma \ - --enable-seccomp \ + --disable-seccomp \ --enable-selinux \ --enable-slirp \ --enable-snappy \ From bcc9973ccfe6225a134476bdf998f6c82dc83093 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Tue, 12 May 2026 18:29:39 +0300 Subject: [PATCH 02/13] remove legacy avx2 options Signed-off-by: Maksim Fedotov --- images/qemu/werf.inc.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/images/qemu/werf.inc.yaml b/images/qemu/werf.inc.yaml index 795490c486..fea994bf5a 100644 --- a/images/qemu/werf.inc.yaml +++ b/images/qemu/werf.inc.yaml @@ -226,8 +226,6 @@ shell: --block-drv-ro-whitelist="vdi,vmdk,vhdx,vpc,https" \ --disable-alsa \ --disable-auth-pam \ - --enable-avx2 \ - --disable-avx512bw \ --disable-block-drv-whitelist-in-tools \ --disable-bochs \ --disable-bpf \ From 8e3c13fb24ee5cc08beb1dad9cff5983caae8391 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Fri, 15 May 2026 11:56:30 +0300 Subject: [PATCH 03/13] remove ht in nehalem Signed-off-by: Maksim Fedotov --- images/qemu/patches/002-remove-nehalem-ht.patch | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 images/qemu/patches/002-remove-nehalem-ht.patch diff --git a/images/qemu/patches/002-remove-nehalem-ht.patch b/images/qemu/patches/002-remove-nehalem-ht.patch new file mode 100644 index 0000000000..b621e3ae28 --- /dev/null +++ b/images/qemu/patches/002-remove-nehalem-ht.patch @@ -0,0 +1,12 @@ +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 78308a82a0..b88063f234 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -3708,6 +3708,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, ++ .filtered_features[FEAT_1_EDX] = CPUID_HT, + .features[FEAT_1_ECX] = + CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | + CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE3, From 4b71befec27d7ffa71cac3757b18b7a4593d5c06 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Fri, 15 May 2026 12:18:05 +0300 Subject: [PATCH 04/13] update patch Signed-off-by: Maksim Fedotov --- .../qemu/patches/002-remove-nehalem-ht.patch | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/images/qemu/patches/002-remove-nehalem-ht.patch b/images/qemu/patches/002-remove-nehalem-ht.patch index b621e3ae28..e1b5b492b7 100644 --- a/images/qemu/patches/002-remove-nehalem-ht.patch +++ b/images/qemu/patches/002-remove-nehalem-ht.patch @@ -1,8 +1,16 @@ diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index 78308a82a0..b88063f234 100644 +index 78308a82a0..d5f3f5276e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c -@@ -3708,6 +3708,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { +@@ -2213,6 +2213,7 @@ typedef struct X86CPUDefinition { + int stepping; + uint8_t avx10_version; + FeatureWordArray features; ++ FeatureWordArray filtered_features; + const char *model_id; + const CPUCaches *const cache_info; + /* +@@ -3708,6 +3709,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE | CPUID_FP87, @@ -10,3 +18,11 @@ index 78308a82a0..b88063f234 100644 .features[FEAT_1_ECX] = CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE3, +@@ -7735,6 +7737,7 @@ static void x86_cpu_load_model(X86CPU *cpu, const X86CPUModel *model) + &error_abort); + for (w = 0; w < FEATURE_WORDS; w++) { + env->features[w] = def->features[w]; ++ cpu->filtered_features[w] |= def->filtered_features[w]; + } + + /* legacy-cache defaults to 'off' if CPU model provides cache info */ From 75bd648c0764453670a5124628dcfab6e725b5d9 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 18 May 2026 16:33:05 +0300 Subject: [PATCH 05/13] drop ht feature Signed-off-by: Maksim Fedotov --- build/components/versions.yml | 2 +- .../qemu/patches/002-remove-nehalem-ht.patch | 28 ------------------- 2 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 images/qemu/patches/002-remove-nehalem-ht.patch diff --git a/build/components/versions.yml b/build/components/versions.yml index b9ea908d83..4c293a59c3 100644 --- a/build/components/versions.yml +++ b/build/components/versions.yml @@ -3,7 +3,7 @@ firmware: libvirt: v10.9.0 edk2: stable202411 core: - 3p-kubevirt: v1.6.2-v12n.34 + 3p-kubevirt: fix/migration/drop-ht-feature 3p-containerized-data-importer: v1.60.3-v12n.19 distribution: 2.8.3 package: diff --git a/images/qemu/patches/002-remove-nehalem-ht.patch b/images/qemu/patches/002-remove-nehalem-ht.patch deleted file mode 100644 index e1b5b492b7..0000000000 --- a/images/qemu/patches/002-remove-nehalem-ht.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index 78308a82a0..d5f3f5276e 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -2213,6 +2213,7 @@ typedef struct X86CPUDefinition { - int stepping; - uint8_t avx10_version; - FeatureWordArray features; -+ FeatureWordArray filtered_features; - const char *model_id; - const CPUCaches *const cache_info; - /* -@@ -3708,6 +3709,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { - CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | - CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | - CPUID_DE | CPUID_FP87, -+ .filtered_features[FEAT_1_EDX] = CPUID_HT, - .features[FEAT_1_ECX] = - CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | - CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE3, -@@ -7735,6 +7737,7 @@ static void x86_cpu_load_model(X86CPU *cpu, const X86CPUModel *model) - &error_abort); - for (w = 0; w < FEATURE_WORDS; w++) { - env->features[w] = def->features[w]; -+ cpu->filtered_features[w] |= def->filtered_features[w]; - } - - /* legacy-cache defaults to 'off' if CPU model provides cache info */ From c7aaf9c4f6f73fd526797cd9ada377fd49408e2c Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 18 May 2026 18:11:03 +0300 Subject: [PATCH 06/13] test Signed-off-by: Maksim Fedotov --- images/virt-artifact/werf.inc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/images/virt-artifact/werf.inc.yaml b/images/virt-artifact/werf.inc.yaml index 6381ba5eae..c61cc37752 100644 --- a/images/virt-artifact/werf.inc.yaml +++ b/images/virt-artifact/werf.inc.yaml @@ -12,6 +12,7 @@ fromImage: builder/src secrets: - id: SOURCE_REPO value: {{ $.SOURCE_REPO }} +fromCacheVersion: test shell: install: - | From 355a386a31267be29c341654968f04eafcdcb575 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 18 May 2026 18:14:33 +0300 Subject: [PATCH 07/13] remove unconditionally Signed-off-by: Maksim Fedotov --- images/virt-artifact/werf.inc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/virt-artifact/werf.inc.yaml b/images/virt-artifact/werf.inc.yaml index c61cc37752..48b73aacd1 100644 --- a/images/virt-artifact/werf.inc.yaml +++ b/images/virt-artifact/werf.inc.yaml @@ -12,7 +12,7 @@ fromImage: builder/src secrets: - id: SOURCE_REPO value: {{ $.SOURCE_REPO }} -fromCacheVersion: test +fromCacheVersion: remove unconditionally shell: install: - | From 88b3e10538b6957ab734326cc160583817b1806d Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 18 May 2026 18:46:03 +0300 Subject: [PATCH 08/13] drop ht Signed-off-by: Maksim Fedotov --- build/components/versions.yml | 3 ++- .../002-revert-cpuid-ht-expand-features.patch | 26 +++++++++++++++++++ images/virt-artifact/werf.inc.yaml | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 images/qemu/patches/002-revert-cpuid-ht-expand-features.patch diff --git a/build/components/versions.yml b/build/components/versions.yml index 4c293a59c3..1d155a23a9 100644 --- a/build/components/versions.yml +++ b/build/components/versions.yml @@ -3,7 +3,8 @@ firmware: libvirt: v10.9.0 edk2: stable202411 core: - 3p-kubevirt: fix/migration/drop-ht-feature + # 3p-kubevirt: fix/migration/drop-ht-feature + 3p-kubevirt: v1.6.2-v12n.31 3p-containerized-data-importer: v1.60.3-v12n.19 distribution: 2.8.3 package: diff --git a/images/qemu/patches/002-revert-cpuid-ht-expand-features.patch b/images/qemu/patches/002-revert-cpuid-ht-expand-features.patch new file mode 100644 index 0000000000..92f817ca1d --- /dev/null +++ b/images/qemu/patches/002-revert-cpuid-ht-expand-features.patch @@ -0,0 +1,26 @@ +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 3f0821c15f..3f9475b485 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -7906,10 +7906,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + } + *edx = env->features[FEAT_1_EDX]; + if (threads_per_pkg > 1) { + *ebx |= threads_per_pkg << 16; ++ *edx |= CPUID_HT; + } + if (cpu->pdcm_on_even_without_pmu) { + if (!cpu->enable_pmu) { + *ecx &= ~CPUID_EXT_PDCM; + } +@@ -8977,9 +8978,6 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) + } + } + +- if (x86_threads_per_pkg(&env->topo_info) > 1) { +- env->features[FEAT_1_EDX] |= CPUID_HT; +- } + + for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { + FeatureDep *d = &feature_dependencies[i]; + if (!(env->features[d->from.index] & d->from.mask)) { diff --git a/images/virt-artifact/werf.inc.yaml b/images/virt-artifact/werf.inc.yaml index 48b73aacd1..e5f972c662 100644 --- a/images/virt-artifact/werf.inc.yaml +++ b/images/virt-artifact/werf.inc.yaml @@ -12,7 +12,7 @@ fromImage: builder/src secrets: - id: SOURCE_REPO value: {{ $.SOURCE_REPO }} -fromCacheVersion: remove unconditionally +# fromCacheVersion: remove unconditionally shell: install: - | From 97f401cdcdd6e5837b0c66d107361e25e02430b9 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 18 May 2026 19:15:18 +0300 Subject: [PATCH 09/13] patch to test Signed-off-by: Maksim Fedotov --- .../002-revert-cpuid-ht-expand-features.patch | 26 ---------------- .../002-revert-nehalem-ht-feature.patch | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 26 deletions(-) delete mode 100644 images/qemu/patches/002-revert-cpuid-ht-expand-features.patch create mode 100644 images/qemu/patches/002-revert-nehalem-ht-feature.patch diff --git a/images/qemu/patches/002-revert-cpuid-ht-expand-features.patch b/images/qemu/patches/002-revert-cpuid-ht-expand-features.patch deleted file mode 100644 index 92f817ca1d..0000000000 --- a/images/qemu/patches/002-revert-cpuid-ht-expand-features.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index 3f0821c15f..3f9475b485 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -7906,10 +7906,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, - } - *edx = env->features[FEAT_1_EDX]; - if (threads_per_pkg > 1) { - *ebx |= threads_per_pkg << 16; -+ *edx |= CPUID_HT; - } - if (cpu->pdcm_on_even_without_pmu) { - if (!cpu->enable_pmu) { - *ecx &= ~CPUID_EXT_PDCM; - } -@@ -8977,9 +8978,6 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) - } - } - -- if (x86_threads_per_pkg(&env->topo_info) > 1) { -- env->features[FEAT_1_EDX] |= CPUID_HT; -- } - - for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { - FeatureDep *d = &feature_dependencies[i]; - if (!(env->features[d->from.index] & d->from.mask)) { diff --git a/images/qemu/patches/002-revert-nehalem-ht-feature.patch b/images/qemu/patches/002-revert-nehalem-ht-feature.patch new file mode 100644 index 0000000000..844f4b0e24 --- /dev/null +++ b/images/qemu/patches/002-revert-nehalem-ht-feature.patch @@ -0,0 +1,30 @@ +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 78308a82a0..05da8abdd5 100644 +@@ -7908,6 +7908,7 @@ + } + *edx = env->features[FEAT_1_EDX]; + if (threads_per_pkg > 1) { ++ *edx |= CPUID_HT; + uint32_t num; + + /* +@@ -8976,19 +8977,12 @@ + } + } + +- if (x86_threads_per_pkg(&env->topo_info) > 1) { +- env->features[FEAT_1_EDX] |= CPUID_HT; +- + /* + * The Linux kernel checks for the CMPLegacy bit and + * discards multiple thread information if it is set. + * So don't set it here for Intel (and other processors + * following Intel's behavior) to make Linux guests happy. + */ +- if (!IS_INTEL_CPU(env) && !IS_ZHAOXIN_CPU(env)) { +- env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG; +- } +- } + + if (!cpu->pdcm_on_even_without_pmu) { + /* PDCM is fixed1 bit for TDX */ From b256f5c4bfd5043664b3a055dd0aa660098fd07b Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 17:35:51 +0300 Subject: [PATCH 10/13] test migration with required HT feature and qemu 10.2.2 (#2378) Signed-off-by: Maksim Fedotov --- .../pkg/controller/kvbuilder/kvvm.go | 18 ++-- .../pkg/controller/kvbuilder/kvvm_test.go | 84 +++++++++++++++++++ 2 files changed, 97 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..dca2a49b7f 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) @@ -162,12 +163,13 @@ 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: "optional"}} 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 { + hasHT := false + for _, feature := range class.Status.CpuFeatures.Enabled { policy := "require" if feature == "invtsc" { policy = "optional" @@ -175,14 +177,20 @@ func (b *KVVM) SetCPUModel(class *v1alpha2.VirtualMachineClass) error { if feature == "svm" { hasSvm = true } - features[i] = virtv1.CPUFeature{ + 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 !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..17ede1cc9d 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,89 @@ func TestApplyPVNodeAffinity(t *testing.T) { }) } +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: "optional"}) { + t.Fatalf("expected optional 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{ + 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{ + 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 cc866ba2a5b6e51b2ab2054146587fe4fdf204cd Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 17:47:22 +0300 Subject: [PATCH 11/13] temporarily delete qmp patch Signed-off-by: Maksim Fedotov --- images/qemu/patches/002-no-bootable-qmp.patch | 132 ------------------ 1 file changed, 132 deletions(-) delete mode 100644 images/qemu/patches/002-no-bootable-qmp.patch diff --git a/images/qemu/patches/002-no-bootable-qmp.patch b/images/qemu/patches/002-no-bootable-qmp.patch deleted file mode 100644 index 0f8be15869..0000000000 --- a/images/qemu/patches/002-no-bootable-qmp.patch +++ /dev/null @@ -1,132 +0,0 @@ -diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c -index fdb04fe..0cf2325 100644 ---- a/hw/char/debugcon.c -+++ b/hw/char/debugcon.c -@@ -26,6 +26,7 @@ - - #include "qemu/osdep.h" - #include "qapi/error.h" -+#include "qapi/qapi-events-control.h" - #include "qemu/module.h" - #include "chardev/char-fe.h" - #include "hw/isa/isa.h" -@@ -34,6 +35,7 @@ - #include "qom/object.h" - - #define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon" -+#define DEBUGCON_NO_BOOTABLE_DEVICE "No bootable device." - OBJECT_DECLARE_SIMPLE_TYPE(ISADebugconState, ISA_DEBUGCON_DEVICE) - - //#define DEBUG_DEBUGCON -@@ -42,6 +44,9 @@ typedef struct DebugconState { - MemoryRegion io; - CharBackend chr; - uint32_t readback; -+ bool watch_no_bootable_device; -+ char match_buf[sizeof(DEBUGCON_NO_BOOTABLE_DEVICE) - 1]; -+ size_t match_len; - } DebugconState; - - struct ISADebugconState { -@@ -51,6 +56,27 @@ struct ISADebugconState { - DebugconState state; - }; - -+static void debugcon_maybe_emit_no_bootable_device(DebugconState *s, -+ unsigned char ch) -+{ -+ if (!s->watch_no_bootable_device) { -+ return; -+ } -+ -+ if (s->match_len < sizeof(s->match_buf)) { -+ s->match_buf[s->match_len++] = ch; -+ } else { -+ memmove(s->match_buf, s->match_buf + 1, sizeof(s->match_buf) - 1); -+ s->match_buf[sizeof(s->match_buf) - 1] = ch; -+ } -+ -+ if (s->match_len == sizeof(s->match_buf) && -+ memcmp(s->match_buf, DEBUGCON_NO_BOOTABLE_DEVICE, -+ sizeof(s->match_buf)) == 0) { -+ qapi_event_send_no_bootable_device(); -+ } -+} -+ - static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) - { -@@ -64,6 +90,7 @@ static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&s->chr, &ch, 1); -+ debugcon_maybe_emit_no_bootable_device(s, ch); - } - - -@@ -118,6 +145,8 @@ static Property debugcon_isa_properties[] = { - DEFINE_PROP_UINT32("iobase", ISADebugconState, iobase, 0xe9), - DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), - DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9), -+ DEFINE_PROP_BOOL("watch-no-bootable", ISADebugconState, -+ state.watch_no_bootable_device, false), - DEFINE_PROP_END_OF_LIST(), - }; - -diff --git a/qapi/control.json b/qapi/control.json -index 336386f..e1e727e 100644 ---- a/qapi/control.json -+++ b/qapi/control.json -@@ -209,3 +209,13 @@ - '*pretty': 'bool', - 'chardev': 'str' - } } -+ -+## -+# @NO_BOOTABLE_DEVICE: -+# -+# Emitted when `isa-debugcon` with enabled no-bootable watching -+# receives the string "No bootable device.". -+# -+# Since: 9.2 -+## -+{ 'event': 'NO_BOOTABLE_DEVICE' } -diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c -index 22957fa..9b4840e 100644 ---- a/tests/qtest/qmp-test.c -+++ b/tests/qtest/qmp-test.c -@@ -337,6 +337,25 @@ static void test_qmp_missing_any_arg(void) - qtest_quit(qts); - } - -+static void test_qmp_no_bootable_device_event(void) -+{ -+ static const char trigger[] = "No bootable device."; -+ QTestState *qts; -+ size_t i; -+ -+ qts = qtest_initf("-nodefaults -machine q35 " -+ "-chardev null,id=debugcon " -+ "-device isa-debugcon,iobase=0x402,chardev=debugcon," -+ "watch-no-bootable=on"); -+ -+ for (i = 0; i < sizeof(trigger) - 1; i++) { -+ qtest_outb(qts, 0x402, trigger[i]); -+ } -+ -+ qtest_qmp_eventwait(qts, "NO_BOOTABLE_DEVICE"); -+ qtest_quit(qts); -+} -+ - int main(int argc, char *argv[]) - { - g_test_init(&argc, &argv, NULL); -@@ -348,6 +367,8 @@ int main(int argc, char *argv[]) - #endif - qtest_add_func("qmp/preconfig", test_qmp_preconfig); - qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg); -+ qtest_add_func("qmp/no-bootable-device-event", -+ test_qmp_no_bootable_device_event); - - return g_test_run(); - } From ec2c977b93e9767a137c623071bc90654c4df02f Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 18:54:50 +0300 Subject: [PATCH 12/13] require Signed-off-by: Maksim Fedotov --- .../virtualization-artifact/pkg/controller/kvbuilder/kvvm.go | 2 +- .../pkg/controller/kvbuilder/kvvm_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index dca2a49b7f..df8d63a493 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: "optional"}} + 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 17ede1cc9d..58416d8bca 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_test.go @@ -242,8 +242,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 for model cpu, got %#v", 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) } }) From 65e676a04221d22cb2799b2e5c99e24c6c0db41a Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 20 May 2026 19:27:25 +0300 Subject: [PATCH 13/13] return patch Signed-off-by: Maksim Fedotov --- images/qemu/patches/002-no-bootable-qmp.patch | 132 ++++++++++++++++++ ...ch => 003-revert-nehalem-ht-feature.patch} | 0 2 files changed, 132 insertions(+) create mode 100644 images/qemu/patches/002-no-bootable-qmp.patch rename images/qemu/patches/{002-revert-nehalem-ht-feature.patch => 003-revert-nehalem-ht-feature.patch} (100%) diff --git a/images/qemu/patches/002-no-bootable-qmp.patch b/images/qemu/patches/002-no-bootable-qmp.patch new file mode 100644 index 0000000000..015bf6b628 --- /dev/null +++ b/images/qemu/patches/002-no-bootable-qmp.patch @@ -0,0 +1,132 @@ +diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c +index bb323adda5..dad74ca2db 100644 +--- a/hw/char/debugcon.c ++++ b/hw/char/debugcon.c +@@ -26,6 +26,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" ++#include "qapi/qapi-events-control.h" + #include "qemu/module.h" + #include "chardev/char-fe.h" + #include "hw/isa/isa.h" +@@ -34,6 +35,7 @@ + #include "qom/object.h" + + #define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon" ++#define DEBUGCON_NO_BOOTABLE_DEVICE "No bootable device." + OBJECT_DECLARE_SIMPLE_TYPE(ISADebugconState, ISA_DEBUGCON_DEVICE) + + //#define DEBUG_DEBUGCON +@@ -42,6 +44,9 @@ typedef struct DebugconState { + MemoryRegion io; + CharFrontend chr; + uint32_t readback; ++ bool watch_no_bootable_device; ++ char match_buf[sizeof(DEBUGCON_NO_BOOTABLE_DEVICE) - 1]; ++ size_t match_len; + } DebugconState; + + struct ISADebugconState { +@@ -51,6 +56,27 @@ struct ISADebugconState { + DebugconState state; + }; + ++static void debugcon_maybe_emit_no_bootable_device(DebugconState *s, ++ unsigned char ch) ++{ ++ if (!s->watch_no_bootable_device) { ++ return; ++ } ++ ++ if (s->match_len < sizeof(s->match_buf)) { ++ s->match_buf[s->match_len++] = ch; ++ } else { ++ memmove(s->match_buf, s->match_buf + 1, sizeof(s->match_buf) - 1); ++ s->match_buf[sizeof(s->match_buf) - 1] = ch; ++ } ++ ++ if (s->match_len == sizeof(s->match_buf) && ++ memcmp(s->match_buf, DEBUGCON_NO_BOOTABLE_DEVICE, ++ sizeof(s->match_buf)) == 0) { ++ qapi_event_send_no_bootable_device(); ++ } ++} ++ + static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) + { +@@ -64,6 +90,7 @@ static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); ++ debugcon_maybe_emit_no_bootable_device(s, ch); + } + + +@@ -118,6 +145,8 @@ static const Property debugcon_isa_properties[] = { + DEFINE_PROP_UINT32("iobase", ISADebugconState, iobase, 0xe9), + DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), + DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9), ++ DEFINE_PROP_BOOL("watch-no-bootable", ISADebugconState, ++ state.watch_no_bootable_device, false), + }; + + static void debugcon_isa_class_initfn(ObjectClass *klass, const void *data) +diff --git a/qapi/control.json b/qapi/control.json +index 9a5302193d..30301fcf73 100644 +--- a/qapi/control.json ++++ b/qapi/control.json +@@ -211,3 +211,13 @@ + '*pretty': 'bool', + 'chardev': 'str' + } } ++ ++## ++# @NO_BOOTABLE_DEVICE: ++# ++# Emitted when `isa-debugcon` with enabled no-bootable watching ++# receives the string "No bootable device.". ++# ++# Since: 9.2 ++## ++{ 'event': 'NO_BOOTABLE_DEVICE' } +diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c +index edf0886787..97a326aa54 100644 +--- a/tests/qtest/qmp-test.c ++++ b/tests/qtest/qmp-test.c +@@ -337,6 +337,25 @@ static void test_qmp_missing_any_arg(void) + qtest_quit(qts); + } + ++static void test_qmp_no_bootable_device_event(void) ++{ ++ static const char trigger[] = "No bootable device."; ++ QTestState *qts; ++ size_t i; ++ ++ qts = qtest_initf("-nodefaults -machine q35 " ++ "-chardev null,id=debugcon " ++ "-device isa-debugcon,iobase=0x402,chardev=debugcon," ++ "watch-no-bootable=on"); ++ ++ for (i = 0; i < sizeof(trigger) - 1; i++) { ++ qtest_outb(qts, 0x402, trigger[i]); ++ } ++ ++ qtest_qmp_eventwait(qts, "NO_BOOTABLE_DEVICE"); ++ qtest_quit(qts); ++} ++ + int main(int argc, char *argv[]) + { + g_test_init(&argc, &argv, NULL); +@@ -348,6 +367,8 @@ int main(int argc, char *argv[]) + #endif + qtest_add_func("qmp/preconfig", test_qmp_preconfig); + qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg); ++ qtest_add_func("qmp/no-bootable-device-event", ++ test_qmp_no_bootable_device_event); + + return g_test_run(); + } diff --git a/images/qemu/patches/002-revert-nehalem-ht-feature.patch b/images/qemu/patches/003-revert-nehalem-ht-feature.patch similarity index 100% rename from images/qemu/patches/002-revert-nehalem-ht-feature.patch rename to images/qemu/patches/003-revert-nehalem-ht-feature.patch