From 55655ebc32ce4eb3fe52f233ec4ac5926b15603f Mon Sep 17 00:00:00 2001 From: Dominik Csapak Date: Fri, 9 Nov 2018 13:31:09 +0100 Subject: [PATCH] fix #1952: make vga memory configurable we change 'vga' to a property string and add a 'memory' property with this, the user can better control the memory given to the virtual gpu, this is especially useful for spice/qxl since high resolutions need more memory Signed-off-by: Dominik Csapak --- PVE/QemuServer.pm | 134 +++++++++++++++++++++++++++++++++--------- PVE/QemuServer/PCI.pm | 3 +- 2 files changed, 109 insertions(+), 28 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 82c9b96..c3932d0 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -223,6 +223,24 @@ my $agent_fmt = { }, }; +my $vga_fmt = { + type => { + description => "Select the VGA type.", + type => 'string', + default => 'std', + optional => 1, + default_key => 1, + enum => [qw(cirrus qxl qxl2 qxl3 qxl4 serial0 serial1 serial2 serial3 std virtio vmware)], + }, + memory => { + description => "Sets the VGA memory (in MiB). Has no effect with serial display.", + type => 'integer', + optional => 1, + minimum => 4, + maximum => 512, + }, +}; + my $confdesc = { onboot => { optional => 1, @@ -431,17 +449,16 @@ EODESC }, vga => { optional => 1, - type => 'string', - description => "Select the VGA type.", - verbose_description => "Select the VGA type. If you want to use high resolution" . - " modes (>= 1280x1024x16) then you should use the options " . - "'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and " . - "'cirrus' for other OS types. The 'qxl' option enables the SPICE " . - "display sever. For win* OS you can select how many independent " . - "displays you want, Linux guests can add displays them self. " . - "You can also run without any graphic card, using a serial device" . - " as terminal.", - enum => [qw(cirrus qxl qxl2 qxl3 qxl4 serial0 serial1 serial2 serial3 std virtio vmware)], + type => 'string', format => $vga_fmt, + description => "Configure the VGA hardware.", + verbose_description => "Configure the VGA Hardware. If you want to use ". + "high resolution modes (>= 1280x1024x16) you may need to increase " . + "the vga memory option. Since QEMU 2.9 the default VGA display type " . + "is 'std' for all OS types besides some Windows versions (XP and " . + "older) which use 'cirrus'. The 'qxl' option enables the SPICE " . + "display server. For win* OS you can select how many independent " . + "displays you want, Linux guests can add displays them self.\n". + "You can also run without any graphic card, using a serial device as terminal.", }, watchdog => { optional => 1, @@ -1961,6 +1978,54 @@ sub print_cpu_device { return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0"; } +my $vga_map = { + 'cirrus' => 'cirrus-vga', + 'std' => 'VGA', + 'vmware' => 'vmware-svga', + 'virtio' => 'virtio-vga', +}; + +sub print_vga_device { + my ($conf, $vga, $id, $qxlnum, $bridges) = @_; + + my $type = $vga_map->{$vga->{type}}; + my $vgamem_mb = $vga->{memory}; + if ($qxlnum) { + $type = $id ? 'qxl' : 'qxl-vga'; + } + die "no devicetype for $vga->{type}\n" if !$type; + + my $memory = ""; + if ($vgamem_mb) { + if ($vga->{type} eq 'virtio') { + my $bytes = PVE::Tools::convert_size($vgamem_mb, "mb" => "b"); + $memory = ",max_hostmem=$bytes"; + } elsif ($qxlnum) { + # from https://www.spice-space.org/multiple-monitors.html + $memory = ",vgamem_mb=$vga->{memory}"; + my $ram = $vgamem_mb * 4; + my $vram = $vgamem_mb * 2; + $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram"; + } else { + $memory = ",vgamem_mb=$vga->{memory}"; + } + } elsif ($qxlnum && $id) { + $memory = ",ram_size=67108864,vram_size=33554432"; + } + + my $q35 = machine_type_is_q35($conf); + my $vgaid = "vga" . ($id // ''); + my $pciaddr; + if ($q35 && $vgaid eq 'vga') { + # on is on the pcie.0 bus on q35 + $pciaddr = print_pcie_addr($vgaid, $bridges); + } else { + $pciaddr = print_pci_addr($vgaid, $bridges); + } + + return "$type,id=${vgaid}${memory}${pciaddr}"; +} + sub drive_is_cloudinit { my ($drive) = @_; return $drive->{file} =~ m@[:/]vm-\d+-cloudinit(?:\.$QEMU_FORMAT_RE)?$@; @@ -2286,6 +2351,15 @@ sub parse_guest_agent { return $res; } +sub parse_vga { + my ($value) = @_; + + return {} if !$value; + my $res = eval { PVE::JSONSchema::parse_property_string($vga_fmt, $value) }; + warn $@ if $@; + return $res; +} + PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device); sub verify_usb_device { my ($value, $noerr) = @_; @@ -3167,7 +3241,9 @@ sub conf_has_serial { sub vga_conf_has_spice { my ($vga) = @_; - return 0 if !$vga || $vga !~ m/^qxl([234])?$/; + my $vgaconf = parse_vga($vga); + my $vgatype = $vgaconf->{type}; + return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/; return $1 || 1; } @@ -3277,16 +3353,16 @@ sub config_to_command { # add usb controllers my @usbcontrollers = PVE::QemuServer::USB::get_usb_controllers($conf, $bridges, $q35, $usbdesc->{format}, $MAX_USB_DEVICES); push @$devices, @usbcontrollers if @usbcontrollers; - my $vga = $conf->{vga}; + my $vga = parse_vga($conf->{vga}); - my $qxlnum = vga_conf_has_spice($vga); - $vga = 'qxl' if $qxlnum; + my $qxlnum = vga_conf_has_spice($conf->{vga}); + $vga->{type} = 'qxl' if $qxlnum; - if (!$vga) { + if (!$vga->{type}) { if (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 9)) { - $vga = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus'; + $vga->{type} = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus'; } else { - $vga = ($winversion >= 6) ? 'std' : 'cirrus'; + $vga->{type} = ($winversion >= 6) ? 'std' : 'cirrus'; } } @@ -3297,7 +3373,7 @@ sub config_to_command { } else { $tablet = $defaults->{tablet}; $tablet = 0 if $qxlnum; # disable for spice because it is not needed - $tablet = 0 if $vga =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card) + $tablet = 0 if $vga->{type} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card) } push @$devices, '-device', print_tabletdevice_full($conf) if $tablet; @@ -3325,7 +3401,7 @@ sub config_to_command { if ($d->{'x-vga'}) { $xvga = ',x-vga=on'; $kvm_off = 1; - $vga = 'none'; + $vga->{type} = 'none'; $gpu_passthrough = 1; if ($conf->{bios} && $conf->{bios} eq 'ovmf') { @@ -3428,12 +3504,12 @@ sub config_to_command { push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0; - push @$cmd, '-vga', $vga if $vga && $vga !~ m/^serial\d+$/; # for kvm 77 and later - - if ($vga && $vga !~ m/^serial\d+$/ && $vga ne 'none'){ + if ($vga->{type} && $vga->{type} !~ m/^serial\d+$/ && $vga ne 'none'){ + push @$devices, '-device', print_vga_device($conf, $vga, undef, $qxlnum, $bridges); my $socket = vnc_socket($vmid); push @$cmd, '-vnc', "unix:$socket,x509,password"; } else { + push @$cmd, '-vga', 'none' if $vga->{type} eq 'none'; push @$cmd, '-nographic'; } @@ -3540,13 +3616,17 @@ sub config_to_command { if ($qxlnum > 1) { if ($winversion){ for(my $i = 1; $i < $qxlnum; $i++){ - my $pciaddr = print_pci_addr("vga$i", $bridges); - push @$devices, '-device', "qxl,id=vga$i,ram_size=67108864,vram_size=33554432$pciaddr"; + push @$devices, '-device', print_vga_device($conf, $vga, $i, $qxlnum, $bridges); } } else { # assume other OS works like Linux - push @$cmd, '-global', 'qxl-vga.ram_size=134217728'; - push @$cmd, '-global', 'qxl-vga.vram_size=67108864'; + my ($ram, $vram) = ("134217728", "67108864"); + if ($vga->{memory}) { + $ram = PVE::Tools::convert_size($qxlnum*4*$vga->{memory}, 'mb' => 'b'); + $vram = PVE::Tools::convert_size($qxlnum*2*$vga->{memory}, 'mb' => 'b'); + } + push @$cmd, '-global', "qxl-vga.ram_size=$ram"; + push @$cmd, '-global', "qxl-vga.vram_size=$vram"; } } diff --git a/PVE/QemuServer/PCI.pm b/PVE/QemuServer/PCI.pm index 0d9cf24..5ddda4f 100644 --- a/PVE/QemuServer/PCI.pm +++ b/PVE/QemuServer/PCI.pm @@ -9,7 +9,7 @@ print_pcie_addr my $devices = { piix3 => { bus => 0, addr => 1 }, - #addr2 : first videocard + vga => { bus => 0, addr => 2 }, balloon0 => { bus => 0, addr => 3 }, watchdog => { bus => 0, addr => 4 }, scsihw0 => { bus => 0, addr => 5 }, @@ -130,6 +130,7 @@ sub print_pcie_addr { my $res = ''; my $devices = { + vga => { bus => 'pcie.0', addr => 1 }, hostpci0 => { bus => "ich9-pcie-port-1", addr => 0 }, hostpci1 => { bus => "ich9-pcie-port-2", addr => 0 }, hostpci2 => { bus => "ich9-pcie-port-3", addr => 0 }, -- 2.39.2