diff --git a/src/middlewared/middlewared/alembic/versions/24.10/2024-08-30_22-13_cpu_topology_extension.py b/src/middlewared/middlewared/alembic/versions/24.10/2024-08-30_22-13_cpu_topology_extension.py
new file mode 100644
index 0000000000000..f29c12b1710a1
--- /dev/null
+++ b/src/middlewared/middlewared/alembic/versions/24.10/2024-08-30_22-13_cpu_topology_extension.py
@@ -0,0 +1,26 @@
+"""
+Providing cpu topology extension to VMs
+
+Revision ID: d24d6760fda4
+Revises: 7b13df980355
+Create Date: 2024-08-30 22:13:09.525439+00:00
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+revision = 'd24d6760fda4'
+down_revision = '7b13df980355'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ with op.batch_alter_table('vm_vm', schema=None) as batch_op:
+ batch_op.add_column(
+ sa.Column('enable_cpu_topology_extension', sa.Boolean(), nullable=False, server_default='0')
+ )
+
+
+def downgrade():
+ pass
diff --git a/src/middlewared/middlewared/plugins/vm/supervisor/domain_xml.py b/src/middlewared/middlewared/plugins/vm/supervisor/domain_xml.py
index b27c013f9143b..242d2a13bb2ed 100644
--- a/src/middlewared/middlewared/plugins/vm/supervisor/domain_xml.py
+++ b/src/middlewared/middlewared/plugins/vm/supervisor/domain_xml.py
@@ -75,6 +75,8 @@ def cpu_xml(vm_data, context):
features = []
if vm_data['cpu_mode'] == 'HOST-PASSTHROUGH':
features.append(create_element('cache', mode='passthrough'))
+ if vm_data['enable_cpu_topology_extension']:
+ features.append(create_element('feature', policy='require', name='topoext'))
cpu_nodes = [
create_element(
diff --git a/src/middlewared/middlewared/plugins/vm/vms.py b/src/middlewared/middlewared/plugins/vm/vms.py
index 4c01abf071108..fcd3a5348d65b 100644
--- a/src/middlewared/middlewared/plugins/vm/vms.py
+++ b/src/middlewared/middlewared/plugins/vm/vms.py
@@ -57,6 +57,7 @@ class VMModel(sa.Model):
command_line_args = sa.Column(sa.Text(), default='', nullable=False)
bootloader_ovmf = sa.Column(sa.String(1024), default='OVMF_CODE.fd')
trusted_platform_module = sa.Column(sa.Boolean(), default=False)
+ enable_cpu_topology_extension = sa.Column(sa.Boolean(), default=False)
@functools.cache
@@ -145,6 +146,7 @@ async def extend_vm(self, vm, context):
Int('threads', default=1),
Str('cpuset', default=None, null=True, validators=[NumericSet()]),
Str('nodeset', default=None, null=True, validators=[NumericSet()]),
+ Bool('enable_cpu_topology_extension', default=False),
Bool('pin_vcpus', default=False),
Bool('suspend_on_snapshot', default=False),
Bool('trusted_platform_module', default=False),
diff --git a/src/middlewared/middlewared/pytest/unit/plugins/vm/test_vm_libvirt_xml.py b/src/middlewared/middlewared/pytest/unit/plugins/vm/test_vm_libvirt_xml.py
index fb65836dd2338..58b6c6f19e9c7 100644
--- a/src/middlewared/middlewared/pytest/unit/plugins/vm/test_vm_libvirt_xml.py
+++ b/src/middlewared/middlewared/pytest/unit/plugins/vm/test_vm_libvirt_xml.py
@@ -36,6 +36,7 @@ def test_command_line_xml(vm_data, expected_xml):
'cpuset': None,
'pin_vcpus': False,
'nodeset': None,
+ 'enable_cpu_topology_extension': False
}, {'cpu_model_choices': {}}, [
'',
'6',
@@ -49,10 +50,26 @@ def test_command_line_xml(vm_data, expected_xml):
'cpuset': None,
'pin_vcpus': False,
'nodeset': None,
+ 'enable_cpu_topology_extension': False
}, {'cpu_model_choices': {}}, [
'',
'6',
]),
+ ({
+ 'cpu_mode': 'HOST-PASSTHROUGH',
+ 'vcpus': 1,
+ 'cores': 2,
+ 'threads': 3,
+ 'cpu_model': None,
+ 'cpuset': None,
+ 'pin_vcpus': False,
+ 'nodeset': None,
+ 'enable_cpu_topology_extension': True
+ }, {'cpu_model_choices': {}}, [
+ ''
+ '',
+ '6',
+ ]),
({
'cpu_mode': 'CUSTOM',
'vcpus': 1,
@@ -62,6 +79,7 @@ def test_command_line_xml(vm_data, expected_xml):
'cpuset': None,
'pin_vcpus': False,
'nodeset': None,
+ 'enable_cpu_topology_extension': False
}, {'cpu_model_choices': {'pentium': 'pentium', 'pentium2': 'pentium2'}}, [
''
'pentium',
@@ -76,6 +94,7 @@ def test_command_line_xml(vm_data, expected_xml):
'cpuset': '1-2,4-6',
'pin_vcpus': True,
'nodeset': None,
+ 'enable_cpu_topology_extension': False,
}, {'cpu_model_choices': {}}, [
'',
'6',
@@ -91,6 +110,7 @@ def test_command_line_xml(vm_data, expected_xml):
'cpuset': None,
'pin_vcpus': False,
'nodeset': '1-2,4-6',
+ 'enable_cpu_topology_extension': False
}, {'cpu_model_choices': {}}, [
'',
'6',