vIOMMU enables the option to passthrough pci devices to L2 VMs
in L1 VMs via Nested Virtualisation.

QEMU-Parameters:
https://www.qemu.org/docs/master/system/qemu-manpage.html
https://wiki.qemu.org/Features/VT-d

-machine ...,kernel-irqchip=split:

"split" because of intremap see below.

-device intel-iommu:

* caching-mode=on:

"It is required for -device vfio-pci to work with the VT-d device, because host
assigned devices requires to setup the DMA mapping on the host before guest DMA
starts."

* intremap=on:

"This enables interrupt remapping feature. It's required to enable complete
x2apic. Currently it only supports kvm kernel-irqchip modes off or split, while
full kernel-irqchip is not yet supported."

Signed-off-by: Markus Frank <[email protected]>
---
 PVE/API2/Qemu.pm          | 11 +++++--
 PVE/QemuConfig.pm         |  3 +-
 PVE/QemuServer.pm         | 62 +++++++++++++++++++++++++++++++++++++--
 PVE/QemuServer/Machine.pm |  6 ++--
 4 files changed, 74 insertions(+), 8 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 9606e72..a968f40 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -1043,13 +1043,16 @@ __PACKAGE__->register_method({
                        $conf->{vmgenid} = PVE::QemuServer::generate_uuid();
                    }
 
-                   my $machine = $conf->{machine};
+                   my $machine_conf = 
PVE::QemuServer::parse_machine($conf->{machine});
+                   my $machine = $machine_conf->{type};
                    if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
                        # always pin Windows' machine version on create, they 
get to easily confused
                        if 
(PVE::QemuServer::Helpers::windows_version($conf->{ostype})) {
-                           $conf->{machine} = 
PVE::QemuServer::windows_get_pinned_machine_version($machine);
+                           $machine_conf->{type} = 
PVE::QemuServer::windows_get_pinned_machine_version($machine);
+                           $conf->{machine} = 
PVE::QemuServer::print_machine($machine_conf);
                        }
                    }
+                   PVE::QemuServer::check_machine_config($conf, $machine_conf);
 
                    PVE::QemuConfig->write_config($vmid, $conf);
 
@@ -1880,6 +1883,10 @@ my $update_vm_api  = sub {
                        );
                    }
                    $conf->{pending}->{$opt} = $param->{$opt};
+               } elsif ($opt eq 'machine') {
+                   my $machine_conf = 
PVE::QemuServer::parse_machine($param->{$opt});
+                   PVE::QemuServer::check_machine_config($conf, $machine_conf);
+                   $conf->{pending}->{$opt} = $param->{$opt};
                } else {
                    $conf->{pending}->{$opt} = $param->{$opt};
 
diff --git a/PVE/QemuConfig.pm b/PVE/QemuConfig.pm
index 10e6929..c4834a7 100644
--- a/PVE/QemuConfig.pm
+++ b/PVE/QemuConfig.pm
@@ -433,7 +433,8 @@ sub __snapshot_rollback_hook {
        } else {
            # Note: old code did not store 'machine', so we try to be smart
            # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4).
-           $data->{forcemachine} = $conf->{machine} || 'pc-i440fx-1.4';
+           my $machine_conf = PVE::QemuServer::parse_machine($conf->{machine});
+           $data->{forcemachine} = $machine_conf->{type} || 'pc-i440fx-1.4';
 
            # we remove the 'machine' configuration if not explicitly specified
            # in the original config.
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index bf1de17..013792d 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -118,12 +118,32 @@ 
PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
     optional => 1,
 });
 
-PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
+my $machine_fmt = {
+    type => {
+       default_key => 1,
        description => "Specifies the QEMU machine type.",
        type => 'string',
        pattern => 
'(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
        maxLength => 40,
+       format_description => 'machine type',
        optional => 1,
+    },
+    viommu => {
+       type => 'boolean',
+       description => "Enable/disable guest vIOMMU"
+           ." (needs kvm to be enabled and q35 to be set as machine type).",
+       default => 0,
+       optional => 1,
+    },
+};
+
+PVE::JSONSchema::register_format('pve-qemu-machine-fmt', $machine_fmt);
+
+PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
+    description => "Specify the QEMU machine type & enable/disable vIOMMU.",
+    type => 'string',
+    optional => 1,
+    format => PVE::JSONSchema::get_format('pve-qemu-machine-fmt'),
 });
 
 # FIXME: remove in favor of just using the INotify one, it's cached there 
exactly the same way
@@ -2133,6 +2153,31 @@ sub parse_watchdog {
     return $res;
 }
 
+sub parse_machine {
+    my ($value) = @_;
+
+    return if !$value;
+
+    my $res = parse_property_string($machine_fmt, $value);
+    return $res;
+}
+
+sub check_machine_config {
+    my ($conf, $machine_conf) = @_;
+    my $q35 = $machine_conf->{type} && ($machine_conf->{type} =~ m/q35/) ? 1 : 
0;
+    my $kvm = $conf->{kvm};
+    my $arch = get_vm_arch($conf);
+    $kvm //= 1 if is_native($arch);
+    if ($machine_conf->{viommu} && (!$kvm || !$q35)) {
+       die "to use vIOMMU please enable kvm and set the machine type to q35\n";
+    }
+}
+
+sub print_machine {
+    my ($machine_conf) = @_;
+    return PVE::JSONSchema::print_property_string($machine_conf, $machine_fmt);
+}
+
 sub parse_guest_agent {
     my ($conf) = @_;
 
@@ -2204,8 +2249,9 @@ sub qemu_created_version_fixups {
     # check if we need to apply some handling for VMs that always use the 
latest machine version but
     # had a machine version transition happen that affected HW such that, 
e.g., an OS config change
     # would be required (we do not want to pin machine version for non-windows 
OS type)
+    my $machine_conf = parse_machine($conf->{machine});
     if (
-       (!defined($conf->{machine}) || $conf->{machine} =~ 
m/^(?:pc|q35|virt)$/) # non-versioned machine
+       (!defined($machine_conf->{type}) || $machine_conf->{type} =~ 
m/^(?:pc|q35|virt)$/) # non-versioned machine
        && (!defined($meta->{'creation-qemu'}) || 
!min_version($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
        && (!$forced_vers || min_version($forced_vers, 6, 1)) # handle 
snapshot-rollback/migrations
        && min_version($kvmver, 6, 1) # only need to apply the change since 6.1
@@ -3364,7 +3410,8 @@ sub windows_get_pinned_machine_version {
 sub get_vm_machine {
     my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
 
-    my $machine = $forcemachine || $conf->{machine};
+    my $machine_conf = parse_machine($conf->{machine});
+    my $machine = $forcemachine || $machine_conf->{type};
 
     if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
        $kvmversion //= kvm_user_version();
@@ -3609,6 +3656,8 @@ sub config_to_command {
     my $kvm = $conf->{kvm};
     my $nodename = nodename();
 
+    my $machine_conf = parse_machine($conf->{machine});
+
     my $arch = get_vm_arch($conf);
     my $kvm_binary = get_command_for_arch($arch);
     my $kvmver = kvm_user_version($kvm_binary);
@@ -4174,6 +4223,13 @@ sub config_to_command {
     }
     push @$machineFlags, "type=${machine_type_min}";
 
+    check_machine_config($conf, $machine_conf);
+
+    if ($machine_conf->{viommu}) {
+       unshift @$devices, '-device', "intel-iommu,intremap=on,caching-mode=on";
+       push @$machineFlags, 'kernel-irqchip=split';
+    }
+
     push @$cmd, @$devices;
     push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
     push @$cmd, '-machine', join(',', @$machineFlags) if 
scalar(@$machineFlags);
diff --git a/PVE/QemuServer/Machine.pm b/PVE/QemuServer/Machine.pm
index d9429ed..bfbde59 100644
--- a/PVE/QemuServer/Machine.pm
+++ b/PVE/QemuServer/Machine.pm
@@ -15,7 +15,8 @@ our $PVE_MACHINE_VERSION = {
 sub machine_type_is_q35 {
     my ($conf) = @_;
 
-    return $conf->{machine} && ($conf->{machine} =~ m/q35/) ? 1 : 0;
+    my $machine_conf = PVE::QemuServer::parse_machine($conf->{machine});
+    return $machine_conf->{type} && ($machine_conf->{type} =~ m/q35/) ? 1 : 0;
 }
 
 sub current_from_query_machines {
@@ -120,7 +121,8 @@ sub qemu_machine_pxe {
 
     my $machine =  get_current_qemu_machine($vmid);
 
-    if ($conf->{machine} && $conf->{machine} =~ m/\.pxe$/) {
+    my $machine_conf = PVE::QemuServer::parse_machine($conf->{machine});
+    if ($machine_conf->{type} && $machine_conf->{type} =~ m/\.pxe$/) {
        $machine .= '.pxe';
     }
 
-- 
2.39.2



_______________________________________________
pve-devel mailing list
[email protected]
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to