%queues_fmt,
};
+my $usbformat = {
+ host => {
+ default_key => 1,
+ type => 'string', format => 'pve-qm-usb-device',
+ format_description => 'HOSTUSBDEVICE|spice',
+ description => 'The Host USB device or port or the value spice',
+ },
+ usb3 => {
+ optional => 1,
+ type => 'boolean',
+ format_description => 'yes|no',
+ description => 'Specifies whether if given host option is a USB3 device or port',
+ },
+};
+
my $usbdesc = {
optional => 1,
- type => 'string', format => 'pve-qm-usb-device',
- typetext => 'host=HOSTUSBDEVICE|spice',
+ type => 'string', format => $usbformat,
description => <<EODESCR,
Configure an USB device (n is 0 to 4). This can be used to
pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
'bus-port(.port)*' (decimal numbers) or
-'vendor_id:product_id' (hexadeciaml numbers)
+'vendor_id:product_id' (hexadeciaml numbers) or
+'spice'
You can use the 'lsusb -t' command to list existing usb devices.
The value 'spice' can be used to add a usb redirection devices for spice.
+The 'usb3' option determines whether the device is a USB3 device or not (this does currently not work reliably with spice redirection and is then ignored).
+
EODESCR
};
PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
return undef if !$value;
- my @dl = split(/,/, $value);
- my $found;
-
my $res = {};
- foreach my $v (@dl) {
- if ($v =~ m/^host=(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/) {
- $found = 1;
- $res->{vendorid} = $2;
- $res->{productid} = $4;
- } elsif ($v =~ m/^host=(\d+)\-(\d+(\.\d+)*)$/) {
- $found = 1;
- $res->{hostbus} = $1;
- $res->{hostport} = $2;
- } elsif ($v =~ m/^spice$/) {
- $found = 1;
- $res->{spice} = 1;
- } else {
- return undef;
- }
+ if ($value =~ m/^(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/) {
+ $res->{vendorid} = $2;
+ $res->{productid} = $4;
+ } elsif ($value =~ m/^(\d+)\-(\d+(\.\d+)*)$/) {
+ $res->{hostbus} = $1;
+ $res->{hostport} = $2;
+ } elsif ($value =~ m/^spice$/i) {
+ $res->{spice} = 1;
+ } else {
+ return undef;
}
- return undef if !$found;
return $res;
}
}
sub destroy_vm {
- my ($storecfg, $vmid, $keep_empty_config) = @_;
+ my ($storecfg, $vmid, $keep_empty_config, $skiplock) = @_;
my $conffile = config_file($vmid);
my $conf = load_config($vmid);
- check_lock($conf);
+ check_lock($conf) if !$skiplock;
# only remove disks owned by this VM
foreach_drive($conf, sub {
return $raw;
}
-sub update_config_nolock {
- my ($vmid, $conf, $skiplock) = @_;
-
- check_lock($conf) if !$skiplock;
+sub write_config {
+ my ($vmid, $conf) = @_;
my $cfspath = cfs_config_path($vmid);
PVE::Cluster::cfs_write_file($cfspath, $conf);
}
-sub update_config {
- my ($vmid, $conf, $skiplock) = @_;
-
- lock_config($vmid, &update_config_nolock, $conf, $skiplock);
-}
-
sub load_defaults {
my $res = {};
my $use_usb2 = 0;
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
next if !$conf->{"usb$i"};
+ my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
+ next if !$d || $d->{usb3}; # do not add usb2 controller if we have only usb3 devices
$use_usb2 = 1;
}
# include usb device config
push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
}
+ # add usb3 controller if needed
+
+ my $use_usb3 = 0;
+ for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
+ next if !$conf->{"usb$i"};
+ my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
+ next if !$d || !$d->{usb3};
+ $use_usb3 = 1;
+ }
+
+ $pciaddr = print_pci_addr("xhci", $bridges);
+ push @$devices, '-device', "nec-usb-xhci,id=xhci$pciaddr" if $use_usb3;
+
my $vga = $conf->{vga};
my $qxlnum = vga_conf_has_spice($vga);
# usb devices
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
- my $d = parse_usb_device($conf->{"usb$i"});
+ next if !$conf->{"usb$i"};
+ my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
next if !$d;
- if ($d->{vendorid} && $d->{productid}) {
- push @$devices, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
- } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
- push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
- } elsif ($d->{spice}) {
- # usb redir support for spice
- push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
- push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
+
+ # if it is a usb3 device, attach it to the xhci controller, else omit the bus option
+ my $usbbus = '';
+ if (defined($d->{usb3}) && $d->{usb3}) {
+ $usbbus = ',bus=xhci.0';
+ }
+
+ if (defined($d->{host})) {
+ $d = parse_usb_device($d->{host});
+ if (defined($d->{vendorid}) && defined($d->{productid})) {
+ push @$devices, '-device', "usb-host$usbbus,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
+ } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
+ push @$devices, '-device', "usb-host$usbbus,hostbus=$d->{hostbus},hostport=$d->{hostport}";
+ } elsif (defined($d->{spice}) && $d->{spice}) {
+ # usb redir support for spice, currently no usb3
+ push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
+ push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
+ }
}
}
#if dimm_memory is not aligned to dimm map
if($current_size > $memory) {
$conf->{memory} = $current_size;
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
}
});
}
}
#update conf after each succesful module hotplug
$conf->{memory} = $current_size;
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
});
} else {
$conf->{memory} = $current_size;
eval { qemu_objectdel($vmid, "mem-$name"); };
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
});
}
}
}
if ($changes) {
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
$conf = load_config($vmid); # update/reload
}
# save new config if hotplug was successful
delete $conf->{$opt};
vmconfig_undelete_pending_option($conf, $opt);
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
$conf = load_config($vmid); # update/reload
}
}
# save new config if hotplug was successful
$conf->{$opt} = $value;
delete $conf->{pending}->{$opt};
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
$conf = load_config($vmid); # update/reload
}
}
$conf = load_config($vmid); # update/reload
if (!defined($conf->{$opt})) {
vmconfig_undelete_pending_option($conf, $opt);
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
} elsif (valid_drivename($opt)) {
vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
vmconfig_undelete_pending_option($conf, $opt);
delete $conf->{$opt};
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
} else {
vmconfig_undelete_pending_option($conf, $opt);
delete $conf->{$opt};
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
}
}
}
delete $conf->{pending}->{$opt};
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
}
}
die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
# hotplug new disks
- PVE::Storage::activate_volumes($storecfg, [$drive->{file}]);
+ PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive);
}
my $conf = load_config($vmid);
- check_lock($conf) if !$skiplock;
-
if (!check_running($vmid)) {
- destroy_vm($storecfg, $vmid);
+ destroy_vm($storecfg, $vmid, undef, $skiplock);
} else {
die "VM $vmid is running - destroy failed\n";
}
'net29' => { bus => 1, addr => 24 },
'net30' => { bus => 1, addr => 25 },
'net31' => { bus => 1, addr => 26 },
+ 'xhci' => { bus => 1, addr => 27 },
'virtio6' => { bus => 2, addr => 1 },
'virtio7' => { bus => 2, addr => 2 },
'virtio8' => { bus => 2, addr => 3 },
} elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) {
my $virtdev = $1;
my $value = $3;
- if ($line =~ m/backup=no/) {
+ my $di = parse_drive($virtdev, $value);
+ if (defined($di->{backup}) && !$di->{backup}) {
print $outfd "#$line";
- } elsif ($virtdev && $map->{$virtdev}) {
- my $di = parse_drive($virtdev, $value);
+ } elsif ($map->{$virtdev}) {
delete $di->{format}; # format can change on restore
$di->{file} = $map->{$virtdev};
$value = print_drive($vmid, $di);
my $changes = update_disksize($vmid, $conf, $vm_volids);
- update_config_nolock($vmid, $conf, 1) if $changes;
+ write_config($vmid, $conf) if $changes;
};
if (defined($vmid)) {
# always overwrite machine if we save vmstate. This makes sure we
# can restore it later using correct machine type
$snap->{machine} = get_current_qemu_machine($vmid) if $snap->{vmstate};
-
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
};
lock_config($vmid, $updatefn);
$newconf->{parent} = $snapname;
- update_config_nolock($vmid, $newconf, 1);
+ write_config($vmid, $newconf);
};
lock_config($vmid, $updatefn);
delete $conf->{machine} if $snap->{vmstate} && !$has_machine_config;
}
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
if (!$prepare && $snap->{vmstate}) {
my $statefile = PVE::Storage::path($storecfg, $snap->{vmstate});
}
}
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
};
lock_config($vmid, $updatefn);
my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
$drive->{file} = $voliddst;
$conf->{$ds} = print_drive($vmid, $drive);
- update_config_nolock($vmid, $conf, 1);
+ write_config($vmid, $conf);
});
}