use Storable qw(dclone);
use PVE::Exception qw(raise raise_param_exc);
use PVE::Storage;
-use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline);
+use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach);
use PVE::JSONSchema qw(get_standard_option);
use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
use PVE::INotify;
optional => 1,
type => 'string',
description => "scsi controller model",
- enum => [qw(lsi virtio-scsi-pci megasas)],
+ enum => [qw(lsi lsi53c810 virtio-scsi-pci megasas pvscsi)],
default => 'lsi',
},
description => {
minimum => 1,
default => 1,
},
+ maxcpus => {
+ optional => 1,
+ type => 'integer',
+ description => "Maximum cpus for hotplug.",
+ minimum => 1,
+ default => 1,
+ },
acpi => {
optional => 1,
type => 'boolean',
maxLength => 40,
optional => 1,
},
+ smbios1 => {
+ description => "Specify SMBIOS type 1 fields.",
+ type => 'string', format => 'pve-qm-smbios1',
+ typetext => "[manufacturer=str][,product=str][,version=str][,serial=str] [,uuid=uuid][,sku=str][,family=str]",
+ maxLength => 256,
+ optional => 1,
+ },
};
# what about other qemu settings ?
my $MAX_PARALLEL_PORTS = 3;
my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio',
- 'ne2k_isa', 'i82551', 'i82557b', 'i82559er'];
+ 'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3'];
my $nic_model_list_txt = join(' ', sort @$nic_model_list);
my $netdesc = {
optional => 1,
type => 'string', format => 'pve-qm-net',
- typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=<dev>][,rate=<mbps>][,tag=<vlanid>]",
+ typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=<dev>][,queues=<nbqueues>][,rate=<mbps>][,tag=<vlanid>][,firewall=0|1]",
description => <<EODESCR,
Specify network devices.
my $idedesc = {
optional => 1,
type => 'string', format => 'pve-qm-drive',
- typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
+ typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads] [,discard=ignore|on]',
description => "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
my $scsidesc = {
optional => 1,
type => 'string', format => 'pve-qm-drive',
- typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
+ typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads] [,discard=ignore|on]',
description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
my $satadesc = {
optional => 1,
type => 'string', format => 'pve-qm-drive',
- typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
+ typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads] [,discard=ignore|on]',
description => "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc);
my $virtiodesc = {
optional => 1,
type => 'string', format => 'pve-qm-drive',
- typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
+ typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads] [,discard=ignore|on]',
description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
my $hostpcidesc = {
optional => 1,
type => 'string', format => 'pve-qm-hostpci',
- typetext => "HOSTPCIDEVICE",
+ typetext => "[host=]HOSTPCIDEVICE [,driver=kvm|vfio] [,rombar=on|off] [,pcie=0|1] [,x-vga=on|off]",
description => <<EODESCR,
Map host pci devices. HOSTPCIDEVICE syntax is:
You can us the 'lspci' command to list existing pci devices.
+The 'rombar' option determines whether or not the device's ROM will be visible in the guest's memory map (default is 'on').
+
Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
Experimental: user reported problems with this option.
# ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
# [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
# [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
-# [,aio=native|threads]
+# [,aio=native|threads][,discard=ignore|on]
sub parse_drive {
my ($key, $data) = @_;
foreach my $p (split (/,/, $data)) {
next if $p =~ m/^\s*$/;
- if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|mbps|bps_rd|mbps_rd|bps_wr|mbps_wr|iops|iops_rd|iops_wr|size)=(.+)$/) {
+ if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|mbps|mbps_max|bps_rd|mbps_rd|mbps_rd_max|bps_wr|mbps_wr|mbps_wr_max|iops|iops_max|iops_rd|iops_rd_max|iops_wr|iops_wr_max|size|discard)=(.+)$/) {
my ($k, $v) = ($1, $2);
$k = 'file' if $k eq 'volume';
return undef if $res->{werror} && $res->{werror} !~ m/^(enospc|ignore|report|stop)$/;
return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/;
return undef if $res->{aio} && $res->{aio} !~ m/^(native|threads)$/;
-
+ return undef if $res->{discard} && $res->{discard} !~ m/^(ignore|on)$/;
return undef if $res->{mbps_rd} && $res->{mbps};
return undef if $res->{mbps_wr} && $res->{mbps};
return undef if $res->{mbps} && $res->{mbps} !~ m/^\d+(\.\d+)?$/;
+ return undef if $res->{mbps_max} && $res->{mbps_max} !~ m/^\d+(\.\d+)?$/;
return undef if $res->{mbps_rd} && $res->{mbps_rd} !~ m/^\d+(\.\d+)?$/;
+ return undef if $res->{mbps_rd_max} && $res->{mbps_rd_max} !~ m/^\d+(\.\d+)?$/;
return undef if $res->{mbps_wr} && $res->{mbps_wr} !~ m/^\d+(\.\d+)?$/;
+ return undef if $res->{mbps_wr_max} && $res->{mbps_wr_max} !~ m/^\d+(\.\d+)?$/;
return undef if $res->{iops_rd} && $res->{iops};
return undef if $res->{iops_wr} && $res->{iops};
+
+
return undef if $res->{iops} && $res->{iops} !~ m/^\d+$/;
+ return undef if $res->{iops_max} && $res->{iops_max} !~ m/^\d+$/;
return undef if $res->{iops_rd} && $res->{iops_rd} !~ m/^\d+$/;
+ return undef if $res->{iops_rd_max} && $res->{iops_rd_max} !~ m/^\d+$/;
return undef if $res->{iops_wr} && $res->{iops_wr} !~ m/^\d+$/;
+ return undef if $res->{iops_wr_max} && $res->{iops_wr_max} !~ m/^\d+$/;
if ($res->{size}) {
return $res;
}
-my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio iops iops_rd iops_wr);
+my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio discard iops iops_rd iops_wr iops_max iops_rd_max iops_wr_max);
sub print_drive {
my ($vmid, $drive) = @_;
my $opts = '';
- foreach my $o (@qemu_drive_options, 'mbps', 'mbps_rd', 'mbps_wr', 'backup') {
+ foreach my $o (@qemu_drive_options, 'mbps', 'mbps_rd', 'mbps_wr', 'mbps_max', 'mbps_rd_max', 'mbps_wr_max', 'backup') {
$opts .= ",$o=$drive->{$o}" if $drive->{$o};
}
return $res;
}
+sub machine_type_is_q35 {
+ my ($conf) = @_;
+
+ return $conf->{machine} && ($conf->{machine} =~ m/q35/) ? 1 : 0;
+}
+
+sub print_tabletdevice_full {
+ my ($conf) = @_;
+
+ my $q35 = machine_type_is_q35($conf);
+
+ # we use uhci for old VMs because tablet driver was buggy in older qemu
+ my $usbbus = $q35 ? "ehci" : "uhci";
+
+ return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
+}
+
sub print_drivedevice_full {
my ($storecfg, $conf, $vmid, $drive, $bridges) = @_;
my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}", $bridges);
$device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
} elsif ($drive->{interface} eq 'scsi') {
- $maxdev = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7;
+ $maxdev = ($conf->{scsihw} && ($conf->{scsihw} !~ m/^lsi/)) ? 256 : 7;
my $controller = int($drive->{index} / $maxdev);
my $unit = $drive->{index} % $maxdev;
my $devicetype = 'hd';
}
}
- if (!$conf->{scsihw} || $conf->{scsihw} eq 'lsi'){
- $device = "scsi-$devicetype,bus=scsihw$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}" if !$conf->{scsihw} || $conf->{scsihw} eq 'lsi';
+ if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)){
+ $device = "scsi-$devicetype,bus=scsihw$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
} else {
$device = "scsi-$devicetype,bus=scsihw$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
}
return $device;
}
+sub get_initiator_name {
+ my $initiator;
+
+ my $fh = IO::File->new('/etc/iscsi/initiatorname.iscsi') || return undef;
+ while (defined(my $line = <$fh>)) {
+ next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
+ $initiator = $1;
+ last;
+ }
+ $fh->close();
+
+ return $initiator;
+}
+
sub print_drive_full {
my ($storecfg, $vmid, $drive) = @_;
my $extra = ($bootorder !~ m/n/) ? "romfile=," : '';
my $pciaddr = print_pci_addr("$netid", $bridges);
my $tmpstr = "$device,${extra}mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
+ if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){
+ #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq)
+ my $vectors = $net->{queues} * 2 + 2;
+ $tmpstr .= ",vectors=$vectors,mq=on";
+ }
$tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
return $tmpstr;
}
my $vmname = $conf->{name} || "vm$vmid";
+ my $netdev = "";
+
if ($net->{bridge}) {
- return "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/pve-bridge$vhostparam";
+ $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
} else {
- return "type=user,id=$netid,hostname=$vmname";
+ $netdev = "type=user,id=$netid,hostname=$vmname";
}
+
+ $netdev .= ",queues=$net->{queues}" if ($net->{queues} && $net->{model} eq 'virtio');
+
+ return $netdev;
}
sub drive_is_cdrom {
return undef if !$value;
+
+ my @list = split(/,/, $value);
+ my $found;
+
my $res = {};
+ foreach my $kv (@list) {
- if ($value =~ m/^[a-f0-9]{2}:[a-f0-9]{2}\.[a-f0-9]$/) {
- $res->{pciid} = $value;
- } else {
- return undef;
+ if ($kv =~ m/^(host=)?([a-f0-9]{2}:[a-f0-9]{2})(\.([a-f0-9]))?$/) {
+ $found = 1;
+ if(defined($4)){
+ push @{$res->{pciid}}, { id => $2 , function => $4};
+
+ }else{
+ my $pcidevices = lspci($2);
+ $res->{pciid} = $pcidevices->{$2};
+ }
+ } elsif ($kv =~ m/^driver=(kvm|vfio)$/) {
+ $res->{driver} = $1;
+ } elsif ($kv =~ m/^rombar=(on|off)$/) {
+ $res->{rombar} = $1;
+ } elsif ($kv =~ m/^x-vga=(on|off)$/) {
+ $res->{'x-vga'} = $1;
+ } elsif ($kv =~ m/^pcie=(\d+)$/) {
+ $res->{pcie} = 1 if $1 == 1;
+ } else {
+ warn "unknown hostpci setting '$kv'\n";
+ }
}
+ return undef if !$found;
+
return $res;
}
foreach my $kvp (split(/,/, $data)) {
- if ($kvp =~ m/^(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i) {
+ if ($kvp =~ m/^(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er|vmxnet3)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i) {
my $model = lc($1);
my $mac = defined($3) ? uc($3) : PVE::Tools::random_ether_addr();
$res->{model} = $model;
$res->{macaddr} = $mac;
} elsif ($kvp =~ m/^bridge=(\S+)$/) {
$res->{bridge} = $1;
+ } elsif ($kvp =~ m/^queues=(\d+)$/) {
+ $res->{queues} = $1;
} elsif ($kvp =~ m/^rate=(\d+(\.\d+)?)$/) {
$res->{rate} = $1;
} elsif ($kvp =~ m/^tag=(\d+)$/) {
$res->{tag} = $1;
+ } elsif ($kvp =~ m/^firewall=(\d+)$/) {
+ $res->{firewall} = $1;
} else {
return undef;
}
$res .= ",bridge=$net->{bridge}" if $net->{bridge};
$res .= ",rate=$net->{rate}" if $net->{rate};
$res .= ",tag=$net->{tag}" if $net->{tag};
+ $res .= ",firewall=$net->{firewall}" if $net->{firewall};
return $res;
}
return $key;
}
+my $valid_smbios1_options = {
+ manufacturer => '\S+',
+ product => '\S+',
+ version => '\S+',
+ serial => '\S+',
+ uuid => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
+ sku => '\S+',
+ family => '\S+',
+};
+
+# smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str]
+sub parse_smbios1 {
+ my ($data) = @_;
+
+ my $res = {};
+
+ foreach my $kvp (split(/,/, $data)) {
+ return undef if $kvp !~ m/^(\S+)=(.+)$/;
+ my ($k, $v) = split(/=/, $kvp);
+ return undef if !defined($k) || !defined($v);
+ return undef if !$valid_smbios1_options->{$k};
+ return undef if $v !~ m/^$valid_smbios1_options->{$k}$/;
+ $res->{$k} = $v;
+ }
+
+ return $res;
+}
+
+PVE::JSONSchema::register_format('pve-qm-smbios1', \&verify_smbios1);
+sub verify_smbios1 {
+ my ($value, $noerr) = @_;
+
+ return $value if parse_smbios1($value);
+
+ return undef if $noerr;
+
+ die "unable to parse smbios (type 1) options\n";
+}
+
PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
sub verify_bootdisk {
my ($value, $noerr) = @_;
delete $conf->{smp};
}
+ if ($conf->{maxcpus} && $conf->{sockets}) {
+ delete $conf->{sockets};
+ }
+
my $used_volids = {};
my $cleanup_config = sub {
my $have_ovz = -f '/proc/vz/vestat';
+ my $q35 = machine_type_is_q35($conf);
+
push @$cmd, '/usr/bin/kvm';
push @$cmd, '-id', $vmid;
push @$cmd, '-daemonize';
- $pciaddr = print_pci_addr("piix3", $bridges);
- push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2";
+ if ($conf->{smbios1}) {
+ push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
+ }
- my $use_usb2 = 0;
- for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
- next if !$conf->{"usb$i"};
- $use_usb2 = 1;
+ if ($q35) {
+ # the q35 chipset support native usb2, so we enable usb controller
+ # by default for this machine type
+ push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
+ } else {
+ $pciaddr = print_pci_addr("piix3", $bridges);
+ push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2";
+
+ my $use_usb2 = 0;
+ for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
+ next if !$conf->{"usb$i"};
+ $use_usb2 = 1;
+ }
+ # include usb device config
+ push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
}
- # include usb device config
- push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
my $vga = $conf->{vga};
$vga = 'qxl' if $qxlnum;
if (!$vga) {
- if ($conf->{ostype} && ($conf->{ostype} eq 'win8' ||
- $conf->{ostype} eq 'win7' ||
+ if ($conf->{ostype} && ($conf->{ostype} eq 'win8' ||
+ $conf->{ostype} eq 'win7' ||
$conf->{ostype} eq 'w2k8')) {
$vga = 'std';
} else {
$tablet = 0 if $vga =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
}
- push @$devices, '-device', 'usb-tablet,id=tablet,bus=uhci.0,port=1' if $tablet;
+ push @$devices, '-device', print_tabletdevice_full($conf) if $tablet;
# host pci devices
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
- my $d = parse_hostpci($conf->{"hostpci$i"});
- next if !$d;
- $pciaddr = print_pci_addr("hostpci$i", $bridges);
- push @$devices, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
+ my $d = parse_hostpci($conf->{"hostpci$i"});
+ next if !$d;
+
+ my $pcie = $d->{pcie};
+ if($pcie){
+ die "q35 machine model is not enabled" if !$q35;
+ $pciaddr = print_pcie_addr("hostpci$i");
+ }else{
+ $pciaddr = print_pci_addr("hostpci$i", $bridges);
+ }
+
+ my $rombar = $d->{rombar} && $d->{rombar} eq 'off' ? ",rombar=0" : "";
+ my $driver = $d->{driver} && $d->{driver} eq 'vfio' ? "vfio-pci" : "pci-assign";
+ my $xvga = $d->{'x-vga'} && $d->{'x-vga'} eq 'on' ? ",x-vga=on" : "";
+ $driver = "vfio-pci" if $xvga ne '';
+ my $pcidevices = $d->{pciid};
+ my $multifunction = 1 if @$pcidevices > 1;
+
+ my $j=0;
+ foreach my $pcidevice (@$pcidevices) {
+
+ my $id = "hostpci$i";
+ $id .= ".$j" if $multifunction;
+ my $addr = $pciaddr;
+ $addr .= ".$j" if $multifunction;
+ my $devicestr = "$driver,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr";
+
+ if($j == 0){
+ $devicestr .= "$rombar$xvga";
+ $devicestr .= ",multifunction=on" if $multifunction;
+ }
+
+ push @$devices, '-device', $devicestr;
+ $j++;
+ }
}
# usb devices
$sockets = $conf->{sockets} if $conf->{sockets};
my $cores = $conf->{cores} || 1;
- push @$cmd, '-smp', "sockets=$sockets,cores=$cores";
+ my $maxcpus = $conf->{maxcpus} if $conf->{maxcpus};
+
+ if ($maxcpus) {
+ push @$cmd, '-smp', "cpus=$cores,maxcpus=$maxcpus";
+ } else {
+ push @$cmd, '-smp', "sockets=$sockets,cores=$cores";
+ }
push @$cmd, '-nodefaults';
$cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
+ # Note: enforce needs kernel 3.10, so we do not use it for now
+ # push @$cmd, '-cpu', "$cpu,enforce";
push @$cmd, '-cpu', $cpu;
push @$cmd, '-S' if $conf->{freeze};
my $pciaddr = print_pci_addr("spice", $bridges);
- $spice_port = PVE::Tools::next_unused_port(61000, 61099);
+ $spice_port = PVE::Tools::next_spice_port();
push @$cmd, '-spice', "tls-port=${spice_port},addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on";
my $ahcicontroller = {};
my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : $defaults->{scsihw};
+ # Add iscsi initiator name if available
+ if (my $initiator = get_initiator_name()) {
+ push @$devices, '-iscsi', "initiator-name=$initiator";
+ }
+
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
if ($drive->{interface} eq 'scsi') {
- my $maxdev = ($scsihw ne 'lsi') ? 256 : 7;
+ my $maxdev = ($scsihw !~ m/^lsi/) ? 256 : 7;
my $controller = int($drive->{index} / $maxdev);
$pciaddr = print_pci_addr("scsihw$controller", $bridges);
push @$devices, '-device', "$scsihw,id=scsihw$controller$pciaddr" if !$scsicontroller->{$controller};
$ahcicontroller->{$controller}=1;
}
- push @$devices, '-drive',print_drive_full($storecfg, $vmid, $drive);
- push @$devices, '-device',print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
+ my $drive_cmd = print_drive_full($storecfg, $vmid, $drive);
+ push @$devices, '-drive',$drive_cmd;
+ push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
});
push @$cmd, '-m', $conf->{memory} || $defaults->{memory};
push @$devices, '-device', $netdevicefull;
}
- #bridges
- while (my ($k, $v) = each %$bridges) {
- $pciaddr = print_pci_addr("pci.$k");
- unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
+ if (!$q35) {
+ # add pci bridges
+ while (my ($k, $v) = each %$bridges) {
+ $pciaddr = print_pci_addr("pci.$k");
+ unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
+ }
}
-
# hack: virtio with fairsched is unreliable, so we do not use fairsched
# when the VM uses virtio devices.
if (!$use_virtio && $have_ovz) {
return 1 if !check_running($vmid);
+ my $q35 = machine_type_is_q35($conf);
+
if ($deviceid eq 'tablet') {
- my $devicefull = "usb-tablet,id=tablet,bus=uhci.0,port=1";
- qemu_deviceadd($vmid, $devicefull);
+ qemu_deviceadd($vmid, print_tabletdevice_full($conf));
return 1;
}
}
if ($deviceid =~ m/^(scsi)(\d+)$/) {
- return 1 if ($conf->{scsihw} && $conf->{scsihw} ne 'lsi'); #virtio-scsi not yet support hotplug
+ return 1 if ($conf->{scsihw} && ($conf->{scsihw} !~ m/^lsi/)); #virtio-scsi not yet support hotplug
return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device);
return undef if !qemu_driveadd($storecfg, $vmid, $device);
my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
}
}
- if ($deviceid =~ m/^(pci\.)(\d+)$/) {
+
+ if (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
my $bridgeid = $2;
my $pciaddr = print_pci_addr($deviceid);
my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
sub qemu_findorcreatescsihw {
my ($storecfg, $conf, $vmid, $device) = @_;
- my $maxdev = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7;
+ my $maxdev = ($conf->{scsihw} && ($conf->{scsihw} !~ m/^lsi/)) ? 256 : 7;
my $controller = int($device->{index} / $maxdev);
my $scsihwid="scsihw$controller";
my $devices_list = vm_devices_list($vmid);
return 1;
}
+sub qemu_cpu_hotplug {
+ my ($vmid, $conf, $cores) = @_;
+
+ die "new cores config is not defined" if !$cores;
+ die "you can't add more cores than maxcpus"
+ if $conf->{maxcpus} && ($cores > $conf->{maxcpus});
+ return if !check_running($vmid);
+
+ my $currentcores = $conf->{cores} if $conf->{cores};
+ die "current cores is not defined" if !$currentcores;
+ die "maxcpus is not defined" if !$conf->{maxcpus};
+ raise_param_exc({ 'cores' => "online cpu unplug is not yet possible" })
+ if($cores < $currentcores);
+
+ my $currentrunningcores = vm_mon_cmd($vmid, "query-cpus");
+ raise_param_exc({ 'cores' => "cores number if running vm is different than configuration" })
+ if scalar (@{$currentrunningcores}) != $currentcores;
+
+ for(my $i = $currentcores; $i < $cores; $i++) {
+ vm_mon_cmd($vmid, "cpu-add", id => int($i));
+ }
+}
+
sub qemu_block_set_io_throttle {
my ($vmid, $deviceid, $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr) = @_;
#need to impplement call to qemu-ga
}
+sub set_migration_caps {
+ my ($vmid) = @_;
+
+ my $cap_ref = [];
+
+ my $enabled_cap = {
+ "auto-converge" => 1,
+ "xbzrle" => 0,
+ "x-rdma-pin-all" => 0,
+ "zero-blocks" => 0,
+ };
+
+ my $supported_capabilities = vm_mon_cmd_nocheck($vmid, "query-migrate-capabilities");
+
+ for my $supported_capability (@$supported_capabilities) {
+ push @$cap_ref, {
+ capability => $supported_capability->{capability},
+ state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
+ };
+ }
+
+ vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
+}
+
sub vm_start {
my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine, $spice_ticket) = @_;
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
my $d = parse_hostpci($conf->{"hostpci$i"});
next if !$d;
- my $info = pci_device_info("0000:$d->{pciid}");
- die "IOMMU not present\n" if !check_iommu_support();
- die "no pci device info for device '$d->{pciid}'\n" if !$info;
- die "can't unbind pci device '$d->{pciid}'\n" if !pci_dev_bind_to_stub($info);
- die "can't reset pci device '$d->{pciid}'\n" if !pci_dev_reset($info);
+ my $pcidevices = $d->{pciid};
+ foreach my $pcidevice (@$pcidevices) {
+ my $pciid = $pcidevice->{id}.".".$pcidevice->{function};
+
+ my $info = pci_device_info("0000:$pciid");
+ die "IOMMU not present\n" if !check_iommu_support();
+ die "no pci device info for device '$pciid'\n" if !$info;
+
+ if ($d->{driver} && $d->{driver} eq "vfio") {
+ die "can't unbind/bind pci group to vfio '$pciid'\n" if !pci_dev_group_bind_to_vfio($pciid);
+ } else {
+ die "can't unbind/bind to stub pci device '$pciid'\n" if !pci_dev_bind_to_stub($info);
+ }
+
+ die "can't reset pci device '$pciid'\n" if !pci_dev_reset($info);
+ }
}
PVE::Storage::activate_volumes($storecfg, $vollist);
}
if ($migratedfrom) {
- my $capabilities = {};
- $capabilities->{capability} = "xbzrle";
- $capabilities->{state} = JSON::true;
- eval { vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
+
+ eval {
+ PVE::QemuServer::set_migration_caps($vmid);
+ };
warn $@ if $@;
-
+
if ($spice_port) {
print "spice listens on port $spice_port\n";
if ($spice_ticket) {
return -d $testdir;
}
+sub pci_dev_bind_to_vfio {
+ my ($dev) = @_;
+
+ my $name = $dev->{name};
+
+ my $vfio_basedir = "$pcisysfs/drivers/vfio-pci";
+
+ if (!-d $vfio_basedir) {
+ system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null");
+ }
+ die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir;
+
+ my $testdir = "$vfio_basedir/$name";
+ return 1 if -d $testdir;
+
+ my $data = "$dev->{vendor} $dev->{product}";
+ return undef if !file_write("$vfio_basedir/new_id", $data);
+
+ my $fn = "$pcisysfs/devices/$name/driver/unbind";
+ if (!file_write($fn, $name)) {
+ return undef if -f $fn;
+ }
+
+ $fn = "$vfio_basedir/bind";
+ if (! -d $testdir) {
+ return undef if !file_write($fn, $name);
+ }
+
+ return -d $testdir;
+}
+
+sub pci_dev_group_bind_to_vfio {
+ my ($pciid) = @_;
+
+ my $vfio_basedir = "$pcisysfs/drivers/vfio-pci";
+
+ if (!-d $vfio_basedir) {
+ system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null");
+ }
+ die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir;
+
+ # get IOMMU group devices
+ opendir(my $D, "$pcisysfs/devices/0000:$pciid/iommu_group/devices/") || die "Cannot open iommu_group: $!\n";
+ my @devs = grep /^0000:/, readdir($D);
+ closedir($D);
+
+ foreach my $pciid (@devs) {
+ $pciid =~ m/^([:\.\da-f]+)$/ or die "PCI ID $pciid not valid!\n";
+ my $info = pci_device_info($1);
+ pci_dev_bind_to_vfio($info) || die "Cannot bind $pciid to vfio\n";
+ }
+
+ return 1;
+}
+
sub print_pci_addr {
my ($id, $bridges) = @_;
}
+sub print_pcie_addr {
+ my ($id) = @_;
+
+ my $res = '';
+ my $devices = {
+ hostpci0 => { bus => "ich9-pcie-port-1", addr => 0 },
+ hostpci1 => { bus => "ich9-pcie-port-2", addr => 0 },
+ hostpci2 => { bus => "ich9-pcie-port-3", addr => 0 },
+ hostpci3 => { bus => "ich9-pcie-port-4", addr => 0 },
+ };
+
+ if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
+ my $addr = sprintf("0x%x", $devices->{$id}->{addr});
+ my $bus = $devices->{$id}->{bus};
+ $res = ",bus=$bus,addr=$addr";
+ }
+ return $res;
+
+}
+
# vzdump restore implementaion
sub tar_archive_read_firstfile {
my ($dev_id, $size, $devname) = ($1, $2, $3);
$devinfo->{$devname} = { size => $size, dev_id => $dev_id };
} elsif ($line =~ m/^CTIME: /) {
+ # we correctly received the vma config, so we can disable
+ # the timeout now for disk allocation (set to 10 minutes, so
+ # that we always timeout if something goes wrong)
+ alarm(600);
&$print_devmap();
print $fifofh "done\n";
my $tmp = $oldtimeout || 0;
return $current || $default || 'pc';
}
-sub read_x509_subject_spice {
- my ($filename) = @_;
+sub lspci {
- # read x509 subject
- my $bio = Net::SSLeay::BIO_new_file($filename, 'r');
- my $x509 = Net::SSLeay::PEM_read_bio_X509($bio);
- Net::SSLeay::BIO_free($bio);
- my $nameobj = Net::SSLeay::X509_get_subject_name($x509);
- my $subject = Net::SSLeay::X509_NAME_oneline($nameobj);
- Net::SSLeay::X509_free($x509);
-
- # remote-viewer wants comma as seperator (not '/')
- $subject =~ s!^/!!;
- $subject =~ s!/(\w+=)!,$1!g;
+ my $devices = {};
+
+ dir_glob_foreach("$pcisysfs/devices", '[a-f0-9]{4}:([a-f0-9]{2}:[a-f0-9]{2})\.([0-9])', sub {
+ my (undef, $id, $function) = @_;
+ my $res = { id => $id, function => $function};
+ push @{$devices->{$id}}, $res;
+ });
- return $subject;
+ return $devices;
}
+
1;