forked from SLFO-pool/xen
Sync from SUSE:SLFO:Main xen revision 2e30689375849799569c0a388d847125
This commit is contained in:
commit
e0da5782bf
23
.gitattributes
vendored
Normal file
23
.gitattributes
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
## Default LFS
|
||||
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||
*.bsp filter=lfs diff=lfs merge=lfs -text
|
||||
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.gem filter=lfs diff=lfs merge=lfs -text
|
||||
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||
*.lz filter=lfs diff=lfs merge=lfs -text
|
||||
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||
*.obscpio filter=lfs diff=lfs merge=lfs -text
|
||||
*.oxt filter=lfs diff=lfs merge=lfs -text
|
||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.rpm filter=lfs diff=lfs merge=lfs -text
|
||||
*.tbz filter=lfs diff=lfs merge=lfs -text
|
||||
*.tbz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.txz filter=lfs diff=lfs merge=lfs -text
|
||||
*.whl filter=lfs diff=lfs merge=lfs -text
|
||||
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.zst filter=lfs diff=lfs merge=lfs -text
|
31
654370e2-x86-x2APIC-remove-ACPI_FADT_APIC_CLUSTER-use.patch
Normal file
31
654370e2-x86-x2APIC-remove-ACPI_FADT_APIC_CLUSTER-use.patch
Normal file
@ -0,0 +1,31 @@
|
||||
# Commit 26a449ce32cef33f2cb50602be19fcc0c4223ba9
|
||||
# Date 2023-11-02 10:50:26 +0100
|
||||
# Author Roger Pau Monné <roger.pau@citrix.com>
|
||||
# Committer Jan Beulich <jbeulich@suse.com>
|
||||
x86/x2apic: remove usage of ACPI_FADT_APIC_CLUSTER
|
||||
|
||||
The ACPI FADT APIC_CLUSTER flag mandates that when the interrupt delivery is
|
||||
Logical mode APIC must be configured for Cluster destination model. However in
|
||||
apic_x2apic_probe() such flag is incorrectly used to gate whether Physical mode
|
||||
can be used.
|
||||
|
||||
Since Xen when in x2APIC mode only uses Logical mode together with Cluster
|
||||
model completely remove checking for ACPI_FADT_APIC_CLUSTER, as Xen always
|
||||
fulfills the requirement signaled by the flag.
|
||||
|
||||
Fixes: eb40ae41b658 ('x86/Kconfig: add option for default x2APIC destination mode')
|
||||
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
|
||||
--- a/xen/arch/x86/genapic/x2apic.c
|
||||
+++ b/xen/arch/x86/genapic/x2apic.c
|
||||
@@ -231,8 +231,7 @@ const struct genapic *__init apic_x2apic
|
||||
*/
|
||||
x2apic_phys = iommu_intremap != iommu_intremap_full ||
|
||||
(acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL) ||
|
||||
- (IS_ENABLED(CONFIG_X2APIC_PHYSICAL) &&
|
||||
- !(acpi_gbl_FADT.flags & ACPI_FADT_APIC_CLUSTER));
|
||||
+ IS_ENABLED(CONFIG_X2APIC_PHYSICAL);
|
||||
}
|
||||
else if ( !x2apic_phys )
|
||||
switch ( iommu_intremap )
|
103
65437103-x86-i8259-dont-assume-IRQs-always-target-CPU0.patch
Normal file
103
65437103-x86-i8259-dont-assume-IRQs-always-target-CPU0.patch
Normal file
@ -0,0 +1,103 @@
|
||||
# Commit 87f37449d586b4d407b75235bb0a171e018e25ec
|
||||
# Date 2023-11-02 10:50:59 +0100
|
||||
# Author Roger Pau Monné <roger.pau@citrix.com>
|
||||
# Committer Jan Beulich <jbeulich@suse.com>
|
||||
x86/i8259: do not assume interrupts always target CPU0
|
||||
|
||||
Sporadically we have seen the following during AP bringup on AMD platforms
|
||||
only:
|
||||
|
||||
microcode: CPU59 updated from revision 0x830107a to 0x830107a, date = 2023-05-17
|
||||
microcode: CPU60 updated from revision 0x830104d to 0x830107a, date = 2023-05-17
|
||||
CPU60: No irq handler for vector 27 (IRQ -2147483648)
|
||||
microcode: CPU61 updated from revision 0x830107a to 0x830107a, date = 2023-05-17
|
||||
|
||||
This is similar to the issue raised on Linux commit 36e9e1eab777e, where they
|
||||
observed i8259 (active) vectors getting delivered to CPUs different than 0.
|
||||
|
||||
On AMD or Hygon platforms adjust the target CPU mask of i8259 interrupt
|
||||
descriptors to contain all possible CPUs, so that APs will reserve the vector
|
||||
at startup if any legacy IRQ is still delivered through the i8259. Note that
|
||||
if the IO-APIC takes over those interrupt descriptors the CPU mask will be
|
||||
reset.
|
||||
|
||||
Spurious i8259 interrupt vectors however (IRQ7 and IRQ15) can be injected even
|
||||
when all i8259 pins are masked, and hence would need to be handled on all CPUs.
|
||||
|
||||
Continue to reserve PIC vectors on CPU0 only, but do check for such spurious
|
||||
interrupts on all CPUs if the vendor is AMD or Hygon. Note that once the
|
||||
vectors get used by devices detecting PIC spurious interrupts will no longer be
|
||||
possible, however the device driver should be able to cope with spurious
|
||||
interrupts. Such PIC spurious interrupts occurring when the vector is in use
|
||||
by a local APIC routed source will lead to an extra EOI, which might
|
||||
unintentionally clear a different vector from ISR. Note this is already the
|
||||
current behavior, so assume it's infrequent enough to not cause real issues.
|
||||
|
||||
Finally, adjust the printed message to display the CPU where the spurious
|
||||
interrupt has been received, so it looks like:
|
||||
|
||||
microcode: CPU1 updated from revision 0x830107a to 0x830107a, date = 2023-05-17
|
||||
cpu1: spurious 8259A interrupt: IRQ7
|
||||
microcode: CPU2 updated from revision 0x830104d to 0x830107a, date = 2023-05-17
|
||||
|
||||
Amends: 3fba06ba9f8b ('x86/IRQ: re-use legacy vector ranges on APs')
|
||||
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
|
||||
--- a/xen/arch/x86/i8259.c
|
||||
+++ b/xen/arch/x86/i8259.c
|
||||
@@ -222,7 +222,8 @@ static bool _mask_and_ack_8259A_irq(unsi
|
||||
is_real_irq = false;
|
||||
/* Report spurious IRQ, once per IRQ line. */
|
||||
if (!(spurious_irq_mask & irqmask)) {
|
||||
- printk("spurious 8259A interrupt: IRQ%d.\n", irq);
|
||||
+ printk("cpu%u: spurious 8259A interrupt: IRQ%u\n",
|
||||
+ smp_processor_id(), irq);
|
||||
spurious_irq_mask |= irqmask;
|
||||
}
|
||||
/*
|
||||
@@ -349,7 +350,23 @@ void __init init_IRQ(void)
|
||||
continue;
|
||||
desc->handler = &i8259A_irq_type;
|
||||
per_cpu(vector_irq, cpu)[LEGACY_VECTOR(irq)] = irq;
|
||||
- cpumask_copy(desc->arch.cpu_mask, cpumask_of(cpu));
|
||||
+
|
||||
+ /*
|
||||
+ * The interrupt affinity logic never targets interrupts to offline
|
||||
+ * CPUs, hence it's safe to use cpumask_all here.
|
||||
+ *
|
||||
+ * Legacy PIC interrupts are only targeted to CPU0, but depending on
|
||||
+ * the platform they can be distributed to any online CPU in hardware.
|
||||
+ * Note this behavior has only been observed on AMD hardware. In order
|
||||
+ * to cope install all active legacy vectors on all CPUs.
|
||||
+ *
|
||||
+ * IO-APIC will change the destination mask if/when taking ownership of
|
||||
+ * the interrupt.
|
||||
+ */
|
||||
+ cpumask_copy(desc->arch.cpu_mask,
|
||||
+ (boot_cpu_data.x86_vendor &
|
||||
+ (X86_VENDOR_AMD | X86_VENDOR_HYGON) ? &cpumask_all
|
||||
+ : cpumask_of(cpu)));
|
||||
desc->arch.vector = LEGACY_VECTOR(irq);
|
||||
}
|
||||
|
||||
--- a/xen/arch/x86/irq.c
|
||||
+++ b/xen/arch/x86/irq.c
|
||||
@@ -1920,7 +1920,16 @@ void do_IRQ(struct cpu_user_regs *regs)
|
||||
kind = "";
|
||||
if ( !(vector >= FIRST_LEGACY_VECTOR &&
|
||||
vector <= LAST_LEGACY_VECTOR &&
|
||||
- !smp_processor_id() &&
|
||||
+ (!smp_processor_id() ||
|
||||
+ /*
|
||||
+ * For AMD/Hygon do spurious PIC interrupt
|
||||
+ * detection on all CPUs, as it has been observed
|
||||
+ * that during unknown circumstances spurious PIC
|
||||
+ * interrupts have been delivered to CPUs
|
||||
+ * different than the BSP.
|
||||
+ */
|
||||
+ (boot_cpu_data.x86_vendor & (X86_VENDOR_AMD |
|
||||
+ X86_VENDOR_HYGON))) &&
|
||||
bogus_8259A_irq(vector - FIRST_LEGACY_VECTOR)) )
|
||||
{
|
||||
printk("CPU%u: No irq handler for vector %02x (IRQ %d%s)\n",
|
70
655b2ba9-fix-sched_move_domain.patch
Normal file
70
655b2ba9-fix-sched_move_domain.patch
Normal file
@ -0,0 +1,70 @@
|
||||
# Commit 4709ec82917668c2df958ef91b4f21c049c76bee
|
||||
# Date 2023-11-20 10:49:29 +0100
|
||||
# Author Juergen Gross <jgross@suse.com>
|
||||
# Committer Jan Beulich <jbeulich@suse.com>
|
||||
xen/sched: fix sched_move_domain()
|
||||
|
||||
When moving a domain out of a cpupool running with the credit2
|
||||
scheduler and having multiple run-queues, the following ASSERT() can
|
||||
be observed:
|
||||
|
||||
(XEN) Xen call trace:
|
||||
(XEN) [<ffff82d04023a700>] R credit2.c#csched2_unit_remove+0xe3/0xe7
|
||||
(XEN) [<ffff82d040246adb>] S sched_move_domain+0x2f3/0x5b1
|
||||
(XEN) [<ffff82d040234cf7>] S cpupool.c#cpupool_move_domain_locked+0x1d/0x3b
|
||||
(XEN) [<ffff82d040236025>] S cpupool_move_domain+0x24/0x35
|
||||
(XEN) [<ffff82d040206513>] S domain_kill+0xa5/0x116
|
||||
(XEN) [<ffff82d040232b12>] S do_domctl+0xe5f/0x1951
|
||||
(XEN) [<ffff82d0402276ba>] S timer.c#timer_lock+0x69/0x143
|
||||
(XEN) [<ffff82d0402dc71b>] S pv_hypercall+0x44e/0x4a9
|
||||
(XEN) [<ffff82d0402012b7>] S lstar_enter+0x137/0x140
|
||||
(XEN)
|
||||
(XEN)
|
||||
(XEN) ****************************************
|
||||
(XEN) Panic on CPU 1:
|
||||
(XEN) Assertion 'svc->rqd == c2rqd(sched_unit_master(unit))' failed at common/sched/credit2.c:1159
|
||||
(XEN) ****************************************
|
||||
|
||||
This is happening as sched_move_domain() is setting a different cpu
|
||||
for a scheduling unit without telling the scheduler. When this unit is
|
||||
removed from the scheduler, the ASSERT() will trigger.
|
||||
|
||||
In non-debug builds the result is usually a clobbered pointer, leading
|
||||
to another crash a short time later.
|
||||
|
||||
Fix that by swapping the two involved actions (setting another cpu and
|
||||
removing the unit from the scheduler).
|
||||
|
||||
Link: https://github.com/Dasharo/dasharo-issues/issues/488
|
||||
Fixes: 70fadc41635b ("xen/cpupool: support moving domain between cpupools with different granularity")
|
||||
Signed-off-by: Juergen Gross <jgross@suse.com>
|
||||
Reviewed-by: George Dunlap <george.dunlap@cloud.com>
|
||||
|
||||
--- a/xen/common/sched/core.c
|
||||
+++ b/xen/common/sched/core.c
|
||||
@@ -732,18 +732,20 @@ int sched_move_domain(struct domain *d,
|
||||
old_domdata = d->sched_priv;
|
||||
|
||||
/*
|
||||
- * Temporarily move all units to same processor to make locking
|
||||
- * easier when moving the new units to the new processors.
|
||||
+ * Remove all units from the old scheduler, and temporarily move them to
|
||||
+ * the same processor to make locking easier when moving the new units to
|
||||
+ * new processors.
|
||||
*/
|
||||
new_p = cpumask_first(d->cpupool->cpu_valid);
|
||||
for_each_sched_unit ( d, unit )
|
||||
{
|
||||
- spinlock_t *lock = unit_schedule_lock_irq(unit);
|
||||
+ spinlock_t *lock;
|
||||
|
||||
+ sched_remove_unit(old_ops, unit);
|
||||
+
|
||||
+ lock = unit_schedule_lock_irq(unit);
|
||||
sched_set_res(unit, get_sched_res(new_p));
|
||||
spin_unlock_irq(lock);
|
||||
-
|
||||
- sched_remove_unit(old_ops, unit);
|
||||
}
|
||||
|
||||
old_units = d->sched_unit_list;
|
704
README.SUSE
Normal file
704
README.SUSE
Normal file
@ -0,0 +1,704 @@
|
||||
README for the Xen packages
|
||||
===========================
|
||||
|
||||
This file contains SUSE-specific instructions and suggestions for using Xen.
|
||||
|
||||
For more in-depth documentation of using Xen on SUSE, consult the
|
||||
virtualization chapter in the SLES or SUSE Linux manual, or read up-to-date
|
||||
virtualization information, at
|
||||
https://www.suse.com/documentation/sles11/singlehtml/book_xen/book_xen.html
|
||||
|
||||
For more complete documentation on Xen itself, please install the xen-doc-html
|
||||
package and read the documentation installed into /usr/share/doc/packages/xen/.
|
||||
|
||||
|
||||
About
|
||||
-----
|
||||
Xen allows you to run multiple virtual machines on a single physical machine.
|
||||
|
||||
See the Xen homepage for more information:
|
||||
http://www.xenproject.org/
|
||||
|
||||
If you want to use Xen, you need to install the Xen hypervisor and a number of
|
||||
supporting packages. During the initial SUSE installation (or when installing
|
||||
from YaST) check-mark the "Xen Virtual Machine Host Server" pattern. If,
|
||||
instead, you wish to install Xen manually later, click on the "Install
|
||||
Hypervisor and Tools" icon in YaST.
|
||||
|
||||
If you want to install and manage VMs graphically, be sure to install a
|
||||
graphical desktop environment like KDE or GNOME. The following optional
|
||||
packages are needed to manage VMs graphically. Note that "Install Hypervisor
|
||||
and Tools" installs all the packages below:
|
||||
virt-install (Optional, to install VMs)
|
||||
virt-manager (Optional, to manage VMs graphically)
|
||||
virt-viewer (Optional, to view VMs outside virt-manager)
|
||||
vm-install (Optional, to install VMs with xl only)
|
||||
|
||||
You then need to reboot your machine. Instead of booting a normal Linux
|
||||
kernel, you will boot the Xen hypervisor and a slightly changed Linux kernel.
|
||||
This Linux kernel runs in the first virtual machine and will drive most of
|
||||
your hardware.
|
||||
|
||||
This approach is called paravirtualization, since it is a partial
|
||||
virtualization (the Linux kernel needs to be changed slightly, to make the
|
||||
virtualization easier). It results in very good performance (consult
|
||||
http://www.cl.cam.ac.uk/research/srg/netos/xen/performance.html) but has the
|
||||
downside of unchanged operating systems not being supported. However, new
|
||||
hardware features (e.g., Intel VT and AMD-V) are overcoming this limitation.
|
||||
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
The Xen open-source community has a number of terms that you should be
|
||||
familiar with.
|
||||
|
||||
A "domain" is Xen's term for a virtual machine.
|
||||
|
||||
"Domain 0" is the first virtual machine. It can control all other virtual
|
||||
machines. It also (usually) controls the physical hardware. A kernel used in
|
||||
domain 0 may sometimes be referred to as a dom0 kernel.
|
||||
|
||||
"Domain U" is any virtual machine other than domain 0. The "U" indicates it
|
||||
is unprivileged (that is, it cannot control other domains). A kernel used in
|
||||
an unprivileged domain may be referred to as a domU kernel.
|
||||
|
||||
SUSE documentation will use the more industry-standard term "virtual
|
||||
machine", or "VM", rather than "domain" where possible. And to that end,
|
||||
domain 0 will be called the "virtual machine server", since it essentially the
|
||||
server on which the other VMs run. All other domains are simply "virtual
|
||||
machines".
|
||||
|
||||
The acronym "HVM" refers to a hardware-assisted virtual machine. These are
|
||||
VMs that have not been modified (e.g., Windows) and therefore need hardware
|
||||
support such as Intel VT or AMD-V to run on Xen.
|
||||
|
||||
|
||||
Kernels
|
||||
-------
|
||||
Xen supports two kinds of kernels: A privileged kernel (which boots the
|
||||
machine, controls other VMs, and usually controls all your physical hardware)
|
||||
and unprivileged kernels (which can't control other VMs, and usually don't need
|
||||
drivers for physical hardware). The privileged kernel boots first (as the VM
|
||||
server); an unprivileged kernel is used in all subsequent VMs.
|
||||
|
||||
The VM server takes control of the boot process after Xen has initialized the
|
||||
CPU and the memory. This VM contains a privileged kernel and all the hardware
|
||||
drivers.
|
||||
|
||||
For the other virtual machines, you usually don't need the hardware drivers.
|
||||
(It is possible to hide a PCI device from the VM server and re-assign it to
|
||||
another VM for direct access, but that is a more advanced topic.) Instead you
|
||||
use virtual network and block device drivers in the unprivileged VMs to access
|
||||
the physical network and block drivers in the VM server.
|
||||
|
||||
For simplicity, SUSE ships a single Xen-enabled Linux kernel, rather than
|
||||
separate privileged and unprivileged kernels. As most of the hardware drivers
|
||||
are modules anyway, using this kernel as an unprivileged kernel has very
|
||||
little extra overhead.
|
||||
|
||||
The kernel is contained in the kernel-xen package, which you need to install to
|
||||
use Xen.
|
||||
|
||||
|
||||
Booting
|
||||
-------
|
||||
If you installed Xen during the initial SUSE installation, or installed one
|
||||
of the kernel-xen* packages later, a "XEN" option should exist in your Grub
|
||||
bootloader. Select that to boot SUSE on top of Xen.
|
||||
|
||||
If you want to add additional entries, or modify the existing ones, you may
|
||||
run the YaST2 Boot Loader program.
|
||||
|
||||
Once you have booted this configuration successfully, you are running Xen with
|
||||
a privileged kernel on top of it.
|
||||
|
||||
|
||||
Xen Boot Parameters
|
||||
-------------------
|
||||
Normally, xen.gz requires no parameters. However, in special cases (such as
|
||||
debugging or a dedicated VM server) you may wish to pass it parameters.
|
||||
|
||||
Adding parameters to xen.gz can be done by editing the /etc/default/grub file.
|
||||
Add the following line to this file; GRUB_CMDLINE_XEN_DEFAULT="<parameters>". The
|
||||
parameters may be valid options passed to xen.gz (the hypervisor). After
|
||||
editing this file, you must first run 'grub2-mkconfig -o /boot/grub2/grub.cfg'
|
||||
and then reboot for the changes to take effect.
|
||||
|
||||
For more information on how to add options to the hypervisor, see the sections
|
||||
below called; "Dom0 Memory Ballooning" and "Troubleshooting".
|
||||
|
||||
For a more complete discussion of possible parameters, see the user
|
||||
documentation in the xen-doc-html package.
|
||||
|
||||
|
||||
Creating a VM with virt-install
|
||||
-------------------------------
|
||||
The virt-install program (part of the virt-install package, and accessible
|
||||
through YaST's Control Center) is the recommended method to create VMs. This
|
||||
program handles creating both the VM's libvirt XML definition and disk(s).
|
||||
It can help install any operating system, not just SUSE. virt-install has both
|
||||
a command line only mode and a graphical wizard mode that may be used to define
|
||||
and start VM installations.
|
||||
|
||||
virt-install may be launched from the virt-manager VM management tool. Start
|
||||
virt-manager either from the YaST Control Center or from the command line.
|
||||
The installation icon from the main virt-manager screen may be selected to
|
||||
begin the virt-install installation wizard.
|
||||
|
||||
The use of virt-install or virt-manager requires the installation of the
|
||||
libvirt packages and the libvirt daemon must be running on the host unless
|
||||
you are managing a remote host.
|
||||
|
||||
Each VM needs to have its own root filesystem. The root filesystem can live
|
||||
on a block device (e.g., a hard disk partition, or an LVM2 or EVMS volume) or
|
||||
in a file that holds the filesystem image.
|
||||
|
||||
VMs can share filesystems, such as /usr or /opt, that are mounted read-only
|
||||
from _all_ VMs. Never try to share a filesystem that is mounted read-write;
|
||||
filesystem corruption will result. For sharing writable data between VMs, use
|
||||
NFS or other networked or cluster filesystems.
|
||||
|
||||
When defining the virtual network adapter(s), we recommend using a static MAC
|
||||
for the VM rather than allowing Xen to randomly select one each time the VM
|
||||
boots. (See "Network Troubleshooting" below.) The Xen Project has been
|
||||
allocated a range of MAC addresses with the OUI of 00-16-3E. By using MACs
|
||||
from this range you can be sure they will not conflict with any physical
|
||||
adapters.
|
||||
|
||||
When the VM shuts down (because the installation -- or at least the first
|
||||
stage of it -- is done), the wizard finalizes the VM's configuration and
|
||||
restarts the VM.
|
||||
|
||||
The creation of VMs can be automated; read the virt-install man page for more
|
||||
details. The installation of an OS within the VM can be automated if the OS
|
||||
supports it.
|
||||
|
||||
|
||||
Creating a VM with vm-install
|
||||
-----------------------------
|
||||
The vm-install program is also provided to create VMs. Like virt-install,
|
||||
this optional program handles creating both the VM's libvirt XML definition
|
||||
and disk(s). It also creates a legacy configuration file for use with 'xl'.
|
||||
It can help install any operating system, not just SUSE.
|
||||
|
||||
From the command line, run "vm-install". If the DISPLAY environment variable
|
||||
is set and the supporting packages (python-gtk) are installed, a graphical
|
||||
wizard will start. Otherwise, a text wizard will start. If vm-install is
|
||||
started with the '--use-xl' flag, it will not require libvirt nor attempt
|
||||
to communicate with libvirt when creating a VM and instead will only use the
|
||||
'xl' toolstack to start VM installations.
|
||||
|
||||
Once you have the VM configured, click "OK". The wizard will now create a
|
||||
configuration file for the VM, and create a disk image. The disk image will
|
||||
exist in /var/lib/xen/images, and a corresponding configuration file will exist
|
||||
in /etc/xen/vm. The operating system's installation program will then run
|
||||
within the VM.
|
||||
|
||||
When the VM shuts down (because the installation -- or at least the first
|
||||
stage of it -- is done), the wizard finalizes the VM's configuration and
|
||||
restarts the VM.
|
||||
|
||||
The creation of VMs can be automated; read the vm-install man page for more
|
||||
details. The installation of an OS within the VM can be automated if the OS
|
||||
supports it.
|
||||
|
||||
|
||||
Creating a VM Manually
|
||||
----------------------
|
||||
If you create a VM manually (as opposed to using virt-install, which is the
|
||||
recommended way), you will need to create a disk (or reuse an existing one)
|
||||
and a configuration file.
|
||||
|
||||
If you are using a disk or disk image that is already installed with an
|
||||
operating system and you want the VM to run in paravirtual mode, you'll
|
||||
probably need to replace its kernel with a Xen-enabled kernel.
|
||||
|
||||
The kernel and ramdisk used to bootstrap the VM must match any kernel modules
|
||||
that might be present in the VM's disk. It is possible to manually copy the
|
||||
kernel and ramdisk from the VM's disk (for example, after updating the kernel
|
||||
within that VM) to the VM server's filesystem. However, an easier (and less
|
||||
error-prone) method is to use /usr/lib/grub2/x86_64-xen/grub.xen as the VM
|
||||
kernel. When the new VM is started, it runs grub.xen to read the grub
|
||||
configuration from the VM disk, selecting the configured kernel and ramdisk
|
||||
so that it can be used to bootstrap the new VM.
|
||||
|
||||
Next, make a copy of one of the /etc/xen/examples/* files, and modify it to
|
||||
suit your needs. You'll need to change (at very least) the "name" and "disk"
|
||||
parameters. See /etc/xen/examples/ for example configuration files.
|
||||
|
||||
|
||||
Managing Virtual Machines
|
||||
-------------------------
|
||||
VMs can be managed from the command line using 'virsh' or from virt-manager.
|
||||
|
||||
VMs created by virt-install or vm-install (without vm-install's --use-xl flag)
|
||||
will automatically be defined in libvirt. VMs defined in libvirt may be managed
|
||||
by virt-manager or from the command line using the 'virsh' command. However,
|
||||
if you copy a VM from another machine and manually create a VM XML configuration
|
||||
file, you will need to import it into libvirt with a command like:
|
||||
virsh define <path to>/my-vm.xml
|
||||
This imports the configuration into libvirt (and therefore virt-manager becomes
|
||||
aware of it, also).
|
||||
|
||||
Now to start the VM:
|
||||
virsh start my-vm
|
||||
or start it from virt-manager's graphical menu.
|
||||
|
||||
Have a look at running VMs with "virsh list". Attach to the VM's text console
|
||||
with "virsh console <vm-name>". Attaching to multiple VM consoles is most
|
||||
conveniently done with the terminal multiplexer "screen".
|
||||
|
||||
Have a look at the other virsh commands by typing "virsh help". Note that most
|
||||
virsh commands must be done as root.
|
||||
|
||||
|
||||
Changes in the Xen VM Management Toolstack
|
||||
------------------------------------------
|
||||
With SUSE Linux Enterprise Server 12, the way VMs are managed has changed
|
||||
when compared with older SLES versions. Users familiar with the 'xm' command
|
||||
and the xend management daemon will notice that these are absent. The xm/xend
|
||||
toolstack has been replaced with the xl toolstack. The xl toolstack is
|
||||
intended to remain backwards compatible with existing xm domain configuration
|
||||
files. Most 'xm' commands can simply be replaced with 'xl'. One significant
|
||||
difference is that xl does not support the concept of Managed Domains. The xl
|
||||
command can only modify running VMs. Once the VM is shutdown, there is no
|
||||
preserved state information other than what is saved in the configuration
|
||||
file used to start the VM. In order to provide Managed Domains, users are
|
||||
encouraged to use libvirt and it's tools to create and modify VMs. These
|
||||
tools include the command line tool 'virsh' and the graphical tools
|
||||
virt-manager and virt-install.
|
||||
|
||||
Warning: Using xl commands to modify libvirt managed domains will result in
|
||||
errors when virsh or virt-manager is used. Please use only virsh or
|
||||
virt-manager to manage libvirt managed domains. If you are not using libvirt
|
||||
managed domains then using xl commands is the correct way to modify running
|
||||
domains.
|
||||
|
||||
|
||||
Using the Mouse via VNC in Fully Virtual Mode
|
||||
---------------------------------------------
|
||||
In a fully virtualized VM, the mouse may be emulated as a PS/2 mouse, USB
|
||||
mouse, or USB tablet. The virt-install tool selects the best emulation that is
|
||||
known to be automatically detected and supported by the operating system.
|
||||
|
||||
However, when accessing some fully virtualized operating systems via VNC, the
|
||||
mouse may be difficult to control if the VM is emulating a PS/2 mouse. PS/2
|
||||
provides mouse deltas, but VNC only provides absolute coordinates. In such
|
||||
cases, you may want to manually switch the operating system and VM to use a
|
||||
USB tablet.
|
||||
|
||||
Emulation of a SummaSketch graphics tablet is provided for this reason. To
|
||||
use the Summa emulation, you will need to configure your fully virtualized OS.
|
||||
Note that the virtual tablet is connected to the second virtual serial port
|
||||
(/dev/ttyS1 or COM2).
|
||||
|
||||
Most Linux distributions ship with appropriate drivers, and only need to be
|
||||
configured. To configure gpm, edit /etc/sysconfig/mouse and add these lines:
|
||||
MOUSETYPE="summa"
|
||||
XMOUSETYPE="SUMMA"
|
||||
DEVICE=/dev/ttyS1
|
||||
The format and location of your configuration file could vary depending upon
|
||||
your Linux distribution. The goal is to run the gpm daemon as follows:
|
||||
gpm -t summa -m /dev/ttyS1
|
||||
X also needs to be configured to use the Summa emulation. Add the following
|
||||
stanza to /etc/X11/xorg.conf, or use your distribution's tools to add these
|
||||
settings:
|
||||
Section "InputDevice"
|
||||
Identifier "Mouse0"
|
||||
Driver "summa"
|
||||
Option "Device" "/dev/ttyS1"
|
||||
Option "InputFashion" "Tablet"
|
||||
Option "Mode" "Absolute"
|
||||
Option "Name" "EasyPen"
|
||||
Option "Compatible" "True"
|
||||
Option "Protocol" "Auto"
|
||||
Option "SendCoreEvents" "on"
|
||||
Option "Vendor" "GENIUS"
|
||||
EndSection
|
||||
After making these changes, restart gpm and X.
|
||||
|
||||
|
||||
HVM Console in Fully Virtual Mode
|
||||
---------------------------------
|
||||
When running a VM in fully virtual mode, a special console is available that
|
||||
provides some additional ways to control the VM. Press Ctrl-Alt-2 to access
|
||||
the console; press Ctrl-Alt-1 to return to the VM. While at the console,
|
||||
type "help" for help.
|
||||
|
||||
The two most important commands are "send-key" and "change". The "send-key"
|
||||
command allows you to send any key sequence to the VM, which might otherwise
|
||||
be intercepted by your local window manager.
|
||||
|
||||
The "change" command allows the target of a block device to be changed; for
|
||||
example, use it to change from one CD ISO to another. Some versions of Xen
|
||||
have this command disabled for security reasons. Consult the online
|
||||
documentation for workarounds.
|
||||
|
||||
|
||||
Networking
|
||||
----------
|
||||
Your virtual machines become much more useful if you can reach them via the
|
||||
network. Starting with openSUSE11.1 and SLE11, networking in domain 0 is
|
||||
configured and managed via YaST. The yast2-networking module can be used
|
||||
to create and manage bridged networks. During initial installation, a bridged
|
||||
networking proposal will be presented if the "Xen Virtual Machine Host Server"
|
||||
pattern is selected. The proposal will also be presented if you install Xen
|
||||
after initial installation using the "Install Hypervisor and Tools" module in
|
||||
YaST.
|
||||
|
||||
The default proposal creates a virtual bridge in domain 0 for each active
|
||||
ethernet device, enslaving the device to the bridge. Consider a machine
|
||||
containing two ethernet devices (eth0 and eth1), both with active carriers.
|
||||
YaST will create br0 and br1, enslaving the eth0 and eth1 devices repectively.
|
||||
|
||||
VMs get a virtual network interface (e.g. eth0), which is visible in domain 0
|
||||
as vifN.0 and connected to the bridge. This means that if you set up an IP
|
||||
address in the VMs belonging to the same subnet as br0 from your domain 0,
|
||||
you'll be able to communicate not only with the other slave VMs, but also with
|
||||
domain 0 and with the external network. If you have a DHCP server running in
|
||||
your network, your VMs should succeed in getting an IP address.
|
||||
|
||||
Be aware that this may have unwanted security implications. You may want to
|
||||
opt for routing instead of bridging, so you can set up firewalling rules in
|
||||
domain 0.
|
||||
|
||||
Please read about the network configuration in the Xen manual. You can set up
|
||||
bridging or routing for other interfaces also.
|
||||
|
||||
For debugging, here's what happens on bootup of a domU:
|
||||
- xenstored saves the device setup in xenstore
|
||||
- domU is created
|
||||
- vifN.0 shows up in domain 0 and a hotplug event is triggered
|
||||
- hotplug is /sbin/udev; udev looks at /etc/udev/rules.d/40-xen.rules and
|
||||
calls /etc/xen/scripts/vif-bridge online
|
||||
- vif-bridge set the vifN.0 device up and enslaves it to the bridge
|
||||
- eth0 shows up in domU (hotplug event triggered)
|
||||
Similar things happen for block devices, except that /etc/xen/scripts/block is
|
||||
called.
|
||||
|
||||
It's not recommended to use ifplugd nor NetworkManager for managing the
|
||||
interfaces if you use bridging mode. Use routing with nat or proxy-arp
|
||||
in that case. You also need to do that in case you want to send out packets
|
||||
on wireless; you can't bridge Xen "ethernet" packets into 802.11 packets.
|
||||
|
||||
|
||||
Network Troubleshooting
|
||||
-----------------------
|
||||
First ensure the VM server is configured correctly and can access the network.
|
||||
|
||||
Do not use ifplugd or NetworkManager, neither are bridge aware.
|
||||
|
||||
Specify a static virtual MAC in the VM's configuration file. Random MACs can
|
||||
be problematic, since with each boot of the VM it appears that some hardware
|
||||
has been removed (the previous random MAC) and new hardware is present (the
|
||||
new random MAC). This can cause network configuration files (which were
|
||||
intended for the old MAC) to not be matched up with the new virtual hardware.
|
||||
|
||||
In the VM's filesystem, ensure the ifcfg-eth* files are named appropriately.
|
||||
For example, if you do decide to use a randomly-selected MAC for the VM, the
|
||||
ifcfg-eth* file must not include the MAC in its name; name it generically
|
||||
("ifcfg-eth0") instead. If you use a static virtual MAC for the VM, be sure
|
||||
that is reflected in the file's name.
|
||||
|
||||
|
||||
Thread-Local Storage
|
||||
--------------------
|
||||
For some time now, the glibc thread library (NPTL) has used a shortcut to
|
||||
access thread-local variables at a negative segment offset from the segment
|
||||
selector GS instead of reading the linear address from the TDB (offset 0).
|
||||
Unfortunately, this optimization has been made the default by the glibc and
|
||||
gcc maintainers, as it saves one indirection. For Xen this is bad: The access
|
||||
to these variables will trap, and Xen will need to use some tricks to make the
|
||||
access work. It does work, but it's very slow.
|
||||
|
||||
SUSE Linux 9.1 and SLES 9 were prior to this change, and thus are not
|
||||
affected. SUSE Linux 9.2 and 9.3 are affected. For SUSE Linux 10.x and SLES
|
||||
10, we have disabled negative segment references in gcc and glibc, and so
|
||||
these are not affected. Other non-SUSE Linux distributions may be affected.
|
||||
|
||||
For affected distributions, one way to work around the problem is to rename
|
||||
the /lib/tls directory, so the pre-i686 version gets used, where no such
|
||||
tricks are done. An example LSB-compliant init script which automates these
|
||||
steps is installed at /usr/share/doc/packages/xen/boot.xen. This script
|
||||
renames /lib/tls when running on Xen, and restores it when not running on Xen.
|
||||
Modify this script to work with your specific distribution.
|
||||
|
||||
Mono has a similar problem, but this has been fixed in SUSE Linux 10.1 and
|
||||
SLES 10. Older or non-SUSE versions of Mono may have a performance impact.
|
||||
|
||||
|
||||
Security
|
||||
--------
|
||||
Domain 0 has control over all domains. This means that care should be taken to
|
||||
keep domain 0 safe; ideally you strip it down to only do as little there as
|
||||
possible, preferably with no local users except for the system administrator.
|
||||
Most commands in domain 0 can only be performed as root, but this protection
|
||||
scheme only has moderate security and might be defeated. In case domain 0 is
|
||||
compromised, all other domains are compromised as well.
|
||||
|
||||
To allow relocation of VMs (migration), the receiving machine listens on TCP
|
||||
port 8002. You might want to put firewall rules in place in domain 0 to
|
||||
restrict this to machines which you trust. Relocating VMs with sensitive data
|
||||
is not a good idea in untrusted networks, since the data is not sent encrypted.
|
||||
|
||||
The memory protections for the domUs are effective; so far no way to break out
|
||||
of a virtual machine is known. A VM is an effective jail.
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
When booting, Linux reserves data structures matching the amount of RAM found.
|
||||
This has the side-effect that you can't dynamically grow the memory beyond
|
||||
what the kernel has been booted with. But you can trick domU Linux to prepare
|
||||
for a larger amount of RAM by passing the mem= boot parameter.
|
||||
|
||||
The export of virtual hard disks from files in Xen can be handled via the
|
||||
loopback driver (although in Xen >= 3.0.4, this is can be replaced by the
|
||||
"blktap" user-space driver.) If you are still using loopback, it may be
|
||||
possible to run out of loopback devices, as by default only 64 are supported.
|
||||
You can change this by inserting:
|
||||
options loop max_loop=128
|
||||
into /etc/modprobe.conf.local in domain 0.
|
||||
|
||||
|
||||
Upgrading the Host Operating System
|
||||
-----------------------------------
|
||||
When upgrading the host operating system from one major release to another
|
||||
(for example, SLES 11 to SLES 12 or openSUSE 12.3 to openSUSE 13.1) or when
|
||||
applying a service pack like SLES 11 SP3 to SLES 11 SP2 all running VMs must
|
||||
be shut down before the upgrade process is begun.
|
||||
|
||||
On versions of SLES 11 and openSUSE 12 you are using the xm/xend toolstack.
|
||||
After upgrading to SLES 12 and newer openSUSE versions this toolstack will be
|
||||
replaced with the xl toolstack. The xl toolstack does not support Managed
|
||||
Domains. If you wish to continue using Managed Domains you must switch to
|
||||
using libvirt and its command line interface 'virsh'. You may also use
|
||||
virt-manager as a GUI interface to libvirt. After upgrading the host but
|
||||
before you can begin using libvirt on VMs that were previously managed by
|
||||
xm/xend, you must run a conversion tool called /usr/sbin/xen2libvirt for all
|
||||
VMs.
|
||||
|
||||
For example, to convert all domains previously managed by xend:
|
||||
xen2libvirt -r /var/lib/xend/domains/
|
||||
|
||||
Now typing 'virsh list --all' will show your previously xend managed domains
|
||||
being managed by libvirt. Run 'xen2libvirt -h' to see additional options for
|
||||
using this tool.
|
||||
|
||||
|
||||
Memory Ballooning in VMs
|
||||
------------------------
|
||||
Setting a VMs maximum memory value greater than the initial memory value
|
||||
requires support for memory ballooning in the VMs operating system. Modern SLES
|
||||
and openSUSE guests have this capability built-in. Windows installation media
|
||||
does not support memory ballooning so you must first install the VM without
|
||||
memory ballooning (maxmem equal to initial memory). After the installation, the
|
||||
Virtual Machine Driver Pack (vmdp) must be installed. After this, the VMs
|
||||
maxmem value may be increased. A reboot of the VM is required for this action
|
||||
to take effect.
|
||||
|
||||
|
||||
Dom0 Memory Ballooning
|
||||
----------------------
|
||||
It is strongly recommended that you dedicate a fixed amount of RAM to dom0
|
||||
rather than relying on dom0 auto ballooning. Doing so will ensure your dom0
|
||||
has enough resources to operate well and will improve startup times for your
|
||||
VMs. The amount of RAM dedicated to dom0 should never be less than the
|
||||
recommended minimum amount for running your SUSE distribution in native mode.
|
||||
The actual amount of RAM needed for dom0 depends on several factors including
|
||||
how much physical RAM is on the host, the number of physical CPUs, and the
|
||||
number of VMs running simultaneously where each VM has a specific requirement
|
||||
for RAM. The following example shows the syntax for doing this. This would be
|
||||
added to your grub1 or grub2 configuration;
|
||||
|
||||
Grub2 Example:
|
||||
Edit /etc/default/grub and add,
|
||||
GRUB_CMDLINE_XEN_DEFAULT="dom0_mem=1024M,max:1024M"
|
||||
and then run
|
||||
grub2-mkconfig -o /boot/grub2/grub.cfg
|
||||
|
||||
Grub1 Example:
|
||||
Edit /boot/grub/menu.lst and edit the line containing xen.gz
|
||||
kernel /boot/xen.gz dom0_mem=1024M,max:1024M
|
||||
|
||||
After modifying your grub configuration, you will need to edit /etc/xen/xl.conf
|
||||
and set autoballoon="off". This will prevent xl from automatically adjusting
|
||||
the amount of memory assigned to dom0. Reboot the host for these changes to
|
||||
take effect.
|
||||
|
||||
|
||||
Adjusting LIBXL_HOTPLUG_TIMEOUT at runtime
|
||||
------------------------------------------
|
||||
A domU with a large amount of disks may run into the hardcoded
|
||||
LIBXL_HOTPLUG_TIMEOUT limit, which is 40 seconds. This happens if the
|
||||
preparation for each disk takes an unexpected large amount of time. Then
|
||||
the sum of all configured disks and the individual preparation time will
|
||||
be larger than 40 seconds. The hotplug script which does the preparation
|
||||
takes a lock before doing the actual preparation. Since the hotplug
|
||||
scripts for each disk are spawned at nearly the same time, each one has
|
||||
to wait for the lock. Due to this contention, the total execution time
|
||||
of a script can easily exceed the timeout. In this case libxl will
|
||||
terminate the script because it has to assume an error condition.
|
||||
|
||||
Example:
|
||||
10 configured disks, each one takes 3 seconds within the critital
|
||||
section. The total execution time will be 30 seconds, which is still
|
||||
within the limit. With 5 additional configured disks, the total
|
||||
execution time will be 45 seconds, which would trigger the timeout.
|
||||
|
||||
To handle such setup without a recompile of libxl, a special key/value
|
||||
has to be created in xenstore prior domain creation. This can be done
|
||||
either manually, or at system startup. A dedicated systemd service file
|
||||
exists to set the required value. To enable it, run these commands:
|
||||
|
||||
/etc/systemd/system # systemctl enable xen-LIBXL_HOTPLUG_TIMEOUT.service
|
||||
/etc/systemd/system # systemctl start xen-LIBXL_HOTPLUG_TIMEOUT.service
|
||||
|
||||
|
||||
In case the value in this service file needs to be changed, a copy with
|
||||
the exact same name must be created in the /etc/systemd/system directory:
|
||||
|
||||
/etc/systemd/system # cat xen-LIBXL_HOTPLUG_TIMEOUT.service
|
||||
[Unit]
|
||||
Description=set global LIBXL_HOTPLUG_TIMEOUT
|
||||
ConditionPathExists=/proc/xen/capabilities
|
||||
|
||||
Requires=xenstored.service
|
||||
After=xenstored.service
|
||||
Requires=xen-init-dom0.service
|
||||
After=xen-init-dom0.service
|
||||
Before=xencommons.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
ExecStartPre=/bin/grep -q control_d /proc/xen/capabilities
|
||||
ExecStart=/usr/bin/xenstore-write /libxl/suse/per-device-LIBXL_HOTPLUG_TIMEOUT 10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
In this example the per-device value will be set to 10 seconds.
|
||||
|
||||
The change for libxl which handles this xenstore value will enable
|
||||
additional logging if the key is found. That extra logging will show how
|
||||
the execution time of each script.
|
||||
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
First try to get Linux running on bare metal before trying with Xen.
|
||||
|
||||
Be sure your Xen hypervisor (xen) and VM kernels (kernel-xen) are compatible.
|
||||
The hypervisor and domain 0 kernel are a matched set, and usually must be
|
||||
upgraded together. Consult the online documentation for a matrix of supported
|
||||
32- and 64-bit combinations
|
||||
|
||||
If you have trouble early in the boot, try passing pnpacpi=off to the Linux
|
||||
kernel. If you have trouble with interrupts or timers, passing lapic to Xen
|
||||
may help. Xen and Linux understand similar ACPI boot parameters. Try the
|
||||
options acpi=off,force,ht,noirq or acpi_skip_timer_override.
|
||||
|
||||
Other useful debugging options to Xen may be nosmp, noreboot, mem=4096M,
|
||||
sync_console, noirqbalance (Dell). For a complete list of Xen boot options,
|
||||
consult the "Xen Hypervisor Command Line Options" documentation.
|
||||
|
||||
If domain 0 Linux crashes on X11 startup, please try to boot into runlevel 3.
|
||||
|
||||
1) As a first step in debugging Xen you should add the following hypervisor
|
||||
options to the xen.gz line in your grub configuration file. After rebooting,
|
||||
the 'xl dmesg' command will produce more output to better analyze problems.
|
||||
|
||||
Grub2 Example:
|
||||
Edit /etc/default/grub and add,
|
||||
GRUB_CMDLINE_XEN_DEFAULT="loglvl=all guest_loglvl=all"
|
||||
and then run,
|
||||
grub2-mkconfig -o /boot/grub2/grub.cfg
|
||||
|
||||
Grub1 Example:
|
||||
Edit /boot/grub/menu.lst and edit the line containing xen.gz
|
||||
kernel /boot/xen.gz loglvl=all guest_loglvl=all
|
||||
|
||||
2) With the log levels specified above and the host rebooted, more useful
|
||||
information about domain 0 and running VMs can be obtained using the
|
||||
'xl dmesg' and 'xl debug-keys' commands. For example, from the command line
|
||||
run:
|
||||
xl debug-keys h
|
||||
and then run:
|
||||
xl dmesg
|
||||
Note that at the end of the output from 'xl dmesg' it includes help on a
|
||||
series of commands that may be passed to 'xl debug-keys'. For example, by
|
||||
passing the letter 'q' to 'xl debug-keys' it will "dump domain (and guest
|
||||
debug) info".
|
||||
xl debug-keys q
|
||||
Now you can again run 'xl dmesg' to see the domain and guest debug info.
|
||||
|
||||
3) Sometimes it is useful to attach a serial terminal and direct Xen to send
|
||||
its output not only to the screen, but also to that terminal. First you need
|
||||
to attach a serial cable from the serial port on the server to a second
|
||||
machine's serial port. That second machine could be running minicom (or some
|
||||
other program that can be setup to read from the serial port). Do the
|
||||
following to prepare Xen to send its output over this serial line.
|
||||
|
||||
Grub2 Example:
|
||||
Edit /etc/default/grub and add,
|
||||
GRUB_CMDLINE_XEN_DEFAULT="loglvl=all guest_loglvl=all console=com1 com1=115200,8n1"
|
||||
Also append additional serial flags to the option below such that it appears as,
|
||||
GRUB_CMDLINE_LINUX_DEFAULT="<pre-existing flags> console=ttyS0, 115200"
|
||||
where pre-existing flags are those options already present and then run,
|
||||
grub2-mkconfig -o /boot/grub2/grub.cfg
|
||||
|
||||
Grub1 Example:
|
||||
Edit the /etc/grub/menu.lst file and add the following to the Xen entry,
|
||||
kernel /boot/xen.gz loglvl=all guest_loglvl=all console=com1 com1=115200,8n1
|
||||
module /boot/vmlinuz-xen <pre-existing flags> console=ttyS0, 115200
|
||||
|
||||
Once the hardware and software are configured correctly the server is rebooted
|
||||
and its output should appear on the other terminal as the server boots up.
|
||||
|
||||
4) To further debug Xen or domain 0 Linux crashes or hangs, it may be useful to
|
||||
use the debug-enabled hypervisor, and/or to prevent automatic rebooting.
|
||||
|
||||
Grub2 Example:
|
||||
Edit /etc/default/grub and add,
|
||||
GRUB_CMDLINE_XEN_DEFAULT="noreboot loglvl=all guest_loglvl=all"
|
||||
Edit /boot/grub2/grub.cfg and look for these lines:
|
||||
multiboot /boot/xen-<version>.gz ...
|
||||
and replace them with:
|
||||
multiboot /boot/xen-dbg-<version>.gz' ... Replace <version> with the
|
||||
appropriate version string contained in the filename. Note that running
|
||||
grub2-mkconfig -o /boot/grub2/grub.cfg will overwrite all manual changes
|
||||
made to grub.cfg.
|
||||
|
||||
Grub1 Example:
|
||||
Edit your menu.lst configuration from something like this:
|
||||
kernel (hd0,5)/xen.gz
|
||||
To something like this:
|
||||
kernel (hd0,5)/xen-dbg.gz noreboot loglvl=all guest_loglvl=all
|
||||
|
||||
All hypervisor options require a reboot to take effect. After rebooting, the
|
||||
Xen hypervisor will write any error messages to the log file (viewable with
|
||||
the "xl dmesg" command).
|
||||
|
||||
If problems persist, check if a newer version is available. Well-tested
|
||||
versions will be shipped with SUSE and via YaST Online Update.
|
||||
|
||||
|
||||
Resources
|
||||
---------
|
||||
https://www.suse.com/documentation/sles11/singlehtml/book_xen/book_xen.html
|
||||
http://doc.opensuse.org/products/draft/SLES/SLES-xen_sd_draft/cha.xen.basics.html
|
||||
|
||||
|
||||
Feedback
|
||||
--------
|
||||
In case you have remarks about, problems with, ideas for, or praise for Xen,
|
||||
please report it back to the xen-devel list:
|
||||
xen-devel@lists.xen.org
|
||||
If you find issues with the packaging or setup done by SUSE, please report
|
||||
it through bugzilla:
|
||||
https://bugzilla.suse.com
|
||||
|
||||
|
||||
ENJOY!
|
||||
Your SUSE Team.
|
1
baselibs.conf
Normal file
1
baselibs.conf
Normal file
@ -0,0 +1 @@
|
||||
xen-libs
|
50
bin-python3-conversion.patch
Normal file
50
bin-python3-conversion.patch
Normal file
@ -0,0 +1,50 @@
|
||||
Index: xen-4.18.0-testing/tools/misc/xencov_split
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/misc/xencov_split
|
||||
+++ xen-4.18.0-testing/tools/misc/xencov_split
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
from __future__ import print_function
|
||||
from builtins import str
|
||||
Index: xen-4.18.0-testing/tools/python/scripts/convert-legacy-stream
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/scripts/convert-legacy-stream
|
||||
+++ xen-4.18.0-testing/tools/python/scripts/convert-legacy-stream
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Index: xen-4.18.0-testing/tools/python/scripts/verify-stream-v2
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/scripts/verify-stream-v2
|
||||
+++ xen-4.18.0-testing/tools/python/scripts/verify-stream-v2
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Verify a v2 format migration stream """
|
||||
Index: xen-4.18.0-testing/tools/xenmon/xenmon.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/xenmon/xenmon.py
|
||||
+++ xen-4.18.0-testing/tools/xenmon/xenmon.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
#####################################################################
|
||||
# xenmon is a front-end for xenbaked.
|
||||
Index: xen-4.18.0-testing/tools/xentrace/xentrace_format
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/xentrace/xentrace_format
|
||||
+++ xen-4.18.0-testing/tools/xentrace/xentrace_format
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
# by Mark Williamson, (C) 2004 Intel Research Cambridge
|
||||
|
384
block-dmmd
Normal file
384
block-dmmd
Normal file
@ -0,0 +1,384 @@
|
||||
#! /bin/bash
|
||||
|
||||
# Usage: block-dmmd [add args | remove args]
|
||||
#
|
||||
# the dmmd device syntax (in xl commands/configs) is something like:
|
||||
# script=block-dmmd,md;/dev/md0;md;/dev/md1;lvm;/dev/vg1/lv1
|
||||
# or
|
||||
# script=block-dmmd,lvm;/dev/vg1/lv1;lvm;/dev/vg1/lv2;md;/dev/md0
|
||||
# device pairs (type;dev) are processed in order, with the last device
|
||||
# assigned to the VM
|
||||
#
|
||||
# Note - When using the libxl stack, the "script=block-dmmd" option
|
||||
# is required. See man xl-disk-configuration(5) for more information.
|
||||
#
|
||||
# md devices can optionally:
|
||||
# specify a config file through:
|
||||
# md;/dev/md100(/var/xen/config/mdadm.conf)
|
||||
# use an array name (mdadm -N option):
|
||||
# md;My-MD-name;lvm;/dev/vg1/lv1
|
||||
#
|
||||
# Completely expressive syntax should be similar to:
|
||||
# "format=raw, vdev=xvdb, access=rw, script=block-dmmd, \
|
||||
# target=md;/dev/md0(/etc/mdadm.conf);lvm;/dev/vg1/lv1"
|
||||
#
|
||||
##
|
||||
# History:
|
||||
# 2017-07-10, mlatimer@suse.com:
|
||||
# Modification to use syslog for progress messages by ldevulder@suse.com
|
||||
# 2017-06-12, mlatimer@suse.com:
|
||||
# Merge LVM improvements by loic.devulder@mpsa.com
|
||||
# Document libxl "script=block-dmmd" syntax in examples
|
||||
# Remove xm/xend references (e.g. parsed_timeout from xend-config.sxp)
|
||||
# 2016-05-27, mlatimer@suse.com:
|
||||
# Merge improvements by loic.devulder@mpsa.com. Highlights include:
|
||||
# - Re-write and simplification to speed up the script!
|
||||
# - Add some (useful) logging messages and comments
|
||||
# Minor tweaks and logging improvements
|
||||
# 2016-05-26, mlatimer@suse.com:
|
||||
# Verify MD activation if mdadm returns 2
|
||||
# 2016-05-20, mlatimer@suse.com:
|
||||
# Strip leading "dmmd:" if present in xenstore params value
|
||||
# 2013-07-03, loic.devulder@mpsa.com:
|
||||
# Partial rewrite of the script for supporting MD activation by name
|
||||
# 2009-06-09, mh@novell.com:
|
||||
# Emit debugging messages into a temporary file; if no longer needed,
|
||||
# just comment the exec I/O redirection below
|
||||
# Make variables used in functions local to avoid global overridings
|
||||
# Use vgscan and vgchange where required
|
||||
# Use the C locale to avoid dealing with localized messages
|
||||
# Assign output from assembling an MD device to a variable to aid
|
||||
# debugging
|
||||
|
||||
# We do not want to deal with localized messages
|
||||
# We use LC_ALL because LC_ALL superse LANG
|
||||
# But we also use LANG because some applications may still use LANG...
|
||||
export LC_ALL=C
|
||||
export LANG=${LC_ALL}
|
||||
|
||||
# Loading common libraries
|
||||
. $(dirname $0)/block-common.sh
|
||||
|
||||
# Constants
|
||||
typeset -rx MDADM_BIN=/sbin/mdadm
|
||||
typeset -rx LVCHANGE_BIN=/sbin/lvchange
|
||||
typeset -rx PVSCAN_BIN=/sbin/pvscan
|
||||
typeset -rx VGSCAN_BIN=/sbin/vgscan
|
||||
typeset -rx VGCHANGE_BIN=/sbin/vgchange
|
||||
typeset -rx CLVMD_BIN=/usr/sbin/clvmd
|
||||
typeset -rx DATE_SEC="date +%s"
|
||||
|
||||
# We check for errors ourselves
|
||||
set +e
|
||||
|
||||
function reload_clvm()
|
||||
{
|
||||
# If we are in cluster mode
|
||||
if ps -e | grep -q [c]lvmd 2>/dev/null; then
|
||||
# Logging message
|
||||
log info "Synchronizing cLVM..."
|
||||
|
||||
# Synchronize cLVM
|
||||
${CLVMD_BIN} -R > /dev/null 2>&1 \
|
||||
|| return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function run_mdadm()
|
||||
{
|
||||
local mdadm_cmd=$1
|
||||
local msg
|
||||
local rc
|
||||
|
||||
msg="$(${MDADM_BIN} ${mdadm_cmd} 2>&1)"
|
||||
rc=$?
|
||||
case "${msg}" in
|
||||
*"has been started"* | *"already active"*)
|
||||
return 0
|
||||
;;
|
||||
*"is already in use"*)
|
||||
# Hmm, might be used by another device in this domU
|
||||
# Leave it to upper layers to detect a real error
|
||||
return 2
|
||||
;;
|
||||
*)
|
||||
return ${rc}
|
||||
;;
|
||||
esac
|
||||
|
||||
# Normally we should not get here, but if this happens
|
||||
# we have to return an error
|
||||
return 1
|
||||
}
|
||||
|
||||
function activate_md()
|
||||
{
|
||||
# Make it explicitly local
|
||||
local par=$1
|
||||
local cfg dev dev_path rc t mdadm_opts
|
||||
|
||||
if [[ ${par} == ${par%%(*} ]]; then
|
||||
# No configuration file specified
|
||||
dev=${par}
|
||||
cfg=""
|
||||
else
|
||||
dev=${par%%(*}
|
||||
t=${par#*(}
|
||||
cfg="-c ${t%%)*}"
|
||||
fi
|
||||
|
||||
# Looking for device name or aliase
|
||||
if [[ ${dev:0:1} == / ]]; then
|
||||
dev_path=${dev%/*}
|
||||
mdadm_opts=""
|
||||
else
|
||||
dev_path=/dev/md
|
||||
mdadm_opts="-s -N"
|
||||
fi
|
||||
|
||||
# Logging message
|
||||
log info "Activating MD device ${dev}..."
|
||||
|
||||
# Is MD device already active?
|
||||
# We need to use full path name, aliase is not possible...
|
||||
if [ -e ${dev_path}/${dev##*/} ]; then
|
||||
${MDADM_BIN} -Q -D ${dev_path}/${dev##*/} 2>/dev/null \
|
||||
| grep -iq state.*\:.*inactive || return 0
|
||||
fi
|
||||
|
||||
# Activate MD device
|
||||
run_mdadm "-A ${mdadm_opts} ${dev} ${cfg}"
|
||||
rc=$?
|
||||
|
||||
# A return code of 2 can indicate the array configuration was incorrect
|
||||
if [[ ${rc} == 2 ]]; then
|
||||
# Logging message
|
||||
log info "Verifying MD device ${dev} activation..."
|
||||
|
||||
# If the array is active, return 0, otherwise return an error
|
||||
${MDADM_BIN} -Q -D ${dev_path}/${dev##*/} &>/dev/null && return 0 \
|
||||
|| return 1
|
||||
fi
|
||||
|
||||
return ${rc}
|
||||
}
|
||||
|
||||
function deactivate_md()
|
||||
{
|
||||
local par=$1
|
||||
local dev
|
||||
|
||||
if [[ ${par} == ${par%%(*} ]]; then
|
||||
# No configuration file specified
|
||||
dev=${par}
|
||||
else
|
||||
dev=${par%%(*}
|
||||
fi
|
||||
|
||||
# Looking for device name or aliase
|
||||
if [[ ${dev:0:1} == / ]]; then
|
||||
dev_path=${dev%/*}
|
||||
else
|
||||
dev_path=/dev/md
|
||||
fi
|
||||
|
||||
# Logging message
|
||||
log info "Deactivating MD device ${dev}..."
|
||||
|
||||
# We need the device name only while deactivating
|
||||
${MDADM_BIN} -S ${dev_path}/${dev##*/} > /dev/null 2>&1
|
||||
|
||||
return $?
|
||||
}
|
||||
|
||||
function lvm_action()
|
||||
{
|
||||
local action=$1
|
||||
local dev=$2
|
||||
local run_timeout=90
|
||||
local end_time
|
||||
|
||||
# Logging message
|
||||
log info "${action} LVM device ${dev}..."
|
||||
|
||||
# Set end_time for the loop
|
||||
(( end_time = $(${DATE_SEC}) + run_timeout ))
|
||||
|
||||
while true; do
|
||||
# Action depends of what the user asks
|
||||
if [[ ${action} == activate ]]; then
|
||||
# First scan for PVs and VGs
|
||||
# We need this for using MD device as PV
|
||||
${PVSCAN_BIN} > /dev/null 2>&1
|
||||
|
||||
${LVCHANGE_BIN} -aey ${dev} > /dev/null 2>&1 \
|
||||
&& [[ -e ${dev} ]] \
|
||||
&& return 0
|
||||
elif [[ ${action} == deactivate ]]; then
|
||||
${LVCHANGE_BIN} -aen ${dev} > /dev/null 2>&1 \
|
||||
&& return 0
|
||||
|
||||
# If the LV is already deactivated we may be in an infinite loop
|
||||
# So we need to test if the LV is still present
|
||||
[[ -e ${dev} ]] || return 0
|
||||
fi
|
||||
|
||||
# It seems that we had a problem during lvchange
|
||||
# If we are in a cluster the problem may be due to a cLVM locking bug,
|
||||
# so try to reload it
|
||||
reload_clvm
|
||||
|
||||
# If it takes too long we need to return an error
|
||||
if (( $(${DATE_SEC}) >= end_time )); then
|
||||
log err "Failed to ${action} $1 within ${run_timeout} seconds"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Briefly sleep before restarting the loop
|
||||
sleep 0.1
|
||||
|
||||
done
|
||||
|
||||
# Normally we should not get here, but if this happens
|
||||
# we have to return an error
|
||||
return 1
|
||||
}
|
||||
|
||||
# Variables
|
||||
typeset command=$1
|
||||
typeset BP=100
|
||||
typeset SP=${BP}
|
||||
typeset VBD
|
||||
typeset -a stack
|
||||
|
||||
function push()
|
||||
{
|
||||
local value="$1"
|
||||
|
||||
[[ -n "${value}" ]] \
|
||||
&& stack[$((--SP))]="${value}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function pop()
|
||||
{
|
||||
[[ "${SP}" != "${BP}" ]] \
|
||||
&& VBD=${stack[$((SP++))]} \
|
||||
|| VBD=""
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function activate_dmmd()
|
||||
{
|
||||
case "$1" in
|
||||
"md")
|
||||
activate_md $2
|
||||
return $?
|
||||
;;
|
||||
"lvm")
|
||||
lvm_action activate $2
|
||||
return $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# Normally we should not get here, but if this happens
|
||||
# we have to return an error
|
||||
return 1
|
||||
}
|
||||
|
||||
function deactivate_dmmd()
|
||||
{
|
||||
case "$1" in
|
||||
"md")
|
||||
deactivate_md $2
|
||||
return $?
|
||||
;;
|
||||
"lvm")
|
||||
lvm_action deactivate $2
|
||||
return $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# Normally we should not get here, but if this happens
|
||||
# we have to return an error
|
||||
return 1
|
||||
}
|
||||
|
||||
function cleanup_stack()
|
||||
{
|
||||
while true; do
|
||||
pop
|
||||
[[ -z "${VBD}" ]] && break
|
||||
deactivate_dmmd ${VBD}
|
||||
done
|
||||
}
|
||||
|
||||
function parse_par()
|
||||
{
|
||||
# Make these vars explicitly local
|
||||
local ac par rc s t
|
||||
|
||||
ac=$1
|
||||
par="$2"
|
||||
|
||||
par="${par};"
|
||||
while true; do
|
||||
t=${par%%;*}
|
||||
|
||||
[[ -z "${t}" ]] && return 0
|
||||
par=${par#*;}
|
||||
|
||||
s=${par%%;*}
|
||||
[[ -z "${s}" ]] && return 1
|
||||
par=${par#*;}
|
||||
|
||||
if [[ "${ac}" == "activate" ]]; then
|
||||
activate_dmmd ${t} ${s} \
|
||||
|| return 1
|
||||
fi
|
||||
push "${t} ${s}"
|
||||
done
|
||||
}
|
||||
|
||||
case "${command}" in
|
||||
"add")
|
||||
p=$(xenstore-read ${XENBUS_PATH}/params) || true
|
||||
claim_lock "dmmd"
|
||||
dmmd=${p#dmmd:}
|
||||
|
||||
if ! parse_par activate "${dmmd}"; then
|
||||
cleanup_stack
|
||||
release_lock "dmmd"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
lastparam=${dmmd##*;}
|
||||
usedevice=${lastparam%(*}
|
||||
xenstore-write ${XENBUS_PATH}/node "${usedevice}"
|
||||
write_dev "${usedevice}"
|
||||
release_lock "dmmd"
|
||||
|
||||
exit 0
|
||||
;;
|
||||
|
||||
"remove")
|
||||
p=$(xenstore-read ${XENBUS_PATH}/params) || true
|
||||
claim_lock "dmmd"
|
||||
dmmd=${p#dmmd:}
|
||||
|
||||
parse_par noactivate "${dmmd}"
|
||||
|
||||
cleanup_stack
|
||||
release_lock "dmmd"
|
||||
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Normally we should not get here, but if this happens
|
||||
# we have to return an error
|
||||
return 1
|
129
block-npiv
Normal file
129
block-npiv
Normal file
@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: block-npiv [add npiv | remove dev]
|
||||
|
||||
dir=$(dirname "$0")
|
||||
. "$dir/block-npiv-common.sh"
|
||||
. "$dir/block-common.sh"
|
||||
|
||||
#set -x
|
||||
#command=$1
|
||||
|
||||
case "$command" in
|
||||
add)
|
||||
# Params is one big arg, with fields separated by hyphens:
|
||||
# single path:
|
||||
# VPWWPN-TGTWWPN-LUN#
|
||||
# multipath:
|
||||
# {VPWWPN1.VPWWPN2....VPWWPNx}-{TGTWWPN1.TGTWWPN2....TGTWWPNx}-LUN#
|
||||
# arg 1 - VPORT's WWPN
|
||||
# arg 2 - Target's WWPN
|
||||
# arg 3 - LUN # on Target
|
||||
# no wwn contains a leading 0x - it is a 16 character hex value
|
||||
# You may want to optionally pick a specific adapter ?
|
||||
par=`xenstore-read $XENBUS_PATH/params` || true
|
||||
NPIVARGS=(${par//-/ })
|
||||
wc=${#NPIVARGS[@]}
|
||||
if [ $wc -eq 5 ]; then
|
||||
# support old syntax
|
||||
# FABRIC-VPWWPN-VPWWNN-TGTWWPN-LUN
|
||||
VPORTWWPNS=${NPIVARGS[1]}
|
||||
VPORTWWNNS=${NPIVARGS[2]}
|
||||
TGTWWPNS=${NPIVARGS[3]}
|
||||
LUN=${NPIVARGS[4]}
|
||||
elif [ $wc -eq 3 ]; then
|
||||
# new syntax
|
||||
VPORTWWPNS=${NPIVARGS[0]}
|
||||
TGTWWPNS=${NPIVARGS[1]}
|
||||
LUN=${NPIVARGS[2]}
|
||||
else
|
||||
# wrong syntax
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure we compare everything using lower-case hex characters
|
||||
TGTWWPNS=`echo $TGTWWPNS | tr A-Z a-z |sed 's/[{.}]/ /g'`
|
||||
VPORTWWPNS=`echo $VPORTWWPNS | tr A-Z a-z |sed 's/[{.}]/ /g'`
|
||||
# Only one VPWWNN is supported
|
||||
VPORTWWNN=`echo $VPORTWWNNS | tr A-Z a-z | sed -e 's/\..*//g' -e 's/{//'`
|
||||
|
||||
claim_lock "npiv"
|
||||
paths=0
|
||||
for VPORTWWPN in $VPORTWWPNS; do
|
||||
find_vhost $VPORTWWPN
|
||||
if test -z "$vhost" ; then
|
||||
create_vport $VPORTWWPN $VPORTWWNN
|
||||
if [ $? -ne 0 ] ; then exit 2; fi
|
||||
sleep 8
|
||||
find_vhost $VPORTWWPN
|
||||
if test -z "$vhost" ; then exit 3; fi
|
||||
fi
|
||||
for TGTWWPN in $TGTWWPNS; do
|
||||
find_sdev $vhost $TGTWWPN $LUN
|
||||
if test -z "$dev"; then
|
||||
echo "- - -" > /sys/class/scsi_host/$vhost/scan
|
||||
sleep 2
|
||||
find_sdev $vhost $TGTWWPN $LUN
|
||||
fi
|
||||
if test -z "$dev"; then
|
||||
exit 4
|
||||
fi
|
||||
paths=$(($paths+1))
|
||||
done
|
||||
done
|
||||
release_lock "npiv"
|
||||
|
||||
if test $paths -gt 1; then
|
||||
xenstore-write $XENBUS_PATH/multipath 1
|
||||
/etc/init.d/multipathd start
|
||||
if test $? -ne 0 ; then exit 4; fi
|
||||
dm=`multipath -l /dev/$dev | grep dm | cut -f2 -d' '`
|
||||
else
|
||||
xenstore-write $XENBUS_PATH/multipath 0
|
||||
dm=$dev
|
||||
fi
|
||||
|
||||
if test ! -z "$dm"; then
|
||||
xenstore-write $XENBUS_PATH/node /dev/$dm
|
||||
write_dev /dev/$dm
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exit 4
|
||||
;;
|
||||
|
||||
remove)
|
||||
node=`xenstore-read $XENBUS_PATH/node` || true
|
||||
multipath=`xenstore-read $XENBUS_PATH/multipath` || true
|
||||
# this is really screwy. the first delete of a lun will
|
||||
# terminate the entire vport (all luns)
|
||||
if test $multipath = 1; then
|
||||
par=`xenstore-read $XENBUS_PATH/params` || true
|
||||
NPIVARGS=(${par//-/ })
|
||||
wc=${#NPIVARGS[@]}
|
||||
if [ $wc -eq 5 ]; then
|
||||
# old syntax
|
||||
# FABRIC-VPWWPN-VPWWNN-TGTWWPN-LUN
|
||||
VPORTWWPNS=${NPIVARGS[1]}
|
||||
elif [ $wc -eq 3 ]; then
|
||||
# new syntax
|
||||
VPORTWWPNS=${NPIVARGS[0]}
|
||||
fi
|
||||
VPORTWWPNS=`echo $VPORTWWPNS | tr A-Z a-z |sed 's/[{.}]/ /g'`
|
||||
for VPORTWWPN in $VPORTWWPNS; do
|
||||
find_vhost $VPORTWWPN
|
||||
if test -z "$vhost" ; then exit 5; fi
|
||||
flush_nodes_on_vhost $vhost
|
||||
delete_vhost $vhost
|
||||
done
|
||||
else
|
||||
dev=$node; dev=${dev#/dev/}
|
||||
find_vhost_from_dev $dev
|
||||
if test -z "$vhost" ; then exit 5; fi
|
||||
flush_nodes_on_vhost $vhost
|
||||
delete_vhost $vhost
|
||||
fi
|
||||
|
||||
exit 0
|
||||
;;
|
||||
esac
|
277
block-npiv-common.sh
Normal file
277
block-npiv-common.sh
Normal file
@ -0,0 +1,277 @@
|
||||
|
||||
|
||||
# Look for the NPIV vport with the WWPN
|
||||
# $1 contains the WWPN (assumes it does not contain a leading "0x")
|
||||
find_vhost()
|
||||
{
|
||||
unset vhost
|
||||
|
||||
# look in upstream locations
|
||||
for fchost in /sys/class/fc_vports/* ; do
|
||||
if test -e $fchost/port_name ; then
|
||||
wwpn=`cat $fchost/port_name | sed -e s/^0x//`
|
||||
if test $wwpn = $1 ; then
|
||||
# Note: makes the assumption the vport will always have an scsi_host child
|
||||
vhost=`ls -d $fchost/device/host*`
|
||||
vhost=`basename $vhost`
|
||||
return
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# look in vendor-specific locations
|
||||
|
||||
# Emulex - just looks like another scsi_host - so look at fc_hosts...
|
||||
for fchost in /sys/class/fc_host/* ; do
|
||||
if test -e $fchost/port_name ; then
|
||||
wwpn=`cat $fchost/port_name | sed -e s/^0x//`
|
||||
if test $wwpn = $1 ; then
|
||||
# Note: makes the assumption the vport will always have an scsi_host child
|
||||
vhost=`basename $fchost`
|
||||
return
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Create a NPIV vport with WWPN
|
||||
# $1 contains the VPORT WWPN
|
||||
# $2 may contain the VPORT WWNN
|
||||
# (assumes no name contains a leading "0x")
|
||||
create_vport()
|
||||
{
|
||||
wwpn=$1
|
||||
wwnn=$2
|
||||
if [ -z "$wwnn" ]; then
|
||||
# auto generate wwnn, follow FluidLabUpdateForEmulex.pdf
|
||||
# Novell specific identifier
|
||||
# byte 6 = 0 indicates WWNN, = 1 indicates WWPN
|
||||
wwnn=${wwpn:0:6}"0"${wwpn:7}
|
||||
fi
|
||||
# find a base adapter with npiv support that is on the right fabric
|
||||
|
||||
# Look via upstream interfaces
|
||||
for fchost in /sys/class/fc_host/* ; do
|
||||
if test -e $fchost/vport_create ; then
|
||||
# is the link up, w/ NPIV support ?
|
||||
pstate=`cat $fchost/port_state`
|
||||
ptype=`cat $fchost/port_type | cut -c 1-5`
|
||||
if [ $pstate = "Online" -a $ptype = "NPort" ] ; then
|
||||
vmax=`cat $fchost/max_npiv_vports`
|
||||
vinuse=`cat $fchost/npiv_vports_inuse`
|
||||
avail=`expr $vmax - $vinuse`
|
||||
if [ $avail -gt 0 ] ; then
|
||||
# create the vport
|
||||
echo $wwpn":"$wwnn > $fchost/vport_create
|
||||
if [ $? -eq 0 ] ; then
|
||||
return 0
|
||||
fi
|
||||
# failed - so we'll just look for the next adapter
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Look in vendor-specific locations
|
||||
|
||||
# Emulex: interfaces mirror upstream, but are under adapter scsi_host
|
||||
for shost in /sys/class/scsi_host/* ; do
|
||||
if [ -e $shost/vport_create ] ; then
|
||||
fchost=`ls -d $shost/device/fc_host*`
|
||||
# is the link up, w/ NPIV support ?
|
||||
if [ -e $fchost/port_state ] ; then
|
||||
pstate=`cat $fchost/port_state`
|
||||
ptype=`cat $fchost/port_type | cut -c 1-5`
|
||||
if [ $pstate = "Online" -a $ptype = "NPort" ] ; then
|
||||
vmax=`cat $shost/max_npiv_vports`
|
||||
vinuse=`cat $shost/npiv_vports_inuse`
|
||||
avail=`expr $vmax - $vinuse`
|
||||
if [ $avail -gt 0 ] ; then
|
||||
# create the vport
|
||||
echo $wwpn":"$wwnn > $shost/vport_create
|
||||
if [ $? -eq 0 ] ; then
|
||||
return 0
|
||||
fi
|
||||
# failed - so we'll just look for the next adapter
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# BFA are under adapter scsi_host
|
||||
for shost in /sys/class/scsi_host/* ; do
|
||||
if [ -e $shost/vport_create ] ; then
|
||||
fchost=`ls -d $shost/device/fc_host/*`
|
||||
# is the link up, w/ NPIV support ?
|
||||
if [ -e $fchost/port_state ] ; then
|
||||
pstate=`cat $fchost/port_state`
|
||||
ptype=`cat $fchost/port_type | cut -c 1-5`
|
||||
if [ $pstate = "Online" -a $ptype = "NPort" ] ; then
|
||||
# create the vport
|
||||
echo $wwpn":"$wwnn > $shost/vport_create
|
||||
if [ $? -eq 0 ] ; then
|
||||
return 0
|
||||
fi
|
||||
# failed - so we'll just look for the next adapter
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# Look for the LUN on the indicated scsi_host (which is an NPIV vport)
|
||||
# $1 is the scsi_host name (normalized to simply the hostX name)
|
||||
# $2 is the WWPN of the tgt port the lun is on
|
||||
# Note: this implies we don't support a multipath'd lun, or we
|
||||
# are explicitly identifying a "path"
|
||||
# $3 is the LUN number of the scsi device
|
||||
find_sdev()
|
||||
{
|
||||
unset dev
|
||||
hostno=${1/*host/}
|
||||
for sdev in /sys/class/scsi_device/${hostno}:*:$3 ; do
|
||||
if test -e $sdev/device/../fc_trans*/target${hostno}*/port_name ; then
|
||||
tgtwwpn=`cat $sdev/device/../fc_trans*/target${hostno}*/port_name | sed -e s/^0x//`
|
||||
if test $tgtwwpn = $2 ; then
|
||||
if test -e $sdev/device/block* ; then
|
||||
dev=`ls $sdev/device/block*`
|
||||
dev=${dev##*/}
|
||||
return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Look for the NPIV vhost based on a scsi "sdX" name
|
||||
# $1 is the "sdX" name
|
||||
find_vhost_from_dev()
|
||||
{
|
||||
unset vhost
|
||||
hostno=`readlink /sys/block/$1/device`
|
||||
hostno=${hostno##*/}
|
||||
hostno=${hostno%%:*}
|
||||
if test -z "$hostno" ; then return; fi
|
||||
vhost="host"$hostno
|
||||
}
|
||||
|
||||
|
||||
# We're about to terminate a vhost based on a scsi device
|
||||
# Flush all nodes on that vhost as they are about to go away
|
||||
# $1 is the vhost
|
||||
flush_nodes_on_vhost()
|
||||
{
|
||||
if test ! -x /sbin/blockdev ; then return; fi
|
||||
hostno=${1/*host/}
|
||||
for sdev in /sys/class/scsi_device/${hostno}:* ; do
|
||||
if test -e $sdev/device/block* ; then
|
||||
dev=`ls $sdev/device/block*`
|
||||
dev="/dev/"$dev
|
||||
if test -n "$dev"; then
|
||||
blockdev --flushbufs $dev
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Terminate a NPIV vhost
|
||||
# $1 is vhost
|
||||
delete_vhost()
|
||||
{
|
||||
# use upstream interface
|
||||
for vport in /sys/class/fc_vports/* ; do
|
||||
if test -e $vport/device/$1 ; then
|
||||
if test -e $vport/vport_delete ; then
|
||||
echo "1" > $vport/vport_delete
|
||||
if test $? -ne 0 ; then exit 6; fi
|
||||
sleep 4
|
||||
return
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# use vendor specific interface
|
||||
|
||||
# Emulex
|
||||
if test -e /sys/class/fc_host/$1/device/../scsi_host*/lpfc_drvr_version ; then
|
||||
shost=`ls -1d /sys/class/fc_host/$1/device/../scsi_host* | sed s/.*scsi_host://`
|
||||
vportwwpn=`cat /sys/class/fc_host/$1/port_name | sed s/^0x//`
|
||||
vportwwnn=`cat /sys/class/fc_host/$1/node_name | sed s/^0x//`
|
||||
echo "$vportwwpn:$vportwwnn" > /sys/class/scsi_host/$shost/vport_delete
|
||||
if test $? -ne 0 ; then exit 6; fi
|
||||
sleep 4
|
||||
return
|
||||
fi
|
||||
|
||||
# Qlogic
|
||||
if test -e /sys/class/fc_host/$1/device/../scsi_host*/driver_version ; then
|
||||
shost=`ls -1d /sys/class/fc_host/$1/device/../scsi_host* | sed s/.*scsi_host://`
|
||||
vportwwpn=`cat /sys/class/fc_host/$1/port_name | sed s/^0x//`
|
||||
vportwwnn=`cat /sys/class/fc_host/$1/node_name | sed s/^0x//`
|
||||
echo "$vportwwpn:$vportwwnn" > /sys/class/scsi_host/$shost/vport_delete
|
||||
if test $? -ne 0 ; then exit 6; fi
|
||||
sleep 4
|
||||
return
|
||||
fi
|
||||
|
||||
# BFA
|
||||
if test -e /sys/class/fc_host/$1/device/../scsi_host/*/driver_name ; then
|
||||
shost=`ls -1d /sys/class/fc_host/$1/device/../scsi_host/* | sed s#.*scsi_host/##`
|
||||
vportwwpn=`cat /sys/class/fc_host/$1/port_name | sed s/^0x//`
|
||||
vportwwnn=`cat /sys/class/fc_host/$1/node_name | sed s/^0x//`
|
||||
echo "$vportwwpn:$vportwwnn" > /sys/class/scsi_host/$shost/vport_delete
|
||||
if test $? -ne 0 ; then exit 6; fi
|
||||
sleep 4
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
exit 6
|
||||
}
|
||||
|
||||
|
||||
vport_status()
|
||||
{
|
||||
# Look via upstream interfaces
|
||||
for fchost in /sys/class/fc_host/* ; do
|
||||
if test -e $fchost/vport_create ; then
|
||||
vport_status_display $fchost $fchost
|
||||
fi
|
||||
done
|
||||
|
||||
# Look in vendor-specific locations
|
||||
|
||||
# Emulex: interfaces mirror upstream, but are under adapter scsi_host
|
||||
for shost in /sys/class/scsi_host/* ; do
|
||||
if [ -e $shost/vport_create ] ; then
|
||||
fchost=`ls -d $shost/device/fc_host*`
|
||||
vport_status_display $fchost $shost
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
vport_status_display()
|
||||
{
|
||||
echo
|
||||
echo "fc_host: " $2
|
||||
echo "port_state: " `cat $1/port_state`
|
||||
echo "port_type: " `cat $1/port_type`
|
||||
echo "fabric_name: " `cat $1/fabric_name`
|
||||
echo "max_npiv_vports: " `cat $2/max_npiv_vports`
|
||||
echo "npiv_vports_inuse: " `cat $2/npiv_vports_inuse`
|
||||
echo "modeldesc: " `cat $2/modeldesc`
|
||||
echo "speed: " `cat $1/speed`
|
||||
|
||||
return 0
|
||||
}
|
||||
|
79
block-npiv-vport
Normal file
79
block-npiv-vport
Normal file
@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: block-npiv-vport [create npivargs | delete vportwwpn | status]
|
||||
|
||||
dir=$(dirname "$0")
|
||||
. "$dir/block-npiv-common.sh"
|
||||
|
||||
#set -x
|
||||
command=$1
|
||||
params=$2
|
||||
|
||||
case "$command" in
|
||||
create)
|
||||
# Params is one big arg, with fields separated by hyphens:
|
||||
# FABRIC-VPWWPN-VPWWNN-TGTWWPN-LUN#
|
||||
# arg 2 - Fabric Name
|
||||
# arg 3 - VPORT's WWPN
|
||||
# arg 4 - VPORT's WWNN
|
||||
# arg 5 - Target's WWPN
|
||||
# arg 6 - LUN # on Target
|
||||
# no wwn contains a leading 0x - it is a 16 character hex value
|
||||
# You may want to optionally pick a specific adapter ?
|
||||
NPIVARGS=$params;
|
||||
LUN=${NPIVARGS##*-*-*-*-}; NPIVARGS=${NPIVARGS%-*}
|
||||
if test $LUN = $NPIVARGS ; then exit 1; fi
|
||||
TGTWWPN=${NPIVARGS##*-*-*-}; NPIVARGS=${NPIVARGS%-*}
|
||||
if test $TGTWWPN = $NPIVARGS ; then exit 1; fi
|
||||
VPORTWWNN=${NPIVARGS##*-*-}; NPIVARGS=${NPIVARGS%-*}
|
||||
if test $VPORTWWNN = $NPIVARGS ; then exit 1; fi
|
||||
VPORTWWPN=${NPIVARGS##*-}; NPIVARGS=${NPIVARGS%-*}
|
||||
if test $VPORTWWPN = $NPIVARGS ; then exit 1; fi
|
||||
FABRICNM=$NPIVARGS
|
||||
|
||||
# Ensure we compare everything using lower-case hex characters
|
||||
TGTWWPN=`echo $TGTWWPN | tr A-Z a-z`
|
||||
VPORTWWPN=`echo $VPORTWWPN | tr A-Z a-z`
|
||||
VPORTWWNN=`echo $VPORTWWNN | tr A-Z a-z`
|
||||
FABRICNM=`echo $FABRICNM | tr A-Z a-z`
|
||||
|
||||
find_vhost $VPORTWWPN $FABRICNM
|
||||
if test -z "$vhost" ; then
|
||||
create_vport $FABRICNM $VPORTWWPN $VPORTWWNN
|
||||
if [ $? -ne 0 ] ; then exit 2; fi
|
||||
sleep 8
|
||||
find_vhost $VPORTWWPN $FABRICNM
|
||||
if test -z "$vhost" ; then exit 3; fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
;;
|
||||
|
||||
delete)
|
||||
# Params is VPORT's WWPN
|
||||
# no wwn contains a leading 0x - it is a 16 character hex value
|
||||
VPORTWWPN=$params
|
||||
|
||||
# Ensure we compare everything using lower-case hex characters
|
||||
VPORTWWPN=`echo $VPORTWWPN | tr A-Z a-z`
|
||||
|
||||
find_vhost $VPORTWWPN $FABRICNM
|
||||
if test -z "$vhost" ; then exit 4; fi
|
||||
delete_vhost $vhost
|
||||
|
||||
exit 0
|
||||
;;
|
||||
|
||||
status)
|
||||
vport_status
|
||||
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: block-npiv-vport [create npivargs | delete vportwwpn | status]"
|
||||
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
79
boot.local.xenU
Normal file
79
boot.local.xenU
Normal file
@ -0,0 +1,79 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# Copyright (c) 2014 SUSE GmbH Nuernberg, Germany. All rights reserved.
|
||||
#
|
||||
# Author: Werner Fink <werner@suse.de>, 1996
|
||||
# Burchard Steinbild <bs@suse.de>, 1996
|
||||
#
|
||||
# /etc/init.d/boot.local
|
||||
#
|
||||
# script with local commands to be executed from init on system startup
|
||||
#
|
||||
#
|
||||
# Here you should add things, that should happen directly after booting
|
||||
# before we're going to the first run level.
|
||||
#
|
||||
|
||||
date
|
||||
|
||||
# echo "$MACHINE: running $0 $*"
|
||||
my_REDIRECT="$(echo $REDIRECT | sed 's#^/dev/##')"
|
||||
my_DEVICE="$(echo $my_REDIRECT | sed 's#^tty##')"
|
||||
my_SPEED="$(stty speed)"
|
||||
# echo REDIRECT $REDIRECT $my_REDIRECT
|
||||
# echo my_DEVICE $my_DEVICE
|
||||
# echo my_SPEED $my_SPEED
|
||||
|
||||
# compose a line like that for inittab
|
||||
# S0:12345:respawn:/sbin/agetty -L 9600 ttyS0 vt102
|
||||
|
||||
case $my_REDIRECT in
|
||||
ttyS*)
|
||||
echo adding this line to inittab
|
||||
echo "$my_DEVICE:12345:respawn:/sbin/agetty -L $my_SPEED $my_REDIRECT vt102"
|
||||
echo "$my_DEVICE:12345:respawn:/sbin/agetty -L $my_SPEED $my_REDIRECT vt102" >> /etc/inittab
|
||||
echo $my_REDIRECT >> /etc/securetty
|
||||
;;
|
||||
|
||||
hvc*)
|
||||
echo adding this line to inittab
|
||||
echo "$my_DEVICE:12345:respawn:/sbin/agetty -L $my_SPEED $my_REDIRECT vt320"
|
||||
echo "$my_DEVICE:12345:respawn:/sbin/agetty -L $my_SPEED $my_REDIRECT vt320" >> /etc/inittab
|
||||
echo $my_REDIRECT >> /etc/securetty
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "no modification in inittab needed for: $my_REDIRECT"
|
||||
;;
|
||||
esac
|
||||
|
||||
telinit q
|
||||
|
||||
# Changes for Xen
|
||||
test -f /lib/modules/`uname -r`/modules.dep || depmod -ae
|
||||
|
||||
CMDLINE=`cat /proc/cmdline | grep 'ip='`
|
||||
if test ! -z "$CMDLINE"; then
|
||||
OLDIFS=$IFS
|
||||
IFS=":"
|
||||
read ip oth mask gw hostname dev dhcp rest < /proc/cmdline
|
||||
IFS=$OLDIFS
|
||||
hostname $hostname
|
||||
ip=`echo $ip | sed 's/ip= *//'`
|
||||
if test ! -z "$ip"; then
|
||||
if test -z "$mask"; then
|
||||
if [ ${ip%/*} = $ip ]; then
|
||||
ip="$ip/27"
|
||||
fi
|
||||
echo "ip addr add $ip dev $dev"
|
||||
ip addr add $ip dev $dev
|
||||
ip link set $dev up
|
||||
else
|
||||
ifconfig add $ip netmask $mask $dev
|
||||
fi
|
||||
fi
|
||||
if test "${dhcp#dhcp}" != "$dhcp"; then
|
||||
ifup-dhcp $dev
|
||||
fi
|
||||
fi
|
||||
|
101
boot.xen
Normal file
101
boot.xen
Normal file
@ -0,0 +1,101 @@
|
||||
#! /bin/sh
|
||||
# Copyright (c) 2005-2006 SUSE Linux AG, Nuernberg, Germany.
|
||||
# All rights reserved.
|
||||
#
|
||||
# /etc/init.d/boot.xen
|
||||
#
|
||||
# LSB compatible service control script; see http://www.linuxbase.org/spec/
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: Xen
|
||||
# Required-Start: boot.localfs
|
||||
# Should-Start: boot.localnet
|
||||
# Required-Stop: boot.localfs
|
||||
# Should-Stop:
|
||||
# Default-Start: B
|
||||
# Default-Stop:
|
||||
# Short-Description: Switch on and off TLS depending on whether Xen is running
|
||||
# Description: Xen gets a major performance hit by the way
|
||||
# recent glibc (& gcc) set up the TLS offset, as it needs to
|
||||
# play segmentation tricks. This can be avoided by moving away
|
||||
# the tls libs.
|
||||
### END INIT INFO
|
||||
|
||||
. /etc/rc.status
|
||||
|
||||
# Reset status of this service
|
||||
rc_reset
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting Xen setup "
|
||||
if test -d /proc/xen; then
|
||||
export LD_ASSUME_KERNEL=2.4.21
|
||||
echo -n "Xen running "
|
||||
fi
|
||||
if test -d /proc/xen -a -d /lib/tls; then
|
||||
echo -n "move /lib/tls away "
|
||||
mv /lib/tls /lib/tls.save
|
||||
elif test ! -d /proc/xen -a -d /lib/tls.save; then
|
||||
echo -n "move back /lib/tls "
|
||||
mv /lib/tls.save /lib/tls
|
||||
fi
|
||||
rc_status -v
|
||||
;;
|
||||
stop)
|
||||
# rc_status -v
|
||||
;;
|
||||
try-restart|condrestart)
|
||||
$0 restart
|
||||
# Remember status and be quiet
|
||||
rc_status
|
||||
;;
|
||||
restart)
|
||||
## Stop the service and regardless of whether it was
|
||||
## running or not, start it again.
|
||||
$0 start
|
||||
# Remember status and be quiet
|
||||
rc_status
|
||||
;;
|
||||
force-reload)
|
||||
$0 try-restart
|
||||
rc_status
|
||||
;;
|
||||
reload)
|
||||
rc_failed 3
|
||||
rc_status -v
|
||||
;;
|
||||
status)
|
||||
echo -n "Checking for Xen "
|
||||
# Return value is slightly different for the status command:
|
||||
# 0 - service up and running
|
||||
# 1 - service dead, but /var/run/ pid file exists
|
||||
# 2 - service dead, but /var/lock/ lock file exists
|
||||
# 3 - service not running (unused)
|
||||
# 4 - service status unknown :-(
|
||||
# 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.)
|
||||
|
||||
if test -d /proc/xen; then
|
||||
if test -d /lib/tls; then
|
||||
echo -n "Xen running, /lib/tls existing "
|
||||
rc_failed 1
|
||||
else
|
||||
echo -n "Xen running, /lib/tls not existing "
|
||||
fi
|
||||
else
|
||||
if test -d /lib/tls.save; then
|
||||
echo -n "Xen not running, /lib/tls existing "
|
||||
rc_failed 2
|
||||
else
|
||||
echo -n "Xen not running, /lib/tls not existing "
|
||||
rc_failed 3
|
||||
fi
|
||||
fi
|
||||
rc_status -v
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
rc_exit
|
201
build-python3-conversion.patch
Normal file
201
build-python3-conversion.patch
Normal file
@ -0,0 +1,201 @@
|
||||
Index: xen-4.18.0-testing/Config.mk
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/Config.mk
|
||||
+++ xen-4.18.0-testing/Config.mk
|
||||
@@ -77,7 +77,7 @@ EXTRA_INCLUDES += $(EXTRA_PREFIX)/includ
|
||||
EXTRA_LIB += $(EXTRA_PREFIX)/lib
|
||||
endif
|
||||
|
||||
-PYTHON ?= python
|
||||
+PYTHON ?= python3
|
||||
PYTHON_PREFIX_ARG ?= --prefix="$(prefix)"
|
||||
# The above requires that prefix contains *no spaces*. This variable is here
|
||||
# to permit the user to set PYTHON_PREFIX_ARG to '' to workaround this bug:
|
||||
Index: xen-4.18.0-testing/tools/configure
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/configure
|
||||
+++ xen-4.18.0-testing/tools/configure
|
||||
@@ -7392,15 +7392,15 @@ if test x"${PYTHONPATH}" = x"no"
|
||||
then
|
||||
as_fn_error $? "Unable to find $PYTHON, please install $PYTHON" "$LINENO" 5
|
||||
fi
|
||||
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.6 " >&5
|
||||
-$as_echo_n "checking for python version >= 2.6 ... " >&6; }
|
||||
-`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 6)"))'`
|
||||
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python3 version >= 3.0 " >&5
|
||||
+$as_echo_n "checking for python3 version >= 3.0 ... " >&6; }
|
||||
+`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (3, 0)"))'`
|
||||
if test "$?" != "0"
|
||||
then
|
||||
python_version=`$PYTHON -V 2>&1`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
- as_fn_error $? "$python_version is too old, minimum required version is 2.6" "$LINENO" 5
|
||||
+ as_fn_error $? "$python_version is too old, minimum required version is 3.0" "$LINENO" 5
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
Index: xen-4.18.0-testing/tools/configure.ac
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/configure.ac
|
||||
+++ xen-4.18.0-testing/tools/configure.ac
|
||||
@@ -385,7 +385,7 @@ PYTHONPATH=$PYTHON
|
||||
PYTHON=`basename $PYTHONPATH`
|
||||
|
||||
AX_PATH_PROG_OR_FAIL([PYTHONPATH], [$PYTHON])
|
||||
-AX_CHECK_PYTHON_VERSION([2], [6])
|
||||
+AX_CHECK_PYTHON_VERSION([3], [0])
|
||||
|
||||
AS_IF([test "$cross_compiling" != yes], [
|
||||
AX_CHECK_PYTHON_DEVEL()
|
||||
Index: xen-4.18.0-testing/tools/libs/light/idl.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/idl.py
|
||||
+++ xen-4.18.0-testing/tools/libs/light/idl.py
|
||||
@@ -271,7 +271,7 @@ class KeyedUnion(Aggregate):
|
||||
if not isinstance(keyvar_type, Enumeration):
|
||||
raise ValueError
|
||||
|
||||
- kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in kwargs.items() if x.startswith('keyvar_')])
|
||||
+ kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in list(kwargs.items()) if x.startswith('keyvar_')])
|
||||
|
||||
self.keyvar = Field(keyvar_type, keyvar_name, **kv_kwargs)
|
||||
|
||||
@@ -317,7 +317,7 @@ class Array(Type):
|
||||
kwargs.setdefault('json_parse_type', 'JSON_ARRAY')
|
||||
Type.__init__(self, namespace=elem_type.namespace, typename=elem_type.rawname + " *", **kwargs)
|
||||
|
||||
- lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in kwargs.items() if x.startswith('lenvar_')])
|
||||
+ lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in list(kwargs.items()) if x.startswith('lenvar_')])
|
||||
|
||||
self.lenvar = Field(integer, lenvar_name, **lv_kwargs)
|
||||
self.elem_type = elem_type
|
||||
@@ -353,7 +353,7 @@ def parse(f):
|
||||
globs = {}
|
||||
locs = OrderedDict()
|
||||
|
||||
- for n,t in globals().items():
|
||||
+ for n,t in list(globals().items()):
|
||||
if isinstance(t, Type):
|
||||
globs[n] = t
|
||||
elif isinstance(t,type(object)) and issubclass(t, Type):
|
||||
Index: xen-4.18.0-testing/tools/libs/light/gentest.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/gentest.py
|
||||
+++ xen-4.18.0-testing/tools/libs/light/gentest.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
Index: xen-4.18.0-testing/tools/libs/light/gentypes.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/gentypes.py
|
||||
+++ xen-4.18.0-testing/tools/libs/light/gentypes.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
Index: xen-4.18.0-testing/tools/include/xen-foreign/mkheader.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/include/xen-foreign/mkheader.py
|
||||
+++ xen-4.18.0-testing/tools/include/xen-foreign/mkheader.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
Index: xen-4.18.0-testing/tools/include/xen-foreign/mkchecker.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/include/xen-foreign/mkchecker.py
|
||||
+++ xen-4.18.0-testing/tools/include/xen-foreign/mkchecker.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
import sys;
|
||||
from structs import structs, compat_arches;
|
||||
Index: xen-4.18.0-testing/xen/tools/gen-cpuid.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/xen/tools/gen-cpuid.py
|
||||
+++ xen-4.18.0-testing/xen/tools/gen-cpuid.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys, os, re
|
||||
Index: xen-4.18.0-testing/xen/tools/compat-build-source.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/xen/tools/compat-build-source.py
|
||||
+++ xen-4.18.0-testing/xen/tools/compat-build-source.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
import re,sys
|
||||
|
||||
Index: xen-4.18.0-testing/xen/tools/compat-build-header.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/xen/tools/compat-build-header.py
|
||||
+++ xen-4.18.0-testing/xen/tools/compat-build-header.py
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
import re,sys
|
||||
|
||||
Index: xen-4.18.0-testing/tools/misc/xensymoops
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/misc/xensymoops
|
||||
+++ xen-4.18.0-testing/tools/misc/xensymoops
|
||||
@@ -1,4 +1,4 @@
|
||||
-#!/usr/bin/env python
|
||||
+#!/usr/bin/python3
|
||||
|
||||
# An oops analyser for Xen
|
||||
# Usage: xensymoops path-to-xen.s < oops-message
|
||||
@@ -43,12 +43,12 @@ def read_oops():
|
||||
return (eip_addr, stack_addresses)
|
||||
|
||||
def usage():
|
||||
- print >> sys.stderr, """Usage: %s path-to-asm < oops-msg
|
||||
+ print("""Usage: %s path-to-asm < oops-msg
|
||||
The oops message should be fed to the standard input. The
|
||||
command-line argument specifies the path to the Xen assembly dump
|
||||
produced by \"make debug\". The location of EIP and the backtrace
|
||||
will be output to standard output.
|
||||
- """ % sys.argv[0]
|
||||
+ """ % sys.argv[0], file=sys.stderr)
|
||||
sys.exit()
|
||||
|
||||
##### main
|
||||
@@ -99,7 +99,7 @@ while True:
|
||||
|
||||
# if this address was seen as a potential code address in the backtrace then
|
||||
# record it in the backtrace list
|
||||
- if stk_addrs.has_key(address):
|
||||
+ if address in stk_addrs:
|
||||
backtrace.append((stk_addrs[address], address, func))
|
||||
|
||||
# if this was the address that EIP...
|
||||
@@ -107,12 +107,12 @@ while True:
|
||||
eip_func = func
|
||||
|
||||
|
||||
-print "EIP %s in function %s" % (eip_addr, eip_func)
|
||||
-print "Backtrace:"
|
||||
+print("EIP %s in function %s" % (eip_addr, eip_func))
|
||||
+print("Backtrace:")
|
||||
|
||||
# sorting will order primarily by the first element of each tuple,
|
||||
# i.e. the order in the original oops
|
||||
backtrace.sort()
|
||||
|
||||
for (i, a, f) in backtrace:
|
||||
- print "%s in function %s" % ( a, f )
|
||||
+ print("%s in function %s" % ( a, f ))
|
7
etc_pam.d_xen-api
Normal file
7
etc_pam.d_xen-api
Normal file
@ -0,0 +1,7 @@
|
||||
#%PAM-1.0
|
||||
auth required pam_listfile.so onerr=fail item=user \
|
||||
sense=allow file=/etc/xen/xenapiusers
|
||||
auth include common-auth
|
||||
account include common-account
|
||||
password include common-password
|
||||
session include common-session
|
44
hibernate.patch
Normal file
44
hibernate.patch
Normal file
@ -0,0 +1,44 @@
|
||||
Index: xen-4.18.0-testing/tools/libacpi/ssdt_s3.asl
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libacpi/ssdt_s3.asl
|
||||
+++ xen-4.18.0-testing/tools/libacpi/ssdt_s3.asl
|
||||
@@ -7,13 +7,9 @@
|
||||
|
||||
DefinitionBlock ("SSDT_S3.aml", "SSDT", 2, "Xen", "HVM", 0)
|
||||
{
|
||||
- /* Must match piix emulation */
|
||||
- Name (\_S3, Package (0x04)
|
||||
- {
|
||||
- 0x01, /* PM1a_CNT.SLP_TYP */
|
||||
- 0x01, /* PM1b_CNT.SLP_TYP */
|
||||
- 0x0, /* reserved */
|
||||
- 0x0 /* reserved */
|
||||
- })
|
||||
+ /*
|
||||
+ * Turn off support for s3 sleep state to deal with SVVP tests.
|
||||
+ * This is what MSFT does on HyperV.
|
||||
+ */
|
||||
}
|
||||
|
||||
Index: xen-4.18.0-testing/tools/libacpi/ssdt_s4.asl
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libacpi/ssdt_s4.asl
|
||||
+++ xen-4.18.0-testing/tools/libacpi/ssdt_s4.asl
|
||||
@@ -7,13 +7,9 @@
|
||||
|
||||
DefinitionBlock ("SSDT_S4.aml", "SSDT", 2, "Xen", "HVM", 0)
|
||||
{
|
||||
- /* Must match piix emulation */
|
||||
- Name (\_S4, Package (0x04)
|
||||
- {
|
||||
- 0x00, /* PM1a_CNT.SLP_TYP */
|
||||
- 0x00, /* PM1b_CNT.SLP_TYP */
|
||||
- 0x00, /* reserved */
|
||||
- 0x00 /* reserved */
|
||||
- })
|
||||
+ /*
|
||||
+ * Turn off support for s4 sleep state to deal with SVVP tests.
|
||||
+ * This is what MSFT does on HyperV.
|
||||
+ */
|
||||
}
|
||||
|
54
ignore-ip-command-script-errors.patch
Normal file
54
ignore-ip-command-script-errors.patch
Normal file
@ -0,0 +1,54 @@
|
||||
References: bsc#1172356
|
||||
The bug is that virt-manager reports a failure when in fact
|
||||
the host and guest have added the network interface. The Xen
|
||||
scripts are failing with an error when in fact that command
|
||||
is succeeding.
|
||||
|
||||
The 'ip' commands seem to abort the script due to a 'set -e' in
|
||||
xen-script-common.sh with what appears to be an error condition.
|
||||
However, the command actually succeeds when checked from the
|
||||
host console or also by inserting a sleep before each ip command
|
||||
and executing it manually at the command line. This seems to be
|
||||
an artifact of using 'set -e' everywhere.
|
||||
|
||||
Index: xen-4.15.0-testing/tools/hotplug/Linux/xen-network-common.sh
|
||||
===================================================================
|
||||
--- xen-4.15.0-testing.orig/tools/hotplug/Linux/xen-network-common.sh
|
||||
+++ xen-4.15.0-testing/tools/hotplug/Linux/xen-network-common.sh
|
||||
@@ -90,7 +90,7 @@ _setup_bridge_port() {
|
||||
local virtual="$2"
|
||||
|
||||
# take interface down ...
|
||||
- ip link set dev ${dev} down
|
||||
+ (ip link set dev ${dev} down || true)
|
||||
|
||||
if [ $virtual -ne 0 ] ; then
|
||||
# Initialise a dummy MAC address. We choose the numerically
|
||||
@@ -101,7 +101,7 @@ _setup_bridge_port() {
|
||||
fi
|
||||
|
||||
# ... and configure it
|
||||
- ip address flush dev ${dev}
|
||||
+ (ip address flush dev ${dev} || true)
|
||||
}
|
||||
|
||||
setup_physical_bridge_port() {
|
||||
@@ -136,15 +136,15 @@ add_to_bridge () {
|
||||
if [ ! -e "/sys/class/net/${bridge}/brif/${dev}" ]; then
|
||||
log debug "adding $dev to bridge $bridge"
|
||||
if which brctl >&/dev/null; then
|
||||
- brctl addif ${bridge} ${dev}
|
||||
+ (brctl addif ${bridge} ${dev} || true)
|
||||
else
|
||||
- ip link set ${dev} master ${bridge}
|
||||
+ (ip link set ${dev} master ${bridge} || true)
|
||||
fi
|
||||
else
|
||||
log debug "$dev already on bridge $bridge"
|
||||
fi
|
||||
|
||||
- ip link set dev ${dev} up
|
||||
+ (ip link set dev ${dev} up || true)
|
||||
}
|
||||
|
||||
remove_from_bridge () {
|
119
init.pciback
Normal file
119
init.pciback
Normal file
@ -0,0 +1,119 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2014 SUSE GmbH Nuernberg, Germany. All rights reserved.
|
||||
#
|
||||
# /etc/init.d/pciback
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: pciback
|
||||
# Required-Start: $syslog $network
|
||||
# Should-Start: $null
|
||||
# Required-Stop: $syslog $network
|
||||
# Should-Stop: $null
|
||||
# Default-Start: 3 5
|
||||
# Default-Stop: 0 1 2 6
|
||||
# Description: bind PCI devices to pciback
|
||||
### END INIT INFO
|
||||
|
||||
. /etc/rc.status
|
||||
. /etc/sysconfig/pciback
|
||||
|
||||
rc_reset
|
||||
|
||||
load_pciback() {
|
||||
if ! lsmod | grep -qi "pciback"
|
||||
then
|
||||
echo "Loading pciback ..."
|
||||
modprobe pciback
|
||||
fi
|
||||
}
|
||||
|
||||
unload_pciback() {
|
||||
if lsmod | grep -qi "pciback"
|
||||
then
|
||||
echo "Unloading pciback ..."
|
||||
modprobe -r pciback
|
||||
fi
|
||||
}
|
||||
|
||||
bind_dev_to_pciback() {
|
||||
for DEVICE in ${XEN_PCI_HIDE_LIST}
|
||||
do
|
||||
local DRV=`echo ${DEVICE} | /usr/bin/cut -d "," -f 1`
|
||||
local PCIID=`echo ${DEVICE} | /usr/bin/cut -d "," -f 2`
|
||||
|
||||
if ! ls /sys/bus/pci/drivers/pciback/${PCIID} > /dev/null 2>&1
|
||||
then
|
||||
echo "Binding ${PCIID} ..."
|
||||
if ls /sys/bus/pci/drivers/${DRV}/${PCIID} > /dev/null 2>&1
|
||||
then
|
||||
echo -n ${PCIID} > /sys/bus/pci/drivers/${DRV}/unbind
|
||||
fi
|
||||
echo -n ${PCIID} > /sys/bus/pci/drivers/pciback/new_slot
|
||||
echo -n ${PCIID} > /sys/bus/pci/drivers/pciback/bind
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
unbind_dev_from_pciback() {
|
||||
for DEVICE in ${XEN_PCI_HIDE_LIST}
|
||||
do
|
||||
local DRV=`echo ${DEVICE} | /usr/bin/cut -d "," -f 1`
|
||||
local PCIID=`echo ${DEVICE} | /usr/bin/cut -d "," -f 2`
|
||||
|
||||
if ls /sys/bus/pci/drivers/pciback/${PCIID} > /dev/null
|
||||
then
|
||||
echo "Unbinding ${PCIID} ..."
|
||||
echo -n ${PCIID} > /sys/bus/pci/drivers/pciback/unbind
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
test "uname -r" | grep xen && exit 0
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
echo "Starting pciback ..."
|
||||
echo
|
||||
load_pciback
|
||||
bind_dev_to_pciback
|
||||
|
||||
rc_status -v -r
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping pciback ..."
|
||||
echo
|
||||
unbind_dev_from_pciback
|
||||
unload_pciback
|
||||
|
||||
rc_status -v
|
||||
;;
|
||||
reload|restart)
|
||||
echo "Stopping pciback ..."
|
||||
echo
|
||||
unbind_dev_from_pciback
|
||||
unload_pciback
|
||||
echo "Starting pciback ..."
|
||||
echo
|
||||
load_pciback
|
||||
bind_dev_to_pciback
|
||||
;;
|
||||
status)
|
||||
if lsmod | grep -qi pciback
|
||||
then
|
||||
echo
|
||||
echo "pciback: loaded"
|
||||
echo
|
||||
echo "Currently bound devices ..."
|
||||
echo "-----------------------------"
|
||||
ls /sys/bus/pci/drivers/pciback | grep ^0000
|
||||
echo
|
||||
else
|
||||
echo "pciback: not loaded"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [start|stop|restart|reload|status]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
64
libxc-bitmap-long.patch
Normal file
64
libxc-bitmap-long.patch
Normal file
@ -0,0 +1,64 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Wed, 9 Dec 2020 16:40:00 +0100
|
||||
Subject: libxc sr bitmap long
|
||||
|
||||
tools: add API to work with sevaral bits at once
|
||||
|
||||
Introduce new API to test if a fixed number of bits is clear or set,
|
||||
and clear or set them all at once.
|
||||
|
||||
The caller has to make sure the input bitnumber is a multiple of BITS_PER_LONG.
|
||||
|
||||
This API avoids the loop over each bit in a known range just to see
|
||||
if all of them are either clear or set.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
|
||||
v02:
|
||||
- change return type from int to bool (jgross)
|
||||
---
|
||||
tools/libs/ctrl/xc_bitops.h | 28 ++++++++++++++++++++++++++++
|
||||
1 file changed, 28 insertions(+)
|
||||
|
||||
--- a/tools/libs/ctrl/xc_bitops.h
|
||||
+++ b/tools/libs/ctrl/xc_bitops.h
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
/* bitmap operations for single threaded access */
|
||||
|
||||
+#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -81,4 +82,31 @@ static inline void bitmap_or(void *_dst,
|
||||
dst[i] |= other[i];
|
||||
}
|
||||
|
||||
+static inline bool test_bit_long_set(unsigned long nr_base, const void *_addr)
|
||||
+{
|
||||
+ const unsigned long *addr = _addr;
|
||||
+ unsigned long val = addr[nr_base / BITS_PER_LONG];
|
||||
+
|
||||
+ return val == ~0;
|
||||
+}
|
||||
+
|
||||
+static inline bool test_bit_long_clear(unsigned long nr_base, const void *_addr)
|
||||
+{
|
||||
+ const unsigned long *addr = _addr;
|
||||
+ unsigned long val = addr[nr_base / BITS_PER_LONG];
|
||||
+
|
||||
+ return val == 0;
|
||||
+}
|
||||
+
|
||||
+static inline void clear_bit_long(unsigned long nr_base, void *_addr)
|
||||
+{
|
||||
+ unsigned long *addr = _addr;
|
||||
+ addr[nr_base / BITS_PER_LONG] = 0;
|
||||
+}
|
||||
+
|
||||
+static inline void set_bit_long(unsigned long nr_base, void *_addr)
|
||||
+{
|
||||
+ unsigned long *addr = _addr;
|
||||
+ addr[nr_base / BITS_PER_LONG] = ~0;
|
||||
+}
|
||||
#endif /* XC_BITOPS_H */
|
144
libxc-sr-LIBXL_HAVE_DOMAIN_SUSPEND_PROPS.patch
Normal file
144
libxc-sr-LIBXL_HAVE_DOMAIN_SUSPEND_PROPS.patch
Normal file
@ -0,0 +1,144 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Thu, 7 Jan 2021 15:58:30 +0100
|
||||
Subject: libxc sr LIBXL_HAVE_DOMAIN_SUSPEND_PROPS
|
||||
|
||||
tools: adjust libxl_domain_suspend to receive a struct props
|
||||
|
||||
Upcoming changes will pass more knobs down to xc_domain_save.
|
||||
Adjust the libxl_domain_suspend API to allow easy adding of additional knobs.
|
||||
|
||||
No change in behavior intented.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
Acked-by: Christian Lindig <christian.lindig@citrix.com>
|
||||
---
|
||||
tools/include/libxl.h | 26 +++++++++++++++++++++++---
|
||||
tools/libs/light/libxl_domain.c | 7 ++++---
|
||||
tools/xl/xl_migrate.c | 9 ++++++---
|
||||
tools/xl/xl_saverestore.c | 3 ++-
|
||||
4 files changed, 35 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/tools/include/libxl.h
|
||||
+++ b/tools/include/libxl.h
|
||||
@@ -1811,13 +1811,28 @@ static inline int libxl_retrieve_domain_
|
||||
libxl_retrieve_domain_configuration_0x041200
|
||||
#endif
|
||||
|
||||
-int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd,
|
||||
- int flags, /* LIBXL_SUSPEND_* */
|
||||
- const libxl_asyncop_how *ao_how)
|
||||
- LIBXL_EXTERNAL_CALLERS_ONLY;
|
||||
+/*
|
||||
+ * LIBXL_HAVE_DOMAIN_SUSPEND_PROPS indicates that the
|
||||
+ * libxl_domain_suspend_props() function takes a props struct.
|
||||
+ */
|
||||
+#define LIBXL_HAVE_DOMAIN_SUSPEND_PROPS 1
|
||||
+
|
||||
+typedef struct {
|
||||
+ uint32_t flags; /* LIBXL_SUSPEND_* */
|
||||
+} libxl_domain_suspend_suse_properties;
|
||||
#define LIBXL_SUSPEND_DEBUG 1
|
||||
#define LIBXL_SUSPEND_LIVE 2
|
||||
|
||||
+#define LIBXL_HAVE_DOMAIN_SUSPEND_SUSE
|
||||
+int libxl_domain_suspend_suse(libxl_ctx *ctx, uint32_t domid, int fd,
|
||||
+ const libxl_domain_suspend_suse_properties *props, /* optional */
|
||||
+ const libxl_asyncop_how *ao_how)
|
||||
+ LIBXL_EXTERNAL_CALLERS_ONLY;
|
||||
+
|
||||
+int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
|
||||
+ const libxl_asyncop_how *ao_how)
|
||||
+ LIBXL_EXTERNAL_CALLERS_ONLY;
|
||||
+
|
||||
/*
|
||||
* Only suspend domain, do not save its state to file, do not destroy it.
|
||||
* Suspended domain can be resumed with libxl_domain_resume()
|
||||
--- a/tools/libs/light/libxl_domain.c
|
||||
+++ b/tools/libs/light/libxl_domain.c
|
||||
@@ -502,7 +502,8 @@ static void domain_suspend_cb(libxl__egc
|
||||
|
||||
}
|
||||
|
||||
-int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
|
||||
+static int do_libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd,
|
||||
+ const libxl_domain_suspend_suse_properties *props,
|
||||
const libxl_asyncop_how *ao_how)
|
||||
{
|
||||
AO_CREATE(ctx, domid, ao_how);
|
||||
@@ -523,8 +524,8 @@ int libxl_domain_suspend(libxl_ctx *ctx,
|
||||
dss->domid = domid;
|
||||
dss->fd = fd;
|
||||
dss->type = type;
|
||||
- dss->live = flags & LIBXL_SUSPEND_LIVE;
|
||||
- dss->debug = flags & LIBXL_SUSPEND_DEBUG;
|
||||
+ dss->live = props->flags & LIBXL_SUSPEND_LIVE;
|
||||
+ dss->debug = props->flags & LIBXL_SUSPEND_DEBUG;
|
||||
dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
|
||||
|
||||
rc = libxl__fd_flags_modify_save(gc, dss->fd,
|
||||
@@ -539,6 +540,21 @@ int libxl_domain_suspend(libxl_ctx *ctx,
|
||||
return AO_CREATE_FAIL(rc);
|
||||
}
|
||||
|
||||
+int libxl_domain_suspend_suse(libxl_ctx *ctx, uint32_t domid, int fd,
|
||||
+ const libxl_domain_suspend_suse_properties *props,
|
||||
+ const libxl_asyncop_how *ao_how)
|
||||
+{
|
||||
+ return do_libxl_domain_suspend(ctx, domid, fd, props, ao_how);
|
||||
+}
|
||||
+
|
||||
+int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
|
||||
+ const libxl_asyncop_how *ao_how)
|
||||
+{
|
||||
+ libxl_domain_suspend_suse_properties props = { .flags = flags };
|
||||
+
|
||||
+ return do_libxl_domain_suspend(ctx, domid, fd, &props, ao_how);
|
||||
+}
|
||||
+
|
||||
static void domain_suspend_empty_cb(libxl__egc *egc,
|
||||
libxl__domain_suspend_state *dss, int rc)
|
||||
{
|
||||
--- a/tools/xl/xl_migrate.c
|
||||
+++ b/tools/xl/xl_migrate.c
|
||||
@@ -186,7 +186,10 @@ static void migrate_domain(uint32_t domi
|
||||
char *away_domname;
|
||||
char rc_buf;
|
||||
uint8_t *config_data;
|
||||
- int config_len, flags = LIBXL_SUSPEND_LIVE;
|
||||
+ int config_len;
|
||||
+ libxl_domain_suspend_suse_properties props = {
|
||||
+ .flags = LIBXL_SUSPEND_LIVE,
|
||||
+ };
|
||||
|
||||
save_domain_core_begin(domid, preserve_domid, override_config_file,
|
||||
&config_data, &config_len);
|
||||
@@ -205,8 +208,8 @@ static void migrate_domain(uint32_t domi
|
||||
xtl_stdiostream_adjust_flags(logger, XTL_STDIOSTREAM_HIDE_PROGRESS, 0);
|
||||
|
||||
if (debug)
|
||||
- flags |= LIBXL_SUSPEND_DEBUG;
|
||||
- rc = libxl_domain_suspend(ctx, domid, send_fd, flags, NULL);
|
||||
+ props.flags |= LIBXL_SUSPEND_DEBUG;
|
||||
+ rc = libxl_domain_suspend_suse(ctx, domid, send_fd, &props, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "migration sender: libxl_domain_suspend failed"
|
||||
" (rc=%d)\n", rc);
|
||||
--- a/tools/xl/xl_saverestore.c
|
||||
+++ b/tools/xl/xl_saverestore.c
|
||||
@@ -130,6 +130,7 @@ static int save_domain(uint32_t domid, i
|
||||
int fd;
|
||||
uint8_t *config_data;
|
||||
int config_len;
|
||||
+ libxl_domain_suspend_suse_properties props = {};
|
||||
|
||||
save_domain_core_begin(domid, preserve_domid, override_config_file,
|
||||
&config_data, &config_len);
|
||||
@@ -146,7 +147,7 @@ static int save_domain(uint32_t domid, i
|
||||
|
||||
save_domain_core_writeconfig(fd, filename, config_data, config_len);
|
||||
|
||||
- int rc = libxl_domain_suspend(ctx, domid, fd, 0, NULL);
|
||||
+ int rc = libxl_domain_suspend_suse(ctx, domid, fd, &props, NULL);
|
||||
close(fd);
|
||||
|
||||
if (rc < 0) {
|
238
libxc-sr-abort_if_busy.patch
Normal file
238
libxc-sr-abort_if_busy.patch
Normal file
@ -0,0 +1,238 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Thu, 7 Jan 2021 20:25:28 +0100
|
||||
Subject: libxc sr abort_if_busy
|
||||
|
||||
tools: add --abort_if_busy to libxl_domain_suspend
|
||||
|
||||
Provide a knob to the host admin to abort the live migration of a
|
||||
running domU if the downtime during final transit will be too long
|
||||
for the workload within domU.
|
||||
|
||||
Adjust error reporting. Add ERROR_MIGRATION_ABORTED to allow callers of
|
||||
libxl_domain_suspend to distinguish between errors and the requested
|
||||
constraint.
|
||||
|
||||
Adjust precopy_policy to simplify reporting of remaining dirty pages.
|
||||
The loop in send_memory_live populates ->dirty_count in a different
|
||||
place than ->iteration. Let it proceeed one more time to provide the
|
||||
desired information before leaving the loop.
|
||||
|
||||
This patch adjusts xl(1) and the libxl API.
|
||||
External users check LIBXL_HAVE_DOMAIN_SUSPEND_PROPS for the availibility
|
||||
of the new .abort_if_busy property.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
docs/man/xl.1.pod.in | 8 +++++++
|
||||
tools/include/libxl.h | 1 +
|
||||
tools/libs/light/libxl_dom_save.c | 7 ++++++-
|
||||
tools/libs/light/libxl_domain.c | 1 +
|
||||
tools/libs/light/libxl_internal.h | 2 ++
|
||||
tools/libs/light/libxl_stream_write.c | 9 +++++++-
|
||||
tools/libs/light/libxl_types.idl | 1 +
|
||||
tools/xl/xl_cmdtable.c | 6 +++++-
|
||||
tools/xl/xl_migrate.c | 30 ++++++++++++++++++++-------
|
||||
9 files changed, 55 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/docs/man/xl.1.pod.in
|
||||
+++ b/docs/man/xl.1.pod.in
|
||||
@@ -513,6 +513,14 @@ low, the guest is suspended and the domU
|
||||
This allows the host admin to control for how long the domU will likely
|
||||
be suspended during transit.
|
||||
|
||||
+=item B<--abort_if_busy>
|
||||
+
|
||||
+Abort migration instead of doing final suspend/move/resume if the
|
||||
+guest produced more than I<min_remaining> dirty pages during th number
|
||||
+of I<max_iters> iterations.
|
||||
+This avoids long periods of time where the guest is suspended, which
|
||||
+may confuse the workload within domU.
|
||||
+
|
||||
=back
|
||||
|
||||
=item B<remus> [I<OPTIONS>] I<domain-id> I<host>
|
||||
--- a/tools/include/libxl.h
|
||||
+++ b/tools/include/libxl.h
|
||||
@@ -1824,6 +1824,7 @@ typedef struct {
|
||||
} libxl_domain_suspend_suse_properties;
|
||||
#define LIBXL_SUSPEND_DEBUG 1
|
||||
#define LIBXL_SUSPEND_LIVE 2
|
||||
+#define LIBXL_SUSPEND_ABORT_IF_BUSY 4
|
||||
|
||||
#define LIBXL_HAVE_DOMAIN_SUSPEND_SUSE
|
||||
int libxl_domain_suspend_suse(libxl_ctx *ctx, uint32_t domid, int fd,
|
||||
--- a/tools/libs/light/libxl_dom_save.c
|
||||
+++ b/tools/libs/light/libxl_dom_save.c
|
||||
@@ -383,11 +383,16 @@ static int libxl__domain_save_precopy_po
|
||||
stats.iteration, stats.dirty_count, stats.total_written);
|
||||
if (stats.dirty_count >= 0 && stats.dirty_count < dss->min_remaining)
|
||||
goto stop_copy;
|
||||
- if (stats.iteration >= dss->max_iters)
|
||||
+ if (stats.dirty_count >= 0 && stats.iteration >= dss->max_iters)
|
||||
goto stop_copy;
|
||||
return XGS_POLICY_CONTINUE_PRECOPY;
|
||||
|
||||
stop_copy:
|
||||
+ if (dss->abort_if_busy)
|
||||
+ {
|
||||
+ dss->remaining_dirty_pages = stats.dirty_count;
|
||||
+ return XGS_POLICY_ABORT;
|
||||
+ }
|
||||
return XGS_POLICY_STOP_AND_COPY;
|
||||
}
|
||||
|
||||
--- a/tools/libs/light/libxl_domain.c
|
||||
+++ b/tools/libs/light/libxl_domain.c
|
||||
@@ -526,6 +526,7 @@ static int do_libxl_domain_suspend(libxl
|
||||
dss->type = type;
|
||||
dss->max_iters = props->max_iters ?: LIBXL_XGS_POLICY_MAX_ITERATIONS;
|
||||
dss->min_remaining = props->min_remaining ?: LIBXL_XGS_POLICY_TARGET_DIRTY_COUNT;
|
||||
+ dss->abort_if_busy = props->flags & LIBXL_SUSPEND_ABORT_IF_BUSY;
|
||||
dss->live = props->flags & LIBXL_SUSPEND_LIVE;
|
||||
dss->debug = props->flags & LIBXL_SUSPEND_DEBUG;
|
||||
dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
|
||||
--- a/tools/libs/light/libxl_internal.h
|
||||
+++ b/tools/libs/light/libxl_internal.h
|
||||
@@ -3655,9 +3655,11 @@ struct libxl__domain_save_state {
|
||||
libxl_domain_type type;
|
||||
int live;
|
||||
int debug;
|
||||
+ int abort_if_busy;
|
||||
int checkpointed_stream;
|
||||
uint32_t max_iters;
|
||||
uint32_t min_remaining;
|
||||
+ long remaining_dirty_pages;
|
||||
const libxl_domain_remus_info *remus;
|
||||
/* private */
|
||||
int rc;
|
||||
--- a/tools/libs/light/libxl_stream_write.c
|
||||
+++ b/tools/libs/light/libxl_stream_write.c
|
||||
@@ -344,11 +344,18 @@ void libxl__xc_domain_save_done(libxl__e
|
||||
goto err;
|
||||
|
||||
if (retval) {
|
||||
+ if (dss->remaining_dirty_pages) {
|
||||
+ LOGD(NOTICE, dss->domid, "saving domain: aborted,"
|
||||
+ " %ld remaining dirty pages.", dss->remaining_dirty_pages);
|
||||
+ } else {
|
||||
LOGEVD(ERROR, errnoval, dss->domid, "saving domain: %s",
|
||||
dss->dsps.guest_responded ?
|
||||
"domain responded to suspend request" :
|
||||
"domain did not respond to suspend request");
|
||||
- if (!dss->dsps.guest_responded)
|
||||
+ }
|
||||
+ if (dss->remaining_dirty_pages)
|
||||
+ rc = ERROR_MIGRATION_ABORTED;
|
||||
+ else if(!dss->dsps.guest_responded)
|
||||
rc = ERROR_GUEST_TIMEDOUT;
|
||||
else if (dss->rc)
|
||||
rc = dss->rc;
|
||||
--- a/tools/libs/light/libxl_types.idl
|
||||
+++ b/tools/libs/light/libxl_types.idl
|
||||
@@ -76,6 +76,7 @@ libxl_error = Enumeration("error", [
|
||||
(-30, "QMP_DEVICE_NOT_ACTIVE"), # a device has failed to be become active
|
||||
(-31, "QMP_DEVICE_NOT_FOUND"), # the requested device has not been found
|
||||
(-32, "QEMU_API"), # QEMU's replies don't contains expected members
|
||||
+ (-33, "MIGRATION_ABORTED"),
|
||||
], value_namespace = "")
|
||||
|
||||
libxl_domain_type = Enumeration("domain_type", [
|
||||
--- a/tools/xl/xl_cmdtable.c
|
||||
+++ b/tools/xl/xl_cmdtable.c
|
||||
@@ -177,7 +177,11 @@ const struct cmd_spec cmd_table[] = {
|
||||
"-p Do not unpause domain after migrating it.\n"
|
||||
"-D Preserve the domain id\n"
|
||||
"--max_iters N Number of copy iterations before final stop+move\n"
|
||||
- "--min_remaining N Number of remaining dirty pages before final stop+move"
|
||||
+ "--min_remaining N Number of remaining dirty pages before final stop+move\n"
|
||||
+ "--abort_if_busy Abort migration instead of doing final stop+move,\n"
|
||||
+ " if the number of dirty pages is higher than <min_remaining>\n"
|
||||
+ " after <max_iters> iterations. Otherwise the amount of memory\n"
|
||||
+ " to be transfered would exceed maximum allowed domU downtime."
|
||||
},
|
||||
{ "restore",
|
||||
&main_restore, 0, 1,
|
||||
--- a/tools/xl/xl_migrate.c
|
||||
+++ b/tools/xl/xl_migrate.c
|
||||
@@ -177,7 +177,7 @@ static void migrate_do_preamble(int send
|
||||
}
|
||||
|
||||
static void migrate_domain(uint32_t domid, int preserve_domid,
|
||||
- const char *rune, int debug,
|
||||
+ const char *rune, int debug, int abort_if_busy,
|
||||
uint32_t max_iters,
|
||||
uint32_t min_remaining,
|
||||
const char *override_config_file)
|
||||
@@ -213,14 +213,20 @@ static void migrate_domain(uint32_t domi
|
||||
|
||||
if (debug)
|
||||
props.flags |= LIBXL_SUSPEND_DEBUG;
|
||||
+ if (abort_if_busy)
|
||||
+ props.flags |= LIBXL_SUSPEND_ABORT_IF_BUSY;
|
||||
rc = libxl_domain_suspend_suse(ctx, domid, send_fd, &props, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "migration sender: libxl_domain_suspend failed"
|
||||
" (rc=%d)\n", rc);
|
||||
- if (rc == ERROR_GUEST_TIMEDOUT)
|
||||
- goto failed_suspend;
|
||||
- else
|
||||
- goto failed_resume;
|
||||
+ switch (rc) {
|
||||
+ case ERROR_GUEST_TIMEDOUT:
|
||||
+ goto failed_suspend;
|
||||
+ case ERROR_MIGRATION_ABORTED:
|
||||
+ goto failed_busy;
|
||||
+ default:
|
||||
+ goto failed_resume;
|
||||
+ }
|
||||
}
|
||||
|
||||
//fprintf(stderr, "migration sender: Transfer complete.\n");
|
||||
@@ -302,6 +308,12 @@ static void migrate_domain(uint32_t domi
|
||||
fprintf(stderr, "Migration failed, failed to suspend at sender.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
+ failed_busy:
|
||||
+ close(send_fd);
|
||||
+ migration_child_report(recv_fd);
|
||||
+ fprintf(stderr, "Migration aborted as requested, domain is too busy.\n");
|
||||
+ exit(EXIT_FAILURE);
|
||||
+
|
||||
failed_resume:
|
||||
close(send_fd);
|
||||
migration_child_report(recv_fd);
|
||||
@@ -545,13 +557,14 @@ int main_migrate(int argc, char **argv)
|
||||
char *rune = NULL;
|
||||
char *host;
|
||||
int opt, daemonize = 1, monitor = 1, debug = 0, pause_after_migration = 0;
|
||||
- int preserve_domid = 0;
|
||||
+ int preserve_domid = 0, abort_if_busy = 0;
|
||||
uint32_t max_iters = 0;
|
||||
uint32_t min_remaining = 0;
|
||||
static struct option opts[] = {
|
||||
{"debug", 0, 0, 0x100},
|
||||
{"max_iters", 1, 0, 0x101},
|
||||
{"min_remaining", 1, 0, 0x102},
|
||||
+ {"abort_if_busy", 0, 0, 0x103},
|
||||
{"live", 0, 0, 0x200},
|
||||
COMMON_LONG_OPTS
|
||||
};
|
||||
@@ -585,6 +598,9 @@ int main_migrate(int argc, char **argv)
|
||||
case 0x102: /* --min_remaining */
|
||||
min_remaining = atoi(optarg);
|
||||
break;
|
||||
+ case 0x103: /* --abort_if_busy */
|
||||
+ abort_if_busy = 1;
|
||||
+ break;
|
||||
case 0x200: /* --live */
|
||||
/* ignored for compatibility with xm */
|
||||
break;
|
||||
@@ -619,7 +635,7 @@ int main_migrate(int argc, char **argv)
|
||||
pause_after_migration ? " -p" : "");
|
||||
}
|
||||
|
||||
- migrate_domain(domid, preserve_domid, rune, debug,
|
||||
+ migrate_domain(domid, preserve_domid, rune, debug, abort_if_busy,
|
||||
max_iters, min_remaining, config_filename);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
148
libxc-sr-max_iters.patch
Normal file
148
libxc-sr-max_iters.patch
Normal file
@ -0,0 +1,148 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Sat, 9 Jan 2021 11:32:17 +0100
|
||||
Subject: libxc sr max_iters
|
||||
|
||||
tools: add --max_iters to libxl_domain_suspend
|
||||
|
||||
Migrating a large, and potentially busy, domU will take more
|
||||
time than neccessary due to excessive number of copying iterations.
|
||||
|
||||
Allow to host admin to control the number of iterations which
|
||||
copy cumulated domU dirty pages to the target host.
|
||||
|
||||
The default remains 5, which means one initial iteration to copy the
|
||||
entire domU memory, and up to 4 additional iterations to copy dirty
|
||||
memory from the still running domU. After the given number of iterations
|
||||
the domU is suspended, remaining dirty memory is copied and the domU is
|
||||
finally moved to the target host.
|
||||
|
||||
This patch adjusts xl(1) and the libxl API.
|
||||
External users check LIBXL_HAVE_DOMAIN_SUSPEND_PROPS for the availibility
|
||||
of the new .max_iters property.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
docs/man/xl.1.pod.in | 4 ++++
|
||||
tools/include/libxl.h | 1 +
|
||||
tools/libs/light/libxl_dom_save.c | 2 +-
|
||||
tools/libs/light/libxl_domain.c | 1 +
|
||||
tools/libs/light/libxl_internal.h | 1 +
|
||||
tools/xl/xl_cmdtable.c | 3 ++-
|
||||
tools/xl/xl_migrate.c | 10 +++++++++-
|
||||
7 files changed, 19 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/docs/man/xl.1.pod.in
|
||||
+++ b/docs/man/xl.1.pod.in
|
||||
@@ -501,6 +501,10 @@ such that it will be identical on the de
|
||||
configuration is overridden using the B<-C> option. Note that it is not
|
||||
possible to use this option for a 'localhost' migration.
|
||||
|
||||
+=item B<--max_iters> I<iterations>
|
||||
+
|
||||
+Number of copy iterations before final suspend+move (default: 5)
|
||||
+
|
||||
=back
|
||||
|
||||
=item B<remus> [I<OPTIONS>] I<domain-id> I<host>
|
||||
--- a/tools/include/libxl.h
|
||||
+++ b/tools/include/libxl.h
|
||||
@@ -1819,6 +1819,7 @@ static inline int libxl_retrieve_domain_
|
||||
|
||||
typedef struct {
|
||||
uint32_t flags; /* LIBXL_SUSPEND_* */
|
||||
+ uint32_t max_iters;
|
||||
} libxl_domain_suspend_suse_properties;
|
||||
#define LIBXL_SUSPEND_DEBUG 1
|
||||
#define LIBXL_SUSPEND_LIVE 2
|
||||
--- a/tools/libs/light/libxl_dom_save.c
|
||||
+++ b/tools/libs/light/libxl_dom_save.c
|
||||
@@ -383,7 +383,7 @@ static int libxl__domain_save_precopy_po
|
||||
stats.iteration, stats.dirty_count, stats.total_written);
|
||||
if (stats.dirty_count >= 0 && stats.dirty_count < LIBXL_XGS_POLICY_TARGET_DIRTY_COUNT)
|
||||
goto stop_copy;
|
||||
- if (stats.iteration >= LIBXL_XGS_POLICY_MAX_ITERATIONS)
|
||||
+ if (stats.iteration >= dss->max_iters)
|
||||
goto stop_copy;
|
||||
return XGS_POLICY_CONTINUE_PRECOPY;
|
||||
|
||||
--- a/tools/libs/light/libxl_domain.c
|
||||
+++ b/tools/libs/light/libxl_domain.c
|
||||
@@ -524,6 +524,7 @@ static int do_libxl_domain_suspend(libxl
|
||||
dss->domid = domid;
|
||||
dss->fd = fd;
|
||||
dss->type = type;
|
||||
+ dss->max_iters = props->max_iters ?: LIBXL_XGS_POLICY_MAX_ITERATIONS;
|
||||
dss->live = props->flags & LIBXL_SUSPEND_LIVE;
|
||||
dss->debug = props->flags & LIBXL_SUSPEND_DEBUG;
|
||||
dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
|
||||
--- a/tools/libs/light/libxl_internal.h
|
||||
+++ b/tools/libs/light/libxl_internal.h
|
||||
@@ -3656,6 +3656,7 @@ struct libxl__domain_save_state {
|
||||
int live;
|
||||
int debug;
|
||||
int checkpointed_stream;
|
||||
+ uint32_t max_iters;
|
||||
const libxl_domain_remus_info *remus;
|
||||
/* private */
|
||||
int rc;
|
||||
--- a/tools/xl/xl_cmdtable.c
|
||||
+++ b/tools/xl/xl_cmdtable.c
|
||||
@@ -175,7 +175,8 @@ const struct cmd_spec cmd_table[] = {
|
||||
" of the domain.\n"
|
||||
"--debug Enable verification mode.\n"
|
||||
"-p Do not unpause domain after migrating it.\n"
|
||||
- "-D Preserve the domain id"
|
||||
+ "-D Preserve the domain id\n"
|
||||
+ "--max_iters N Number of copy iterations before final stop+move"
|
||||
},
|
||||
{ "restore",
|
||||
&main_restore, 0, 1,
|
||||
--- a/tools/xl/xl_migrate.c
|
||||
+++ b/tools/xl/xl_migrate.c
|
||||
@@ -178,6 +178,7 @@ static void migrate_do_preamble(int send
|
||||
|
||||
static void migrate_domain(uint32_t domid, int preserve_domid,
|
||||
const char *rune, int debug,
|
||||
+ uint32_t max_iters,
|
||||
const char *override_config_file)
|
||||
{
|
||||
pid_t child = -1;
|
||||
@@ -189,6 +190,7 @@ static void migrate_domain(uint32_t domi
|
||||
int config_len;
|
||||
libxl_domain_suspend_suse_properties props = {
|
||||
.flags = LIBXL_SUSPEND_LIVE,
|
||||
+ .max_iters = max_iters,
|
||||
};
|
||||
|
||||
save_domain_core_begin(domid, preserve_domid, override_config_file,
|
||||
@@ -542,8 +544,10 @@ int main_migrate(int argc, char **argv)
|
||||
char *host;
|
||||
int opt, daemonize = 1, monitor = 1, debug = 0, pause_after_migration = 0;
|
||||
int preserve_domid = 0;
|
||||
+ uint32_t max_iters = 0;
|
||||
static struct option opts[] = {
|
||||
{"debug", 0, 0, 0x100},
|
||||
+ {"max_iters", 1, 0, 0x101},
|
||||
{"live", 0, 0, 0x200},
|
||||
COMMON_LONG_OPTS
|
||||
};
|
||||
@@ -571,6 +575,9 @@ int main_migrate(int argc, char **argv)
|
||||
case 0x100: /* --debug */
|
||||
debug = 1;
|
||||
break;
|
||||
+ case 0x101: /* --max_iters */
|
||||
+ max_iters = atoi(optarg);
|
||||
+ break;
|
||||
case 0x200: /* --live */
|
||||
/* ignored for compatibility with xm */
|
||||
break;
|
||||
@@ -605,7 +612,8 @@ int main_migrate(int argc, char **argv)
|
||||
pause_after_migration ? " -p" : "");
|
||||
}
|
||||
|
||||
- migrate_domain(domid, preserve_domid, rune, debug, config_filename);
|
||||
+ migrate_domain(domid, preserve_domid, rune, debug,
|
||||
+ max_iters, config_filename);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
173
libxc-sr-min_remaining.patch
Normal file
173
libxc-sr-min_remaining.patch
Normal file
@ -0,0 +1,173 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Thu, 7 Jan 2021 19:39:28 +0100
|
||||
Subject: libxc sr min_remaining
|
||||
|
||||
tools: add --min_remaining to libxl_domain_suspend
|
||||
|
||||
The decision to stop+move a domU to the new host must be based on two factors:
|
||||
- the available network bandwidth for the migration stream
|
||||
- the maximum time a workload within a domU can be savely suspended
|
||||
|
||||
Both values define how many dirty pages a workload may produce prior the
|
||||
final stop+move.
|
||||
|
||||
The default value of 50 pages is much too low with todays network bandwidths.
|
||||
On an idle 1GiB link these 200K will be transferred within ~2ms.
|
||||
|
||||
Give the admin a knob to adjust the point when the final stop+move will
|
||||
be done, so he can base this decision on his own needs.
|
||||
|
||||
This patch adjusts xl(1) and the libxl API.
|
||||
External users check LIBXL_HAVE_DOMAIN_SUSPEND_PROPS for the availibility
|
||||
of the new .min_remaining property.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
docs/man/xl.1.pod.in | 8 ++++++++
|
||||
tools/include/libxl.h | 1 +
|
||||
tools/libs/light/libxl_dom_save.c | 2 +-
|
||||
tools/libs/light/libxl_domain.c | 1 +
|
||||
tools/libs/light/libxl_internal.h | 1 +
|
||||
tools/xl/xl_cmdtable.c | 23 ++++++++++++-----------
|
||||
tools/xl/xl_migrate.c | 9 ++++++++-
|
||||
7 files changed, 32 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/docs/man/xl.1.pod.in
|
||||
+++ b/docs/man/xl.1.pod.in
|
||||
@@ -505,6 +505,14 @@ possible to use this option for a 'local
|
||||
|
||||
Number of copy iterations before final suspend+move (default: 5)
|
||||
|
||||
+=item B<--min_remaing> I<pages>
|
||||
+
|
||||
+Number of remaining dirty pages. If the number of dirty pages drops that
|
||||
+low, the guest is suspended and the domU will finally be moved to I<host>.
|
||||
+
|
||||
+This allows the host admin to control for how long the domU will likely
|
||||
+be suspended during transit.
|
||||
+
|
||||
=back
|
||||
|
||||
=item B<remus> [I<OPTIONS>] I<domain-id> I<host>
|
||||
--- a/tools/include/libxl.h
|
||||
+++ b/tools/include/libxl.h
|
||||
@@ -1820,6 +1820,7 @@ static inline int libxl_retrieve_domain_
|
||||
typedef struct {
|
||||
uint32_t flags; /* LIBXL_SUSPEND_* */
|
||||
uint32_t max_iters;
|
||||
+ uint32_t min_remaining;
|
||||
} libxl_domain_suspend_suse_properties;
|
||||
#define LIBXL_SUSPEND_DEBUG 1
|
||||
#define LIBXL_SUSPEND_LIVE 2
|
||||
--- a/tools/libs/light/libxl_dom_save.c
|
||||
+++ b/tools/libs/light/libxl_dom_save.c
|
||||
@@ -381,7 +381,7 @@ static int libxl__domain_save_precopy_po
|
||||
|
||||
LOGD(DEBUG, shs->domid, "iteration %u dirty_count %ld total_written %lu",
|
||||
stats.iteration, stats.dirty_count, stats.total_written);
|
||||
- if (stats.dirty_count >= 0 && stats.dirty_count < LIBXL_XGS_POLICY_TARGET_DIRTY_COUNT)
|
||||
+ if (stats.dirty_count >= 0 && stats.dirty_count < dss->min_remaining)
|
||||
goto stop_copy;
|
||||
if (stats.iteration >= dss->max_iters)
|
||||
goto stop_copy;
|
||||
--- a/tools/libs/light/libxl_domain.c
|
||||
+++ b/tools/libs/light/libxl_domain.c
|
||||
@@ -525,6 +525,7 @@ static int do_libxl_domain_suspend(libxl
|
||||
dss->fd = fd;
|
||||
dss->type = type;
|
||||
dss->max_iters = props->max_iters ?: LIBXL_XGS_POLICY_MAX_ITERATIONS;
|
||||
+ dss->min_remaining = props->min_remaining ?: LIBXL_XGS_POLICY_TARGET_DIRTY_COUNT;
|
||||
dss->live = props->flags & LIBXL_SUSPEND_LIVE;
|
||||
dss->debug = props->flags & LIBXL_SUSPEND_DEBUG;
|
||||
dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
|
||||
--- a/tools/libs/light/libxl_internal.h
|
||||
+++ b/tools/libs/light/libxl_internal.h
|
||||
@@ -3657,6 +3657,7 @@ struct libxl__domain_save_state {
|
||||
int debug;
|
||||
int checkpointed_stream;
|
||||
uint32_t max_iters;
|
||||
+ uint32_t min_remaining;
|
||||
const libxl_domain_remus_info *remus;
|
||||
/* private */
|
||||
int rc;
|
||||
--- a/tools/xl/xl_cmdtable.c
|
||||
+++ b/tools/xl/xl_cmdtable.c
|
||||
@@ -166,17 +166,18 @@ const struct cmd_spec cmd_table[] = {
|
||||
&main_migrate, 0, 1,
|
||||
"Migrate a domain to another host",
|
||||
"[options] <Domain> <host>",
|
||||
- "-h Print this help.\n"
|
||||
- "-C <config> Send <config> instead of config file from creation.\n"
|
||||
- "-s <sshcommand> Use <sshcommand> instead of ssh. String will be passed\n"
|
||||
- " to sh. If empty, run <host> instead of ssh <host> xl\n"
|
||||
- " migrate-receive [-d -e]\n"
|
||||
- "-e Do not wait in the background (on <host>) for the death\n"
|
||||
- " of the domain.\n"
|
||||
- "--debug Enable verification mode.\n"
|
||||
- "-p Do not unpause domain after migrating it.\n"
|
||||
- "-D Preserve the domain id\n"
|
||||
- "--max_iters N Number of copy iterations before final stop+move"
|
||||
+ "-h Print this help.\n"
|
||||
+ "-C <config> Send <config> instead of config file from creation.\n"
|
||||
+ "-s <sshcommand> Use <sshcommand> instead of ssh. String will be passed\n"
|
||||
+ " to sh. If empty, run <host> instead of ssh <host> xl\n"
|
||||
+ " migrate-receive [-d -e]\n"
|
||||
+ "-e Do not wait in the background (on <host>) for the death\n"
|
||||
+ " of the domain.\n"
|
||||
+ "--debug Enable verification mode.\n"
|
||||
+ "-p Do not unpause domain after migrating it.\n"
|
||||
+ "-D Preserve the domain id\n"
|
||||
+ "--max_iters N Number of copy iterations before final stop+move\n"
|
||||
+ "--min_remaining N Number of remaining dirty pages before final stop+move"
|
||||
},
|
||||
{ "restore",
|
||||
&main_restore, 0, 1,
|
||||
--- a/tools/xl/xl_migrate.c
|
||||
+++ b/tools/xl/xl_migrate.c
|
||||
@@ -179,6 +179,7 @@ static void migrate_do_preamble(int send
|
||||
static void migrate_domain(uint32_t domid, int preserve_domid,
|
||||
const char *rune, int debug,
|
||||
uint32_t max_iters,
|
||||
+ uint32_t min_remaining,
|
||||
const char *override_config_file)
|
||||
{
|
||||
pid_t child = -1;
|
||||
@@ -191,6 +192,7 @@ static void migrate_domain(uint32_t domi
|
||||
libxl_domain_suspend_suse_properties props = {
|
||||
.flags = LIBXL_SUSPEND_LIVE,
|
||||
.max_iters = max_iters,
|
||||
+ .min_remaining = min_remaining,
|
||||
};
|
||||
|
||||
save_domain_core_begin(domid, preserve_domid, override_config_file,
|
||||
@@ -545,9 +547,11 @@ int main_migrate(int argc, char **argv)
|
||||
int opt, daemonize = 1, monitor = 1, debug = 0, pause_after_migration = 0;
|
||||
int preserve_domid = 0;
|
||||
uint32_t max_iters = 0;
|
||||
+ uint32_t min_remaining = 0;
|
||||
static struct option opts[] = {
|
||||
{"debug", 0, 0, 0x100},
|
||||
{"max_iters", 1, 0, 0x101},
|
||||
+ {"min_remaining", 1, 0, 0x102},
|
||||
{"live", 0, 0, 0x200},
|
||||
COMMON_LONG_OPTS
|
||||
};
|
||||
@@ -578,6 +582,9 @@ int main_migrate(int argc, char **argv)
|
||||
case 0x101: /* --max_iters */
|
||||
max_iters = atoi(optarg);
|
||||
break;
|
||||
+ case 0x102: /* --min_remaining */
|
||||
+ min_remaining = atoi(optarg);
|
||||
+ break;
|
||||
case 0x200: /* --live */
|
||||
/* ignored for compatibility with xm */
|
||||
break;
|
||||
@@ -613,7 +620,7 @@ int main_migrate(int argc, char **argv)
|
||||
}
|
||||
|
||||
migrate_domain(domid, preserve_domid, rune, debug,
|
||||
- max_iters, config_filename);
|
||||
+ max_iters, min_remaining, config_filename);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
24
libxc-sr-number-of-iterations.patch
Normal file
24
libxc-sr-number-of-iterations.patch
Normal file
@ -0,0 +1,24 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Mon, 4 Jan 2021 20:58:42 +0200
|
||||
Subject: libxc sr number of iterations
|
||||
|
||||
Reduce default value of --max_iters from 5 to 1.
|
||||
The workload within domU will continue to produce dirty pages.
|
||||
It is unreasonable to expect any slowdown during migration.
|
||||
Now there is one initial copy of all memory, one instead of five
|
||||
iterations for dirty memory, and a final copy iteration prior move.
|
||||
---
|
||||
tools/libs/light/libxl_internal.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/tools/libs/light/libxl_internal.h
|
||||
+++ b/tools/libs/light/libxl_internal.h
|
||||
@@ -124,7 +124,7 @@
|
||||
#define DOMID_XS_PATH "domid"
|
||||
#define PVSHIM_BASENAME "xen-shim"
|
||||
#define PVSHIM_CMDLINE "pv-shim console=xen,pv"
|
||||
-#define LIBXL_XGS_POLICY_MAX_ITERATIONS 5
|
||||
+#define LIBXL_XGS_POLICY_MAX_ITERATIONS 1
|
||||
#define LIBXL_XGS_POLICY_TARGET_DIRTY_COUNT 50
|
||||
|
||||
/* Size macros. */
|
90
libxc-sr-precopy_policy.patch
Normal file
90
libxc-sr-precopy_policy.patch
Normal file
@ -0,0 +1,90 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 8 Jan 2021 18:19:49 +0100
|
||||
Subject: libxc sr precopy_policy
|
||||
|
||||
tools: add callback to libxl for precopy_policy and precopy_stats
|
||||
|
||||
This duplicates simple_precopy_policy. To recap its purpose:
|
||||
- do up to 5 iterations of copying dirty domU memory to target,
|
||||
including the initial copying of all domU memory, excluding
|
||||
the final copying while the domU is suspended
|
||||
- do fewer iterations in case the domU dirtied less than 50 pages
|
||||
|
||||
Take the opportunity to also move xen_pfn_t into qw().
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
|
||||
v02:
|
||||
- use plain struct precopy_stats instead of inventing
|
||||
a new precopy_stats_t (anthony)
|
||||
---
|
||||
tools/libs/light/libxl_dom_save.c | 19 +++++++++++++++++++
|
||||
tools/libs/light/libxl_internal.h | 2 ++
|
||||
tools/libs/light/libxl_save_msgs_gen.pl | 3 ++-
|
||||
3 files changed, 23 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/tools/libs/light/libxl_dom_save.c
|
||||
+++ b/tools/libs/light/libxl_dom_save.c
|
||||
@@ -373,6 +373,24 @@ int libxl__save_emulator_xenstore_data(l
|
||||
return rc;
|
||||
}
|
||||
|
||||
+static int libxl__domain_save_precopy_policy(struct precopy_stats stats, void *user)
|
||||
+{
|
||||
+ libxl__save_helper_state *shs = user;
|
||||
+ libxl__domain_save_state *dss = shs->caller_state;
|
||||
+ STATE_AO_GC(dss->ao);
|
||||
+
|
||||
+ LOGD(DEBUG, shs->domid, "iteration %u dirty_count %ld total_written %lu",
|
||||
+ stats.iteration, stats.dirty_count, stats.total_written);
|
||||
+ if (stats.dirty_count >= 0 && stats.dirty_count < LIBXL_XGS_POLICY_TARGET_DIRTY_COUNT)
|
||||
+ goto stop_copy;
|
||||
+ if (stats.iteration >= LIBXL_XGS_POLICY_MAX_ITERATIONS)
|
||||
+ goto stop_copy;
|
||||
+ return XGS_POLICY_CONTINUE_PRECOPY;
|
||||
+
|
||||
+stop_copy:
|
||||
+ return XGS_POLICY_STOP_AND_COPY;
|
||||
+}
|
||||
+
|
||||
/*----- main code for saving, in order of execution -----*/
|
||||
|
||||
void libxl__domain_save(libxl__egc *egc, libxl__domain_save_state *dss)
|
||||
@@ -430,6 +448,7 @@ void libxl__domain_save(libxl__egc *egc,
|
||||
callbacks->suspend = libxl__domain_suspend_callback;
|
||||
|
||||
callbacks->switch_qemu_logdirty = libxl__domain_suspend_common_switch_qemu_logdirty;
|
||||
+ callbacks->precopy_policy = libxl__domain_save_precopy_policy;
|
||||
|
||||
dss->sws.ao = dss->ao;
|
||||
dss->sws.dss = dss;
|
||||
--- a/tools/libs/light/libxl_internal.h
|
||||
+++ b/tools/libs/light/libxl_internal.h
|
||||
@@ -124,6 +124,8 @@
|
||||
#define DOMID_XS_PATH "domid"
|
||||
#define PVSHIM_BASENAME "xen-shim"
|
||||
#define PVSHIM_CMDLINE "pv-shim console=xen,pv"
|
||||
+#define LIBXL_XGS_POLICY_MAX_ITERATIONS 5
|
||||
+#define LIBXL_XGS_POLICY_TARGET_DIRTY_COUNT 50
|
||||
|
||||
/* Size macros. */
|
||||
#define MB(_mb) (_AC(_mb, ULL) << 20)
|
||||
--- a/tools/libs/light/libxl_save_msgs_gen.pl
|
||||
+++ b/tools/libs/light/libxl_save_msgs_gen.pl
|
||||
@@ -23,6 +23,7 @@ our @msgs = (
|
||||
STRING doing_what),
|
||||
'unsigned long', 'done',
|
||||
'unsigned long', 'total'] ],
|
||||
+ [ 'scxW', "precopy_policy", ['struct precopy_stats', 'stats'] ],
|
||||
[ 'srcxA', "suspend", [] ],
|
||||
[ 'srcxA', "postcopy", [] ],
|
||||
[ 'srcxA', "checkpoint", [] ],
|
||||
@@ -142,7 +143,7 @@ static void bytes_put(unsigned char *con
|
||||
|
||||
END
|
||||
|
||||
-foreach my $simpletype (qw(int uint16_t uint32_t unsigned), 'unsigned long', 'xen_pfn_t') {
|
||||
+foreach my $simpletype (qw(int uint16_t uint32_t unsigned xen_pfn_t), 'struct precopy_stats', 'unsigned long') {
|
||||
my $typeid = typeid($simpletype);
|
||||
$out_body{'callout'} .= <<END;
|
||||
static int ${typeid}_get(const unsigned char **msg,
|
103
libxc-sr-readv_exact.patch
Normal file
103
libxc-sr-readv_exact.patch
Normal file
@ -0,0 +1,103 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Wed, 28 Oct 2020 12:07:36 +0100
|
||||
Subject: libxc sr readv_exact
|
||||
|
||||
tools: add readv_exact to libxenctrl
|
||||
|
||||
Read a batch of iovec's.
|
||||
|
||||
Short reads are the common case, finish the trailing iov with read_exact.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
|
||||
v2:
|
||||
- add comment to short-read handling
|
||||
---
|
||||
tools/libs/ctrl/xc_private.c | 57 +++++++++++++++++++++++++++++++++++-
|
||||
tools/libs/ctrl/xc_private.h | 1 +
|
||||
2 files changed, 57 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/tools/libs/ctrl/xc_private.c
|
||||
+++ b/tools/libs/ctrl/xc_private.c
|
||||
@@ -699,8 +699,23 @@ int write_exact(int fd, const void *data
|
||||
|
||||
#if defined(__MINIOS__)
|
||||
/*
|
||||
- * MiniOS's libc doesn't know about writev(). Implement it as multiple write()s.
|
||||
+ * MiniOS's libc doesn't know about readv/writev().
|
||||
+ * Implement it as multiple read/write()s.
|
||||
*/
|
||||
+int readv_exact(int fd, const struct iovec *iov, int iovcnt)
|
||||
+{
|
||||
+ int rc, i;
|
||||
+
|
||||
+ for ( i = 0; i < iovcnt; ++i )
|
||||
+ {
|
||||
+ rc = read_exact(fd, iov[i].iov_base, iov[i].iov_len);
|
||||
+ if ( rc )
|
||||
+ return rc;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
int writev_exact(int fd, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
int rc, i;
|
||||
@@ -715,6 +730,46 @@ int writev_exact(int fd, const struct io
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
+int readv_exact(int fd, const struct iovec *iov, int iovcnt)
|
||||
+{
|
||||
+ int rc = 0, idx = 0;
|
||||
+ ssize_t len;
|
||||
+
|
||||
+ while ( idx < iovcnt )
|
||||
+ {
|
||||
+ len = readv(fd, &iov[idx], min(iovcnt - idx, IOV_MAX));
|
||||
+ if ( len == -1 && errno == EINTR )
|
||||
+ continue;
|
||||
+ if ( len <= 0 )
|
||||
+ {
|
||||
+ rc = -1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* Finish a potential short read in the last iov */
|
||||
+ while ( len > 0 && idx < iovcnt )
|
||||
+ {
|
||||
+ if ( len >= iov[idx].iov_len )
|
||||
+ {
|
||||
+ len -= iov[idx].iov_len;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ void *p = iov[idx].iov_base + len;
|
||||
+ size_t l = iov[idx].iov_len - len;
|
||||
+
|
||||
+ rc = read_exact(fd, p, l);
|
||||
+ if ( rc )
|
||||
+ goto out;
|
||||
+ len = 0;
|
||||
+ }
|
||||
+ idx++;
|
||||
+ }
|
||||
+ }
|
||||
+out:
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
int writev_exact(int fd, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct iovec *local_iov = NULL;
|
||||
--- a/tools/libs/ctrl/xc_private.h
|
||||
+++ b/tools/libs/ctrl/xc_private.h
|
||||
@@ -395,6 +395,7 @@ int xc_flush_mmu_updates(xc_interface *x
|
||||
|
||||
/* Return 0 on success; -1 on error setting errno. */
|
||||
int read_exact(int fd, void *data, size_t size); /* EOF => -1, errno=0 */
|
||||
+int readv_exact(int fd, const struct iovec *iov, int iovcnt);
|
||||
int write_exact(int fd, const void *data, size_t size);
|
||||
int writev_exact(int fd, const struct iovec *iov, int iovcnt);
|
||||
|
435
libxc-sr-restore-handle_buffered_page_data.patch
Normal file
435
libxc-sr-restore-handle_buffered_page_data.patch
Normal file
@ -0,0 +1,435 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Tue, 27 Oct 2020 19:21:50 +0100
|
||||
Subject: libxc sr restore handle_buffered_page_data
|
||||
|
||||
tools: restore: split handle_page_data
|
||||
|
||||
handle_page_data must be able to read directly into mapped guest memory.
|
||||
This will avoid unneccesary memcpy calls for data that can be consumed verbatim.
|
||||
|
||||
Split the various steps of record processing:
|
||||
- move processing to handle_buffered_page_data
|
||||
- adjust xenforeignmemory_map to set errno in case of failure
|
||||
- adjust verify mode to set errno in case of failure
|
||||
|
||||
This change is preparation for future changes in handle_page_data,
|
||||
no change in behavior is intended.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 4 +
|
||||
tools/libs/guest/xg_sr_restore.c | 320 ++++++++++++++++++++-----------
|
||||
2 files changed, 207 insertions(+), 117 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -262,6 +262,10 @@ struct xc_sr_context
|
||||
int *map_errs;
|
||||
xen_pfn_t *pp_pfns;
|
||||
xen_pfn_t *pp_mfns;
|
||||
+ void **guest_data;
|
||||
+
|
||||
+ void *guest_mapping;
|
||||
+ uint32_t nr_mapped_pages;
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -183,121 +183,18 @@ int populate_pfns(struct xc_sr_context *
|
||||
return rc;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Given a list of pfns, their types, and a block of page data from the
|
||||
- * stream, populate and record their types, map the relevant subset and copy
|
||||
- * the data into the guest.
|
||||
- */
|
||||
-static int process_page_data(struct xc_sr_context *ctx, unsigned int count,
|
||||
- xen_pfn_t *pfns, uint32_t *types, void *page_data)
|
||||
+static int handle_static_data_end_v2(struct xc_sr_context *ctx)
|
||||
{
|
||||
- xc_interface *xch = ctx->xch;
|
||||
- int rc;
|
||||
- void *mapping = NULL, *guest_page = NULL;
|
||||
- unsigned int i, /* i indexes the pfns from the record. */
|
||||
- j, /* j indexes the subset of pfns we decide to map. */
|
||||
- nr_pages = 0;
|
||||
-
|
||||
- rc = populate_pfns(ctx, count, pfns, types);
|
||||
- if ( rc )
|
||||
- {
|
||||
- ERROR("Failed to populate pfns for batch of %u pages", count);
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
- for ( i = 0; i < count; ++i )
|
||||
- {
|
||||
- ctx->restore.ops.set_page_type(ctx, pfns[i], types[i]);
|
||||
-
|
||||
- if ( page_type_has_stream_data(types[i]) )
|
||||
- ctx->restore.mfns[nr_pages++] = ctx->restore.ops.pfn_to_gfn(ctx, pfns[i]);
|
||||
- }
|
||||
-
|
||||
- /* Nothing to do? */
|
||||
- if ( nr_pages == 0 )
|
||||
- goto done;
|
||||
-
|
||||
- mapping = guest_page = xenforeignmemory_map(
|
||||
- xch->fmem, ctx->domid, PROT_READ | PROT_WRITE,
|
||||
- nr_pages, ctx->restore.mfns, ctx->restore.map_errs);
|
||||
- if ( !mapping )
|
||||
- {
|
||||
- rc = -1;
|
||||
- PERROR("Unable to map %u mfns for %u pages of data",
|
||||
- nr_pages, count);
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
- for ( i = 0, j = 0; i < count; ++i )
|
||||
- {
|
||||
- if ( !page_type_has_stream_data(types[i]) )
|
||||
- continue;
|
||||
-
|
||||
- if ( ctx->restore.map_errs[j] )
|
||||
- {
|
||||
- rc = -1;
|
||||
- ERROR("Mapping pfn %#"PRIpfn" (mfn %#"PRIpfn", type %#"PRIx32") failed with %d",
|
||||
- pfns[i], ctx->restore.mfns[j], types[i], ctx->restore.map_errs[j]);
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
- /* Undo page normalisation done by the saver. */
|
||||
- rc = ctx->restore.ops.localise_page(ctx, types[i], page_data);
|
||||
- if ( rc )
|
||||
- {
|
||||
- ERROR("Failed to localise pfn %#"PRIpfn" (type %#"PRIx32")",
|
||||
- pfns[i], types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT);
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
- if ( ctx->restore.verify )
|
||||
- {
|
||||
- /* Verify mode - compare incoming data to what we already have. */
|
||||
- if ( memcmp(guest_page, page_data, PAGE_SIZE) )
|
||||
- ERROR("verify pfn %#"PRIpfn" failed (type %#"PRIx32")",
|
||||
- pfns[i], types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT);
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- /* Regular mode - copy incoming data into place. */
|
||||
- memcpy(guest_page, page_data, PAGE_SIZE);
|
||||
- }
|
||||
-
|
||||
- ++j;
|
||||
- guest_page += PAGE_SIZE;
|
||||
- page_data += PAGE_SIZE;
|
||||
- }
|
||||
-
|
||||
- done:
|
||||
- rc = 0;
|
||||
-
|
||||
- err:
|
||||
- if ( mapping )
|
||||
- xenforeignmemory_unmap(xch->fmem, mapping, nr_pages);
|
||||
-
|
||||
- return rc;
|
||||
-}
|
||||
+ int rc = 0;
|
||||
|
||||
-/*
|
||||
- * Validate a PAGE_DATA record from the stream, and pass the results to
|
||||
- * process_page_data() to actually perform the legwork.
|
||||
- */
|
||||
-static int handle_page_data(struct xc_sr_context *ctx, struct xc_sr_record *rec)
|
||||
-{
|
||||
+#if defined(__i386__) || defined(__x86_64__)
|
||||
xc_interface *xch = ctx->xch;
|
||||
- struct xc_sr_rec_page_data_header *pages = rec->data;
|
||||
- unsigned int i, pages_of_data = 0;
|
||||
- int rc = -1;
|
||||
-
|
||||
- xen_pfn_t pfn;
|
||||
- uint32_t type;
|
||||
-
|
||||
/*
|
||||
* v2 compatibility only exists for x86 streams. This is a bit of a
|
||||
* bodge, but it is less bad than duplicating handle_page_data() between
|
||||
* different architectures.
|
||||
*/
|
||||
-#if defined(__i386__) || defined(__x86_64__)
|
||||
+
|
||||
/* v2 compat. Infer the position of STATIC_DATA_END. */
|
||||
if ( ctx->restore.format_version < 3 && !ctx->restore.seen_static_data_end )
|
||||
{
|
||||
@@ -315,12 +212,26 @@ static int handle_page_data(struct xc_sr
|
||||
ERROR("No STATIC_DATA_END seen");
|
||||
goto err;
|
||||
}
|
||||
+
|
||||
+ rc = 0;
|
||||
+err:
|
||||
#endif
|
||||
|
||||
- if ( rec->length < sizeof(*pages) )
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+static bool verify_rec_page_hdr(struct xc_sr_context *ctx, uint32_t rec_length,
|
||||
+ struct xc_sr_rec_page_data_header *pages)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ bool ret = false;
|
||||
+
|
||||
+ errno = EINVAL;
|
||||
+
|
||||
+ if ( rec_length < sizeof(*pages) )
|
||||
{
|
||||
ERROR("PAGE_DATA record truncated: length %u, min %zu",
|
||||
- rec->length, sizeof(*pages));
|
||||
+ rec_length, sizeof(*pages));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -330,13 +241,28 @@ static int handle_page_data(struct xc_sr
|
||||
goto err;
|
||||
}
|
||||
|
||||
- if ( rec->length < sizeof(*pages) + (pages->count * sizeof(uint64_t)) )
|
||||
+ if ( rec_length < sizeof(*pages) + (pages->count * sizeof(uint64_t)) )
|
||||
{
|
||||
ERROR("PAGE_DATA record (length %u) too short to contain %u"
|
||||
- " pfns worth of information", rec->length, pages->count);
|
||||
+ " pfns worth of information", rec_length, pages->count);
|
||||
goto err;
|
||||
}
|
||||
|
||||
+ ret = true;
|
||||
+
|
||||
+err:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static bool verify_rec_page_pfns(struct xc_sr_context *ctx, uint32_t rec_length,
|
||||
+ struct xc_sr_rec_page_data_header *pages)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ uint32_t i, pages_of_data = 0;
|
||||
+ xen_pfn_t pfn;
|
||||
+ uint32_t type;
|
||||
+ bool ret = false;
|
||||
+
|
||||
for ( i = 0; i < pages->count; ++i )
|
||||
{
|
||||
pfn = pages->pfn[i] & PAGE_DATA_PFN_MASK;
|
||||
@@ -363,19 +289,177 @@ static int handle_page_data(struct xc_sr
|
||||
ctx->restore.types[i] = type;
|
||||
}
|
||||
|
||||
- if ( rec->length != (sizeof(*pages) +
|
||||
+ if ( rec_length != (sizeof(*pages) +
|
||||
(sizeof(uint64_t) * pages->count) +
|
||||
(PAGE_SIZE * pages_of_data)) )
|
||||
{
|
||||
ERROR("PAGE_DATA record wrong size: length %u, expected "
|
||||
- "%zu + %zu + %lu", rec->length, sizeof(*pages),
|
||||
+ "%zu + %zu + %lu", rec_length, sizeof(*pages),
|
||||
(sizeof(uint64_t) * pages->count), (PAGE_SIZE * pages_of_data));
|
||||
goto err;
|
||||
}
|
||||
|
||||
- rc = process_page_data(ctx, pages->count, ctx->restore.pfns,
|
||||
- ctx->restore.types, &pages->pfn[pages->count]);
|
||||
+ ret = true;
|
||||
+
|
||||
+err:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Populate pfns, if required
|
||||
+ * Fill guest_data with either mapped address or NULL
|
||||
+ * The caller must unmap guest_mapping
|
||||
+ */
|
||||
+static int map_guest_pages(struct xc_sr_context *ctx,
|
||||
+ struct xc_sr_rec_page_data_header *pages)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ uint32_t i, p;
|
||||
+ int rc;
|
||||
+
|
||||
+ rc = populate_pfns(ctx, pages->count, ctx->restore.pfns, ctx->restore.types);
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ ERROR("Failed to populate pfns for batch of %u pages", pages->count);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ ctx->restore.nr_mapped_pages = 0;
|
||||
+
|
||||
+ for ( i = 0; i < pages->count; i++ )
|
||||
+ {
|
||||
+ ctx->restore.ops.set_page_type(ctx, ctx->restore.pfns[i], ctx->restore.types[i]);
|
||||
+
|
||||
+ if ( page_type_has_stream_data(ctx->restore.types[i]) == false )
|
||||
+ {
|
||||
+ ctx->restore.guest_data[i] = NULL;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ctx->restore.mfns[ctx->restore.nr_mapped_pages++] = ctx->restore.ops.pfn_to_gfn(ctx, ctx->restore.pfns[i]);
|
||||
+ }
|
||||
+
|
||||
+ /* Nothing to do? */
|
||||
+ if ( ctx->restore.nr_mapped_pages == 0 )
|
||||
+ goto done;
|
||||
+
|
||||
+ ctx->restore.guest_mapping = xenforeignmemory_map(xch->fmem, ctx->domid,
|
||||
+ PROT_READ | PROT_WRITE, ctx->restore.nr_mapped_pages,
|
||||
+ ctx->restore.mfns, ctx->restore.map_errs);
|
||||
+ if ( !ctx->restore.guest_mapping )
|
||||
+ {
|
||||
+ rc = -1;
|
||||
+ PERROR("Unable to map %u mfns for %u pages of data",
|
||||
+ ctx->restore.nr_mapped_pages, pages->count);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ /* Verify mapping, and assign address to pfn data */
|
||||
+ for ( i = 0, p = 0; i < pages->count; i++ )
|
||||
+ {
|
||||
+ if ( !page_type_has_stream_data(ctx->restore.types[i]) )
|
||||
+ continue;
|
||||
+
|
||||
+ if ( ctx->restore.map_errs[p] == 0 )
|
||||
+ {
|
||||
+ ctx->restore.guest_data[i] = ctx->restore.guest_mapping + (p * PAGE_SIZE);
|
||||
+ p++;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ errno = ctx->restore.map_errs[p];
|
||||
+ rc = -1;
|
||||
+ PERROR("Mapping pfn %#"PRIpfn" (mfn %#"PRIpfn", type %#"PRIx32") failed",
|
||||
+ ctx->restore.pfns[i], ctx->restore.mfns[p], ctx->restore.types[i]);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+done:
|
||||
+ rc = 0;
|
||||
+
|
||||
+err:
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Handle PAGE_DATA record from an existing buffer
|
||||
+ * Given a list of pfns, their types, and a block of page data from the
|
||||
+ * stream, populate and record their types, map the relevant subset and copy
|
||||
+ * the data into the guest.
|
||||
+ */
|
||||
+static int handle_buffered_page_data(struct xc_sr_context *ctx,
|
||||
+ struct xc_sr_record *rec)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ struct xc_sr_rec_page_data_header *pages = rec->data;
|
||||
+ void *p;
|
||||
+ uint32_t i;
|
||||
+ int rc = -1, idx;
|
||||
+
|
||||
+ rc = handle_static_data_end_v2(ctx);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+
|
||||
+ /* First read and verify the header */
|
||||
+ if ( !verify_rec_page_hdr(ctx, rec->length, pages) )
|
||||
+ {
|
||||
+ rc = -1;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ /* Then read and verify the pfn numbers */
|
||||
+ if ( !verify_rec_page_pfns(ctx, rec->length, pages) )
|
||||
+ {
|
||||
+ rc = -1;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ /* Map the target pfn */
|
||||
+ rc = map_guest_pages(ctx, pages);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+
|
||||
+ for ( i = 0, idx = 0; i < pages->count; i++ )
|
||||
+ {
|
||||
+ if ( !ctx->restore.guest_data[i] )
|
||||
+ continue;
|
||||
+
|
||||
+ p = &pages->pfn[pages->count] + (idx * PAGE_SIZE);
|
||||
+ rc = ctx->restore.ops.localise_page(ctx, ctx->restore.types[i], p);
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ ERROR("Failed to localise pfn %#"PRIpfn" (type %#"PRIx32")",
|
||||
+ ctx->restore.pfns[i], ctx->restore.types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT);
|
||||
+ goto err;
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ if ( ctx->restore.verify )
|
||||
+ {
|
||||
+ if ( memcmp(ctx->restore.guest_data[i], p, PAGE_SIZE) )
|
||||
+ {
|
||||
+ errno = EIO;
|
||||
+ ERROR("verify pfn %#"PRIpfn" failed (type %#"PRIx32")",
|
||||
+ ctx->restore.pfns[i], ctx->restore.types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT);
|
||||
+ goto err;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ memcpy(ctx->restore.guest_data[i], p, PAGE_SIZE);
|
||||
+ }
|
||||
+
|
||||
+ idx++;
|
||||
+ }
|
||||
+
|
||||
+ rc = 0;
|
||||
+
|
||||
err:
|
||||
+ if ( ctx->restore.guest_mapping )
|
||||
+ {
|
||||
+ xenforeignmemory_unmap(xch->fmem, ctx->restore.guest_mapping, ctx->restore.nr_mapped_pages);
|
||||
+ ctx->restore.guest_mapping = NULL;
|
||||
+ }
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -623,7 +707,7 @@ static int process_buffered_record(struc
|
||||
break;
|
||||
|
||||
case REC_TYPE_PAGE_DATA:
|
||||
- rc = handle_page_data(ctx, rec);
|
||||
+ rc = handle_buffered_page_data(ctx, rec);
|
||||
break;
|
||||
|
||||
case REC_TYPE_VERIFY:
|
||||
@@ -703,9 +787,10 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->restore.map_errs = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.map_errs));
|
||||
ctx->restore.pp_pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pp_pfns));
|
||||
ctx->restore.pp_mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pp_mfns));
|
||||
+ ctx->restore.guest_data = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.guest_data));
|
||||
if ( !ctx->restore.pfns || !ctx->restore.types || !ctx->restore.mfns ||
|
||||
!ctx->restore.map_errs || !ctx->restore.pp_pfns ||
|
||||
- !ctx->restore.pp_mfns )
|
||||
+ !ctx->restore.pp_mfns || !ctx->restore.guest_data )
|
||||
{
|
||||
ERROR("Unable to allocate memory");
|
||||
rc = -1;
|
||||
@@ -742,6 +827,7 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.guest_data);
|
||||
free(ctx->restore.pp_mfns);
|
||||
free(ctx->restore.pp_pfns);
|
||||
free(ctx->restore.map_errs);
|
230
libxc-sr-restore-handle_incoming_page_data.patch
Normal file
230
libxc-sr-restore-handle_incoming_page_data.patch
Normal file
@ -0,0 +1,230 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Thu, 29 Oct 2020 16:13:10 +0100
|
||||
Subject: libxc sr restore handle_incoming_page_data
|
||||
|
||||
tools: restore: write data directly into guest
|
||||
|
||||
Read incoming migration stream directly into the guest memory.
|
||||
This avoids the memory allocation and copying, and the resulting
|
||||
performance penalty.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 3 +
|
||||
tools/libs/guest/xg_sr_restore.c | 155 ++++++++++++++++++++++++++++++-
|
||||
2 files changed, 153 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -263,6 +263,8 @@ struct xc_sr_context
|
||||
xen_pfn_t *pp_pfns;
|
||||
xen_pfn_t *pp_mfns;
|
||||
void **guest_data;
|
||||
+ struct iovec *iov;
|
||||
+ struct xc_sr_rec_page_data_header *pages;
|
||||
|
||||
void *guest_mapping;
|
||||
uint32_t nr_mapped_pages;
|
||||
@@ -311,6 +313,7 @@ struct xc_sr_context
|
||||
|
||||
/* Sender has invoked verify mode on the stream. */
|
||||
bool verify;
|
||||
+ void *verify_buf;
|
||||
} restore;
|
||||
};
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -382,6 +382,129 @@ err:
|
||||
}
|
||||
|
||||
/*
|
||||
+ * Handle PAGE_DATA record from the stream.
|
||||
+ * Given a list of pfns, their types, and a block of page data from the
|
||||
+ * stream, populate and record their types, map the relevant subset and copy
|
||||
+ * the data into the guest.
|
||||
+ */
|
||||
+static int handle_incoming_page_data(struct xc_sr_context *ctx,
|
||||
+ struct xc_sr_rhdr *rhdr)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ struct xc_sr_rec_page_data_header *pages = ctx->restore.pages;
|
||||
+ uint64_t *pfn_nums = &pages->pfn[0];
|
||||
+ uint32_t i;
|
||||
+ int rc, iov_idx;
|
||||
+
|
||||
+ rc = handle_static_data_end_v2(ctx);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+
|
||||
+ /* First read and verify the header */
|
||||
+ rc = read_exact(ctx->fd, pages, sizeof(*pages));
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ PERROR("Could not read rec_pfn header");
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ if ( !verify_rec_page_hdr(ctx, rhdr->length, pages) )
|
||||
+ {
|
||||
+ rc = -1;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ /* Then read and verify the incoming pfn numbers */
|
||||
+ rc = read_exact(ctx->fd, pfn_nums, sizeof(*pfn_nums) * pages->count);
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ PERROR("Could not read rec_pfn data");
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ if ( !verify_rec_page_pfns(ctx, rhdr->length, pages) )
|
||||
+ {
|
||||
+ rc = -1;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ /* Finally read and verify the incoming pfn data */
|
||||
+ rc = map_guest_pages(ctx, pages);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+
|
||||
+ /* Prepare read buffers, either guest or throw-away memory */
|
||||
+ for ( i = 0, iov_idx = 0; i < pages->count; i++ )
|
||||
+ {
|
||||
+ struct iovec *iov;
|
||||
+
|
||||
+ if ( !ctx->restore.guest_data[i] )
|
||||
+ continue;
|
||||
+
|
||||
+ iov = &ctx->restore.iov[iov_idx];
|
||||
+ iov->iov_len = PAGE_SIZE;
|
||||
+ if ( ctx->restore.verify )
|
||||
+ iov->iov_base = ctx->restore.verify_buf + (i * PAGE_SIZE);
|
||||
+ else
|
||||
+ iov->iov_base = ctx->restore.guest_data[i];
|
||||
+ iov_idx++;
|
||||
+ }
|
||||
+
|
||||
+ if ( !iov_idx )
|
||||
+ goto done;
|
||||
+
|
||||
+ rc = readv_exact(ctx->fd, ctx->restore.iov, iov_idx);
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ PERROR("read of %d pages failed", iov_idx);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ /* Post-processing of pfn data */
|
||||
+ for ( i = 0, iov_idx = 0; i < pages->count; i++ )
|
||||
+ {
|
||||
+ void *addr;
|
||||
+
|
||||
+ if ( !ctx->restore.guest_data[i] )
|
||||
+ continue;
|
||||
+
|
||||
+ addr = ctx->restore.iov[iov_idx].iov_base;
|
||||
+ rc = ctx->restore.ops.localise_page(ctx, ctx->restore.types[i], addr);
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ ERROR("Failed to localise pfn %#"PRIpfn" (type %#"PRIx32")",
|
||||
+ ctx->restore.pfns[i],
|
||||
+ ctx->restore.types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT);
|
||||
+ goto err;
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ if ( ctx->restore.verify )
|
||||
+ {
|
||||
+ if ( memcmp(ctx->restore.guest_data[i], addr, PAGE_SIZE) )
|
||||
+ {
|
||||
+ ERROR("verify pfn %#"PRIpfn" failed (type %#"PRIx32")",
|
||||
+ ctx->restore.pfns[i],
|
||||
+ ctx->restore.types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ iov_idx++;
|
||||
+ }
|
||||
+
|
||||
+done:
|
||||
+ rc = 0;
|
||||
+
|
||||
+err:
|
||||
+ if ( ctx->restore.guest_mapping )
|
||||
+ {
|
||||
+ xenforeignmemory_unmap(xch->fmem, ctx->restore.guest_mapping, ctx->restore.nr_mapped_pages);
|
||||
+ ctx->restore.guest_mapping = NULL;
|
||||
+ }
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
* Handle PAGE_DATA record from an existing buffer
|
||||
* Given a list of pfns, their types, and a block of page data from the
|
||||
* stream, populate and record their types, map the relevant subset and copy
|
||||
@@ -713,6 +836,15 @@ static int process_buffered_record(struc
|
||||
case REC_TYPE_VERIFY:
|
||||
DPRINTF("Verify mode enabled");
|
||||
ctx->restore.verify = true;
|
||||
+ if ( !ctx->restore.verify_buf )
|
||||
+ {
|
||||
+ ctx->restore.verify_buf = malloc(MAX_BATCH_SIZE * PAGE_SIZE);
|
||||
+ if ( !ctx->restore.verify_buf )
|
||||
+ {
|
||||
+ PERROR("Unable to allocate verify_buf");
|
||||
+ rc = -1;
|
||||
+ }
|
||||
+ }
|
||||
break;
|
||||
|
||||
case REC_TYPE_CHECKPOINT:
|
||||
@@ -739,11 +871,19 @@ static int process_incoming_record_heade
|
||||
struct xc_sr_record rec;
|
||||
int rc;
|
||||
|
||||
- rc = read_record_data(ctx, ctx->fd, rhdr, &rec);
|
||||
- if ( rc )
|
||||
- return rc;
|
||||
+ switch ( rhdr->type )
|
||||
+ {
|
||||
+ case REC_TYPE_PAGE_DATA:
|
||||
+ rc = handle_incoming_page_data(ctx, rhdr);
|
||||
+ break;
|
||||
+ default:
|
||||
+ rc = read_record_data(ctx, ctx->fd, rhdr, &rec);
|
||||
+ if ( rc == 0 )
|
||||
+ rc = process_buffered_record(ctx, &rec);;
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- return process_buffered_record(ctx, &rec);
|
||||
+ return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -788,9 +928,12 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->restore.pp_pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pp_pfns));
|
||||
ctx->restore.pp_mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pp_mfns));
|
||||
ctx->restore.guest_data = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.guest_data));
|
||||
+ ctx->restore.iov = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.iov));
|
||||
+ ctx->restore.pages = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pages->pfn) + sizeof(*ctx->restore.pages));
|
||||
if ( !ctx->restore.pfns || !ctx->restore.types || !ctx->restore.mfns ||
|
||||
!ctx->restore.map_errs || !ctx->restore.pp_pfns ||
|
||||
- !ctx->restore.pp_mfns || !ctx->restore.guest_data )
|
||||
+ !ctx->restore.pp_mfns || !ctx->restore.guest_data ||
|
||||
+ !ctx->restore.iov || !ctx->restore.pages )
|
||||
{
|
||||
ERROR("Unable to allocate memory");
|
||||
rc = -1;
|
||||
@@ -827,6 +970,8 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.pages);
|
||||
+ free(ctx->restore.iov);
|
||||
free(ctx->restore.guest_data);
|
||||
free(ctx->restore.pp_mfns);
|
||||
free(ctx->restore.pp_pfns);
|
701
libxc-sr-restore-hvm-legacy-superpage.patch
Normal file
701
libxc-sr-restore-hvm-legacy-superpage.patch
Normal file
@ -0,0 +1,701 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Mon, 7 Aug 2017 12:58:02 +0000
|
||||
Subject: libxc sr restore hvm legacy superpage
|
||||
|
||||
tools: use superpages during restore of HVM guest
|
||||
|
||||
bsc#1035231 - migration of HVM domU does not use superpages on destination dom0
|
||||
bsc#1055695 - XEN: 11SP4 and 12SP3 HVM guests can not be restored
|
||||
|
||||
During creating of a HVM domU meminit_hvm() tries to map superpages.
|
||||
After save/restore or migration this mapping is lost, everything is
|
||||
allocated in single pages. This causes a performance degradation after
|
||||
migration.
|
||||
|
||||
Add neccessary code to preallocate a superpage for an incoming chunk of
|
||||
pfns. In case a pfn was not populated on the sending side, it must be
|
||||
freed on the receiving side to avoid over-allocation.
|
||||
|
||||
The existing code for x86_pv is moved unmodified into its own file.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_dom_x86.c | 5 -
|
||||
tools/libs/guest/xg_private.h | 5 +
|
||||
tools/libs/guest/xg_sr_common.h | 28 +-
|
||||
tools/libs/guest/xg_sr_restore.c | 60 +---
|
||||
tools/libs/guest/xg_sr_restore_x86_hvm.c | 381 ++++++++++++++++++++++-
|
||||
tools/libs/guest/xg_sr_restore_x86_pv.c | 61 +++-
|
||||
6 files changed, 467 insertions(+), 73 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_dom_x86.c
|
||||
+++ b/tools/libs/guest/xg_dom_x86.c
|
||||
@@ -44,11 +44,6 @@
|
||||
|
||||
#define SUPERPAGE_BATCH_SIZE 512
|
||||
|
||||
-#define SUPERPAGE_2MB_SHIFT 9
|
||||
-#define SUPERPAGE_2MB_NR_PFNS (1UL << SUPERPAGE_2MB_SHIFT)
|
||||
-#define SUPERPAGE_1GB_SHIFT 18
|
||||
-#define SUPERPAGE_1GB_NR_PFNS (1UL << SUPERPAGE_1GB_SHIFT)
|
||||
-
|
||||
#define X86_CR0_PE 0x01
|
||||
#define X86_CR0_ET 0x10
|
||||
|
||||
--- a/tools/libs/guest/xg_private.h
|
||||
+++ b/tools/libs/guest/xg_private.h
|
||||
@@ -180,4 +180,9 @@ struct xc_cpu_policy {
|
||||
};
|
||||
#endif /* x86 */
|
||||
|
||||
+#define SUPERPAGE_2MB_SHIFT 9
|
||||
+#define SUPERPAGE_2MB_NR_PFNS (1UL << SUPERPAGE_2MB_SHIFT)
|
||||
+#define SUPERPAGE_1GB_SHIFT 18
|
||||
+#define SUPERPAGE_1GB_NR_PFNS (1UL << SUPERPAGE_1GB_SHIFT)
|
||||
+
|
||||
#endif /* XG_PRIVATE_H */
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -208,6 +208,16 @@ struct xc_sr_restore_ops
|
||||
int (*setup)(struct xc_sr_context *ctx);
|
||||
|
||||
/**
|
||||
+ * Populate PFNs
|
||||
+ *
|
||||
+ * Given a set of pfns, obtain memory from Xen to fill the physmap for the
|
||||
+ * unpopulated subset.
|
||||
+ */
|
||||
+ int (*populate_pfns)(struct xc_sr_context *ctx, unsigned count,
|
||||
+ const xen_pfn_t *original_pfns, const uint32_t *types);
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
* Process an individual record from the stream. The caller shall take
|
||||
* care of processing common records (e.g. END, PAGE_DATA).
|
||||
*
|
||||
@@ -338,6 +348,8 @@ struct xc_sr_context
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
+ unsigned long max_pages;
|
||||
+ unsigned long tot_pages;
|
||||
xc_hypercall_buffer_t dirty_bitmap_hbuf;
|
||||
|
||||
/* From Image Header. */
|
||||
@@ -471,6 +483,14 @@ struct xc_sr_context
|
||||
{
|
||||
/* HVM context blob. */
|
||||
struct xc_sr_blob context;
|
||||
+
|
||||
+ /* Bitmap of currently allocated PFNs during restore. */
|
||||
+ struct sr_bitmap attempted_1g;
|
||||
+ struct sr_bitmap attempted_2m;
|
||||
+ struct sr_bitmap allocated_pfns;
|
||||
+ xen_pfn_t prev_populated_pfn;
|
||||
+ xen_pfn_t iteration_tracker_pfn;
|
||||
+ unsigned long iteration;
|
||||
} restore;
|
||||
};
|
||||
} hvm;
|
||||
@@ -535,14 +555,6 @@ int read_record_header(struct xc_sr_cont
|
||||
int read_record_data(struct xc_sr_context *ctx, int fd, struct xc_sr_rhdr *rhdr,
|
||||
struct xc_sr_record *rec);
|
||||
|
||||
-/*
|
||||
- * This would ideally be private in restore.c, but is needed by
|
||||
- * x86_pv_localise_page() if we receive pagetables frames ahead of the
|
||||
- * contents of the frames they point at.
|
||||
- */
|
||||
-int populate_pfns(struct xc_sr_context *ctx, unsigned int count,
|
||||
- const xen_pfn_t *original_pfns, const uint32_t *types);
|
||||
-
|
||||
/* Handle a STATIC_DATA_END record. */
|
||||
int handle_static_data_end(struct xc_sr_context *ctx);
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -71,60 +71,6 @@ static int read_headers(struct xc_sr_con
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Given a set of pfns, obtain memory from Xen to fill the physmap for the
|
||||
- * unpopulated subset. If types is NULL, no page type checking is performed
|
||||
- * and all unpopulated pfns are populated.
|
||||
- */
|
||||
-int populate_pfns(struct xc_sr_context *ctx, unsigned int count,
|
||||
- const xen_pfn_t *original_pfns, const uint32_t *types)
|
||||
-{
|
||||
- xc_interface *xch = ctx->xch;
|
||||
- unsigned int i, nr_pfns = 0;
|
||||
- int rc = -1;
|
||||
-
|
||||
- for ( i = 0; i < count; ++i )
|
||||
- {
|
||||
- if ( (!types || page_type_to_populate(types[i])) &&
|
||||
- !pfn_is_populated(ctx, original_pfns[i]) )
|
||||
- {
|
||||
- rc = pfn_set_populated(ctx, original_pfns[i]);
|
||||
- if ( rc )
|
||||
- goto err;
|
||||
- ctx->restore.pp_pfns[nr_pfns] = ctx->restore.pp_mfns[nr_pfns] = original_pfns[i];
|
||||
- ++nr_pfns;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if ( nr_pfns )
|
||||
- {
|
||||
- rc = xc_domain_populate_physmap_exact(
|
||||
- xch, ctx->domid, nr_pfns, 0, 0, ctx->restore.pp_mfns);
|
||||
- if ( rc )
|
||||
- {
|
||||
- PERROR("Failed to populate physmap");
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
- for ( i = 0; i < nr_pfns; ++i )
|
||||
- {
|
||||
- if ( ctx->restore.pp_mfns[i] == INVALID_MFN )
|
||||
- {
|
||||
- ERROR("Populate physmap failed for pfn %u", i);
|
||||
- rc = -1;
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
- ctx->restore.ops.set_gfn(ctx, ctx->restore.pp_pfns[i], ctx->restore.pp_mfns[i]);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- rc = 0;
|
||||
-
|
||||
- err:
|
||||
- return rc;
|
||||
-}
|
||||
-
|
||||
static int handle_static_data_end_v2(struct xc_sr_context *ctx)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -259,7 +205,8 @@ static int map_guest_pages(struct xc_sr_
|
||||
uint32_t i, p;
|
||||
int rc;
|
||||
|
||||
- rc = populate_pfns(ctx, pages->count, ctx->restore.pfns, ctx->restore.types);
|
||||
+ rc = ctx->restore.ops.populate_pfns(ctx, pages->count, ctx->restore.pfns,
|
||||
+ ctx->restore.types);
|
||||
if ( rc )
|
||||
{
|
||||
ERROR("Failed to populate pfns for batch of %u pages", pages->count);
|
||||
@@ -1074,6 +1021,9 @@ int xc_domain_restore(xc_interface *xch,
|
||||
return -1;
|
||||
}
|
||||
|
||||
+ /* See xc_domain_getinfo */
|
||||
+ ctx.restore.max_pages = ctx.dominfo.max_pages;
|
||||
+ ctx.restore.tot_pages = ctx.dominfo.tot_pages;
|
||||
ctx.restore.p2m_size = nr_pfns;
|
||||
ctx.restore.ops = hvm ? restore_ops_x86_hvm : restore_ops_x86_pv;
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_restore_x86_hvm.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore_x86_hvm.c
|
||||
@@ -130,6 +130,33 @@ static int x86_hvm_localise_page(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool x86_hvm_expand_sp_bitmaps(struct xc_sr_context *ctx, unsigned long max_pfn)
|
||||
+{
|
||||
+ struct sr_bitmap *bm;
|
||||
+
|
||||
+ bm = &ctx->x86.hvm.restore.attempted_1g;
|
||||
+ if ( !sr_bitmap_expand(bm, max_pfn >> SUPERPAGE_1GB_SHIFT) )
|
||||
+ return false;
|
||||
+
|
||||
+ bm = &ctx->x86.hvm.restore.attempted_2m;
|
||||
+ if ( !sr_bitmap_expand(bm, max_pfn >> SUPERPAGE_2MB_SHIFT) )
|
||||
+ return false;
|
||||
+
|
||||
+ bm = &ctx->x86.hvm.restore.allocated_pfns;
|
||||
+ if ( !sr_bitmap_expand(bm, max_pfn) )
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static void x86_hvm_no_superpage(struct xc_sr_context *ctx, unsigned long addr)
|
||||
+{
|
||||
+ unsigned long pfn = addr >> XC_PAGE_SHIFT;
|
||||
+
|
||||
+ sr_set_bit(pfn >> SUPERPAGE_1GB_SHIFT, &ctx->x86.hvm.restore.attempted_1g);
|
||||
+ sr_set_bit(pfn >> SUPERPAGE_2MB_SHIFT, &ctx->x86.hvm.restore.attempted_2m);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* restore_ops function. Confirms the stream matches the domain.
|
||||
*/
|
||||
@@ -164,12 +191,24 @@ static int x86_hvm_setup(struct xc_sr_co
|
||||
|
||||
max_pfn = max(ctx->restore.p2m_size, max_pages);
|
||||
if ( !sr_bitmap_expand(&ctx->restore.populated_pfns, max_pfn) )
|
||||
- {
|
||||
- PERROR("Unable to allocate memory for populated_pfns bitmap");
|
||||
- return -1;
|
||||
- }
|
||||
+ goto out;
|
||||
+
|
||||
+ if ( !x86_hvm_expand_sp_bitmaps(ctx, max_pfn) )
|
||||
+ goto out;
|
||||
+
|
||||
+ /* FIXME: distinguish between PVH and HVM */
|
||||
+ /* No superpage in 1st 2MB due to VGA hole */
|
||||
+ x86_hvm_no_superpage(ctx, 0xA0000u);
|
||||
+#define LAPIC_BASE_ADDRESS 0xfee00000u
|
||||
+#define ACPI_INFO_PHYSICAL_ADDRESS 0xfc000000u
|
||||
+ x86_hvm_no_superpage(ctx, LAPIC_BASE_ADDRESS);
|
||||
+ x86_hvm_no_superpage(ctx, ACPI_INFO_PHYSICAL_ADDRESS);
|
||||
|
||||
return 0;
|
||||
+
|
||||
+out:
|
||||
+ PERROR("Unable to allocate memory for pfn bitmaps");
|
||||
+ return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -250,6 +289,9 @@ static int x86_hvm_stream_complete(struc
|
||||
static int x86_hvm_cleanup(struct xc_sr_context *ctx)
|
||||
{
|
||||
sr_bitmap_free(&ctx->restore.populated_pfns);
|
||||
+ sr_bitmap_free(&ctx->x86.hvm.restore.attempted_1g);
|
||||
+ sr_bitmap_free(&ctx->x86.hvm.restore.attempted_2m);
|
||||
+ sr_bitmap_free(&ctx->x86.hvm.restore.allocated_pfns);
|
||||
free(ctx->x86.hvm.restore.context.ptr);
|
||||
|
||||
free(ctx->x86.restore.cpuid.ptr);
|
||||
@@ -258,6 +300,336 @@ static int x86_hvm_cleanup(struct xc_sr_
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set a range of pfns as allocated
|
||||
+ */
|
||||
+static void pfn_set_long_allocated(struct xc_sr_context *ctx, xen_pfn_t base_pfn)
|
||||
+{
|
||||
+ sr_set_long_bit(base_pfn, &ctx->x86.hvm.restore.allocated_pfns);
|
||||
+}
|
||||
+
|
||||
+static void pfn_set_allocated(struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
+{
|
||||
+ sr_set_bit(pfn, &ctx->x86.hvm.restore.allocated_pfns);
|
||||
+}
|
||||
+
|
||||
+struct x86_hvm_sp {
|
||||
+ xen_pfn_t pfn;
|
||||
+ xen_pfn_t base_pfn;
|
||||
+ unsigned long index;
|
||||
+ unsigned long count;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Try to allocate a 1GB page for this pfn, but avoid Over-allocation.
|
||||
+ * If this succeeds, mark the range of 2MB pages as busy.
|
||||
+ */
|
||||
+static bool x86_hvm_alloc_1g(struct xc_sr_context *ctx, struct x86_hvm_sp *sp)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ unsigned int order;
|
||||
+ int i, done;
|
||||
+ xen_pfn_t extent;
|
||||
+
|
||||
+ /* Only one attempt to avoid overlapping allocation */
|
||||
+ if ( sr_test_and_set_bit(sp->index, &ctx->x86.hvm.restore.attempted_1g) )
|
||||
+ return false;
|
||||
+
|
||||
+ order = SUPERPAGE_1GB_SHIFT;
|
||||
+ sp->count = SUPERPAGE_1GB_NR_PFNS;
|
||||
+
|
||||
+ /* Allocate only if there is room for another superpage */
|
||||
+ if ( ctx->restore.tot_pages + sp->count > ctx->restore.max_pages )
|
||||
+ return false;
|
||||
+
|
||||
+ extent = sp->base_pfn = (sp->pfn >> order) << order;
|
||||
+ done = xc_domain_populate_physmap(xch, ctx->domid, 1, order, 0, &extent);
|
||||
+ if ( done < 0 ) {
|
||||
+ PERROR("populate_physmap failed.");
|
||||
+ return false;
|
||||
+ }
|
||||
+ if ( done == 0 )
|
||||
+ return false;
|
||||
+
|
||||
+ DPRINTF("1G %" PRI_xen_pfn "\n", sp->base_pfn);
|
||||
+
|
||||
+ /* Mark all 2MB pages as done to avoid overlapping allocation */
|
||||
+ for ( i = 0; i < (SUPERPAGE_1GB_NR_PFNS/SUPERPAGE_2MB_NR_PFNS); i++ )
|
||||
+ sr_set_bit((sp->base_pfn >> SUPERPAGE_2MB_SHIFT) + i, &ctx->x86.hvm.restore.attempted_2m);
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+/* Allocate a 2MB page if x86_hvm_alloc_1g failed, avoid Over-allocation. */
|
||||
+static bool x86_hvm_alloc_2m(struct xc_sr_context *ctx, struct x86_hvm_sp *sp)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ unsigned int order;
|
||||
+ int done;
|
||||
+ xen_pfn_t extent;
|
||||
+
|
||||
+ /* Only one attempt to avoid overlapping allocation */
|
||||
+ if ( sr_test_and_set_bit(sp->index, &ctx->x86.hvm.restore.attempted_2m) )
|
||||
+ return false;
|
||||
+
|
||||
+ order = SUPERPAGE_2MB_SHIFT;
|
||||
+ sp->count = SUPERPAGE_2MB_NR_PFNS;
|
||||
+
|
||||
+ /* Allocate only if there is room for another superpage */
|
||||
+ if ( ctx->restore.tot_pages + sp->count > ctx->restore.max_pages )
|
||||
+ return false;
|
||||
+
|
||||
+ extent = sp->base_pfn = (sp->pfn >> order) << order;
|
||||
+ done = xc_domain_populate_physmap(xch, ctx->domid, 1, order, 0, &extent);
|
||||
+ if ( done < 0 ) {
|
||||
+ PERROR("populate_physmap failed.");
|
||||
+ return false;
|
||||
+ }
|
||||
+ if ( done == 0 )
|
||||
+ return false;
|
||||
+
|
||||
+ DPRINTF("2M %" PRI_xen_pfn "\n", sp->base_pfn);
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+/* Allocate a single page if x86_hvm_alloc_2m failed. */
|
||||
+static bool x86_hvm_alloc_4k(struct xc_sr_context *ctx, struct x86_hvm_sp *sp)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ unsigned int order;
|
||||
+ int done;
|
||||
+ xen_pfn_t extent;
|
||||
+
|
||||
+ order = 0;
|
||||
+ sp->count = 1UL;
|
||||
+
|
||||
+ /* Allocate only if there is room for another page */
|
||||
+ if ( ctx->restore.tot_pages + sp->count > ctx->restore.max_pages ) {
|
||||
+ errno = E2BIG;
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ extent = sp->base_pfn = (sp->pfn >> order) << order;
|
||||
+ done = xc_domain_populate_physmap(xch, ctx->domid, 1, order, 0, &extent);
|
||||
+ if ( done < 0 ) {
|
||||
+ PERROR("populate_physmap failed.");
|
||||
+ return false;
|
||||
+ }
|
||||
+ if ( done == 0 ) {
|
||||
+ errno = ENOMEM;
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ DPRINTF("4K %" PRI_xen_pfn "\n", sp->base_pfn);
|
||||
+ return true;
|
||||
+}
|
||||
+/*
|
||||
+ * Attempt to allocate a superpage where the pfn resides.
|
||||
+ */
|
||||
+static int x86_hvm_allocate_pfn(struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
+{
|
||||
+ bool success;
|
||||
+ unsigned long idx_1g, idx_2m;
|
||||
+ struct x86_hvm_sp sp = {
|
||||
+ .pfn = pfn
|
||||
+ };
|
||||
+
|
||||
+ if ( sr_test_bit(pfn, &ctx->x86.hvm.restore.allocated_pfns) )
|
||||
+ return 0;
|
||||
+
|
||||
+ idx_1g = pfn >> SUPERPAGE_1GB_SHIFT;
|
||||
+ idx_2m = pfn >> SUPERPAGE_2MB_SHIFT;
|
||||
+
|
||||
+ sp.index = idx_1g;
|
||||
+ success = x86_hvm_alloc_1g(ctx, &sp);
|
||||
+
|
||||
+ if ( success == false ) {
|
||||
+ sp.index = idx_2m;
|
||||
+ success = x86_hvm_alloc_2m(ctx, &sp);
|
||||
+ }
|
||||
+
|
||||
+ if ( success == false ) {
|
||||
+ sp.index = 0;
|
||||
+ success = x86_hvm_alloc_4k(ctx, &sp);
|
||||
+ }
|
||||
+
|
||||
+ if ( success == false )
|
||||
+ return -1;
|
||||
+
|
||||
+ do {
|
||||
+ if ( sp.count >= BITS_PER_LONG && (sp.count % BITS_PER_LONG) == 0 ) {
|
||||
+ sp.count -= BITS_PER_LONG;
|
||||
+ ctx->restore.tot_pages += BITS_PER_LONG;
|
||||
+ pfn_set_long_allocated(ctx, sp.base_pfn + sp.count);
|
||||
+ } else {
|
||||
+ sp.count--;
|
||||
+ ctx->restore.tot_pages++;
|
||||
+ pfn_set_allocated(ctx, sp.base_pfn + sp.count);
|
||||
+ }
|
||||
+ } while ( sp.count );
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Deallocate memory.
|
||||
+ * There was likely an optimistic superpage allocation.
|
||||
+ * This means more pages may have been allocated past gap_end.
|
||||
+ * This range is not freed now. Incoming higher pfns will release it.
|
||||
+ */
|
||||
+static int x86_hvm_punch_hole(struct xc_sr_context *ctx,
|
||||
+ xen_pfn_t gap_start, xen_pfn_t gap_end)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ xen_pfn_t _pfn, pfn;
|
||||
+ uint32_t domid, freed = 0;
|
||||
+ int rc;
|
||||
+
|
||||
+ pfn = gap_start >> SUPERPAGE_1GB_SHIFT;
|
||||
+ do
|
||||
+ {
|
||||
+ sr_set_bit(pfn, &ctx->x86.hvm.restore.attempted_1g);
|
||||
+ } while (++pfn <= gap_end >> SUPERPAGE_1GB_SHIFT);
|
||||
+
|
||||
+ pfn = gap_start >> SUPERPAGE_2MB_SHIFT;
|
||||
+ do
|
||||
+ {
|
||||
+ sr_set_bit(pfn, &ctx->x86.hvm.restore.attempted_2m);
|
||||
+ } while (++pfn <= gap_end >> SUPERPAGE_2MB_SHIFT);
|
||||
+
|
||||
+ pfn = gap_start;
|
||||
+
|
||||
+ while ( pfn <= gap_end )
|
||||
+ {
|
||||
+ if ( sr_test_and_clear_bit(pfn, &ctx->x86.hvm.restore.allocated_pfns) )
|
||||
+ {
|
||||
+ domid = ctx->domid;
|
||||
+ _pfn = pfn;
|
||||
+ rc = xc_domain_decrease_reservation_exact(xch, domid, 1, 0, &_pfn);
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ PERROR("Failed to release pfn %" PRI_xen_pfn, pfn);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ ctx->restore.tot_pages--;
|
||||
+ freed++;
|
||||
+ }
|
||||
+ pfn++;
|
||||
+ }
|
||||
+ if ( freed )
|
||||
+ DPRINTF("freed %u between %" PRI_xen_pfn " %" PRI_xen_pfn "\n",
|
||||
+ freed, gap_start, gap_end);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int x86_hvm_unpopulate_page(struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
+{
|
||||
+ sr_clear_bit(pfn, &ctx->restore.populated_pfns);
|
||||
+ return x86_hvm_punch_hole(ctx, pfn, pfn);
|
||||
+}
|
||||
+
|
||||
+static int x86_hvm_populate_page(struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
+{
|
||||
+ xen_pfn_t gap_start, gap_end;
|
||||
+ bool has_gap, first_iteration;
|
||||
+ int rc;
|
||||
+
|
||||
+ /*
|
||||
+ * Check for a gap between the previous populated pfn and this pfn.
|
||||
+ * In case a gap exists, it is required to punch a hole to release memory,
|
||||
+ * starting after the previous pfn and before this pfn.
|
||||
+ *
|
||||
+ * But: this can be done only during the first iteration, which is the
|
||||
+ * only place where superpage allocations are attempted. All following
|
||||
+ * iterations lack the info to properly maintain prev_populated_pfn.
|
||||
+ */
|
||||
+ has_gap = ctx->x86.hvm.restore.prev_populated_pfn + 1 < pfn;
|
||||
+ first_iteration = ctx->x86.hvm.restore.iteration == 0;
|
||||
+ if ( has_gap && first_iteration )
|
||||
+ {
|
||||
+ gap_start = ctx->x86.hvm.restore.prev_populated_pfn + 1;
|
||||
+ gap_end = pfn - 1;
|
||||
+
|
||||
+ rc = x86_hvm_punch_hole(ctx, gap_start, gap_end);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ rc = x86_hvm_allocate_pfn(ctx, pfn);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+ pfn_set_populated(ctx, pfn);
|
||||
+ ctx->x86.hvm.restore.prev_populated_pfn = pfn;
|
||||
+
|
||||
+ rc = 0;
|
||||
+err:
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Try to allocate superpages.
|
||||
+ * This works without memory map because the pfns arrive in incremental order.
|
||||
+ * All pfn numbers and their type are submitted.
|
||||
+ * Only pfns with data will have also pfn content transmitted.
|
||||
+ */
|
||||
+static int x86_hvm_populate_pfns(struct xc_sr_context *ctx, unsigned count,
|
||||
+ const xen_pfn_t *original_pfns,
|
||||
+ const uint32_t *types)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ xen_pfn_t pfn, min_pfn, max_pfn;
|
||||
+ bool to_populate, populated;
|
||||
+ unsigned i = count;
|
||||
+ int rc = 0;
|
||||
+
|
||||
+ min_pfn = count ? original_pfns[0] : 0;
|
||||
+ max_pfn = count ? original_pfns[count - 1] : 0;
|
||||
+ DPRINTF("batch of %u pfns between %" PRI_xen_pfn " %" PRI_xen_pfn "\n",
|
||||
+ count, min_pfn, max_pfn);
|
||||
+
|
||||
+ if ( !x86_hvm_expand_sp_bitmaps(ctx, max_pfn) )
|
||||
+ {
|
||||
+ ERROR("Unable to allocate memory for pfn bitmaps");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * There is no indicator for a new iteration.
|
||||
+ * Simulate it by checking if a lower pfn is coming in.
|
||||
+ * In the end it matters only to know if this iteration is the first one.
|
||||
+ */
|
||||
+ if ( min_pfn < ctx->x86.hvm.restore.iteration_tracker_pfn )
|
||||
+ ctx->x86.hvm.restore.iteration++;
|
||||
+ ctx->x86.hvm.restore.iteration_tracker_pfn = min_pfn;
|
||||
+
|
||||
+ for ( i = 0; i < count; ++i )
|
||||
+ {
|
||||
+ pfn = original_pfns[i];
|
||||
+
|
||||
+ to_populate = page_type_to_populate(types[i]);
|
||||
+ populated = pfn_is_populated(ctx, pfn);
|
||||
+
|
||||
+ /*
|
||||
+ * page has data, pfn populated: nothing to do
|
||||
+ * page has data, pfn not populated: likely never seen before
|
||||
+ * page has no data, pfn populated: likely ballooned out during migration
|
||||
+ * page has no data, pfn not populated: nothing to do
|
||||
+ */
|
||||
+ if ( to_populate && !populated )
|
||||
+ {
|
||||
+ rc = x86_hvm_populate_page(ctx, pfn);
|
||||
+ } else if ( !to_populate && populated )
|
||||
+ {
|
||||
+ rc = x86_hvm_unpopulate_page(ctx, pfn);
|
||||
+ }
|
||||
+ if ( rc )
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+
|
||||
struct xc_sr_restore_ops restore_ops_x86_hvm =
|
||||
{
|
||||
.pfn_is_valid = x86_hvm_pfn_is_valid,
|
||||
@@ -266,6 +638,7 @@ struct xc_sr_restore_ops restore_ops_x86
|
||||
.set_page_type = x86_hvm_set_page_type,
|
||||
.localise_page = x86_hvm_localise_page,
|
||||
.setup = x86_hvm_setup,
|
||||
+ .populate_pfns = x86_hvm_populate_pfns,
|
||||
.process_record = x86_hvm_process_record,
|
||||
.static_data_complete = x86_static_data_complete,
|
||||
.stream_complete = x86_hvm_stream_complete,
|
||||
--- a/tools/libs/guest/xg_sr_restore_x86_pv.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore_x86_pv.c
|
||||
@@ -960,6 +960,64 @@ static void x86_pv_set_gfn(struct xc_sr_
|
||||
}
|
||||
|
||||
/*
|
||||
+ * Given a set of pfns, obtain memory from Xen to fill the physmap for the
|
||||
+ * unpopulated subset. If types is NULL, no page type checking is performed
|
||||
+ * and all unpopulated pfns are populated.
|
||||
+ */
|
||||
+static int x86_pv_populate_pfns(struct xc_sr_context *ctx, unsigned count,
|
||||
+ const xen_pfn_t *original_pfns,
|
||||
+ const uint32_t *types)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ xen_pfn_t *mfns = ctx->restore.pp_mfns,
|
||||
+ *pfns = ctx->restore.pp_pfns;
|
||||
+ unsigned int i, nr_pfns = 0;
|
||||
+ int rc = -1;
|
||||
+
|
||||
+ for ( i = 0; i < count; ++i )
|
||||
+ {
|
||||
+ if ( (!types ||
|
||||
+ (types && page_type_has_stream_data(types[i]) == true)) &&
|
||||
+ !pfn_is_populated(ctx, original_pfns[i]) )
|
||||
+ {
|
||||
+ rc = pfn_set_populated(ctx, original_pfns[i]);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+ pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i];
|
||||
+ ++nr_pfns;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if ( nr_pfns )
|
||||
+ {
|
||||
+ rc = xc_domain_populate_physmap_exact(
|
||||
+ xch, ctx->domid, nr_pfns, 0, 0, mfns);
|
||||
+ if ( rc )
|
||||
+ {
|
||||
+ PERROR("Failed to populate physmap");
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ for ( i = 0; i < nr_pfns; ++i )
|
||||
+ {
|
||||
+ if ( mfns[i] == INVALID_MFN )
|
||||
+ {
|
||||
+ ERROR("Populate physmap failed for pfn %u", i);
|
||||
+ rc = -1;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ ctx->restore.ops.set_gfn(ctx, pfns[i], mfns[i]);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ rc = 0;
|
||||
+
|
||||
+ err:
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
* restore_ops function. Convert pfns back to mfns in pagetables. Possibly
|
||||
* needs to populate new frames if a PTE is found referring to a frame which
|
||||
* hasn't yet been seen from PAGE_DATA records.
|
||||
@@ -1003,7 +1061,7 @@ static int x86_pv_localise_page(struct x
|
||||
}
|
||||
}
|
||||
|
||||
- if ( to_populate && populate_pfns(ctx, to_populate, pfns, NULL) )
|
||||
+ if ( to_populate && x86_pv_populate_pfns(ctx, to_populate, pfns, NULL) )
|
||||
return -1;
|
||||
|
||||
for ( i = 0; i < (PAGE_SIZE / sizeof(uint64_t)); ++i )
|
||||
@@ -1200,6 +1258,7 @@ struct xc_sr_restore_ops restore_ops_x86
|
||||
.set_gfn = x86_pv_set_gfn,
|
||||
.localise_page = x86_pv_localise_page,
|
||||
.setup = x86_pv_setup,
|
||||
+ .populate_pfns = x86_pv_populate_pfns,
|
||||
.process_record = x86_pv_process_record,
|
||||
.static_data_complete = x86_static_data_complete,
|
||||
.stream_complete = x86_pv_stream_complete,
|
101
libxc-sr-restore-map_errs.patch
Normal file
101
libxc-sr-restore-map_errs.patch
Normal file
@ -0,0 +1,101 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 14:44:09 +0200
|
||||
Subject: libxc sr restore map_errs
|
||||
|
||||
tools: restore: preallocate map_errs array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in an incoming batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_restore.c | 22 +++++++---------------
|
||||
2 files changed, 8 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -259,6 +259,7 @@ struct xc_sr_context
|
||||
xen_pfn_t *pfns;
|
||||
uint32_t *types;
|
||||
xen_pfn_t *mfns;
|
||||
+ int *map_errs;
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -204,21 +204,12 @@ static int process_page_data(struct xc_s
|
||||
xen_pfn_t *pfns, uint32_t *types, void *page_data)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- int *map_errs = malloc(count * sizeof(*map_errs));
|
||||
int rc;
|
||||
void *mapping = NULL, *guest_page = NULL;
|
||||
unsigned int i, /* i indexes the pfns from the record. */
|
||||
j, /* j indexes the subset of pfns we decide to map. */
|
||||
nr_pages = 0;
|
||||
|
||||
- if ( !map_errs )
|
||||
- {
|
||||
- rc = -1;
|
||||
- ERROR("Failed to allocate %zu bytes to process page data",
|
||||
- count * sizeof(*map_errs));
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
rc = populate_pfns(ctx, count, pfns, types);
|
||||
if ( rc )
|
||||
{
|
||||
@@ -240,7 +231,7 @@ static int process_page_data(struct xc_s
|
||||
|
||||
mapping = guest_page = xenforeignmemory_map(
|
||||
xch->fmem, ctx->domid, PROT_READ | PROT_WRITE,
|
||||
- nr_pages, ctx->restore.mfns, map_errs);
|
||||
+ nr_pages, ctx->restore.mfns, ctx->restore.map_errs);
|
||||
if ( !mapping )
|
||||
{
|
||||
rc = -1;
|
||||
@@ -254,11 +245,11 @@ static int process_page_data(struct xc_s
|
||||
if ( !page_type_has_stream_data(types[i]) )
|
||||
continue;
|
||||
|
||||
- if ( map_errs[j] )
|
||||
+ if ( ctx->restore.map_errs[j] )
|
||||
{
|
||||
rc = -1;
|
||||
ERROR("Mapping pfn %#"PRIpfn" (mfn %#"PRIpfn", type %#"PRIx32") failed with %d",
|
||||
- pfns[i], ctx->restore.mfns[j], types[i], map_errs[j]);
|
||||
+ pfns[i], ctx->restore.mfns[j], types[i], ctx->restore.map_errs[j]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -296,8 +287,6 @@ static int process_page_data(struct xc_s
|
||||
if ( mapping )
|
||||
xenforeignmemory_unmap(xch->fmem, mapping, nr_pages);
|
||||
|
||||
- free(map_errs);
|
||||
-
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -704,7 +693,9 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->restore.pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pfns));
|
||||
ctx->restore.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.types));
|
||||
ctx->restore.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.mfns));
|
||||
- if ( !ctx->restore.pfns || !ctx->restore.types || !ctx->restore.mfns )
|
||||
+ ctx->restore.map_errs = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.map_errs));
|
||||
+ if ( !ctx->restore.pfns || !ctx->restore.types || !ctx->restore.mfns ||
|
||||
+ !ctx->restore.map_errs )
|
||||
{
|
||||
ERROR("Unable to allocate memory");
|
||||
rc = -1;
|
||||
@@ -741,6 +732,7 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.map_errs);
|
||||
free(ctx->restore.mfns);
|
||||
free(ctx->restore.types);
|
||||
free(ctx->restore.pfns);
|
103
libxc-sr-restore-mfns.patch
Normal file
103
libxc-sr-restore-mfns.patch
Normal file
@ -0,0 +1,103 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 14:42:19 +0200
|
||||
Subject: libxc sr restore mfns
|
||||
|
||||
tools: restore: preallocate mfns array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in an incoming batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_restore.c | 16 ++++++++--------
|
||||
2 files changed, 9 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -258,6 +258,7 @@ struct xc_sr_context
|
||||
struct restore_callbacks *callbacks;
|
||||
xen_pfn_t *pfns;
|
||||
uint32_t *types;
|
||||
+ xen_pfn_t *mfns;
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -204,7 +204,6 @@ static int process_page_data(struct xc_s
|
||||
xen_pfn_t *pfns, uint32_t *types, void *page_data)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- xen_pfn_t *mfns = malloc(count * sizeof(*mfns));
|
||||
int *map_errs = malloc(count * sizeof(*map_errs));
|
||||
int rc;
|
||||
void *mapping = NULL, *guest_page = NULL;
|
||||
@@ -212,11 +211,11 @@ static int process_page_data(struct xc_s
|
||||
j, /* j indexes the subset of pfns we decide to map. */
|
||||
nr_pages = 0;
|
||||
|
||||
- if ( !mfns || !map_errs )
|
||||
+ if ( !map_errs )
|
||||
{
|
||||
rc = -1;
|
||||
ERROR("Failed to allocate %zu bytes to process page data",
|
||||
- count * (sizeof(*mfns) + sizeof(*map_errs)));
|
||||
+ count * sizeof(*map_errs));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -232,7 +231,7 @@ static int process_page_data(struct xc_s
|
||||
ctx->restore.ops.set_page_type(ctx, pfns[i], types[i]);
|
||||
|
||||
if ( page_type_has_stream_data(types[i]) )
|
||||
- mfns[nr_pages++] = ctx->restore.ops.pfn_to_gfn(ctx, pfns[i]);
|
||||
+ ctx->restore.mfns[nr_pages++] = ctx->restore.ops.pfn_to_gfn(ctx, pfns[i]);
|
||||
}
|
||||
|
||||
/* Nothing to do? */
|
||||
@@ -241,7 +240,7 @@ static int process_page_data(struct xc_s
|
||||
|
||||
mapping = guest_page = xenforeignmemory_map(
|
||||
xch->fmem, ctx->domid, PROT_READ | PROT_WRITE,
|
||||
- nr_pages, mfns, map_errs);
|
||||
+ nr_pages, ctx->restore.mfns, map_errs);
|
||||
if ( !mapping )
|
||||
{
|
||||
rc = -1;
|
||||
@@ -259,7 +258,7 @@ static int process_page_data(struct xc_s
|
||||
{
|
||||
rc = -1;
|
||||
ERROR("Mapping pfn %#"PRIpfn" (mfn %#"PRIpfn", type %#"PRIx32") failed with %d",
|
||||
- pfns[i], mfns[j], types[i], map_errs[j]);
|
||||
+ pfns[i], ctx->restore.mfns[j], types[i], map_errs[j]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -298,7 +297,6 @@ static int process_page_data(struct xc_s
|
||||
xenforeignmemory_unmap(xch->fmem, mapping, nr_pages);
|
||||
|
||||
free(map_errs);
|
||||
- free(mfns);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -705,7 +703,8 @@ static int setup(struct xc_sr_context *c
|
||||
|
||||
ctx->restore.pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pfns));
|
||||
ctx->restore.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.types));
|
||||
- if ( !ctx->restore.pfns || !ctx->restore.types )
|
||||
+ ctx->restore.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.mfns));
|
||||
+ if ( !ctx->restore.pfns || !ctx->restore.types || !ctx->restore.mfns )
|
||||
{
|
||||
ERROR("Unable to allocate memory");
|
||||
rc = -1;
|
||||
@@ -742,6 +741,7 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.mfns);
|
||||
free(ctx->restore.types);
|
||||
free(ctx->restore.pfns);
|
||||
|
108
libxc-sr-restore-pfns.patch
Normal file
108
libxc-sr-restore-pfns.patch
Normal file
@ -0,0 +1,108 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 14:39:30 +0200
|
||||
Subject: libxc sr restore pfns
|
||||
|
||||
tools: restore: preallocate pfns array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in an incoming batch.
|
||||
Allocate the space once.
|
||||
|
||||
Adjust the verification for page count. It must be at least one page,
|
||||
but not more than MAX_BATCH_SIZE.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_restore.c | 23 +++++++++++++++--------
|
||||
2 files changed, 16 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -256,6 +256,7 @@ struct xc_sr_context
|
||||
{
|
||||
struct xc_sr_restore_ops ops;
|
||||
struct restore_callbacks *callbacks;
|
||||
+ xen_pfn_t *pfns;
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -314,7 +314,7 @@ static int handle_page_data(struct xc_sr
|
||||
unsigned int i, pages_of_data = 0;
|
||||
int rc = -1;
|
||||
|
||||
- xen_pfn_t *pfns = NULL, pfn;
|
||||
+ xen_pfn_t pfn;
|
||||
uint32_t *types = NULL, type;
|
||||
|
||||
/*
|
||||
@@ -349,9 +349,9 @@ static int handle_page_data(struct xc_sr
|
||||
goto err;
|
||||
}
|
||||
|
||||
- if ( pages->count < 1 )
|
||||
+ if ( !pages->count || pages->count > MAX_BATCH_SIZE )
|
||||
{
|
||||
- ERROR("Expected at least 1 pfn in PAGE_DATA record");
|
||||
+ ERROR("Unexpected pfn count %u in PAGE_DATA record", pages->count);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -362,9 +362,8 @@ static int handle_page_data(struct xc_sr
|
||||
goto err;
|
||||
}
|
||||
|
||||
- pfns = malloc(pages->count * sizeof(*pfns));
|
||||
types = malloc(pages->count * sizeof(*types));
|
||||
- if ( !pfns || !types )
|
||||
+ if ( !types )
|
||||
{
|
||||
ERROR("Unable to allocate enough memory for %u pfns",
|
||||
pages->count);
|
||||
@@ -393,7 +392,7 @@ static int handle_page_data(struct xc_sr
|
||||
* have a page worth of data in the record. */
|
||||
pages_of_data++;
|
||||
|
||||
- pfns[i] = pfn;
|
||||
+ ctx->restore.pfns[i] = pfn;
|
||||
types[i] = type;
|
||||
}
|
||||
|
||||
@@ -407,11 +406,10 @@ static int handle_page_data(struct xc_sr
|
||||
goto err;
|
||||
}
|
||||
|
||||
- rc = process_page_data(ctx, pages->count, pfns, types,
|
||||
+ rc = process_page_data(ctx, pages->count, ctx->restore.pfns, types,
|
||||
&pages->pfn[pages->count]);
|
||||
err:
|
||||
free(types);
|
||||
- free(pfns);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -715,6 +713,14 @@ static int setup(struct xc_sr_context *c
|
||||
goto err;
|
||||
}
|
||||
|
||||
+ ctx->restore.pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pfns));
|
||||
+ if ( !ctx->restore.pfns )
|
||||
+ {
|
||||
+ ERROR("Unable to allocate memory");
|
||||
+ rc = -1;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
ctx->restore.buffered_records = malloc(
|
||||
DEFAULT_BUF_RECORDS * sizeof(struct xc_sr_record));
|
||||
if ( !ctx->restore.buffered_records )
|
||||
@@ -745,6 +751,7 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.pfns);
|
||||
|
||||
if ( ctx->restore.ops.cleanup(ctx) )
|
||||
PERROR("Failed to clean up");
|
111
libxc-sr-restore-populate_pfns-mfns.patch
Normal file
111
libxc-sr-restore-populate_pfns-mfns.patch
Normal file
@ -0,0 +1,111 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 14:54:12 +0200
|
||||
Subject: libxc sr restore populate_pfns mfns
|
||||
|
||||
tools: restore: preallocate populate_pfns mfns array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in an incoming batch.
|
||||
Allocate the space once.
|
||||
|
||||
Use some prefix to avoid conflict with an array used in handle_page_data.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_restore.c | 23 ++++++++---------------
|
||||
2 files changed, 9 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -261,6 +261,7 @@ struct xc_sr_context
|
||||
xen_pfn_t *mfns;
|
||||
int *map_errs;
|
||||
xen_pfn_t *pp_pfns;
|
||||
+ xen_pfn_t *pp_mfns;
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -138,17 +138,9 @@ int populate_pfns(struct xc_sr_context *
|
||||
const xen_pfn_t *original_pfns, const uint32_t *types)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- xen_pfn_t *mfns = malloc(count * sizeof(*mfns));
|
||||
unsigned int i, nr_pfns = 0;
|
||||
int rc = -1;
|
||||
|
||||
- if ( !mfns )
|
||||
- {
|
||||
- ERROR("Failed to allocate %zu bytes for populating the physmap",
|
||||
- 2 * count * sizeof(*mfns));
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
for ( i = 0; i < count; ++i )
|
||||
{
|
||||
if ( (!types || page_type_to_populate(types[i])) &&
|
||||
@@ -157,7 +149,7 @@ int populate_pfns(struct xc_sr_context *
|
||||
rc = pfn_set_populated(ctx, original_pfns[i]);
|
||||
if ( rc )
|
||||
goto err;
|
||||
- ctx->restore.pp_pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i];
|
||||
+ ctx->restore.pp_pfns[nr_pfns] = ctx->restore.pp_mfns[nr_pfns] = original_pfns[i];
|
||||
++nr_pfns;
|
||||
}
|
||||
}
|
||||
@@ -165,7 +157,7 @@ int populate_pfns(struct xc_sr_context *
|
||||
if ( nr_pfns )
|
||||
{
|
||||
rc = xc_domain_populate_physmap_exact(
|
||||
- xch, ctx->domid, nr_pfns, 0, 0, mfns);
|
||||
+ xch, ctx->domid, nr_pfns, 0, 0, ctx->restore.pp_mfns);
|
||||
if ( rc )
|
||||
{
|
||||
PERROR("Failed to populate physmap");
|
||||
@@ -174,22 +166,20 @@ int populate_pfns(struct xc_sr_context *
|
||||
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
- if ( mfns[i] == INVALID_MFN )
|
||||
+ if ( ctx->restore.pp_mfns[i] == INVALID_MFN )
|
||||
{
|
||||
ERROR("Populate physmap failed for pfn %u", i);
|
||||
rc = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
- ctx->restore.ops.set_gfn(ctx, ctx->restore.pp_pfns[i], mfns[i]);
|
||||
+ ctx->restore.ops.set_gfn(ctx, ctx->restore.pp_pfns[i], ctx->restore.pp_mfns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
err:
|
||||
- free(mfns);
|
||||
-
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -693,8 +683,10 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->restore.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.mfns));
|
||||
ctx->restore.map_errs = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.map_errs));
|
||||
ctx->restore.pp_pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pp_pfns));
|
||||
+ ctx->restore.pp_mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pp_mfns));
|
||||
if ( !ctx->restore.pfns || !ctx->restore.types || !ctx->restore.mfns ||
|
||||
- !ctx->restore.map_errs || !ctx->restore.pp_pfns )
|
||||
+ !ctx->restore.map_errs || !ctx->restore.pp_pfns ||
|
||||
+ !ctx->restore.pp_mfns )
|
||||
{
|
||||
ERROR("Unable to allocate memory");
|
||||
rc = -1;
|
||||
@@ -731,6 +723,7 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.pp_mfns);
|
||||
free(ctx->restore.pp_pfns);
|
||||
free(ctx->restore.map_errs);
|
||||
free(ctx->restore.mfns);
|
89
libxc-sr-restore-populate_pfns-pfns.patch
Normal file
89
libxc-sr-restore-populate_pfns-pfns.patch
Normal file
@ -0,0 +1,89 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 14:58:53 +0200
|
||||
Subject: libxc sr restore populate_pfns pfns
|
||||
|
||||
tools: restore: preallocate populate_pfns pfns array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in an incoming batch.
|
||||
Allocate the space once.
|
||||
|
||||
Use some prefix to avoid conflict with an array used in handle_page_data.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_restore.c | 14 +++++++-------
|
||||
2 files changed, 8 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -260,6 +260,7 @@ struct xc_sr_context
|
||||
uint32_t *types;
|
||||
xen_pfn_t *mfns;
|
||||
int *map_errs;
|
||||
+ xen_pfn_t *pp_pfns;
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -138,12 +138,11 @@ int populate_pfns(struct xc_sr_context *
|
||||
const xen_pfn_t *original_pfns, const uint32_t *types)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- xen_pfn_t *mfns = malloc(count * sizeof(*mfns)),
|
||||
- *pfns = malloc(count * sizeof(*pfns));
|
||||
+ xen_pfn_t *mfns = malloc(count * sizeof(*mfns));
|
||||
unsigned int i, nr_pfns = 0;
|
||||
int rc = -1;
|
||||
|
||||
- if ( !mfns || !pfns )
|
||||
+ if ( !mfns )
|
||||
{
|
||||
ERROR("Failed to allocate %zu bytes for populating the physmap",
|
||||
2 * count * sizeof(*mfns));
|
||||
@@ -158,7 +157,7 @@ int populate_pfns(struct xc_sr_context *
|
||||
rc = pfn_set_populated(ctx, original_pfns[i]);
|
||||
if ( rc )
|
||||
goto err;
|
||||
- pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i];
|
||||
+ ctx->restore.pp_pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i];
|
||||
++nr_pfns;
|
||||
}
|
||||
}
|
||||
@@ -182,14 +181,13 @@ int populate_pfns(struct xc_sr_context *
|
||||
goto err;
|
||||
}
|
||||
|
||||
- ctx->restore.ops.set_gfn(ctx, pfns[i], mfns[i]);
|
||||
+ ctx->restore.ops.set_gfn(ctx, ctx->restore.pp_pfns[i], mfns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
err:
|
||||
- free(pfns);
|
||||
free(mfns);
|
||||
|
||||
return rc;
|
||||
@@ -694,8 +692,9 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->restore.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.types));
|
||||
ctx->restore.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.mfns));
|
||||
ctx->restore.map_errs = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.map_errs));
|
||||
+ ctx->restore.pp_pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pp_pfns));
|
||||
if ( !ctx->restore.pfns || !ctx->restore.types || !ctx->restore.mfns ||
|
||||
- !ctx->restore.map_errs )
|
||||
+ !ctx->restore.map_errs || !ctx->restore.pp_pfns )
|
||||
{
|
||||
ERROR("Unable to allocate memory");
|
||||
rc = -1;
|
||||
@@ -732,6 +731,7 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.pp_pfns);
|
||||
free(ctx->restore.map_errs);
|
||||
free(ctx->restore.mfns);
|
||||
free(ctx->restore.types);
|
272
libxc-sr-restore-read_record.patch
Normal file
272
libxc-sr-restore-read_record.patch
Normal file
@ -0,0 +1,272 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Mon, 26 Oct 2020 12:19:17 +0100
|
||||
Subject: libxc sr restore read_record
|
||||
|
||||
tools: restore: split record processing
|
||||
|
||||
handle_page_data must be able to read directly into mapped guest memory.
|
||||
This will avoid unneccesary memcpy calls for data which can be consumed verbatim.
|
||||
|
||||
Rearrange the code to allow decisions based on the incoming record.
|
||||
|
||||
This change is preparation for future changes in handle_page_data,
|
||||
no change in behavior is intended.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
Reviewed-by: Juergen Gross <jgross@suse.com>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.c | 33 ++++++++++++---------
|
||||
tools/libs/guest/xg_sr_common.h | 4 ++-
|
||||
tools/libs/guest/xg_sr_restore.c | 49 ++++++++++++++++++++++----------
|
||||
tools/libs/guest/xg_sr_save.c | 7 ++++-
|
||||
4 files changed, 63 insertions(+), 30 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.c
|
||||
+++ b/tools/libs/guest/xg_sr_common.c
|
||||
@@ -91,26 +91,33 @@ int write_split_record(struct xc_sr_cont
|
||||
return -1;
|
||||
}
|
||||
|
||||
-int read_record(struct xc_sr_context *ctx, int fd, struct xc_sr_record *rec)
|
||||
+int read_record_header(struct xc_sr_context *ctx, int fd, struct xc_sr_rhdr *rhdr)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- struct xc_sr_rhdr rhdr;
|
||||
- size_t datasz;
|
||||
|
||||
- if ( read_exact(fd, &rhdr, sizeof(rhdr)) )
|
||||
+ if ( read_exact(fd, rhdr, sizeof(*rhdr)) )
|
||||
{
|
||||
PERROR("Failed to read Record Header from stream");
|
||||
return -1;
|
||||
}
|
||||
|
||||
- if ( rhdr.length > REC_LENGTH_MAX )
|
||||
+ if ( rhdr->length > REC_LENGTH_MAX )
|
||||
{
|
||||
- ERROR("Record (0x%08x, %s) length %#x exceeds max (%#x)", rhdr.type,
|
||||
- rec_type_to_str(rhdr.type), rhdr.length, REC_LENGTH_MAX);
|
||||
+ ERROR("Record (0x%08x, %s) length %#x exceeds max (%#x)", rhdr->type,
|
||||
+ rec_type_to_str(rhdr->type), rhdr->length, REC_LENGTH_MAX);
|
||||
return -1;
|
||||
}
|
||||
|
||||
- datasz = ROUNDUP(rhdr.length, REC_ALIGN_ORDER);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int read_record_data(struct xc_sr_context *ctx, int fd, struct xc_sr_rhdr *rhdr,
|
||||
+ struct xc_sr_record *rec)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ size_t datasz;
|
||||
+
|
||||
+ datasz = ROUNDUP(rhdr->length, REC_ALIGN_ORDER);
|
||||
|
||||
if ( datasz )
|
||||
{
|
||||
@@ -119,7 +126,7 @@ int read_record(struct xc_sr_context *ct
|
||||
if ( !rec->data )
|
||||
{
|
||||
ERROR("Unable to allocate %zu bytes for record data (0x%08x, %s)",
|
||||
- datasz, rhdr.type, rec_type_to_str(rhdr.type));
|
||||
+ datasz, rhdr->type, rec_type_to_str(rhdr->type));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -128,18 +135,18 @@ int read_record(struct xc_sr_context *ct
|
||||
free(rec->data);
|
||||
rec->data = NULL;
|
||||
PERROR("Failed to read %zu bytes of data for record (0x%08x, %s)",
|
||||
- datasz, rhdr.type, rec_type_to_str(rhdr.type));
|
||||
+ datasz, rhdr->type, rec_type_to_str(rhdr->type));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
rec->data = NULL;
|
||||
|
||||
- rec->type = rhdr.type;
|
||||
- rec->length = rhdr.length;
|
||||
+ rec->type = rhdr->type;
|
||||
+ rec->length = rhdr->length;
|
||||
|
||||
return 0;
|
||||
-};
|
||||
+}
|
||||
|
||||
static void __attribute__((unused)) build_assertions(void)
|
||||
{
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -458,7 +458,9 @@ static inline int write_record(struct xc
|
||||
*
|
||||
* On failure, the contents of the record structure are undefined.
|
||||
*/
|
||||
-int read_record(struct xc_sr_context *ctx, int fd, struct xc_sr_record *rec);
|
||||
+int read_record_header(struct xc_sr_context *ctx, int fd, struct xc_sr_rhdr *rhdr);
|
||||
+int read_record_data(struct xc_sr_context *ctx, int fd, struct xc_sr_rhdr *rhdr,
|
||||
+ struct xc_sr_record *rec);
|
||||
|
||||
/*
|
||||
* This would ideally be private in restore.c, but is needed by
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -453,7 +453,7 @@ static int send_checkpoint_dirty_pfn_lis
|
||||
return rc;
|
||||
}
|
||||
|
||||
-static int process_record(struct xc_sr_context *ctx, struct xc_sr_record *rec);
|
||||
+static int process_buffered_record(struct xc_sr_context *ctx, struct xc_sr_record *rec);
|
||||
static int handle_checkpoint(struct xc_sr_context *ctx)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
@@ -492,7 +492,7 @@ static int handle_checkpoint(struct xc_s
|
||||
|
||||
for ( i = 0; i < ctx->restore.buffered_rec_num; i++ )
|
||||
{
|
||||
- rc = process_record(ctx, &ctx->restore.buffered_records[i]);
|
||||
+ rc = process_buffered_record(ctx, &ctx->restore.buffered_records[i]);
|
||||
if ( rc )
|
||||
goto err;
|
||||
}
|
||||
@@ -553,10 +553,11 @@ static int handle_checkpoint(struct xc_s
|
||||
return rc;
|
||||
}
|
||||
|
||||
-static int buffer_record(struct xc_sr_context *ctx, struct xc_sr_record *rec)
|
||||
+static int buffer_record(struct xc_sr_context *ctx, struct xc_sr_rhdr *rhdr)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
unsigned int new_alloc_num;
|
||||
+ struct xc_sr_record rec;
|
||||
struct xc_sr_record *p;
|
||||
|
||||
if ( ctx->restore.buffered_rec_num >= ctx->restore.allocated_rec_num )
|
||||
@@ -574,8 +575,13 @@ static int buffer_record(struct xc_sr_co
|
||||
ctx->restore.allocated_rec_num = new_alloc_num;
|
||||
}
|
||||
|
||||
+ if ( read_record_data(ctx, ctx->fd, rhdr, &rec) )
|
||||
+ {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
memcpy(&ctx->restore.buffered_records[ctx->restore.buffered_rec_num++],
|
||||
- rec, sizeof(*rec));
|
||||
+ &rec, sizeof(rec));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -606,7 +612,7 @@ int handle_static_data_end(struct xc_sr_
|
||||
return rc;
|
||||
}
|
||||
|
||||
-static int process_record(struct xc_sr_context *ctx, struct xc_sr_record *rec)
|
||||
+static int process_buffered_record(struct xc_sr_context *ctx, struct xc_sr_record *rec)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
int rc = 0;
|
||||
@@ -644,6 +650,19 @@ static int process_record(struct xc_sr_c
|
||||
return rc;
|
||||
}
|
||||
|
||||
+static int process_incoming_record_header(struct xc_sr_context *ctx, struct xc_sr_rhdr *rhdr)
|
||||
+{
|
||||
+ struct xc_sr_record rec;
|
||||
+ int rc;
|
||||
+
|
||||
+ rc = read_record_data(ctx, ctx->fd, rhdr, &rec);
|
||||
+ if ( rc )
|
||||
+ return rc;
|
||||
+
|
||||
+ return process_buffered_record(ctx, &rec);
|
||||
+}
|
||||
+
|
||||
+
|
||||
static int setup(struct xc_sr_context *ctx)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
@@ -740,7 +759,7 @@ static void cleanup(struct xc_sr_context
|
||||
static int restore(struct xc_sr_context *ctx)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- struct xc_sr_record rec;
|
||||
+ struct xc_sr_rhdr rhdr;
|
||||
int rc, saved_rc = 0, saved_errno = 0;
|
||||
|
||||
IPRINTF("Restoring domain");
|
||||
@@ -751,7 +770,7 @@ static int restore(struct xc_sr_context
|
||||
|
||||
do
|
||||
{
|
||||
- rc = read_record(ctx, ctx->fd, &rec);
|
||||
+ rc = read_record_header(ctx, ctx->fd, &rhdr);
|
||||
if ( rc )
|
||||
{
|
||||
if ( ctx->restore.buffer_all_records )
|
||||
@@ -761,25 +780,25 @@ static int restore(struct xc_sr_context
|
||||
}
|
||||
|
||||
if ( ctx->restore.buffer_all_records &&
|
||||
- rec.type != REC_TYPE_END &&
|
||||
- rec.type != REC_TYPE_CHECKPOINT )
|
||||
+ rhdr.type != REC_TYPE_END &&
|
||||
+ rhdr.type != REC_TYPE_CHECKPOINT )
|
||||
{
|
||||
- rc = buffer_record(ctx, &rec);
|
||||
+ rc = buffer_record(ctx, &rhdr);
|
||||
if ( rc )
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
- rc = process_record(ctx, &rec);
|
||||
+ rc = process_incoming_record_header(ctx, &rhdr);
|
||||
if ( rc == RECORD_NOT_PROCESSED )
|
||||
{
|
||||
- if ( rec.type & REC_TYPE_OPTIONAL )
|
||||
+ if ( rhdr.type & REC_TYPE_OPTIONAL )
|
||||
DPRINTF("Ignoring optional record %#x (%s)",
|
||||
- rec.type, rec_type_to_str(rec.type));
|
||||
+ rhdr.type, rec_type_to_str(rhdr.type));
|
||||
else
|
||||
{
|
||||
ERROR("Mandatory record %#x (%s) not handled",
|
||||
- rec.type, rec_type_to_str(rec.type));
|
||||
+ rhdr.type, rec_type_to_str(rhdr.type));
|
||||
rc = -1;
|
||||
goto err;
|
||||
}
|
||||
@@ -790,7 +809,7 @@ static int restore(struct xc_sr_context
|
||||
goto err;
|
||||
}
|
||||
|
||||
- } while ( rec.type != REC_TYPE_END );
|
||||
+ } while ( rhdr.type != REC_TYPE_END );
|
||||
|
||||
remus_failover:
|
||||
if ( ctx->stream_type == XC_STREAM_COLO )
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -590,6 +590,7 @@ static int send_memory_live(struct xc_sr
|
||||
static int colo_merge_secondary_dirty_bitmap(struct xc_sr_context *ctx)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
+ struct xc_sr_rhdr rhdr;
|
||||
struct xc_sr_record rec;
|
||||
uint64_t *pfns = NULL;
|
||||
uint64_t pfn;
|
||||
@@ -598,7 +599,11 @@ static int colo_merge_secondary_dirty_bi
|
||||
DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap,
|
||||
&ctx->save.dirty_bitmap_hbuf);
|
||||
|
||||
- rc = read_record(ctx, ctx->save.recv_fd, &rec);
|
||||
+ rc = read_record_header(ctx, ctx->save.recv_fd, &rhdr);
|
||||
+ if ( rc )
|
||||
+ goto err;
|
||||
+
|
||||
+ rc = read_record_data(ctx, ctx->save.recv_fd, &rhdr, &rec);
|
||||
if ( rc )
|
||||
goto err;
|
||||
|
93
libxc-sr-restore-types.patch
Normal file
93
libxc-sr-restore-types.patch
Normal file
@ -0,0 +1,93 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 14:39:31 +0200
|
||||
Subject: libxc sr restore types
|
||||
|
||||
tools: restore: preallocate types array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in an incoming batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_restore.c | 22 +++++++---------------
|
||||
2 files changed, 8 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -257,6 +257,7 @@ struct xc_sr_context
|
||||
struct xc_sr_restore_ops ops;
|
||||
struct restore_callbacks *callbacks;
|
||||
xen_pfn_t *pfns;
|
||||
+ uint32_t *types;
|
||||
|
||||
int send_back_fd;
|
||||
unsigned long p2m_size;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -315,7 +315,7 @@ static int handle_page_data(struct xc_sr
|
||||
int rc = -1;
|
||||
|
||||
xen_pfn_t pfn;
|
||||
- uint32_t *types = NULL, type;
|
||||
+ uint32_t type;
|
||||
|
||||
/*
|
||||
* v2 compatibility only exists for x86 streams. This is a bit of a
|
||||
@@ -362,14 +362,6 @@ static int handle_page_data(struct xc_sr
|
||||
goto err;
|
||||
}
|
||||
|
||||
- types = malloc(pages->count * sizeof(*types));
|
||||
- if ( !types )
|
||||
- {
|
||||
- ERROR("Unable to allocate enough memory for %u pfns",
|
||||
- pages->count);
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
for ( i = 0; i < pages->count; ++i )
|
||||
{
|
||||
pfn = pages->pfn[i] & PAGE_DATA_PFN_MASK;
|
||||
@@ -393,7 +385,7 @@ static int handle_page_data(struct xc_sr
|
||||
pages_of_data++;
|
||||
|
||||
ctx->restore.pfns[i] = pfn;
|
||||
- types[i] = type;
|
||||
+ ctx->restore.types[i] = type;
|
||||
}
|
||||
|
||||
if ( rec->length != (sizeof(*pages) +
|
||||
@@ -406,11 +398,9 @@ static int handle_page_data(struct xc_sr
|
||||
goto err;
|
||||
}
|
||||
|
||||
- rc = process_page_data(ctx, pages->count, ctx->restore.pfns, types,
|
||||
- &pages->pfn[pages->count]);
|
||||
+ rc = process_page_data(ctx, pages->count, ctx->restore.pfns,
|
||||
+ ctx->restore.types, &pages->pfn[pages->count]);
|
||||
err:
|
||||
- free(types);
|
||||
-
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -714,7 +704,8 @@ static int setup(struct xc_sr_context *c
|
||||
}
|
||||
|
||||
ctx->restore.pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pfns));
|
||||
- if ( !ctx->restore.pfns )
|
||||
+ ctx->restore.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.types));
|
||||
+ if ( !ctx->restore.pfns || !ctx->restore.types )
|
||||
{
|
||||
ERROR("Unable to allocate memory");
|
||||
rc = -1;
|
||||
@@ -751,6 +742,7 @@ static void cleanup(struct xc_sr_context
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
free(ctx->restore.populated_pfns);
|
||||
+ free(ctx->restore.types);
|
||||
free(ctx->restore.pfns);
|
||||
|
||||
if ( ctx->restore.ops.cleanup(ctx) )
|
109
libxc-sr-save-errors.patch
Normal file
109
libxc-sr-save-errors.patch
Normal file
@ -0,0 +1,109 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 11:26:05 +0200
|
||||
Subject: libxc sr save errors
|
||||
|
||||
tools: save: preallocate errors array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in a batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_save.c | 20 ++++++++++----------
|
||||
2 files changed, 11 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -246,6 +246,7 @@ struct xc_sr_context
|
||||
xen_pfn_t *batch_pfns;
|
||||
xen_pfn_t *mfns;
|
||||
xen_pfn_t *types;
|
||||
+ int *errors;
|
||||
unsigned int nr_batch_pfns;
|
||||
unsigned long *deferred_pages;
|
||||
unsigned long nr_deferred_pages;
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -91,7 +91,7 @@ static int write_batch(struct xc_sr_cont
|
||||
void *guest_mapping = NULL;
|
||||
void **guest_data = NULL;
|
||||
void **local_pages = NULL;
|
||||
- int *errors = NULL, rc = -1;
|
||||
+ int rc = -1;
|
||||
unsigned int i, p, nr_pages = 0, nr_pages_mapped = 0;
|
||||
unsigned int nr_pfns = ctx->save.nr_batch_pfns;
|
||||
void *page, *orig_page;
|
||||
@@ -104,8 +104,6 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
assert(nr_pfns != 0);
|
||||
|
||||
- /* Errors from attempting to map the gfns. */
|
||||
- errors = malloc(nr_pfns * sizeof(*errors));
|
||||
/* Pointers to page data to send. Mapped gfns or local allocations. */
|
||||
guest_data = calloc(nr_pfns, sizeof(*guest_data));
|
||||
/* Pointers to locally allocated pages. Need freeing. */
|
||||
@@ -113,7 +111,7 @@ static int write_batch(struct xc_sr_cont
|
||||
/* iovec[] for writev(). */
|
||||
iov = malloc((nr_pfns + 4) * sizeof(*iov));
|
||||
|
||||
- if ( !errors || !guest_data || !local_pages || !iov )
|
||||
+ if ( !guest_data || !local_pages || !iov )
|
||||
{
|
||||
ERROR("Unable to allocate arrays for a batch of %u pages",
|
||||
nr_pfns);
|
||||
@@ -158,8 +156,8 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
if ( nr_pages > 0 )
|
||||
{
|
||||
- guest_mapping = xenforeignmemory_map(
|
||||
- xch->fmem, ctx->domid, PROT_READ, nr_pages, ctx->save.mfns, errors);
|
||||
+ guest_mapping = xenforeignmemory_map(xch->fmem, ctx->domid, PROT_READ,
|
||||
+ nr_pages, ctx->save.mfns, ctx->save.errors);
|
||||
if ( !guest_mapping )
|
||||
{
|
||||
PERROR("Failed to map guest pages");
|
||||
@@ -172,10 +170,11 @@ static int write_batch(struct xc_sr_cont
|
||||
if ( !page_type_has_stream_data(ctx->save.types[i]) )
|
||||
continue;
|
||||
|
||||
- if ( errors[p] )
|
||||
+ if ( ctx->save.errors[p] )
|
||||
{
|
||||
ERROR("Mapping of pfn %#"PRIpfn" (mfn %#"PRIpfn") failed %d",
|
||||
- ctx->save.batch_pfns[i], ctx->save.mfns[p], errors[p]);
|
||||
+ ctx->save.batch_pfns[i], ctx->save.mfns[p],
|
||||
+ ctx->save.errors[p]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -271,7 +270,6 @@ static int write_batch(struct xc_sr_cont
|
||||
free(iov);
|
||||
free(local_pages);
|
||||
free(guest_data);
|
||||
- free(errors);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -846,10 +844,11 @@ static int setup(struct xc_sr_context *c
|
||||
sizeof(*ctx->save.batch_pfns));
|
||||
ctx->save.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.mfns));
|
||||
ctx->save.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.types));
|
||||
+ ctx->save.errors = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.errors));
|
||||
ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
|
||||
|
||||
if ( !ctx->save.batch_pfns || !ctx->save.mfns || !ctx->save.types ||
|
||||
- !dirty_bitmap || !ctx->save.deferred_pages )
|
||||
+ !ctx->save.errors || !dirty_bitmap || !ctx->save.deferred_pages )
|
||||
{
|
||||
ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
|
||||
" deferred pages");
|
||||
@@ -880,6 +879,7 @@ static void cleanup(struct xc_sr_context
|
||||
xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
|
||||
NRPAGES(bitmap_size(ctx->save.p2m_size)));
|
||||
free(ctx->save.deferred_pages);
|
||||
+ free(ctx->save.errors);
|
||||
free(ctx->save.types);
|
||||
free(ctx->save.mfns);
|
||||
free(ctx->save.batch_pfns);
|
123
libxc-sr-save-guest_data.patch
Normal file
123
libxc-sr-save-guest_data.patch
Normal file
@ -0,0 +1,123 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 11:40:45 +0200
|
||||
Subject: libxc sr save guest_data
|
||||
|
||||
tools: save: preallocate guest_data array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in a batch.
|
||||
Allocate the space once.
|
||||
|
||||
Because this was allocated with calloc:
|
||||
Adjust the loop to clear unused entries as needed.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_save.c | 20 +++++++++++---------
|
||||
2 files changed, 12 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -249,6 +249,7 @@ struct xc_sr_context
|
||||
int *errors;
|
||||
struct iovec *iov;
|
||||
uint64_t *rec_pfns;
|
||||
+ void **guest_data;
|
||||
unsigned int nr_batch_pfns;
|
||||
unsigned long *deferred_pages;
|
||||
unsigned long nr_deferred_pages;
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -89,7 +89,6 @@ static int write_batch(struct xc_sr_cont
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
void *guest_mapping = NULL;
|
||||
- void **guest_data = NULL;
|
||||
void **local_pages = NULL;
|
||||
int rc = -1;
|
||||
unsigned int i, p, nr_pages = 0, nr_pages_mapped = 0;
|
||||
@@ -103,12 +102,10 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
assert(nr_pfns != 0);
|
||||
|
||||
- /* Pointers to page data to send. Mapped gfns or local allocations. */
|
||||
- guest_data = calloc(nr_pfns, sizeof(*guest_data));
|
||||
/* Pointers to locally allocated pages. Need freeing. */
|
||||
local_pages = calloc(nr_pfns, sizeof(*local_pages));
|
||||
|
||||
- if ( !guest_data || !local_pages )
|
||||
+ if ( !local_pages )
|
||||
{
|
||||
ERROR("Unable to allocate arrays for a batch of %u pages",
|
||||
nr_pfns);
|
||||
@@ -165,7 +162,10 @@ static int write_batch(struct xc_sr_cont
|
||||
for ( i = 0, p = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
if ( !page_type_has_stream_data(ctx->save.types[i]) )
|
||||
+ {
|
||||
+ ctx->save.guest_data[i] = NULL;
|
||||
continue;
|
||||
+ }
|
||||
|
||||
if ( ctx->save.errors[p] )
|
||||
{
|
||||
@@ -183,6 +183,7 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
if ( rc )
|
||||
{
|
||||
+ ctx->save.guest_data[i] = NULL;
|
||||
if ( rc == -1 && errno == EAGAIN )
|
||||
{
|
||||
set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
|
||||
@@ -194,7 +195,7 @@ static int write_batch(struct xc_sr_cont
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
- guest_data[i] = page;
|
||||
+ ctx->save.guest_data[i] = page;
|
||||
|
||||
rc = -1;
|
||||
++p;
|
||||
@@ -232,9 +233,9 @@ static int write_batch(struct xc_sr_cont
|
||||
{
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
- if ( guest_data[i] )
|
||||
+ if ( ctx->save.guest_data[i] )
|
||||
{
|
||||
- ctx->save.iov[iovcnt].iov_base = guest_data[i];
|
||||
+ ctx->save.iov[iovcnt].iov_base = ctx->save.guest_data[i];
|
||||
ctx->save.iov[iovcnt].iov_len = PAGE_SIZE;
|
||||
iovcnt++;
|
||||
--nr_pages;
|
||||
@@ -258,7 +259,6 @@ static int write_batch(struct xc_sr_cont
|
||||
for ( i = 0; local_pages && i < nr_pfns; ++i )
|
||||
free(local_pages[i]);
|
||||
free(local_pages);
|
||||
- free(guest_data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -836,11 +836,12 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->save.errors = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.errors));
|
||||
ctx->save.iov = malloc((4 + MAX_BATCH_SIZE) * sizeof(*ctx->save.iov));
|
||||
ctx->save.rec_pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.rec_pfns));
|
||||
+ ctx->save.guest_data = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.guest_data));
|
||||
ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
|
||||
|
||||
if ( !ctx->save.batch_pfns || !ctx->save.mfns || !ctx->save.types ||
|
||||
!ctx->save.errors || !ctx->save.iov || !ctx->save.rec_pfns ||
|
||||
- !dirty_bitmap || !ctx->save.deferred_pages )
|
||||
+ !ctx->save.guest_data ||!dirty_bitmap || !ctx->save.deferred_pages )
|
||||
{
|
||||
ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
|
||||
" deferred pages");
|
||||
@@ -871,6 +872,7 @@ static void cleanup(struct xc_sr_context
|
||||
xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
|
||||
NRPAGES(bitmap_size(ctx->save.p2m_size)));
|
||||
free(ctx->save.deferred_pages);
|
||||
+ free(ctx->save.guest_data);
|
||||
free(ctx->save.rec_pfns);
|
||||
free(ctx->save.iov);
|
||||
free(ctx->save.errors);
|
124
libxc-sr-save-iov.patch
Normal file
124
libxc-sr-save-iov.patch
Normal file
@ -0,0 +1,124 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 11:30:41 +0200
|
||||
Subject: libxc sr save iov
|
||||
|
||||
tools: save: preallocate iov array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in a batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_save.c | 34 ++++++++++++++++-----------------
|
||||
2 files changed, 18 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -247,6 +247,7 @@ struct xc_sr_context
|
||||
xen_pfn_t *mfns;
|
||||
xen_pfn_t *types;
|
||||
int *errors;
|
||||
+ struct iovec *iov;
|
||||
unsigned int nr_batch_pfns;
|
||||
unsigned long *deferred_pages;
|
||||
unsigned long nr_deferred_pages;
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -96,7 +96,7 @@ static int write_batch(struct xc_sr_cont
|
||||
unsigned int nr_pfns = ctx->save.nr_batch_pfns;
|
||||
void *page, *orig_page;
|
||||
uint64_t *rec_pfns = NULL;
|
||||
- struct iovec *iov = NULL; int iovcnt = 0;
|
||||
+ int iovcnt = 0;
|
||||
struct xc_sr_rec_page_data_header hdr = { 0 };
|
||||
struct xc_sr_record rec = {
|
||||
.type = REC_TYPE_PAGE_DATA,
|
||||
@@ -108,10 +108,8 @@ static int write_batch(struct xc_sr_cont
|
||||
guest_data = calloc(nr_pfns, sizeof(*guest_data));
|
||||
/* Pointers to locally allocated pages. Need freeing. */
|
||||
local_pages = calloc(nr_pfns, sizeof(*local_pages));
|
||||
- /* iovec[] for writev(). */
|
||||
- iov = malloc((nr_pfns + 4) * sizeof(*iov));
|
||||
|
||||
- if ( !guest_data || !local_pages || !iov )
|
||||
+ if ( !guest_data || !local_pages )
|
||||
{
|
||||
ERROR("Unable to allocate arrays for a batch of %u pages",
|
||||
nr_pfns);
|
||||
@@ -221,17 +219,17 @@ static int write_batch(struct xc_sr_cont
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
rec_pfns[i] = ((uint64_t)(ctx->save.types[i]) << 32) | ctx->save.batch_pfns[i];
|
||||
|
||||
- iov[0].iov_base = &rec.type;
|
||||
- iov[0].iov_len = sizeof(rec.type);
|
||||
+ ctx->save.iov[0].iov_base = &rec.type;
|
||||
+ ctx->save.iov[0].iov_len = sizeof(rec.type);
|
||||
|
||||
- iov[1].iov_base = &rec.length;
|
||||
- iov[1].iov_len = sizeof(rec.length);
|
||||
+ ctx->save.iov[1].iov_base = &rec.length;
|
||||
+ ctx->save.iov[1].iov_len = sizeof(rec.length);
|
||||
|
||||
- iov[2].iov_base = &hdr;
|
||||
- iov[2].iov_len = sizeof(hdr);
|
||||
+ ctx->save.iov[2].iov_base = &hdr;
|
||||
+ ctx->save.iov[2].iov_len = sizeof(hdr);
|
||||
|
||||
- iov[3].iov_base = rec_pfns;
|
||||
- iov[3].iov_len = nr_pfns * sizeof(*rec_pfns);
|
||||
+ ctx->save.iov[3].iov_base = rec_pfns;
|
||||
+ ctx->save.iov[3].iov_len = nr_pfns * sizeof(*rec_pfns);
|
||||
|
||||
iovcnt = 4;
|
||||
ctx->save.pages_sent += nr_pages;
|
||||
@@ -243,15 +241,15 @@ static int write_batch(struct xc_sr_cont
|
||||
{
|
||||
if ( guest_data[i] )
|
||||
{
|
||||
- iov[iovcnt].iov_base = guest_data[i];
|
||||
- iov[iovcnt].iov_len = PAGE_SIZE;
|
||||
+ ctx->save.iov[iovcnt].iov_base = guest_data[i];
|
||||
+ ctx->save.iov[iovcnt].iov_len = PAGE_SIZE;
|
||||
iovcnt++;
|
||||
--nr_pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- if ( writev_exact(ctx->fd, iov, iovcnt) )
|
||||
+ if ( writev_exact(ctx->fd, ctx->save.iov, iovcnt) )
|
||||
{
|
||||
PERROR("Failed to write page data to stream");
|
||||
goto err;
|
||||
@@ -267,7 +265,6 @@ static int write_batch(struct xc_sr_cont
|
||||
xenforeignmemory_unmap(xch->fmem, guest_mapping, nr_pages_mapped);
|
||||
for ( i = 0; local_pages && i < nr_pfns; ++i )
|
||||
free(local_pages[i]);
|
||||
- free(iov);
|
||||
free(local_pages);
|
||||
free(guest_data);
|
||||
|
||||
@@ -845,10 +842,12 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->save.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.mfns));
|
||||
ctx->save.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.types));
|
||||
ctx->save.errors = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.errors));
|
||||
+ ctx->save.iov = malloc((4 + MAX_BATCH_SIZE) * sizeof(*ctx->save.iov));
|
||||
ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
|
||||
|
||||
if ( !ctx->save.batch_pfns || !ctx->save.mfns || !ctx->save.types ||
|
||||
- !ctx->save.errors || !dirty_bitmap || !ctx->save.deferred_pages )
|
||||
+ !ctx->save.errors || !ctx->save.iov || !dirty_bitmap ||
|
||||
+ !ctx->save.deferred_pages )
|
||||
{
|
||||
ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
|
||||
" deferred pages");
|
||||
@@ -879,6 +878,7 @@ static void cleanup(struct xc_sr_context
|
||||
xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
|
||||
NRPAGES(bitmap_size(ctx->save.p2m_size)));
|
||||
free(ctx->save.deferred_pages);
|
||||
+ free(ctx->save.iov);
|
||||
free(ctx->save.errors);
|
||||
free(ctx->save.types);
|
||||
free(ctx->save.mfns);
|
218
libxc-sr-save-local_pages.patch
Normal file
218
libxc-sr-save-local_pages.patch
Normal file
@ -0,0 +1,218 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 12:47:56 +0200
|
||||
Subject: libxc sr save local_pages
|
||||
|
||||
tools: save: preallocate local_pages array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in a batch.
|
||||
Allocate the space once.
|
||||
|
||||
Adjust the code to use the unmodified src page in case of HVM.
|
||||
In case of PV the page may need to be normalised, use a private memory
|
||||
area for this purpose.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 22 ++++++++++---------
|
||||
tools/libs/guest/xg_sr_save.c | 26 ++++------------------
|
||||
tools/libs/guest/xg_sr_save_x86_hvm.c | 5 +++--
|
||||
tools/libs/guest/xg_sr_save_x86_pv.c | 31 ++++++++++++++++++---------
|
||||
4 files changed, 40 insertions(+), 44 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -33,16 +33,12 @@ struct xc_sr_save_ops
|
||||
* Optionally transform the contents of a page from being specific to the
|
||||
* sending environment, to being generic for the stream.
|
||||
*
|
||||
- * The page of data at the end of 'page' may be a read-only mapping of a
|
||||
- * running guest; it must not be modified. If no transformation is
|
||||
- * required, the callee should leave '*pages' untouched.
|
||||
+ * The page of data '*src' may be a read-only mapping of a running guest;
|
||||
+ * it must not be modified. If no transformation is required, the callee
|
||||
+ * should leave '*src' untouched, and return it via '**ptr'.
|
||||
*
|
||||
- * If a transformation is required, the callee should allocate themselves
|
||||
- * a local page using malloc() and return it via '*page'.
|
||||
- *
|
||||
- * The caller shall free() '*page' in all cases. In the case that the
|
||||
- * callee encounters an error, it should *NOT* free() the memory it
|
||||
- * allocated for '*page'.
|
||||
+ * If a transformation is required, the callee should provide the
|
||||
+ * transformed page in a private buffer and return it via '**ptr'.
|
||||
*
|
||||
* It is valid to fail with EAGAIN if the transformation is not able to be
|
||||
* completed at this point. The page shall be retried later.
|
||||
@@ -50,7 +46,7 @@ struct xc_sr_save_ops
|
||||
* @returns 0 for success, -1 for failure, with errno appropriately set.
|
||||
*/
|
||||
int (*normalise_page)(struct xc_sr_context *ctx, xen_pfn_t type,
|
||||
- void **page);
|
||||
+ void *src, unsigned int idx, void **ptr);
|
||||
|
||||
/**
|
||||
* Set up local environment to save a domain. (Typically querying
|
||||
@@ -359,6 +355,12 @@ struct xc_sr_context
|
||||
{
|
||||
struct
|
||||
{
|
||||
+ /* Used by write_batch for modified pages. */
|
||||
+ void *normalised_pages;
|
||||
+ } save;
|
||||
+
|
||||
+ struct
|
||||
+ {
|
||||
/* State machine for the order of received records. */
|
||||
bool seen_pv_info;
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -89,11 +89,10 @@ static int write_batch(struct xc_sr_cont
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
void *guest_mapping = NULL;
|
||||
- void **local_pages = NULL;
|
||||
int rc = -1;
|
||||
unsigned int i, p, nr_pages = 0, nr_pages_mapped = 0;
|
||||
unsigned int nr_pfns = ctx->save.nr_batch_pfns;
|
||||
- void *page, *orig_page;
|
||||
+ void *src;
|
||||
int iovcnt = 0;
|
||||
struct xc_sr_rec_page_data_header hdr = { 0 };
|
||||
struct xc_sr_record rec = {
|
||||
@@ -102,16 +101,6 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
assert(nr_pfns != 0);
|
||||
|
||||
- /* Pointers to locally allocated pages. Need freeing. */
|
||||
- local_pages = calloc(nr_pfns, sizeof(*local_pages));
|
||||
-
|
||||
- if ( !local_pages )
|
||||
- {
|
||||
- ERROR("Unable to allocate arrays for a batch of %u pages",
|
||||
- nr_pfns);
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
ctx->save.types[i] = ctx->save.mfns[i] = ctx->save.ops.pfn_to_gfn(ctx,
|
||||
@@ -175,11 +164,9 @@ static int write_batch(struct xc_sr_cont
|
||||
goto err;
|
||||
}
|
||||
|
||||
- orig_page = page = guest_mapping + (p * PAGE_SIZE);
|
||||
- rc = ctx->save.ops.normalise_page(ctx, ctx->save.types[i], &page);
|
||||
-
|
||||
- if ( orig_page != page )
|
||||
- local_pages[i] = page;
|
||||
+ src = guest_mapping + (p * PAGE_SIZE);
|
||||
+ rc = ctx->save.ops.normalise_page(ctx, ctx->save.types[i], src, i,
|
||||
+ &ctx->save.guest_data[i]);
|
||||
|
||||
if ( rc )
|
||||
{
|
||||
@@ -194,8 +181,6 @@ static int write_batch(struct xc_sr_cont
|
||||
else
|
||||
goto err;
|
||||
}
|
||||
- else
|
||||
- ctx->save.guest_data[i] = page;
|
||||
|
||||
rc = -1;
|
||||
++p;
|
||||
@@ -256,9 +241,6 @@ static int write_batch(struct xc_sr_cont
|
||||
err:
|
||||
if ( guest_mapping )
|
||||
xenforeignmemory_unmap(xch->fmem, guest_mapping, nr_pages_mapped);
|
||||
- for ( i = 0; local_pages && i < nr_pfns; ++i )
|
||||
- free(local_pages[i]);
|
||||
- free(local_pages);
|
||||
|
||||
return rc;
|
||||
}
|
||||
--- a/tools/libs/guest/xg_sr_save_x86_hvm.c
|
||||
+++ b/tools/libs/guest/xg_sr_save_x86_hvm.c
|
||||
@@ -129,9 +129,10 @@ static xen_pfn_t x86_hvm_pfn_to_gfn(cons
|
||||
return pfn;
|
||||
}
|
||||
|
||||
-static int x86_hvm_normalise_page(struct xc_sr_context *ctx,
|
||||
- xen_pfn_t type, void **page)
|
||||
+static int x86_hvm_normalise_page(struct xc_sr_context *ctx, xen_pfn_t type,
|
||||
+ void *src, unsigned int idx, void **ptr)
|
||||
{
|
||||
+ *ptr = src;
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_save_x86_pv.c
|
||||
+++ b/tools/libs/guest/xg_sr_save_x86_pv.c
|
||||
@@ -999,29 +999,31 @@ static xen_pfn_t x86_pv_pfn_to_gfn(const
|
||||
* save_ops function. Performs pagetable normalisation on appropriate pages.
|
||||
*/
|
||||
static int x86_pv_normalise_page(struct xc_sr_context *ctx, xen_pfn_t type,
|
||||
- void **page)
|
||||
+ void *src, unsigned int idx, void **ptr)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- void *local_page;
|
||||
+ void *dst;
|
||||
int rc;
|
||||
|
||||
type &= XEN_DOMCTL_PFINFO_LTABTYPE_MASK;
|
||||
|
||||
if ( type < XEN_DOMCTL_PFINFO_L1TAB || type > XEN_DOMCTL_PFINFO_L4TAB )
|
||||
+ {
|
||||
+ *ptr = src;
|
||||
return 0;
|
||||
+ }
|
||||
|
||||
- local_page = malloc(PAGE_SIZE);
|
||||
- if ( !local_page )
|
||||
+ if ( idx >= MAX_BATCH_SIZE )
|
||||
{
|
||||
- ERROR("Unable to allocate scratch page");
|
||||
- rc = -1;
|
||||
- goto out;
|
||||
+ ERROR("idx %u out of range", idx);
|
||||
+ errno = ERANGE;
|
||||
+ return -1;
|
||||
}
|
||||
|
||||
- rc = normalise_pagetable(ctx, *page, local_page, type);
|
||||
- *page = local_page;
|
||||
+ dst = ctx->x86.pv.save.normalised_pages + (idx * PAGE_SIZE);
|
||||
+ rc = normalise_pagetable(ctx, src, dst, type);
|
||||
+ *ptr = dst;
|
||||
|
||||
- out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1031,8 +1033,16 @@ static int x86_pv_normalise_page(struct
|
||||
*/
|
||||
static int x86_pv_setup(struct xc_sr_context *ctx)
|
||||
{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
int rc;
|
||||
|
||||
+ ctx->x86.pv.save.normalised_pages = malloc(MAX_BATCH_SIZE * PAGE_SIZE);
|
||||
+ if ( !ctx->x86.pv.save.normalised_pages )
|
||||
+ {
|
||||
+ PERROR("Failed to allocate normalised_pages");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
rc = x86_pv_domain_info(ctx);
|
||||
if ( rc )
|
||||
return rc;
|
||||
@@ -1118,6 +1128,7 @@ static int x86_pv_check_vm_state(struct
|
||||
|
||||
static int x86_pv_cleanup(struct xc_sr_context *ctx)
|
||||
{
|
||||
+ free(ctx->x86.pv.save.normalised_pages);
|
||||
free(ctx->x86.pv.p2m_pfns);
|
||||
|
||||
if ( ctx->x86.pv.p2m )
|
132
libxc-sr-save-mfns.patch
Normal file
132
libxc-sr-save-mfns.patch
Normal file
@ -0,0 +1,132 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 11:20:36 +0200
|
||||
Subject: libxc sr save mfns
|
||||
|
||||
tools: save: preallocate mfns array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in a batch, see add_to_batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_save.c | 25 +++++++++++++------------
|
||||
2 files changed, 14 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -244,6 +244,7 @@ struct xc_sr_context
|
||||
struct precopy_stats stats;
|
||||
|
||||
xen_pfn_t *batch_pfns;
|
||||
+ xen_pfn_t *mfns;
|
||||
unsigned int nr_batch_pfns;
|
||||
unsigned long *deferred_pages;
|
||||
unsigned long nr_deferred_pages;
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -88,7 +88,7 @@ static int write_checkpoint_record(struc
|
||||
static int write_batch(struct xc_sr_context *ctx)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- xen_pfn_t *mfns = NULL, *types = NULL;
|
||||
+ xen_pfn_t *types = NULL;
|
||||
void *guest_mapping = NULL;
|
||||
void **guest_data = NULL;
|
||||
void **local_pages = NULL;
|
||||
@@ -105,8 +105,6 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
assert(nr_pfns != 0);
|
||||
|
||||
- /* Mfns of the batch pfns. */
|
||||
- mfns = malloc(nr_pfns * sizeof(*mfns));
|
||||
/* Types of the batch pfns. */
|
||||
types = malloc(nr_pfns * sizeof(*types));
|
||||
/* Errors from attempting to map the gfns. */
|
||||
@@ -118,7 +116,7 @@ static int write_batch(struct xc_sr_cont
|
||||
/* iovec[] for writev(). */
|
||||
iov = malloc((nr_pfns + 4) * sizeof(*iov));
|
||||
|
||||
- if ( !mfns || !types || !errors || !guest_data || !local_pages || !iov )
|
||||
+ if ( !types || !errors || !guest_data || !local_pages || !iov )
|
||||
{
|
||||
ERROR("Unable to allocate arrays for a batch of %u pages",
|
||||
nr_pfns);
|
||||
@@ -127,11 +125,11 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
- types[i] = mfns[i] = ctx->save.ops.pfn_to_gfn(ctx,
|
||||
+ types[i] = ctx->save.mfns[i] = ctx->save.ops.pfn_to_gfn(ctx,
|
||||
ctx->save.batch_pfns[i]);
|
||||
|
||||
/* Likely a ballooned page. */
|
||||
- if ( mfns[i] == INVALID_MFN )
|
||||
+ if ( ctx->save.mfns[i] == INVALID_MFN )
|
||||
{
|
||||
set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
|
||||
++ctx->save.nr_deferred_pages;
|
||||
@@ -150,20 +148,21 @@ static int write_batch(struct xc_sr_cont
|
||||
{
|
||||
if ( !is_known_page_type(types[i]) )
|
||||
{
|
||||
- ERROR("Unknown type %#"PRIpfn" for pfn %#"PRIpfn, types[i], mfns[i]);
|
||||
+ ERROR("Unknown type %#"PRIpfn" for pfn %#"PRIpfn,
|
||||
+ types[i], ctx->save.mfns[i]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ( !page_type_has_stream_data(types[i]) )
|
||||
continue;
|
||||
|
||||
- mfns[nr_pages++] = mfns[i];
|
||||
+ ctx->save.mfns[nr_pages++] = ctx->save.mfns[i];
|
||||
}
|
||||
|
||||
if ( nr_pages > 0 )
|
||||
{
|
||||
guest_mapping = xenforeignmemory_map(
|
||||
- xch->fmem, ctx->domid, PROT_READ, nr_pages, mfns, errors);
|
||||
+ xch->fmem, ctx->domid, PROT_READ, nr_pages, ctx->save.mfns, errors);
|
||||
if ( !guest_mapping )
|
||||
{
|
||||
PERROR("Failed to map guest pages");
|
||||
@@ -179,7 +178,7 @@ static int write_batch(struct xc_sr_cont
|
||||
if ( errors[p] )
|
||||
{
|
||||
ERROR("Mapping of pfn %#"PRIpfn" (mfn %#"PRIpfn") failed %d",
|
||||
- ctx->save.batch_pfns[i], mfns[p], errors[p]);
|
||||
+ ctx->save.batch_pfns[i], ctx->save.mfns[p], errors[p]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -277,7 +276,6 @@ static int write_batch(struct xc_sr_cont
|
||||
free(guest_data);
|
||||
free(errors);
|
||||
free(types);
|
||||
- free(mfns);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -850,9 +848,11 @@ static int setup(struct xc_sr_context *c
|
||||
xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->save.p2m_size)));
|
||||
ctx->save.batch_pfns = malloc(MAX_BATCH_SIZE *
|
||||
sizeof(*ctx->save.batch_pfns));
|
||||
+ ctx->save.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.mfns));
|
||||
ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
|
||||
|
||||
- if ( !ctx->save.batch_pfns || !dirty_bitmap || !ctx->save.deferred_pages )
|
||||
+ if ( !ctx->save.batch_pfns || !ctx->save.mfns ||
|
||||
+ !dirty_bitmap || !ctx->save.deferred_pages )
|
||||
{
|
||||
ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
|
||||
" deferred pages");
|
||||
@@ -883,6 +883,7 @@ static void cleanup(struct xc_sr_context
|
||||
xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
|
||||
NRPAGES(bitmap_size(ctx->save.p2m_size)));
|
||||
free(ctx->save.deferred_pages);
|
||||
+ free(ctx->save.mfns);
|
||||
free(ctx->save.batch_pfns);
|
||||
}
|
||||
|
110
libxc-sr-save-rec_pfns.patch
Normal file
110
libxc-sr-save-rec_pfns.patch
Normal file
@ -0,0 +1,110 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 11:34:00 +0200
|
||||
Subject: libxc sr save rec_pfns
|
||||
|
||||
tools: save: preallocate rec_pfns array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in a batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_save.c | 28 +++++++++++-----------------
|
||||
2 files changed, 12 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -248,6 +248,7 @@ struct xc_sr_context
|
||||
xen_pfn_t *types;
|
||||
int *errors;
|
||||
struct iovec *iov;
|
||||
+ uint64_t *rec_pfns;
|
||||
unsigned int nr_batch_pfns;
|
||||
unsigned long *deferred_pages;
|
||||
unsigned long nr_deferred_pages;
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -95,7 +95,6 @@ static int write_batch(struct xc_sr_cont
|
||||
unsigned int i, p, nr_pages = 0, nr_pages_mapped = 0;
|
||||
unsigned int nr_pfns = ctx->save.nr_batch_pfns;
|
||||
void *page, *orig_page;
|
||||
- uint64_t *rec_pfns = NULL;
|
||||
int iovcnt = 0;
|
||||
struct xc_sr_rec_page_data_header hdr = { 0 };
|
||||
struct xc_sr_record rec = {
|
||||
@@ -202,22 +201,15 @@ static int write_batch(struct xc_sr_cont
|
||||
}
|
||||
}
|
||||
|
||||
- rec_pfns = malloc(nr_pfns * sizeof(*rec_pfns));
|
||||
- if ( !rec_pfns )
|
||||
- {
|
||||
- ERROR("Unable to allocate %zu bytes of memory for page data pfn list",
|
||||
- nr_pfns * sizeof(*rec_pfns));
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
hdr.count = nr_pfns;
|
||||
|
||||
rec.length = sizeof(hdr);
|
||||
- rec.length += nr_pfns * sizeof(*rec_pfns);
|
||||
+ rec.length += nr_pfns * sizeof(*ctx->save.rec_pfns);
|
||||
rec.length += nr_pages * PAGE_SIZE;
|
||||
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
- rec_pfns[i] = ((uint64_t)(ctx->save.types[i]) << 32) | ctx->save.batch_pfns[i];
|
||||
+ ctx->save.rec_pfns[i] = ((uint64_t)(ctx->save.types[i]) << 32) |
|
||||
+ ctx->save.batch_pfns[i];
|
||||
|
||||
ctx->save.iov[0].iov_base = &rec.type;
|
||||
ctx->save.iov[0].iov_len = sizeof(rec.type);
|
||||
@@ -228,12 +220,13 @@ static int write_batch(struct xc_sr_cont
|
||||
ctx->save.iov[2].iov_base = &hdr;
|
||||
ctx->save.iov[2].iov_len = sizeof(hdr);
|
||||
|
||||
- ctx->save.iov[3].iov_base = rec_pfns;
|
||||
- ctx->save.iov[3].iov_len = nr_pfns * sizeof(*rec_pfns);
|
||||
+ ctx->save.iov[3].iov_base = ctx->save.rec_pfns;
|
||||
+ ctx->save.iov[3].iov_len = nr_pfns * sizeof(*ctx->save.rec_pfns);
|
||||
|
||||
iovcnt = 4;
|
||||
ctx->save.pages_sent += nr_pages;
|
||||
- ctx->save.overhead_sent += sizeof(rec) + sizeof(hdr) + nr_pfns * sizeof(*rec_pfns);
|
||||
+ ctx->save.overhead_sent += sizeof(rec) + sizeof(hdr) +
|
||||
+ nr_pfns * sizeof(*ctx->save.rec_pfns);
|
||||
|
||||
if ( nr_pages )
|
||||
{
|
||||
@@ -260,7 +253,6 @@ static int write_batch(struct xc_sr_cont
|
||||
rc = ctx->save.nr_batch_pfns = 0;
|
||||
|
||||
err:
|
||||
- free(rec_pfns);
|
||||
if ( guest_mapping )
|
||||
xenforeignmemory_unmap(xch->fmem, guest_mapping, nr_pages_mapped);
|
||||
for ( i = 0; local_pages && i < nr_pfns; ++i )
|
||||
@@ -843,11 +835,12 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->save.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.types));
|
||||
ctx->save.errors = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.errors));
|
||||
ctx->save.iov = malloc((4 + MAX_BATCH_SIZE) * sizeof(*ctx->save.iov));
|
||||
+ ctx->save.rec_pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.rec_pfns));
|
||||
ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
|
||||
|
||||
if ( !ctx->save.batch_pfns || !ctx->save.mfns || !ctx->save.types ||
|
||||
- !ctx->save.errors || !ctx->save.iov || !dirty_bitmap ||
|
||||
- !ctx->save.deferred_pages )
|
||||
+ !ctx->save.errors || !ctx->save.iov || !ctx->save.rec_pfns ||
|
||||
+ !dirty_bitmap || !ctx->save.deferred_pages )
|
||||
{
|
||||
ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
|
||||
" deferred pages");
|
||||
@@ -878,6 +871,7 @@ static void cleanup(struct xc_sr_context
|
||||
xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
|
||||
NRPAGES(bitmap_size(ctx->save.p2m_size)));
|
||||
free(ctx->save.deferred_pages);
|
||||
+ free(ctx->save.rec_pfns);
|
||||
free(ctx->save.iov);
|
||||
free(ctx->save.errors);
|
||||
free(ctx->save.types);
|
116
libxc-sr-save-show_transfer_rate.patch
Normal file
116
libxc-sr-save-show_transfer_rate.patch
Normal file
@ -0,0 +1,116 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 15:39:59 +0200
|
||||
Subject: libxc sr save show_transfer_rate
|
||||
|
||||
tools: show migration transfer rate in send_dirty_pages
|
||||
|
||||
Show how fast domU pages are transferred in each iteration.
|
||||
|
||||
The relevant data is how fast the pfns travel, not so much how much
|
||||
protocol overhead exists. So the reported MiB/sec is just for pfns.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
|
||||
v02:
|
||||
- rearrange MiB_sec calculation (jgross)
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 2 ++
|
||||
tools/libs/guest/xg_sr_save.c | 46 +++++++++++++++++++++++++++++++++
|
||||
2 files changed, 48 insertions(+)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -238,6 +238,8 @@ struct xc_sr_context
|
||||
bool debug;
|
||||
|
||||
unsigned long p2m_size;
|
||||
+ size_t pages_sent;
|
||||
+ size_t overhead_sent;
|
||||
|
||||
struct precopy_stats stats;
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <assert.h>
|
||||
#include <arpa/inet.h>
|
||||
+#include <time.h>
|
||||
|
||||
#include "xg_sr_common.h"
|
||||
|
||||
@@ -238,6 +239,8 @@ static int write_batch(struct xc_sr_cont
|
||||
iov[3].iov_len = nr_pfns * sizeof(*rec_pfns);
|
||||
|
||||
iovcnt = 4;
|
||||
+ ctx->save.pages_sent += nr_pages;
|
||||
+ ctx->save.overhead_sent += sizeof(rec) + sizeof(hdr) + nr_pfns * sizeof(*rec_pfns);
|
||||
|
||||
if ( nr_pages )
|
||||
{
|
||||
@@ -356,6 +359,42 @@ static int suspend_domain(struct xc_sr_c
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void show_transfer_rate(struct xc_sr_context *ctx, struct timespec *start)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+ struct timespec end = {}, diff = {};
|
||||
+ size_t ms, MiB_sec;
|
||||
+
|
||||
+ if (!ctx->save.pages_sent)
|
||||
+ return;
|
||||
+
|
||||
+ if ( clock_gettime(CLOCK_MONOTONIC, &end) )
|
||||
+ PERROR("clock_gettime");
|
||||
+
|
||||
+ if ( (end.tv_nsec - start->tv_nsec) < 0 )
|
||||
+ {
|
||||
+ diff.tv_sec = end.tv_sec - start->tv_sec - 1;
|
||||
+ diff.tv_nsec = end.tv_nsec - start->tv_nsec + (1000U*1000U*1000U);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ diff.tv_sec = end.tv_sec - start->tv_sec;
|
||||
+ diff.tv_nsec = end.tv_nsec - start->tv_nsec;
|
||||
+ }
|
||||
+
|
||||
+ ms = (diff.tv_nsec / (1000U*1000U));
|
||||
+ ms += (diff.tv_sec * 1000U);
|
||||
+ if (!ms)
|
||||
+ ms = 1;
|
||||
+
|
||||
+ MiB_sec = (ctx->save.pages_sent * PAGE_SIZE * 1000U) / ms / (1024U*1024U);
|
||||
+
|
||||
+ errno = 0;
|
||||
+ IPRINTF("%s: %zu bytes + %zu pages in %ld.%09ld sec, %zu MiB/sec", __func__,
|
||||
+ ctx->save.overhead_sent, ctx->save.pages_sent,
|
||||
+ diff.tv_sec, diff.tv_nsec, MiB_sec);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Send a subset of pages in the guests p2m, according to the dirty bitmap.
|
||||
* Used for each subsequent iteration of the live migration loop.
|
||||
@@ -369,9 +408,15 @@ static int send_dirty_pages(struct xc_sr
|
||||
xen_pfn_t p;
|
||||
unsigned long written;
|
||||
int rc;
|
||||
+ struct timespec start = {};
|
||||
DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap,
|
||||
&ctx->save.dirty_bitmap_hbuf);
|
||||
|
||||
+ ctx->save.pages_sent = 0;
|
||||
+ ctx->save.overhead_sent = 0;
|
||||
+ if ( clock_gettime(CLOCK_MONOTONIC, &start) )
|
||||
+ PERROR("clock_gettime");
|
||||
+
|
||||
for ( p = 0, written = 0; p < ctx->save.p2m_size; ++p )
|
||||
{
|
||||
if ( !test_bit(p, dirty_bitmap) )
|
||||
@@ -395,6 +440,7 @@ static int send_dirty_pages(struct xc_sr
|
||||
if ( written > entries )
|
||||
DPRINTF("Bitmap contained more entries than expected...");
|
||||
|
||||
+ show_transfer_rate(ctx, &start);
|
||||
xc_report_progress_step(xch, entries, entries);
|
||||
|
||||
return ctx->save.ops.check_vm_state(ctx);
|
154
libxc-sr-save-types.patch
Normal file
154
libxc-sr-save-types.patch
Normal file
@ -0,0 +1,154 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 23 Oct 2020 11:23:51 +0200
|
||||
Subject: libxc sr save types
|
||||
|
||||
tools: save: preallocate types array
|
||||
|
||||
Remove repeated allocation from migration loop. There will never be
|
||||
more than MAX_BATCH_SIZE pages to process in a batch.
|
||||
Allocate the space once.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 1 +
|
||||
tools/libs/guest/xg_sr_save.c | 28 +++++++++++++---------------
|
||||
2 files changed, 14 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -245,6 +245,7 @@ struct xc_sr_context
|
||||
|
||||
xen_pfn_t *batch_pfns;
|
||||
xen_pfn_t *mfns;
|
||||
+ xen_pfn_t *types;
|
||||
unsigned int nr_batch_pfns;
|
||||
unsigned long *deferred_pages;
|
||||
unsigned long nr_deferred_pages;
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -88,7 +88,6 @@ static int write_checkpoint_record(struc
|
||||
static int write_batch(struct xc_sr_context *ctx)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
- xen_pfn_t *types = NULL;
|
||||
void *guest_mapping = NULL;
|
||||
void **guest_data = NULL;
|
||||
void **local_pages = NULL;
|
||||
@@ -105,8 +104,6 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
assert(nr_pfns != 0);
|
||||
|
||||
- /* Types of the batch pfns. */
|
||||
- types = malloc(nr_pfns * sizeof(*types));
|
||||
/* Errors from attempting to map the gfns. */
|
||||
errors = malloc(nr_pfns * sizeof(*errors));
|
||||
/* Pointers to page data to send. Mapped gfns or local allocations. */
|
||||
@@ -116,7 +113,7 @@ static int write_batch(struct xc_sr_cont
|
||||
/* iovec[] for writev(). */
|
||||
iov = malloc((nr_pfns + 4) * sizeof(*iov));
|
||||
|
||||
- if ( !types || !errors || !guest_data || !local_pages || !iov )
|
||||
+ if ( !errors || !guest_data || !local_pages || !iov )
|
||||
{
|
||||
ERROR("Unable to allocate arrays for a batch of %u pages",
|
||||
nr_pfns);
|
||||
@@ -125,7 +122,7 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
- types[i] = ctx->save.mfns[i] = ctx->save.ops.pfn_to_gfn(ctx,
|
||||
+ ctx->save.types[i] = ctx->save.mfns[i] = ctx->save.ops.pfn_to_gfn(ctx,
|
||||
ctx->save.batch_pfns[i]);
|
||||
|
||||
/* Likely a ballooned page. */
|
||||
@@ -136,7 +133,7 @@ static int write_batch(struct xc_sr_cont
|
||||
}
|
||||
}
|
||||
|
||||
- rc = xc_get_pfn_type_batch(xch, ctx->domid, nr_pfns, types);
|
||||
+ rc = xc_get_pfn_type_batch(xch, ctx->domid, nr_pfns, ctx->save.types);
|
||||
if ( rc )
|
||||
{
|
||||
PERROR("Failed to get types for pfn batch");
|
||||
@@ -146,14 +143,14 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
- if ( !is_known_page_type(types[i]) )
|
||||
+ if ( !is_known_page_type(ctx->save.types[i]) )
|
||||
{
|
||||
ERROR("Unknown type %#"PRIpfn" for pfn %#"PRIpfn,
|
||||
- types[i], ctx->save.mfns[i]);
|
||||
+ ctx->save.types[i], ctx->save.mfns[i]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
- if ( !page_type_has_stream_data(types[i]) )
|
||||
+ if ( !page_type_has_stream_data(ctx->save.types[i]) )
|
||||
continue;
|
||||
|
||||
ctx->save.mfns[nr_pages++] = ctx->save.mfns[i];
|
||||
@@ -172,7 +169,7 @@ static int write_batch(struct xc_sr_cont
|
||||
|
||||
for ( i = 0, p = 0; i < nr_pfns; ++i )
|
||||
{
|
||||
- if ( !page_type_has_stream_data(types[i]) )
|
||||
+ if ( !page_type_has_stream_data(ctx->save.types[i]) )
|
||||
continue;
|
||||
|
||||
if ( errors[p] )
|
||||
@@ -183,7 +180,7 @@ static int write_batch(struct xc_sr_cont
|
||||
}
|
||||
|
||||
orig_page = page = guest_mapping + (p * PAGE_SIZE);
|
||||
- rc = ctx->save.ops.normalise_page(ctx, types[i], &page);
|
||||
+ rc = ctx->save.ops.normalise_page(ctx, ctx->save.types[i], &page);
|
||||
|
||||
if ( orig_page != page )
|
||||
local_pages[i] = page;
|
||||
@@ -194,7 +191,7 @@ static int write_batch(struct xc_sr_cont
|
||||
{
|
||||
set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
|
||||
++ctx->save.nr_deferred_pages;
|
||||
- types[i] = XEN_DOMCTL_PFINFO_XTAB;
|
||||
+ ctx->save.types[i] = XEN_DOMCTL_PFINFO_XTAB;
|
||||
--nr_pages;
|
||||
}
|
||||
else
|
||||
@@ -223,7 +220,7 @@ static int write_batch(struct xc_sr_cont
|
||||
rec.length += nr_pages * PAGE_SIZE;
|
||||
|
||||
for ( i = 0; i < nr_pfns; ++i )
|
||||
- rec_pfns[i] = ((uint64_t)(types[i]) << 32) | ctx->save.batch_pfns[i];
|
||||
+ rec_pfns[i] = ((uint64_t)(ctx->save.types[i]) << 32) | ctx->save.batch_pfns[i];
|
||||
|
||||
iov[0].iov_base = &rec.type;
|
||||
iov[0].iov_len = sizeof(rec.type);
|
||||
@@ -275,7 +272,6 @@ static int write_batch(struct xc_sr_cont
|
||||
free(local_pages);
|
||||
free(guest_data);
|
||||
free(errors);
|
||||
- free(types);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -849,9 +845,10 @@ static int setup(struct xc_sr_context *c
|
||||
ctx->save.batch_pfns = malloc(MAX_BATCH_SIZE *
|
||||
sizeof(*ctx->save.batch_pfns));
|
||||
ctx->save.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.mfns));
|
||||
+ ctx->save.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.types));
|
||||
ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
|
||||
|
||||
- if ( !ctx->save.batch_pfns || !ctx->save.mfns ||
|
||||
+ if ( !ctx->save.batch_pfns || !ctx->save.mfns || !ctx->save.types ||
|
||||
!dirty_bitmap || !ctx->save.deferred_pages )
|
||||
{
|
||||
ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
|
||||
@@ -883,6 +880,7 @@ static void cleanup(struct xc_sr_context
|
||||
xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
|
||||
NRPAGES(bitmap_size(ctx->save.p2m_size)));
|
||||
free(ctx->save.deferred_pages);
|
||||
+ free(ctx->save.types);
|
||||
free(ctx->save.mfns);
|
||||
free(ctx->save.batch_pfns);
|
||||
}
|
263
libxc-sr-track-migration-time.patch
Normal file
263
libxc-sr-track-migration-time.patch
Normal file
@ -0,0 +1,263 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Thu, 4 Feb 2021 20:33:53 +0100
|
||||
Subject: libxc sr track migration time
|
||||
|
||||
Track live migration state unconditionally in logfiles to see how long a domU was suspended.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/include/xentoollog.h | 1 +
|
||||
tools/libs/ctrl/xc_domain.c | 12 +++++--
|
||||
tools/libs/ctrl/xc_private.h | 9 +++++
|
||||
tools/libs/guest/xg_resume.c | 5 ++-
|
||||
tools/libs/guest/xg_sr_common.c | 59 ++++++++++++++++++++++++++++++++
|
||||
tools/libs/guest/xg_sr_common.h | 3 ++
|
||||
tools/libs/guest/xg_sr_restore.c | 3 ++
|
||||
tools/libs/guest/xg_sr_save.c | 6 +++-
|
||||
tools/xl/xl.c | 2 ++
|
||||
9 files changed, 96 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/tools/include/xentoollog.h
|
||||
+++ b/tools/include/xentoollog.h
|
||||
@@ -133,6 +133,7 @@ const char *xtl_level_to_string(xentooll
|
||||
});
|
||||
|
||||
|
||||
+#define XL_NO_SUSEINFO "XL_NO_SUSEINFO"
|
||||
#endif /* XENTOOLLOG_H */
|
||||
|
||||
/*
|
||||
--- a/tools/libs/ctrl/xc_domain.c
|
||||
+++ b/tools/libs/ctrl/xc_domain.c
|
||||
@@ -66,20 +66,28 @@ int xc_domain_cacheflush(xc_interface *x
|
||||
int xc_domain_pause(xc_interface *xch,
|
||||
uint32_t domid)
|
||||
{
|
||||
+ int ret;
|
||||
DECLARE_DOMCTL;
|
||||
domctl.cmd = XEN_DOMCTL_pausedomain;
|
||||
domctl.domain = domid;
|
||||
- return do_domctl(xch, &domctl);
|
||||
+ ret = do_domctl(xch, &domctl);
|
||||
+ if (getenv(XL_NO_SUSEINFO) == NULL)
|
||||
+ SUSEINFO("domid %u: %s returned %d", domid, __func__, ret);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
|
||||
int xc_domain_unpause(xc_interface *xch,
|
||||
uint32_t domid)
|
||||
{
|
||||
+ int ret;
|
||||
DECLARE_DOMCTL;
|
||||
domctl.cmd = XEN_DOMCTL_unpausedomain;
|
||||
domctl.domain = domid;
|
||||
- return do_domctl(xch, &domctl);
|
||||
+ ret = do_domctl(xch, &domctl);
|
||||
+ if (getenv(XL_NO_SUSEINFO) == NULL)
|
||||
+ SUSEINFO("domid %u: %s returned %d", domid, __func__, ret);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
|
||||
--- a/tools/libs/ctrl/xc_private.h
|
||||
+++ b/tools/libs/ctrl/xc_private.h
|
||||
@@ -42,6 +42,15 @@
|
||||
|
||||
#include <xen-tools/common-macros.h>
|
||||
|
||||
+/*
|
||||
+ * Using loglevel ERROR to make sure the intended informational messages appear
|
||||
+ * in libvirts libxl-driver.log
|
||||
+ */
|
||||
+#define SUSEINFO(_m, _a...) do { int ERROR_errno = errno; \
|
||||
+ xc_report(xch, xch->error_handler, XTL_ERROR, XC_ERROR_NONE, "SUSEINFO: " _m , ## _a ); \
|
||||
+ errno = ERROR_errno; \
|
||||
+ } while (0)
|
||||
+
|
||||
#if defined(HAVE_VALGRIND_MEMCHECK_H) && !defined(NDEBUG) && !defined(__MINIOS__)
|
||||
/* Compile in Valgrind client requests? */
|
||||
#include <valgrind/memcheck.h>
|
||||
--- a/tools/libs/guest/xg_resume.c
|
||||
+++ b/tools/libs/guest/xg_resume.c
|
||||
@@ -259,7 +259,10 @@ out:
|
||||
*/
|
||||
int xc_domain_resume(xc_interface *xch, uint32_t domid, int fast)
|
||||
{
|
||||
- return (fast
|
||||
+ int ret = (fast
|
||||
? xc_domain_resume_cooperative(xch, domid)
|
||||
: xc_domain_resume_any(xch, domid));
|
||||
+ if (getenv(XL_NO_SUSEINFO) == NULL)
|
||||
+ SUSEINFO("domid %u: %s%s returned %d", domid, __func__, fast ? " fast" : "", ret);
|
||||
+ return ret;
|
||||
}
|
||||
--- a/tools/libs/guest/xg_sr_common.c
|
||||
+++ b/tools/libs/guest/xg_sr_common.c
|
||||
@@ -163,6 +163,65 @@ static void __attribute__((unused)) buil
|
||||
BUILD_BUG_ON(sizeof(struct xc_sr_rec_hvm_params) != 8);
|
||||
}
|
||||
|
||||
+/* Write a two-character hex representation of 'byte' to digits[].
|
||||
+ Pre-condition: sizeof(digits) >= 2 */
|
||||
+static void byte_to_hex(char *digits, const uint8_t byte)
|
||||
+{
|
||||
+ uint8_t nybbel = byte >> 4;
|
||||
+
|
||||
+ if ( nybbel > 9 )
|
||||
+ digits[0] = 'a' + nybbel-10;
|
||||
+ else
|
||||
+ digits[0] = '0' + nybbel;
|
||||
+
|
||||
+ nybbel = byte & 0x0f;
|
||||
+ if ( nybbel > 9 )
|
||||
+ digits[1] = 'a' + nybbel-10;
|
||||
+ else
|
||||
+ digits[1] = '0' + nybbel;
|
||||
+}
|
||||
+
|
||||
+/* Convert an array of 16 unsigned bytes to a DCE/OSF formatted UUID
|
||||
+ string.
|
||||
+
|
||||
+ Pre-condition: sizeof(dest) >= 37 */
|
||||
+void sr_uuid_to_string(char *dest, const uint8_t *uuid)
|
||||
+{
|
||||
+ int i = 0;
|
||||
+ char *p = dest;
|
||||
+
|
||||
+ for (; i < 4; i++ )
|
||||
+ {
|
||||
+ byte_to_hex(p, uuid[i]);
|
||||
+ p += 2;
|
||||
+ }
|
||||
+ *p++ = '-';
|
||||
+ for (; i < 6; i++ )
|
||||
+ {
|
||||
+ byte_to_hex(p, uuid[i]);
|
||||
+ p += 2;
|
||||
+ }
|
||||
+ *p++ = '-';
|
||||
+ for (; i < 8; i++ )
|
||||
+ {
|
||||
+ byte_to_hex(p, uuid[i]);
|
||||
+ p += 2;
|
||||
+ }
|
||||
+ *p++ = '-';
|
||||
+ for (; i < 10; i++ )
|
||||
+ {
|
||||
+ byte_to_hex(p, uuid[i]);
|
||||
+ p += 2;
|
||||
+ }
|
||||
+ *p++ = '-';
|
||||
+ for (; i < 16; i++ )
|
||||
+ {
|
||||
+ byte_to_hex(p, uuid[i]);
|
||||
+ p += 2;
|
||||
+ }
|
||||
+ *p = '\0';
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Expand the tracking structures as needed.
|
||||
* To avoid realloc()ing too excessively, the size increased to the nearest
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -294,6 +294,7 @@ struct xc_sr_context
|
||||
xc_stream_type_t stream_type;
|
||||
|
||||
xc_domaininfo_t dominfo;
|
||||
+ char uuid[16*2+4+1];
|
||||
|
||||
union /* Common save or restore data. */
|
||||
{
|
||||
@@ -505,6 +506,8 @@ extern struct xc_sr_save_ops save_ops_x8
|
||||
extern struct xc_sr_restore_ops restore_ops_x86_pv;
|
||||
extern struct xc_sr_restore_ops restore_ops_x86_hvm;
|
||||
|
||||
+extern void sr_uuid_to_string(char *dest, const uint8_t *uuid);
|
||||
+
|
||||
struct xc_sr_record
|
||||
{
|
||||
uint32_t type;
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -871,6 +871,8 @@ static int restore(struct xc_sr_context
|
||||
struct xc_sr_rhdr rhdr;
|
||||
int rc, saved_rc = 0, saved_errno = 0;
|
||||
|
||||
+ SUSEINFO("domid %u: %s %s start", ctx->domid, ctx->uuid, __func__);
|
||||
+ DPRINTF("domid %u: max_pages %lx tot_pages %lx p2m_size %lx", ctx->domid, ctx->restore.max_pages, ctx->restore.tot_pages, ctx->restore.p2m_size);
|
||||
IPRINTF("Restoring domain");
|
||||
|
||||
rc = setup(ctx);
|
||||
@@ -946,6 +948,7 @@ static int restore(struct xc_sr_context
|
||||
PERROR("Restore failed");
|
||||
|
||||
done:
|
||||
+ SUSEINFO("domid %u: %s done", ctx->domid, __func__);
|
||||
cleanup(ctx);
|
||||
|
||||
if ( saved_rc )
|
||||
@@ -1011,6 +1014,7 @@ int xc_domain_restore(xc_interface *xch,
|
||||
io_fd, dom, hvm, stream_type);
|
||||
|
||||
ctx.domid = dom;
|
||||
+ sr_uuid_to_string(ctx.uuid, ctx.dominfo.handle);
|
||||
|
||||
if ( read_headers(&ctx) )
|
||||
return -1;
|
||||
--- a/tools/libs/guest/xg_sr_save.c
|
||||
+++ b/tools/libs/guest/xg_sr_save.c
|
||||
@@ -353,7 +353,7 @@ static void show_transfer_rate(struct xc
|
||||
MiB_sec = (ctx->save.pages_sent * PAGE_SIZE * 1000U) / ms / (1024U*1024U);
|
||||
|
||||
errno = 0;
|
||||
- IPRINTF("%s: %zu bytes + %zu pages in %ld.%09ld sec, %zu MiB/sec", __func__,
|
||||
+ SUSEINFO("domid %u: %zu bytes + %zu pages in %ld.%09ld sec, %zu MiB/sec", ctx->domid,
|
||||
ctx->save.overhead_sent, ctx->save.pages_sent,
|
||||
diff.tv_sec, diff.tv_nsec, MiB_sec);
|
||||
}
|
||||
@@ -875,13 +875,16 @@ static int save(struct xc_sr_context *ct
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
int rc, saved_rc = 0, saved_errno = 0;
|
||||
+ unsigned long tot_pages = ctx->dominfo.tot_pages;
|
||||
|
||||
+ SUSEINFO("domid %u: %s %s start, %lu pages allocated", ctx->domid, ctx->uuid, __func__, tot_pages);
|
||||
IPRINTF("Saving domain %d, type %s",
|
||||
ctx->domid, dhdr_type_to_str(guest_type));
|
||||
|
||||
rc = setup(ctx);
|
||||
if ( rc )
|
||||
goto err;
|
||||
+ SUSEINFO("domid %u: p2m_size %lx", ctx->domid, ctx->save.p2m_size);
|
||||
|
||||
xc_report_progress_single(xch, "Start of stream");
|
||||
|
||||
@@ -995,6 +998,7 @@ static int save(struct xc_sr_context *ct
|
||||
PERROR("Save failed");
|
||||
|
||||
done:
|
||||
+ SUSEINFO("domid %u: %s done", ctx->domid, __func__);
|
||||
cleanup(ctx);
|
||||
|
||||
if ( saved_rc )
|
||||
@@ -1054,6 +1058,7 @@ int xc_domain_save(xc_interface *xch, in
|
||||
io_fd, dom, flags, hvm);
|
||||
|
||||
ctx.domid = dom;
|
||||
+ sr_uuid_to_string(ctx.uuid, ctx.dominfo.handle);
|
||||
|
||||
if ( hvm )
|
||||
{
|
||||
--- a/tools/xl/xl.c
|
||||
+++ b/tools/xl/xl.c
|
||||
@@ -424,6 +424,8 @@ int main(int argc, char **argv)
|
||||
logger = xtl_createlogger_stdiostream(stderr, minmsglevel, xtl_flags);
|
||||
if (!logger) exit(EXIT_FAILURE);
|
||||
|
||||
+ /* Provide context to libxl and libxc: no SUSEINFO() from xl */
|
||||
+ setenv(XL_NO_SUSEINFO, "1", 0);
|
||||
xl_ctx_alloc();
|
||||
|
||||
atexit(xl_ctx_free);
|
197
libxc-sr-xg_sr_bitmap-populated_pfns.patch
Normal file
197
libxc-sr-xg_sr_bitmap-populated_pfns.patch
Normal file
@ -0,0 +1,197 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 5 Feb 2021 20:16:02 +0100
|
||||
Subject: libxc sr xg_sr_bitmap populated_pfns
|
||||
|
||||
tools: use xg_sr_bitmap for populated_pfns
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.h | 20 ++++++-
|
||||
tools/libs/guest/xg_sr_restore.c | 69 ------------------------
|
||||
tools/libs/guest/xg_sr_restore_x86_hvm.c | 9 ++++
|
||||
tools/libs/guest/xg_sr_restore_x86_pv.c | 7 +++
|
||||
4 files changed, 34 insertions(+), 71 deletions(-)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -375,8 +375,7 @@ struct xc_sr_context
|
||||
uint32_t xenstore_domid, console_domid;
|
||||
|
||||
/* Bitmap of currently populated PFNs during restore. */
|
||||
- unsigned long *populated_pfns;
|
||||
- xen_pfn_t max_populated_pfn;
|
||||
+ struct sr_bitmap populated_pfns;
|
||||
|
||||
/* Sender has invoked verify mode on the stream. */
|
||||
bool verify;
|
||||
@@ -632,6 +631,23 @@ static inline bool page_type_has_stream_
|
||||
}
|
||||
}
|
||||
|
||||
+static inline bool pfn_is_populated(struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
+{
|
||||
+ return sr_test_bit(pfn, &ctx->restore.populated_pfns);
|
||||
+}
|
||||
+
|
||||
+static inline int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
+{
|
||||
+ xc_interface *xch = ctx->xch;
|
||||
+
|
||||
+ if ( sr_set_bit(pfn, &ctx->restore.populated_pfns) == false )
|
||||
+ {
|
||||
+ PERROR("Failed to realloc populated_pfns bitmap");
|
||||
+ errno = ENOMEM;
|
||||
+ return -1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
#endif
|
||||
/*
|
||||
* Local variables:
|
||||
--- a/tools/libs/guest/xg_sr_restore.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore.c
|
||||
@@ -72,64 +72,6 @@ static int read_headers(struct xc_sr_con
|
||||
}
|
||||
|
||||
/*
|
||||
- * Is a pfn populated?
|
||||
- */
|
||||
-static bool pfn_is_populated(const struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
-{
|
||||
- if ( pfn > ctx->restore.max_populated_pfn )
|
||||
- return false;
|
||||
- return test_bit(pfn, ctx->restore.populated_pfns);
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * Set a pfn as populated, expanding the tracking structures if needed. To
|
||||
- * avoid realloc()ing too excessively, the size increased to the nearest power
|
||||
- * of two large enough to contain the required pfn.
|
||||
- */
|
||||
-static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn)
|
||||
-{
|
||||
- xc_interface *xch = ctx->xch;
|
||||
-
|
||||
- if ( pfn > ctx->restore.max_populated_pfn )
|
||||
- {
|
||||
- xen_pfn_t new_max;
|
||||
- size_t old_sz, new_sz;
|
||||
- unsigned long *p;
|
||||
-
|
||||
- /* Round up to the nearest power of two larger than pfn, less 1. */
|
||||
- new_max = pfn;
|
||||
- new_max |= new_max >> 1;
|
||||
- new_max |= new_max >> 2;
|
||||
- new_max |= new_max >> 4;
|
||||
- new_max |= new_max >> 8;
|
||||
- new_max |= new_max >> 16;
|
||||
-#ifdef __x86_64__
|
||||
- new_max |= new_max >> 32;
|
||||
-#endif
|
||||
-
|
||||
- old_sz = bitmap_size(ctx->restore.max_populated_pfn + 1);
|
||||
- new_sz = bitmap_size(new_max + 1);
|
||||
- p = realloc(ctx->restore.populated_pfns, new_sz);
|
||||
- if ( !p )
|
||||
- {
|
||||
- ERROR("Failed to realloc populated bitmap");
|
||||
- errno = ENOMEM;
|
||||
- return -1;
|
||||
- }
|
||||
-
|
||||
- memset((uint8_t *)p + old_sz, 0x00, new_sz - old_sz);
|
||||
-
|
||||
- ctx->restore.populated_pfns = p;
|
||||
- ctx->restore.max_populated_pfn = new_max;
|
||||
- }
|
||||
-
|
||||
- assert(!test_bit(pfn, ctx->restore.populated_pfns));
|
||||
- set_bit(pfn, ctx->restore.populated_pfns);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
* Given a set of pfns, obtain memory from Xen to fill the physmap for the
|
||||
* unpopulated subset. If types is NULL, no page type checking is performed
|
||||
* and all unpopulated pfns are populated.
|
||||
@@ -911,16 +853,6 @@ static int setup(struct xc_sr_context *c
|
||||
if ( rc )
|
||||
goto err;
|
||||
|
||||
- ctx->restore.max_populated_pfn = (32 * 1024 / 4) - 1;
|
||||
- ctx->restore.populated_pfns = bitmap_alloc(
|
||||
- ctx->restore.max_populated_pfn + 1);
|
||||
- if ( !ctx->restore.populated_pfns )
|
||||
- {
|
||||
- ERROR("Unable to allocate memory for populated_pfns bitmap");
|
||||
- rc = -1;
|
||||
- goto err;
|
||||
- }
|
||||
-
|
||||
ctx->restore.pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.pfns));
|
||||
ctx->restore.types = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.types));
|
||||
ctx->restore.mfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->restore.mfns));
|
||||
@@ -969,7 +901,6 @@ static void cleanup(struct xc_sr_context
|
||||
xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->restore.p2m_size)));
|
||||
|
||||
free(ctx->restore.buffered_records);
|
||||
- free(ctx->restore.populated_pfns);
|
||||
free(ctx->restore.pages);
|
||||
free(ctx->restore.iov);
|
||||
free(ctx->restore.guest_data);
|
||||
--- a/tools/libs/guest/xg_sr_restore_x86_hvm.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore_x86_hvm.c
|
||||
@@ -136,6 +136,7 @@ static int x86_hvm_localise_page(struct
|
||||
static int x86_hvm_setup(struct xc_sr_context *ctx)
|
||||
{
|
||||
xc_interface *xch = ctx->xch;
|
||||
+ unsigned long max_pfn, max_pages = ctx->dominfo.max_pages;
|
||||
|
||||
if ( ctx->restore.guest_type != DHDR_TYPE_X86_HVM )
|
||||
{
|
||||
@@ -161,6 +162,13 @@ static int x86_hvm_setup(struct xc_sr_co
|
||||
}
|
||||
#endif
|
||||
|
||||
+ max_pfn = max(ctx->restore.p2m_size, max_pages);
|
||||
+ if ( !sr_bitmap_expand(&ctx->restore.populated_pfns, max_pfn) )
|
||||
+ {
|
||||
+ PERROR("Unable to allocate memory for populated_pfns bitmap");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -241,6 +249,7 @@ static int x86_hvm_stream_complete(struc
|
||||
|
||||
static int x86_hvm_cleanup(struct xc_sr_context *ctx)
|
||||
{
|
||||
+ sr_bitmap_free(&ctx->restore.populated_pfns);
|
||||
free(ctx->x86.hvm.restore.context.ptr);
|
||||
|
||||
free(ctx->x86.restore.cpuid.ptr);
|
||||
--- a/tools/libs/guest/xg_sr_restore_x86_pv.c
|
||||
+++ b/tools/libs/guest/xg_sr_restore_x86_pv.c
|
||||
@@ -1060,6 +1060,12 @@ static int x86_pv_setup(struct xc_sr_con
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
+ if ( !sr_bitmap_expand(&ctx->restore.populated_pfns, 32 * 1024 / 4) )
|
||||
+ {
|
||||
+ PERROR("Unable to allocate memory for populated_pfns bitmap");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
ctx->x86.pv.restore.nr_vcpus = ctx->dominfo.max_vcpu_id + 1;
|
||||
ctx->x86.pv.restore.vcpus = calloc(sizeof(struct xc_sr_x86_pv_restore_vcpu),
|
||||
ctx->x86.pv.restore.nr_vcpus);
|
||||
@@ -1153,6 +1159,7 @@ static int x86_pv_stream_complete(struct
|
||||
*/
|
||||
static int x86_pv_cleanup(struct xc_sr_context *ctx)
|
||||
{
|
||||
+ sr_bitmap_free(&ctx->restore.populated_pfns);
|
||||
free(ctx->x86.pv.p2m);
|
||||
free(ctx->x86.pv.p2m_pfns);
|
||||
|
141
libxc-sr-xg_sr_bitmap.patch
Normal file
141
libxc-sr-xg_sr_bitmap.patch
Normal file
@ -0,0 +1,141 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 5 Feb 2021 19:50:03 +0100
|
||||
Subject: libxc sr xg_sr_bitmap
|
||||
|
||||
tools: add API for expandable bitmaps
|
||||
|
||||
Since the incoming migration stream lacks info about what the highest pfn
|
||||
will be, some data structures can not be allocated upfront.
|
||||
|
||||
Add an API for expandable bitmaps, loosely based on pfn_set_populated.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libs/guest/xg_sr_common.c | 39 +++++++++++++++++++
|
||||
tools/libs/guest/xg_sr_common.h | 67 +++++++++++++++++++++++++++++++++
|
||||
2 files changed, 106 insertions(+)
|
||||
|
||||
--- a/tools/libs/guest/xg_sr_common.c
|
||||
+++ b/tools/libs/guest/xg_sr_common.c
|
||||
@@ -164,6 +164,45 @@ static void __attribute__((unused)) buil
|
||||
}
|
||||
|
||||
/*
|
||||
+ * Expand the tracking structures as needed.
|
||||
+ * To avoid realloc()ing too excessively, the size increased to the nearest
|
||||
+ * power of two large enough to contain the required number of bits.
|
||||
+ */
|
||||
+bool _sr_bitmap_expand(struct sr_bitmap *bm, unsigned long bits)
|
||||
+{
|
||||
+ size_t new_max;
|
||||
+ size_t old_sz, new_sz;
|
||||
+ void *p;
|
||||
+
|
||||
+ if (bits <= bm->bits)
|
||||
+ return true;
|
||||
+
|
||||
+ /* Round up to the nearest power of two larger than bit, less 1. */
|
||||
+ new_max = bits;
|
||||
+ new_max |= new_max >> 1;
|
||||
+ new_max |= new_max >> 2;
|
||||
+ new_max |= new_max >> 4;
|
||||
+ new_max |= new_max >> 8;
|
||||
+ new_max |= new_max >> 16;
|
||||
+ new_max |= sizeof(unsigned long) > 4 ? new_max >> 32 : 0;
|
||||
+
|
||||
+ /* Allocate units of unsigned long */
|
||||
+ new_max = (new_max + BITS_PER_LONG - 1) & ~(BITS_PER_LONG - 1);
|
||||
+
|
||||
+ old_sz = bitmap_size(bm->bits);
|
||||
+ new_sz = bitmap_size(new_max);
|
||||
+ p = realloc(bm->p, new_sz);
|
||||
+ if (!p)
|
||||
+ return false;
|
||||
+
|
||||
+ memset(p + old_sz, 0, new_sz - old_sz);
|
||||
+ bm->p = p;
|
||||
+ bm->bits = new_max;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
--- a/tools/libs/guest/xg_sr_common.h
|
||||
+++ b/tools/libs/guest/xg_sr_common.h
|
||||
@@ -18,6 +18,73 @@ const char *rec_type_to_str(uint32_t typ
|
||||
struct xc_sr_context;
|
||||
struct xc_sr_record;
|
||||
|
||||
+struct sr_bitmap
|
||||
+{
|
||||
+ void *p;
|
||||
+ unsigned long bits;
|
||||
+};
|
||||
+
|
||||
+extern bool _sr_bitmap_expand(struct sr_bitmap *bm, unsigned long bits);
|
||||
+
|
||||
+static inline bool sr_bitmap_expand(struct sr_bitmap *bm, unsigned long bits)
|
||||
+{
|
||||
+ if (bits > bm->bits)
|
||||
+ return _sr_bitmap_expand(bm, bits);
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static inline void sr_bitmap_free(struct sr_bitmap *bm)
|
||||
+{
|
||||
+ free(bm->p);
|
||||
+ bm->p = NULL;
|
||||
+}
|
||||
+
|
||||
+static inline bool sr_set_bit(unsigned long bit, struct sr_bitmap *bm)
|
||||
+{
|
||||
+ if (sr_bitmap_expand(bm, bit + 1) == false)
|
||||
+ return false;
|
||||
+
|
||||
+ set_bit(bit, bm->p);
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static inline bool sr_test_bit(unsigned long bit, struct sr_bitmap *bm)
|
||||
+{
|
||||
+ if (bit + 1 > bm->bits)
|
||||
+ return false;
|
||||
+ return !!test_bit(bit, bm->p);
|
||||
+}
|
||||
+
|
||||
+static inline void sr_clear_bit(unsigned long bit, struct sr_bitmap *bm)
|
||||
+{
|
||||
+ if (bit + 1 <= bm->bits)
|
||||
+ clear_bit(bit, bm->p);
|
||||
+}
|
||||
+
|
||||
+static inline bool sr_test_and_clear_bit(unsigned long bit, struct sr_bitmap *bm)
|
||||
+{
|
||||
+ if (bit + 1 > bm->bits)
|
||||
+ return false;
|
||||
+ return !!test_and_clear_bit(bit, bm->p);
|
||||
+}
|
||||
+
|
||||
+/* No way to report potential allocation error, bitmap must be expanded prior usage */
|
||||
+static inline bool sr_test_and_set_bit(unsigned long bit, struct sr_bitmap *bm)
|
||||
+{
|
||||
+ if (bit + 1 > bm->bits)
|
||||
+ return false;
|
||||
+ return !!test_and_set_bit(bit, bm->p);
|
||||
+}
|
||||
+
|
||||
+static inline bool sr_set_long_bit(unsigned long base_bit, struct sr_bitmap *bm)
|
||||
+{
|
||||
+ if (sr_bitmap_expand(bm, base_bit + BITS_PER_LONG) == false)
|
||||
+ return false;
|
||||
+
|
||||
+ set_bit_long(base_bit, bm->p);
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Save operations. To be implemented for each type of guest, for use by the
|
||||
* common save algorithm.
|
46
libxc-sr-xl-migration-debug.patch
Normal file
46
libxc-sr-xl-migration-debug.patch
Normal file
@ -0,0 +1,46 @@
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Thu, 29 Oct 2020 17:00:19 +0100
|
||||
Subject: libxc sr xl migration debug
|
||||
|
||||
xl: fix description of migrate --debug
|
||||
|
||||
xl migrate --debug used to track every pfn in every batch of pages.
|
||||
But these times are gone. The code in xc_domain_save is the consumer
|
||||
of this knob, now may enable verification mode.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
|
||||
v03:
|
||||
- adjust to describe what --debug would do when the code which
|
||||
consumes this knob is fixed.
|
||||
v02:
|
||||
- the option has no effect anymore
|
||||
---
|
||||
docs/man/xl.1.pod.in | 4 +++-
|
||||
tools/xl/xl_cmdtable.c | 2 +-
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/docs/man/xl.1.pod.in
|
||||
+++ b/docs/man/xl.1.pod.in
|
||||
@@ -486,7 +486,9 @@ domain.
|
||||
|
||||
=item B<--debug>
|
||||
|
||||
-Display huge (!) amount of debug information during the migration process.
|
||||
+This enables verification mode, which will transfer the entire domU memory
|
||||
+once more to the receiving host to make sure the content is identical on
|
||||
+both sides.
|
||||
|
||||
=item B<-p>
|
||||
|
||||
--- a/tools/xl/xl_cmdtable.c
|
||||
+++ b/tools/xl/xl_cmdtable.c
|
||||
@@ -173,7 +173,7 @@ const struct cmd_spec cmd_table[] = {
|
||||
" migrate-receive [-d -e]\n"
|
||||
"-e Do not wait in the background (on <host>) for the death\n"
|
||||
" of the domain.\n"
|
||||
- "--debug Print huge (!) amount of debug during the migration process.\n"
|
||||
+ "--debug Enable verification mode.\n"
|
||||
"-p Do not unpause domain after migrating it.\n"
|
||||
"-D Preserve the domain id"
|
||||
},
|
318
libxl.LIBXL_HOTPLUG_TIMEOUT.patch
Normal file
318
libxl.LIBXL_HOTPLUG_TIMEOUT.patch
Normal file
@ -0,0 +1,318 @@
|
||||
References: bsc#1120095
|
||||
|
||||
A domU with a large amount of disks may run into the hardcoded
|
||||
LIBXL_HOTPLUG_TIMEOUT limit, which is 40 seconds. This happens if the
|
||||
preparation for each disk takes an unexpected large amount of time. Then
|
||||
the sum of all configured disks and the individual preparation time will
|
||||
be larger than 40 seconds. The hotplug script which does the preparation
|
||||
takes a lock before doing the actual preparation. Since the hotplug
|
||||
scripts for each disk are spawned at nearly the same time, each one has
|
||||
to wait for the lock. Due to this contention, the total execution time
|
||||
of a script can easily exceed the timeout. In this case libxl will
|
||||
terminate the script because it has to assume an error condition.
|
||||
|
||||
Example:
|
||||
10 configured disks, each one takes 3 seconds within the critital
|
||||
section. The total execution time will be 30 seconds, which is still
|
||||
within the limit. With 5 additional configured disks, the total
|
||||
execution time will be 45 seconds, which would trigger the timeout.
|
||||
|
||||
To handle such setup without a recompile of libxl, a special key/value
|
||||
has to be created in xenstore prior domain creation. This can be done
|
||||
either manually, or at system startup.
|
||||
|
||||
If this systemd service file is placed in /etc/systemd/system/, and
|
||||
activated, it will create the required entry in xenstore:
|
||||
|
||||
/etc/systemd/system # cat xen-LIBXL_HOTPLUG_TIMEOUT.service
|
||||
[Unit]
|
||||
Description=set global LIBXL_HOTPLUG_TIMEOUT
|
||||
ConditionPathExists=/proc/xen/capabilities
|
||||
|
||||
Requires=xenstored.service
|
||||
After=xenstored.service
|
||||
Requires=xen-init-dom0.service
|
||||
After=xen-init-dom0.service
|
||||
Before=xencommons.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
ExecStartPre=/bin/grep -q control_d /proc/xen/capabilities
|
||||
ExecStart=/usr/bin/xenstore-write /libxl/suse/per-device-LIBXL_HOTPLUG_TIMEOUT 5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
/etc/systemd/system # systemctl enable xen-LIBXL_HOTPLUG_TIMEOUT.service
|
||||
/etc/systemd/system # systemctl start xen-LIBXL_HOTPLUG_TIMEOUT.service
|
||||
|
||||
In this example the per-device value will be set to 5 seconds.
|
||||
|
||||
The change for libxl which handles this xenstore value will enable
|
||||
additional logging if the key is found. That extra logging will show how
|
||||
the execution time of each script.
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_aoutils.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_aoutils.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_aoutils.c
|
||||
@@ -529,6 +529,8 @@ static void async_exec_timeout(libxl__eg
|
||||
{
|
||||
libxl__async_exec_state *aes = CONTAINER_OF(ev, *aes, time);
|
||||
STATE_AO_GC(aes->ao);
|
||||
+ char b[64];
|
||||
+ libxl__suse_diff_timespec(&aes->start, b, sizeof(b));
|
||||
|
||||
if (!aes->rc)
|
||||
aes->rc = rc;
|
||||
@@ -536,7 +538,7 @@ static void async_exec_timeout(libxl__eg
|
||||
libxl__ev_time_deregister(gc, &aes->time);
|
||||
|
||||
assert(libxl__ev_child_inuse(&aes->child));
|
||||
- LOG(ERROR, "killing execution of %s because of timeout", aes->what);
|
||||
+ LOG(ERROR, "killing execution of %s because of timeout%s", aes->what, b);
|
||||
|
||||
if (kill(aes->child.pid, SIGKILL)) {
|
||||
LOGEV(ERROR, errno, "unable to kill %s [%ld]",
|
||||
@@ -552,6 +554,10 @@ static void async_exec_done(libxl__egc *
|
||||
{
|
||||
libxl__async_exec_state *aes = CONTAINER_OF(child, *aes, child);
|
||||
STATE_AO_GC(aes->ao);
|
||||
+ char b[64];
|
||||
+ libxl__suse_diff_timespec(&aes->start, b, sizeof(b));
|
||||
+ if (b[0])
|
||||
+ LOG(NOTICE, "finished execution of '%s'%s", aes->what, b);
|
||||
|
||||
libxl__ev_time_deregister(gc, &aes->time);
|
||||
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_create.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_create.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_create.c
|
||||
@@ -1323,6 +1323,7 @@ static void initiate_domain_create(libxl
|
||||
* build info around just to know if the domain has a device model or not.
|
||||
*/
|
||||
store_libxl_entry(gc, domid, &d_config->b_info);
|
||||
+ libxl__suse_domain_set_hotplug_timeout(gc, domid, d_config->num_disks, d_config->num_nics);
|
||||
|
||||
for (i = 0; i < d_config->num_disks; i++) {
|
||||
ret = libxl__disk_devtype.set_default(gc, domid, &d_config->disks[i],
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_device.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_device.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_device.c
|
||||
@@ -1278,7 +1278,7 @@ static void device_hotplug(libxl__egc *e
|
||||
}
|
||||
|
||||
aes->ao = ao;
|
||||
- aes->what = GCSPRINTF("%s %s", args[0], args[1]);
|
||||
+ aes->what = GCSPRINTF("%s %s for %s", args[0], args[1], be_path);
|
||||
aes->env = env;
|
||||
aes->args = args;
|
||||
aes->callback = device_hotplug_child_death_cb;
|
||||
@@ -1287,6 +1287,15 @@ static void device_hotplug(libxl__egc *e
|
||||
aes->stdfds[1] = 2;
|
||||
aes->stdfds[2] = -1;
|
||||
|
||||
+ switch (aodev->dev->backend_kind) {
|
||||
+ case LIBXL__DEVICE_KIND_VBD:
|
||||
+ case LIBXL__DEVICE_KIND_VIF:
|
||||
+ if (aodev->num_exec == 0)
|
||||
+ libxl__suse_domain_get_hotplug_timeout(gc, aodev->dev->domid, aodev->dev->backend_kind, &aes->start, &aes->timeout_ms, be_path);
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
rc = libxl__async_exec_start(aes);
|
||||
if (rc)
|
||||
goto out;
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_event.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_event.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_event.c
|
||||
@@ -1032,27 +1032,29 @@ static void devstate_callback(libxl__egc
|
||||
{
|
||||
EGC_GC;
|
||||
libxl__ev_devstate *ds = CONTAINER_OF(xsw, *ds, w);
|
||||
+ char b[64];
|
||||
+ libxl__suse_diff_timespec(&ds->w.start, b, sizeof(b));
|
||||
|
||||
if (rc) {
|
||||
if (rc == ERROR_TIMEDOUT)
|
||||
- LOG(DEBUG, "backend %s wanted state %d "" timed out", ds->w.path,
|
||||
- ds->wanted);
|
||||
+ LOG(DEBUG, "backend %s wanted state %d "" timed out%s", ds->w.path,
|
||||
+ ds->wanted, b);
|
||||
goto out;
|
||||
}
|
||||
if (!sstate) {
|
||||
- LOG(DEBUG, "backend %s wanted state %d"" but it was removed",
|
||||
- ds->w.path, ds->wanted);
|
||||
+ LOG(DEBUG, "backend %s wanted state %d"" but it was removed%s",
|
||||
+ ds->w.path, ds->wanted, b);
|
||||
rc = ERROR_INVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int got = atoi(sstate);
|
||||
if (got == ds->wanted) {
|
||||
- LOG(DEBUG, "backend %s wanted state %d ok", ds->w.path, ds->wanted);
|
||||
+ LOG(DEBUG, "backend %s wanted state %d ok%s", ds->w.path, ds->wanted, b);
|
||||
rc = 0;
|
||||
} else {
|
||||
- LOG(DEBUG, "backend %s wanted state %d"" still waiting state %d",
|
||||
- ds->w.path, ds->wanted, got);
|
||||
+ LOG(DEBUG, "backend %s wanted state %d"" still waiting state %d%s",
|
||||
+ ds->w.path, ds->wanted, got, b);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1078,6 +1080,8 @@ int libxl__ev_devstate_wait(libxl__ao *a
|
||||
ds->w.path = state_path;
|
||||
ds->w.timeout_ms = milliseconds;
|
||||
ds->w.callback = devstate_callback;
|
||||
+ rc = clock_gettime(CLOCK_MONOTONIC, &ds->w.start);
|
||||
+ if (rc) goto out;
|
||||
rc = libxl__xswait_start(gc, &ds->w);
|
||||
if (rc) goto out;
|
||||
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_internal.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_internal.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_internal.c
|
||||
@@ -18,6 +18,97 @@
|
||||
#include "libxl_internal.h"
|
||||
#include "libxl_arch.h"
|
||||
|
||||
+#define LIBXL_SUSE_PATH_TIMEOUT "/libxl/suse/per-device-LIBXL_HOTPLUG_TIMEOUT"
|
||||
+#define LIBXL_SUSE_PATH_DISK_TIMEOUT "suse/disks-LIBXL_HOTPLUG_TIMEOUT"
|
||||
+#define LIBXL_SUSE_PATH_NIC_TIMEOUT "suse/nics-LIBXL_HOTPLUG_TIMEOUT"
|
||||
+
|
||||
+void libxl__suse_domain_set_hotplug_timeout(libxl__gc *gc, uint32_t domid, long d, long n)
|
||||
+{
|
||||
+ char *path;
|
||||
+ char *val, *p;
|
||||
+ long v;
|
||||
+
|
||||
+ val = libxl__xs_read(gc, XBT_NULL, LIBXL_SUSE_PATH_TIMEOUT);
|
||||
+ if (!val)
|
||||
+ return;
|
||||
+
|
||||
+ v = strtol(val, NULL, 0);
|
||||
+ if (v <= 0)
|
||||
+ return;
|
||||
+
|
||||
+ path = libxl__xs_libxl_path(gc, domid);
|
||||
+ if (d > 0) {
|
||||
+ p = GCSPRINTF("%s/" LIBXL_SUSE_PATH_DISK_TIMEOUT, path);
|
||||
+ LOGD(NOTICE, domid, "Setting %s to %ld*%ld=%ld", p, d, v, d*v);
|
||||
+ libxl__xs_printf(gc, XBT_NULL, p, "%ld", d*v);
|
||||
+ }
|
||||
+ if (n > 0) {
|
||||
+ p = GCSPRINTF("%s/" LIBXL_SUSE_PATH_NIC_TIMEOUT, path);
|
||||
+ LOGD(NOTICE, domid, "Setting %s to %ld*%ld=%ld", p, n, v, n*v);
|
||||
+ libxl__xs_printf(gc, XBT_NULL, p, "%ld", n*v);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void libxl__suse_domain_get_hotplug_timeout(libxl__gc *gc, uint32_t domid, libxl__device_kind kind, struct timespec *ts, int *timeout_ms, const char *be_path)
|
||||
+{
|
||||
+ char *path;
|
||||
+ char *val, *p;
|
||||
+ long v = 0;
|
||||
+
|
||||
+ path = libxl__xs_libxl_path(gc, domid);
|
||||
+ if (!path)
|
||||
+ return;
|
||||
+
|
||||
+ switch (kind) {
|
||||
+ case LIBXL__DEVICE_KIND_VBD:
|
||||
+ p = GCSPRINTF("%s/" LIBXL_SUSE_PATH_DISK_TIMEOUT, path);
|
||||
+ break;
|
||||
+ case LIBXL__DEVICE_KIND_VIF:
|
||||
+ p = GCSPRINTF("%s/" LIBXL_SUSE_PATH_NIC_TIMEOUT, path);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return;
|
||||
+ }
|
||||
+ errno = 0;
|
||||
+ val = libxl__xs_read(gc, XBT_NULL, p);
|
||||
+ if (val)
|
||||
+ v = strtol(val, NULL, 0);
|
||||
+ LOGED(DEBUG, domid, "Got from '%s' = %ld from %s for %s", val?:"", v, p, be_path);
|
||||
+ if (!val || v <= 0)
|
||||
+ return;
|
||||
+
|
||||
+ if (v > (INT_MAX/1000))
|
||||
+ v = (INT_MAX/1000);
|
||||
+ v *= 1000;
|
||||
+ LOGD(NOTICE, domid, "Replacing timeout %d with %ld for %s", *timeout_ms, v, be_path);
|
||||
+ *timeout_ms = v;
|
||||
+ if (clock_gettime(CLOCK_MONOTONIC, ts) < 0) {
|
||||
+ LOGED(ERROR, domid, "clock_gettime failed for %s", be_path);
|
||||
+ ts->tv_sec = ts->tv_nsec = 0;
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
+
|
||||
+void libxl__suse_diff_timespec(const struct timespec *old, char *b, size_t s)
|
||||
+{
|
||||
+ struct timespec new, diff;
|
||||
+
|
||||
+ if (old->tv_sec == 0 && old->tv_nsec == 0) {
|
||||
+ *b = '\0';
|
||||
+ return;
|
||||
+ }
|
||||
+ if (clock_gettime(CLOCK_MONOTONIC, &new))
|
||||
+ new = *old;
|
||||
+ if ((new.tv_nsec - old->tv_nsec) < 0) {
|
||||
+ diff.tv_sec = new.tv_sec - old->tv_sec - 1;
|
||||
+ diff.tv_nsec = new.tv_nsec - old->tv_nsec + (1000*1000*1000);
|
||||
+ } else {
|
||||
+ diff.tv_sec = new.tv_sec - old->tv_sec;
|
||||
+ diff.tv_nsec = new.tv_nsec - old->tv_nsec;
|
||||
+ }
|
||||
+ snprintf(b, s, " (%ld.%09lds)", (long)diff.tv_sec, diff.tv_nsec);
|
||||
+}
|
||||
+
|
||||
void libxl__alloc_failed(libxl_ctx *ctx, const char *func,
|
||||
size_t nmemb, size_t size) {
|
||||
#define M "libxl: FATAL ERROR: memory allocation failure"
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_internal.h
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_internal.h
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_internal.h
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <sys/un.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
+#include <time.h>
|
||||
|
||||
#include <xenevtchn.h>
|
||||
#include <xenstore.h>
|
||||
@@ -1629,6 +1630,7 @@ struct libxl__xswait_state {
|
||||
const char *what; /* for error msgs: noun phrase, what we're waiting for */
|
||||
const char *path;
|
||||
int timeout_ms; /* as for poll(2) */
|
||||
+ struct timespec start;
|
||||
libxl__xswait_callback *callback;
|
||||
/* remaining fields are private to xswait */
|
||||
libxl__ev_time time_ev;
|
||||
@@ -2707,6 +2709,7 @@ struct libxl__async_exec_state {
|
||||
char **args; /* execution arguments */
|
||||
char **env; /* execution environment */
|
||||
|
||||
+ struct timespec start;
|
||||
/* private */
|
||||
libxl__ev_time time;
|
||||
libxl__ev_child child;
|
||||
@@ -4896,6 +4899,9 @@ _hidden int userlookup_helper_getpwuid(l
|
||||
|
||||
#endif
|
||||
|
||||
+_hidden void libxl__suse_domain_set_hotplug_timeout(libxl__gc *gc, uint32_t domid, long d, long n);
|
||||
+_hidden void libxl__suse_domain_get_hotplug_timeout(libxl__gc *gc, uint32_t domid, libxl__device_kind kind, struct timespec *ts, int *timeout_ms, const char *be_path);
|
||||
+_hidden void libxl__suse_diff_timespec(const struct timespec *old, char *b, size_t s);
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
215
libxl.add-option-to-disable-disk-cache-flushes-in-qdisk.patch
Normal file
215
libxl.add-option-to-disable-disk-cache-flushes-in-qdisk.patch
Normal file
@ -0,0 +1,215 @@
|
||||
https://bugzilla.novell.com/show_bug.cgi?id=879425
|
||||
---
|
||||
tools/libxl/libxl.c | 2 ++
|
||||
tools/libxl/libxl.h | 12 ++++++++++++
|
||||
tools/libxl/libxlu_disk.c | 2 ++
|
||||
tools/libxl/libxlu_disk_i.h | 2 +-
|
||||
tools/libxl/libxlu_disk_l.l | 1 +
|
||||
5 files changed, 18 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: xen-4.18.0-testing/docs/man/xl-disk-configuration.5.pod.in
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/docs/man/xl-disk-configuration.5.pod.in
|
||||
+++ xen-4.18.0-testing/docs/man/xl-disk-configuration.5.pod.in
|
||||
@@ -337,6 +337,32 @@ No
|
||||
|
||||
discard
|
||||
|
||||
+=item B<suse-diskcache-disable-flush>
|
||||
+
|
||||
+=over 4
|
||||
+
|
||||
+=item Description
|
||||
+
|
||||
+Request that the qemu block driver does not automatically flush written data to the backend storage.
|
||||
+
|
||||
+=item Supported values
|
||||
+
|
||||
+absent, present
|
||||
+
|
||||
+=item Mandatory
|
||||
+
|
||||
+No
|
||||
+
|
||||
+=item Default value
|
||||
+
|
||||
+absent
|
||||
+
|
||||
+=back
|
||||
+
|
||||
+This enables the '-disk cache=unsafe' mode inside qemu.
|
||||
+In this mode writes to the underlying blockdevice are delayed.
|
||||
+While using this option in production is dangerous, it improves performance during installation of a domU.
|
||||
+
|
||||
=back
|
||||
|
||||
An advisory setting for the backend driver, specifying whether to
|
||||
Index: xen-4.18.0-testing/tools/include/libxl.h
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/include/libxl.h
|
||||
+++ xen-4.18.0-testing/tools/include/libxl.h
|
||||
@@ -584,6 +584,21 @@
|
||||
*/
|
||||
#define LIBXL_HAVE_CONSOLE_ADD_XENSTORE 1
|
||||
/*
|
||||
+ * The libxl_device_disk has no way to indicate that cache=unsafe is
|
||||
+ * supposed to be used. Provide this knob without breaking the ABI.
|
||||
+ * This is done by overloading struct libxl_device_disk->readwrite:
|
||||
+ * readwrite == 0: disk is readonly, cache= does not matter
|
||||
+ * readwrite == 1: disk is readwrite, backend driver may tweak cache=
|
||||
+ * readwrite == MAGIC: disk is readwrite, backend driver should ignore
|
||||
+ * flush requests from the frontend driver.
|
||||
+ * Note: the macro with MAGIC is used by libvirt to decide if this patch is applied
|
||||
+ */
|
||||
+#define LIBXL_HAVE_LIBXL_DEVICE_DISK_DISABLE_FLUSH_MAGIC 0x00006000U
|
||||
+#define LIBXL_HAVE_LIBXL_DEVICE_DISK_DISABLE_FLUSH_MASK 0xffff0fffU
|
||||
+#define LIBXL_SUSE_IS_CACHE_UNSAFE(rw) (((rw) & ~LIBXL_HAVE_LIBXL_DEVICE_DISK_DISABLE_FLUSH_MASK) == LIBXL_HAVE_LIBXL_DEVICE_DISK_DISABLE_FLUSH_MAGIC)
|
||||
+#define LIBXL_SUSE_SET_CACHE_UNSAFE(rw) (((rw) & LIBXL_HAVE_LIBXL_DEVICE_DISK_DISABLE_FLUSH_MASK) | LIBXL_HAVE_LIBXL_DEVICE_DISK_DISABLE_FLUSH_MAGIC)
|
||||
+
|
||||
+/*
|
||||
* libxl ABI compatibility
|
||||
*
|
||||
* The only guarantee which libxl makes regarding ABI compatibility
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_disk.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_disk.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_disk.c
|
||||
@@ -422,6 +422,8 @@ static void device_disk_add(libxl__egc *
|
||||
flexarray_append_pair(back, "discard-enable",
|
||||
libxl_defbool_val(disk->discard_enable) ?
|
||||
"1" : "0");
|
||||
+ if (LIBXL_SUSE_IS_CACHE_UNSAFE(disk->readwrite))
|
||||
+ flexarray_append_pair(back, "suse-diskcache-disable-flush", "1");
|
||||
flexarray_append(back, "specification");
|
||||
flexarray_append(back, libxl__device_disk_string_of_specification(disk->specification));
|
||||
if (disk->specification == LIBXL_DISK_SPECIFICATION_VIRTIO) {
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_dm.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_dm.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_dm.c
|
||||
@@ -1019,14 +1019,27 @@ enum {
|
||||
LIBXL__COLO_SECONDARY,
|
||||
};
|
||||
|
||||
+static const char *qemu_cache_mode(const libxl_device_disk *disk)
|
||||
+{
|
||||
+ static const char cache_directsync[] = "directsync";
|
||||
+ static const char cache_writeback[] = "writeback";
|
||||
+ static const char cache_unsafe[] = "unsafe";
|
||||
+
|
||||
+ if (LIBXL_SUSE_IS_CACHE_UNSAFE(disk->readwrite))
|
||||
+ return cache_unsafe;
|
||||
+ if (disk->direct_io_safe)
|
||||
+ return cache_directsync;
|
||||
+ return cache_writeback;
|
||||
+}
|
||||
+
|
||||
static char *qemu_disk_scsi_drive_string(libxl__gc *gc, const char *target_path,
|
||||
int unit, const char *format,
|
||||
const libxl_device_disk *disk,
|
||||
int colo_mode, const char **id_ptr)
|
||||
{
|
||||
char *drive = NULL;
|
||||
- char *common = GCSPRINTF("if=none,readonly=%s,cache=writeback",
|
||||
- disk->readwrite ? "off" : "on");
|
||||
+ char *common = GCSPRINTF("if=none,readonly=%s,cache=%s",
|
||||
+ disk->readwrite ? "off" : "on", qemu_cache_mode(disk));
|
||||
const char *exportname = disk->colo_export;
|
||||
const char *active_disk = disk->active_disk;
|
||||
const char *hidden_disk = disk->hidden_disk;
|
||||
@@ -1085,8 +1098,8 @@ static char *qemu_disk_ide_drive_string(
|
||||
switch (colo_mode) {
|
||||
case LIBXL__COLO_NONE:
|
||||
drive = GCSPRINTF
|
||||
- ("file=%s,if=ide,index=%d,media=disk,format=%s,cache=writeback",
|
||||
- target_path, unit, format);
|
||||
+ ("file=%s,if=ide,index=%d,media=disk,format=%s,cache=%s",
|
||||
+ target_path, unit, format, qemu_cache_mode(disk));
|
||||
break;
|
||||
case LIBXL__COLO_PRIMARY:
|
||||
/*
|
||||
@@ -1099,13 +1112,14 @@ static char *qemu_disk_ide_drive_string(
|
||||
* vote-threshold=1
|
||||
*/
|
||||
drive = GCSPRINTF(
|
||||
- "if=ide,index=%d,media=disk,cache=writeback,driver=quorum,"
|
||||
+ "if=ide,index=%d,media=disk,cache=%s,driver=quorum,"
|
||||
"id=%s,"
|
||||
"children.0.file.filename=%s,"
|
||||
"children.0.driver=%s,"
|
||||
"read-pattern=fifo,"
|
||||
"vote-threshold=1",
|
||||
- unit, exportname, target_path, format);
|
||||
+ unit, qemu_cache_mode(disk),
|
||||
+ exportname, target_path, format);
|
||||
break;
|
||||
case LIBXL__COLO_SECONDARY:
|
||||
/*
|
||||
@@ -1119,7 +1133,7 @@ static char *qemu_disk_ide_drive_string(
|
||||
* file.backing.backing=exportname,
|
||||
*/
|
||||
drive = GCSPRINTF(
|
||||
- "if=ide,index=%d,id=top-colo,media=disk,cache=writeback,"
|
||||
+ "if=ide,index=%d,id=top-colo,media=disk,cache=%s,"
|
||||
"driver=replication,"
|
||||
"mode=secondary,"
|
||||
"top-id=top-colo,"
|
||||
@@ -1128,7 +1142,8 @@ static char *qemu_disk_ide_drive_string(
|
||||
"file.backing.driver=qcow2,"
|
||||
"file.backing.file.filename=%s,"
|
||||
"file.backing.backing=%s",
|
||||
- unit, active_disk, hidden_disk, exportname);
|
||||
+ unit, qemu_cache_mode(disk),
|
||||
+ active_disk, hidden_disk, exportname);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
@@ -1998,8 +2013,8 @@ static int libxl__build_device_model_arg
|
||||
return ERROR_INVAL;
|
||||
}
|
||||
flexarray_vappend(dm_args, "-drive",
|
||||
- GCSPRINTF("file=%s,if=none,id=ahcidisk-%d,format=%s,cache=writeback",
|
||||
- target_path, disk, format),
|
||||
+ GCSPRINTF("file=%s,if=none,id=ahcidisk-%d,format=%s,cache=%s",
|
||||
+ target_path, disk, format, qemu_cache_mode(&disks[i])),
|
||||
"-device", GCSPRINTF("ide-hd,bus=ahci0.%d,unit=0,drive=ahcidisk-%d",
|
||||
disk, disk), NULL);
|
||||
continue;
|
||||
Index: xen-4.18.0-testing/tools/libs/util/libxlu_disk.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/util/libxlu_disk.c
|
||||
+++ xen-4.18.0-testing/tools/libs/util/libxlu_disk.c
|
||||
@@ -78,6 +78,8 @@ int xlu_disk_parse(XLU_Config *cfg,
|
||||
if (!disk->pdev_path || !strcmp(disk->pdev_path, ""))
|
||||
disk->format = LIBXL_DISK_FORMAT_EMPTY;
|
||||
}
|
||||
+ if (disk->readwrite && dpc.suse_diskcache_disable_flush)
|
||||
+ disk->readwrite = LIBXL_SUSE_SET_CACHE_UNSAFE(disk->readwrite);
|
||||
|
||||
if (!disk->vdev) {
|
||||
xlu__disk_err(&dpc,0, "no vdev specified");
|
||||
Index: xen-4.18.0-testing/tools/libs/util/libxlu_disk_i.h
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/util/libxlu_disk_i.h
|
||||
+++ xen-4.18.0-testing/tools/libs/util/libxlu_disk_i.h
|
||||
@@ -10,7 +10,7 @@ typedef struct {
|
||||
void *scanner;
|
||||
YY_BUFFER_STATE buf;
|
||||
libxl_device_disk *disk;
|
||||
- int access_set, had_depr_prefix;
|
||||
+ int access_set, suse_diskcache_disable_flush, had_depr_prefix;
|
||||
const char *spec;
|
||||
} DiskParseContext;
|
||||
|
||||
Index: xen-4.18.0-testing/tools/libs/util/libxlu_disk_l.l
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/util/libxlu_disk_l.l
|
||||
+++ xen-4.18.0-testing/tools/libs/util/libxlu_disk_l.l
|
||||
@@ -216,6 +216,7 @@ colo-port=[^,]*,? { STRIP(','); setcolop
|
||||
colo-export=[^,]*,? { STRIP(','); SAVESTRING("colo-export", colo_export, FROMEQUALS); }
|
||||
active-disk=[^,]*,? { STRIP(','); SAVESTRING("active-disk", active_disk, FROMEQUALS); }
|
||||
hidden-disk=[^,]*,? { STRIP(','); SAVESTRING("hidden-disk", hidden_disk, FROMEQUALS); }
|
||||
+suse-diskcache-disable-flush,? { DPC->suse_diskcache_disable_flush = 1; }
|
||||
|
||||
trusted,? { libxl_defbool_set(&DPC->disk->trusted, true); }
|
||||
untrusted,? { libxl_defbool_set(&DPC->disk->trusted, false); }
|
51
libxl.helper_done-crash.patch
Normal file
51
libxl.helper_done-crash.patch
Normal file
@ -0,0 +1,51 @@
|
||||
From fb0f946726ff8aaa15b76bc3ec3b18878851a447 Mon Sep 17 00:00:00 2001
|
||||
From: Olaf Hering <olaf@aepfle.de>
|
||||
Date: Fri, 27 Sep 2019 18:06:12 +0200
|
||||
Subject: libxl: fix crash in helper_done due to uninitialized data
|
||||
|
||||
A crash in helper_done, called from libxl_domain_suspend, was reported,
|
||||
triggered by 'virsh migrate --live xen+ssh://host':
|
||||
|
||||
#1 helper_done (...) at libxl_save_callout.c:371
|
||||
helper_failed
|
||||
helper_stop
|
||||
libxl__save_helper_abort
|
||||
#2 check_all_finished (..., rc=-3) at libxl_stream_write.c:671
|
||||
stream_done
|
||||
stream_complete
|
||||
write_done
|
||||
dc->callback == write_done
|
||||
efd->func == datacopier_writable
|
||||
#3 afterpoll_internal (...) at libxl_event.c:1269
|
||||
|
||||
This is triggered by a failed poll, the actual error was:
|
||||
|
||||
libxl_aoutils.c:328:datacopier_writable: unexpected poll event 0x1c on fd 37 (should be POLLOUT) writing libxc header during copy of save v2 stream
|
||||
|
||||
In this case revents in datacopier_writable is POLLHUP|POLLERR|POLLOUT,
|
||||
which triggers datacopier_callback. In helper_done,
|
||||
shs->completion_callback is still zero. libxl__xc_domain_save fills
|
||||
dss.sws.shs. But that function is only called after stream_header_done.
|
||||
Any error before that will leave dss partly uninitialized.
|
||||
|
||||
Fix this crash by checking if ->completion_callback is valid.
|
||||
|
||||
Signed-off-by: Olaf Hering <olaf@aepfle.de>
|
||||
---
|
||||
tools/libxl/libxl_save_callout.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/tools/libs/light/libxl_save_callout.c
|
||||
+++ b/tools/libs/light/libxl_save_callout.c
|
||||
@@ -364,8 +364,9 @@ static void helper_done(libxl__egc *egc,
|
||||
assert(!libxl__save_helper_inuse(shs));
|
||||
|
||||
shs->egc = egc;
|
||||
- shs->completion_callback(egc, shs->caller_state,
|
||||
- shs->rc, shs->retval, shs->errnoval);
|
||||
+ if (shs->completion_callback)
|
||||
+ shs->completion_callback(egc, shs->caller_state,
|
||||
+ shs->rc, shs->retval, shs->errnoval);
|
||||
shs->egc = 0;
|
||||
}
|
||||
|
23
libxl.max_event_channels.patch
Normal file
23
libxl.max_event_channels.patch
Normal file
@ -0,0 +1,23 @@
|
||||
References: bsc#1167608
|
||||
unbound limits for max_event_channels
|
||||
1023 is too low for a three digit value of vcpus
|
||||
it is difficult to make the value depend on the number of vcpus
|
||||
adding devices at runtime also needs event channels
|
||||
|
||||
But, having an unbound value (of 128k) may have a negative effect on XSA-344.
|
||||
|
||||
Therefore, just let the built-in default depend on the number of vcpus.
|
||||
|
||||
Index: xen-4.17.0-testing/tools/libs/light/libxl_create.c
|
||||
===================================================================
|
||||
--- xen-4.17.0-testing.orig/tools/libs/light/libxl_create.c
|
||||
+++ xen-4.17.0-testing/tools/libs/light/libxl_create.c
|
||||
@@ -263,7 +263,7 @@ int libxl__domain_build_info_setdefault(
|
||||
b_info->iomem[i].gfn = b_info->iomem[i].start;
|
||||
|
||||
if (!b_info->event_channels)
|
||||
- b_info->event_channels = 1023;
|
||||
+ b_info->event_channels = max(1023, 8 * b_info->max_vcpus + 511);
|
||||
|
||||
rc = libxl_get_physinfo(CTX, &info);
|
||||
if (rc) {
|
19
logrotate.conf
Normal file
19
logrotate.conf
Normal file
@ -0,0 +1,19 @@
|
||||
/var/log/xen/xen-hotplug.log {
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
rotate 2
|
||||
size 100k
|
||||
copytruncate
|
||||
}
|
||||
|
||||
/var/log/xen/xl-*.log /var/log/xen/qemu-dm-*.log /var/log/xen/console/*.log {
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
rotate 4
|
||||
dateext
|
||||
dateformat -%Y%m%d-%H%M
|
||||
size 2M
|
||||
copytruncate
|
||||
}
|
63
migration-python3-conversion.patch
Normal file
63
migration-python3-conversion.patch
Normal file
@ -0,0 +1,63 @@
|
||||
Index: xen-4.18.0-testing/tools/python/xen/migration/legacy.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/xen/migration/legacy.py
|
||||
+++ xen-4.18.0-testing/tools/python/xen/migration/legacy.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Index: xen-4.18.0-testing/tools/python/xen/migration/libxc.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/xen/migration/libxc.py
|
||||
+++ xen-4.18.0-testing/tools/python/xen/migration/libxc.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Index: xen-4.18.0-testing/tools/python/xen/migration/libxl.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/xen/migration/libxl.py
|
||||
+++ xen-4.18.0-testing/tools/python/xen/migration/libxl.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Index: xen-4.18.0-testing/tools/python/xen/migration/public.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/xen/migration/public.py
|
||||
+++ xen-4.18.0-testing/tools/python/xen/migration/public.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Index: xen-4.18.0-testing/tools/python/xen/migration/tests.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/xen/migration/tests.py
|
||||
+++ xen-4.18.0-testing/tools/python/xen/migration/tests.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Index: xen-4.18.0-testing/tools/python/xen/migration/verify.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/xen/migration/verify.py
|
||||
+++ xen-4.18.0-testing/tools/python/xen/migration/verify.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Index: xen-4.18.0-testing/tools/python/xen/migration/xl.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/python/xen/migration/xl.py
|
||||
+++ xen-4.18.0-testing/tools/python/xen/migration/xl.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
BIN
mini-os.tar.bz2
(Stored with Git LFS)
Normal file
BIN
mini-os.tar.bz2
(Stored with Git LFS)
Normal file
Binary file not shown.
63
pygrub-boot-legacy-sles.patch
Normal file
63
pygrub-boot-legacy-sles.patch
Normal file
@ -0,0 +1,63 @@
|
||||
Index: xen-4.17.2-testing/tools/pygrub/src/pygrub
|
||||
===================================================================
|
||||
--- xen-4.17.2-testing.orig/tools/pygrub/src/pygrub
|
||||
+++ xen-4.17.2-testing/tools/pygrub/src/pygrub
|
||||
@@ -579,7 +579,7 @@ class Grub:
|
||||
self.cf.filename = f
|
||||
break
|
||||
if self.__dict__.get('cf', None) is None:
|
||||
- raise RuntimeError("couldn't find bootloader config file in the image provided.")
|
||||
+ return
|
||||
f = fs.open_file(self.cf.filename)
|
||||
# limit read size to avoid pathological cases
|
||||
buf = f.read(FS_READ_MAX)
|
||||
@@ -754,6 +754,20 @@ def run_grub(file, entry, fs, cfg_args):
|
||||
|
||||
g = Grub(file, fs)
|
||||
|
||||
+ # If missing config or grub has no menu entries to select, look for
|
||||
+ # vmlinuz-xen and initrd-xen in /boot
|
||||
+ if g.__dict__.get('cf', None) is None or len(g.cf.images) == 0 or re.search(r"xen(-pae)?\.gz",g.cf.images[0].kernel[1]):
|
||||
+ if not list_entries:
|
||||
+ chosencfg = { "kernel": None, "ramdisk": None, "args": "" }
|
||||
+ chosencfg = sniff_xen_kernel(fs, incfg)
|
||||
+ if chosencfg["kernel"] and chosencfg["ramdisk"]:
|
||||
+ chosencfg["args"] = cfg_args
|
||||
+ return chosencfg
|
||||
+ if g.__dict__.get('cf', None) is None:
|
||||
+ raise RuntimeError("couldn't find bootloader config file in the image provided.")
|
||||
+ else:
|
||||
+ return
|
||||
+
|
||||
if list_entries:
|
||||
for i in range(len(g.cf.images)):
|
||||
img = g.cf.images[i]
|
||||
@@ -840,6 +854,19 @@ def sniff_netware(fs, cfg):
|
||||
|
||||
return cfg
|
||||
|
||||
+def sniff_xen_kernel(fs, cfg):
|
||||
+ if not cfg["kernel"]:
|
||||
+ if fs.file_exists('/boot/vmlinuz-xen'):
|
||||
+ cfg["kernel"] = '/boot/vmlinuz-xen'
|
||||
+ elif fs.file_exists('/boot/vmlinuz-xenpae'):
|
||||
+ cfg["kernel"] = '/boot/vmlinuz-xenpae'
|
||||
+ if cfg["kernel"] and not cfg["ramdisk"]:
|
||||
+ if fs.file_exists('/boot/initrd-xen'):
|
||||
+ cfg["ramdisk"] = '/boot/initrd-xen'
|
||||
+ elif fs.file_exists('/boot/initrd-xenpae'):
|
||||
+ cfg["ramdisk"] = '/boot/initrd-xenpae'
|
||||
+ return cfg
|
||||
+
|
||||
def format_sxp(kernel, ramdisk, args):
|
||||
s = "linux (kernel %s)" % repr(kernel)
|
||||
if ramdisk:
|
||||
@@ -918,7 +945,7 @@ if __name__ == "__main__":
|
||||
debug = False
|
||||
not_really = False
|
||||
output_format = "sxp"
|
||||
- output_directory = "/var/run/xen/pygrub/"
|
||||
+ output_directory = "/var/run/xen"
|
||||
uid = None
|
||||
|
||||
# what was passed in
|
59
pygrub-handle-one-line-menu-entries.patch
Normal file
59
pygrub-handle-one-line-menu-entries.patch
Normal file
@ -0,0 +1,59 @@
|
||||
References: bsc#978413
|
||||
|
||||
The parsing code can't handle a single line menu entry.
|
||||
For example: menuentry 'halt' { halt }
|
||||
Force it to fall through where it will handle the closing brace.
|
||||
Also change warning to debug to cut down on verbose output.
|
||||
|
||||
Index: xen-4.18.0-testing/tools/pygrub/src/GrubConf.py
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/pygrub/src/GrubConf.py
|
||||
+++ xen-4.18.0-testing/tools/pygrub/src/GrubConf.py
|
||||
@@ -150,7 +150,7 @@ class GrubImage(_GrubImage):
|
||||
else:
|
||||
logging.info("Ignored image directive %s" %(com,))
|
||||
else:
|
||||
- logging.warning("Unknown image directive %s" %(com,))
|
||||
+ logging.debug("Unknown image directive %s" %(com,))
|
||||
|
||||
# now put the line in the list of lines
|
||||
if replace is None:
|
||||
@@ -309,7 +309,7 @@ class GrubConfigFile(_GrubConfigFile):
|
||||
else:
|
||||
logging.info("Ignored directive %s" %(com,))
|
||||
else:
|
||||
- logging.warning("Unknown directive %s" %(com,))
|
||||
+ logging.debug("Unknown directive %s" %(com,))
|
||||
|
||||
if img:
|
||||
self.add_image(GrubImage(title, img))
|
||||
@@ -343,7 +343,7 @@ class Grub2Image(_GrubImage):
|
||||
elif com.startswith('set:'):
|
||||
pass
|
||||
else:
|
||||
- logging.warning("Unknown image directive %s" %(com,))
|
||||
+ logging.debug("Unknown image directive %s" %(com,))
|
||||
|
||||
# now put the line in the list of lines
|
||||
if replace is None:
|
||||
@@ -408,7 +408,10 @@ class Grub2ConfigFile(_GrubConfigFile):
|
||||
raise RuntimeError("syntax error: cannot nest menuentry (%d %s)" % (len(img),img))
|
||||
img = []
|
||||
title = title_match.group(1)
|
||||
- continue
|
||||
+ if not l.endswith('}'):
|
||||
+ continue
|
||||
+ # One line menuentry, Ex. menuentry 'halt' { halt }
|
||||
+ l = '}'
|
||||
|
||||
if l.startswith("submenu"):
|
||||
menu_level += 1
|
||||
@@ -447,7 +450,7 @@ class Grub2ConfigFile(_GrubConfigFile):
|
||||
elif com.startswith('set:'):
|
||||
pass
|
||||
else:
|
||||
- logging.warning("Unknown directive %s" %(com,))
|
||||
+ logging.debug("Unknown directive %s" %(com,))
|
||||
|
||||
if img is not None:
|
||||
raise RuntimeError("syntax error: end of file with open menuentry(%d %s)" % (len(img),img))
|
151
replace-obsolete-network-configuration-commands-in-s.patch
Normal file
151
replace-obsolete-network-configuration-commands-in-s.patch
Normal file
@ -0,0 +1,151 @@
|
||||
From 5e1e18fde92bae1ae87f78d470e80b1ffc9350d1 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Kubecek <mkubecek@suse.cz>
|
||||
Date: Wed, 26 Jul 2017 10:28:54 +0200
|
||||
Subject: [PATCH] replace obsolete network configuration commands in scripts
|
||||
|
||||
Some scripts still use obsolete network configuration commands ifconfig and
|
||||
brctl. Replace them by commands from iproute2 package.
|
||||
---
|
||||
README | 3 +--
|
||||
tools/hotplug/Linux/colo-proxy-setup | 14 ++++++--------
|
||||
tools/hotplug/Linux/remus-netbuf-setup | 3 ++-
|
||||
tools/hotplug/Linux/vif-bridge | 7 ++++---
|
||||
tools/hotplug/Linux/vif-nat | 2 +-
|
||||
tools/hotplug/Linux/vif-route | 6 ++++--
|
||||
tools/hotplug/Linux/xen-network-common.sh | 6 ++----
|
||||
.../i386-dm/qemu-ifup-Linux | 5 +++--
|
||||
9 files changed, 26 insertions(+), 26 deletions(-)
|
||||
|
||||
Index: xen-4.17.0-testing/README
|
||||
===================================================================
|
||||
--- xen-4.17.0-testing.orig/README
|
||||
+++ xen-4.17.0-testing/README
|
||||
@@ -61,8 +61,7 @@ provided by your OS distributor:
|
||||
* Development install of GLib v2.0 (e.g. libglib2.0-dev)
|
||||
* Development install of Pixman (e.g. libpixman-1-dev)
|
||||
* pkg-config
|
||||
- * bridge-utils package (/sbin/brctl)
|
||||
- * iproute package (/sbin/ip)
|
||||
+ * iproute package (/sbin/ip, /sbin/bridge)
|
||||
* GNU bison and GNU flex
|
||||
* ACPI ASL compiler (iasl)
|
||||
|
||||
Index: xen-4.17.0-testing/tools/hotplug/Linux/remus-netbuf-setup
|
||||
===================================================================
|
||||
--- xen-4.17.0-testing.orig/tools/hotplug/Linux/remus-netbuf-setup
|
||||
+++ xen-4.17.0-testing/tools/hotplug/Linux/remus-netbuf-setup
|
||||
@@ -76,6 +76,7 @@
|
||||
#specific setup code such as renaming.
|
||||
dir=$(dirname "$0")
|
||||
. "$dir/xen-hotplug-common.sh"
|
||||
+. "$dir/xen-network-common.sh"
|
||||
|
||||
findCommand "$@"
|
||||
|
||||
@@ -139,8 +140,16 @@ check_ifb() {
|
||||
|
||||
setup_ifb() {
|
||||
|
||||
- for ifb in `ifconfig -a -s|egrep ^ifb|cut -d ' ' -f1`
|
||||
+ if [ "$legacy_tools" ]; then
|
||||
+ ifbs=`ifconfig -a -s|egrep ^ifb|cut -d ' ' -f1`
|
||||
+ else
|
||||
+ ifbs=$(ip --oneline link show type ifb | cut -d ' ' -f2)
|
||||
+ fi
|
||||
+ for ifb in $ifbs
|
||||
do
|
||||
+ if [ ! "$legacy_tools" ]; then
|
||||
+ ifb="${ifb%:}"
|
||||
+ fi
|
||||
check_ifb "$ifb" || continue
|
||||
REMUS_IFB="$ifb"
|
||||
break
|
||||
Index: xen-4.17.0-testing/tools/hotplug/Linux/vif-bridge
|
||||
===================================================================
|
||||
--- xen-4.17.0-testing.orig/tools/hotplug/Linux/vif-bridge
|
||||
+++ xen-4.17.0-testing/tools/hotplug/Linux/vif-bridge
|
||||
@@ -42,7 +42,8 @@ if [ -z "$bridge" ]; then
|
||||
if which brctl >&/dev/null; then
|
||||
bridge=$(brctl show | awk 'NR==2{print$1}')
|
||||
else
|
||||
- bridge=$(bridge link | cut -d" " -f7)
|
||||
+ bridge=$(ip --oneline link show type bridge | awk '(NR == 1) { print $2; }')
|
||||
+ bridge="${bridge%:}"
|
||||
fi
|
||||
if [ -z "$bridge" ]
|
||||
then
|
||||
Index: xen-4.17.0-testing/tools/hotplug/Linux/vif-nat
|
||||
===================================================================
|
||||
--- xen-4.17.0-testing.orig/tools/hotplug/Linux/vif-nat
|
||||
+++ xen-4.17.0-testing/tools/hotplug/Linux/vif-nat
|
||||
@@ -172,7 +172,11 @@ case "$command" in
|
||||
;;
|
||||
offline)
|
||||
[ "$dhcp" != 'no' ] && dhcp_down
|
||||
- do_without_error ifconfig "${dev}" down
|
||||
+ if [ "$legacy_tools" ]; then
|
||||
+ do_without_error ifconfig "${dev}" down
|
||||
+ else
|
||||
+ do_without_error ip link set "${dev}" down
|
||||
+ fi
|
||||
;;
|
||||
esac
|
||||
|
||||
Index: xen-4.17.0-testing/tools/hotplug/Linux/vif-route
|
||||
===================================================================
|
||||
--- xen-4.17.0-testing.orig/tools/hotplug/Linux/vif-route
|
||||
+++ xen-4.17.0-testing/tools/hotplug/Linux/vif-route
|
||||
@@ -23,13 +23,23 @@ main_ip=$(dom0_ip)
|
||||
|
||||
case "${command}" in
|
||||
add|online)
|
||||
- ifconfig ${dev} ${main_ip} netmask 255.255.255.255 up
|
||||
+ if [ "$legacy_tools" ]; then
|
||||
+ ifconfig ${dev} ${main_ip} netmask 255.255.255.255 up
|
||||
+ else
|
||||
+ ip addr add "${main_ip}/32" dev "$dev"
|
||||
+ fi
|
||||
+ ip link set "dev" up
|
||||
echo 1 >/proc/sys/net/ipv4/conf/${dev}/proxy_arp
|
||||
ipcmd='add'
|
||||
cmdprefix=''
|
||||
;;
|
||||
remove|offline)
|
||||
- do_without_error ifdown ${dev}
|
||||
+ if [ "$legacy_tools" ]; then
|
||||
+ do_without_error ifdown ${dev}
|
||||
+ else
|
||||
+ do_without_error ip addr flush dev "$dev"
|
||||
+ do_without_error ip link set "$dev" down
|
||||
+ fi
|
||||
ipcmd='del'
|
||||
cmdprefix='do_without_error'
|
||||
;;
|
||||
Index: xen-4.17.0-testing/tools/hotplug/Linux/xen-network-common.sh
|
||||
===================================================================
|
||||
--- xen-4.17.0-testing.orig/tools/hotplug/Linux/xen-network-common.sh
|
||||
+++ xen-4.17.0-testing/tools/hotplug/Linux/xen-network-common.sh
|
||||
@@ -15,6 +15,12 @@
|
||||
#
|
||||
|
||||
|
||||
+# Use brctl and ifconfig on older systems
|
||||
+legacy_tools=
|
||||
+if [ -f /sbin/brctl -a -f /sbin/ifconfig ]; then
|
||||
+ legacy_tools="true"
|
||||
+fi
|
||||
+
|
||||
# Gentoo doesn't have ifup/ifdown, so we define appropriate alternatives.
|
||||
|
||||
# Other platforms just use ifup / ifdown directly.
|
||||
@@ -152,8 +158,10 @@ remove_from_bridge () {
|
||||
log debug "removing $dev from bridge $bridge"
|
||||
if which brctl >&/dev/null; then
|
||||
do_without_error brctl delif ${bridge} ${dev}
|
||||
+ do_without_error ifconfig "$dev" down
|
||||
else
|
||||
do_without_error ip link set ${dev} nomaster
|
||||
+ do_without_error ip link set "$dev" down
|
||||
fi
|
||||
else
|
||||
log debug "$dev not on bridge $bridge"
|
16
stdvga-cache.patch
Normal file
16
stdvga-cache.patch
Normal file
@ -0,0 +1,16 @@
|
||||
Index: xen-4.18.0-testing/xen/arch/x86/hvm/stdvga.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/xen/arch/x86/hvm/stdvga.c
|
||||
+++ xen-4.18.0-testing/xen/arch/x86/hvm/stdvga.c
|
||||
@@ -165,7 +165,10 @@ static int stdvga_outb(uint64_t addr, ui
|
||||
|
||||
/* When in standard vga mode, emulate here all writes to the vram buffer
|
||||
* so we can immediately satisfy reads without waiting for qemu. */
|
||||
- s->stdvga = (s->sr[7] == 0x00);
|
||||
+ s->stdvga =
|
||||
+ (s->sr[7] == 0x00) && /* standard vga mode */
|
||||
+ (s->gr[6] == 0x05); /* misc graphics register w/ MemoryMapSelect=1
|
||||
+ * 0xa0000-0xaffff (64k region), AlphaDis=1 */
|
||||
|
||||
if ( !prev_stdvga && s->stdvga )
|
||||
{
|
BIN
stubdom.tar.bz2
(Stored with Git LFS)
Normal file
BIN
stubdom.tar.bz2
(Stored with Git LFS)
Normal file
Binary file not shown.
20
suse-xendomains-service.patch
Normal file
20
suse-xendomains-service.patch
Normal file
@ -0,0 +1,20 @@
|
||||
xendomains: remove libvirtd conflict
|
||||
|
||||
Conflicting with libvirtd is fine for upstream, where xl/libxl is king.
|
||||
But down the SUSE stream, we promote libvirt and all the libvirt-based
|
||||
tools. If a user installs libvirt on their SUSE Xen host, then libvirt
|
||||
should be king and override xendomains.
|
||||
|
||||
bsc#1015348
|
||||
Index: xen-4.8.0-testing/tools/hotplug/Linux/systemd/xendomains.service.in
|
||||
===================================================================
|
||||
--- xen-4.8.0-testing.orig/tools/hotplug/Linux/systemd/xendomains.service.in
|
||||
+++ xen-4.8.0-testing/tools/hotplug/Linux/systemd/xendomains.service.in
|
||||
@@ -5,7 +5,6 @@ After=proc-xen.mount xenstored.service x
|
||||
After=network-online.target
|
||||
After=remote-fs.target
|
||||
ConditionPathExists=/proc/xen/capabilities
|
||||
-Conflicts=libvirtd.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
78
suspend_evtchn_lock.patch
Normal file
78
suspend_evtchn_lock.patch
Normal file
@ -0,0 +1,78 @@
|
||||
Fix problems that suspend eventchannel lock file might be obselete for some reason
|
||||
like segment fault or other abnormal exit, and once obselete lock file exists,
|
||||
it might affact latter save process.
|
||||
Have discussed with upstream, for some reason not accepted.
|
||||
http://xen.1045712.n5.nabble.com/Re-PATCH-improve-suspend-evtchn-lock-processing-td3395229.html
|
||||
|
||||
Signed-off-by: Chunyan Liu <cyliu@suse.com>
|
||||
|
||||
Index: xen-4.10.0-testing/tools/libs/guest/xg_suspend.c
|
||||
===================================================================
|
||||
--- xen-4.10.0-testing.orig/tools/libs/guest/xg_suspend.c
|
||||
+++ xen-4.10.0-testing/tools/libs/guest/xg_suspend.c
|
||||
@@ -20,6 +20,10 @@
|
||||
|
||||
#include "xc_private.h"
|
||||
#include "xenguest.h"
|
||||
+#include <signal.h>
|
||||
+#ifdef __MINIOS__
|
||||
+extern int kill (__pid_t __pid, int __sig);
|
||||
+#endif
|
||||
|
||||
#define SUSPEND_LOCK_FILE XEN_RUN_DIR "/suspend-evtchn-%d.lock"
|
||||
|
||||
@@ -35,6 +39,37 @@
|
||||
|
||||
#define SUSPEND_FILE_BUFLEN (sizeof(SUSPEND_LOCK_FILE) + 10)
|
||||
|
||||
+/* cleanup obsolete suspend lock file which is unlinked for any reason,
|
||||
+so that current process can get lock */
|
||||
+static void clean_obsolete_lock(int domid)
|
||||
+{
|
||||
+ int fd, pid, n;
|
||||
+ char buf[128];
|
||||
+ char suspend_file[256];
|
||||
+
|
||||
+ snprintf(suspend_file, sizeof(suspend_file), "%s_%d_lock.d",
|
||||
+ SUSPEND_LOCK_FILE, domid);
|
||||
+ fd = open(suspend_file, O_RDWR);
|
||||
+
|
||||
+ if (fd < 0)
|
||||
+ return;
|
||||
+
|
||||
+ n = read(fd, buf, 127);
|
||||
+
|
||||
+ close(fd);
|
||||
+
|
||||
+ if (n > 0)
|
||||
+ {
|
||||
+ sscanf(buf, "%d", &pid);
|
||||
+ /* pid does not exist, this lock file is obsolete, just delete it */
|
||||
+ if ( kill(pid,0) )
|
||||
+ {
|
||||
+ unlink(suspend_file);
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void get_suspend_file(char buf[], uint32_t domid)
|
||||
{
|
||||
snprintf(buf, SUSPEND_FILE_BUFLEN, SUSPEND_LOCK_FILE, domid);
|
||||
@@ -48,6 +83,7 @@ static int lock_suspend_event(xc_interfa
|
||||
struct flock fl;
|
||||
|
||||
get_suspend_file(suspend_file, domid);
|
||||
+ clean_obsolete_lock(domid);
|
||||
|
||||
*lockfd = -1;
|
||||
|
||||
@@ -97,6 +133,8 @@ static int lock_suspend_event(xc_interfa
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
+ unlink(suspend_file);
|
||||
+
|
||||
return -1;
|
||||
}
|
||||
|
9
sysconfig.pciback
Normal file
9
sysconfig.pciback
Normal file
@ -0,0 +1,9 @@
|
||||
## Path: System/Virtualization
|
||||
## Type: string
|
||||
## Default: ""
|
||||
#
|
||||
# Space delimited list of PCI devices to late bind to pciback
|
||||
# Format: <driver>,<PCI ID>
|
||||
#
|
||||
#XEN_PCI_HIDE_LIST="e1000,0000:0b:00.0 e1000,0000:0b:00.1"
|
||||
XEN_PCI_HIDE_LIST=""
|
13
vif-bridge-no-iptables.patch
Normal file
13
vif-bridge-no-iptables.patch
Normal file
@ -0,0 +1,13 @@
|
||||
Index: xen-4.15.0-testing/tools/hotplug/Linux/vif-bridge
|
||||
===================================================================
|
||||
--- xen-4.15.0-testing.orig/tools/hotplug/Linux/vif-bridge
|
||||
+++ xen-4.15.0-testing/tools/hotplug/Linux/vif-bridge
|
||||
@@ -87,7 +87,7 @@ case "$command" in
|
||||
;;
|
||||
esac
|
||||
|
||||
-handle_iptable
|
||||
+#handle_iptable
|
||||
|
||||
call_hooks vif post
|
||||
|
30
vif-bridge-tap-fix.patch
Normal file
30
vif-bridge-tap-fix.patch
Normal file
@ -0,0 +1,30 @@
|
||||
# HG changeset patch
|
||||
# User Jim Fehlig <jfehlig@suse.com>
|
||||
# Date 1319581952 21600
|
||||
# Node ID 74da2a3a1db1476d627f42e4a99e9e720cc6774d
|
||||
# Parent 6c583d35d76dda2236c81d9437ff9d57ab02c006
|
||||
Prevent vif-bridge from adding user-created tap interfaces to a bridge
|
||||
|
||||
Exit vif-bridge script if there is no device info in xenstore, preventing
|
||||
it from adding user-created taps to bridges.
|
||||
|
||||
Signed-off-by: Jim Fehlig <jfehlig@suse.com>
|
||||
|
||||
Index: xen-4.5.0-testing/tools/hotplug/Linux/vif-bridge
|
||||
===================================================================
|
||||
--- xen-4.5.0-testing.orig/tools/hotplug/Linux/vif-bridge
|
||||
+++ xen-4.5.0-testing/tools/hotplug/Linux/vif-bridge
|
||||
@@ -28,6 +28,13 @@
|
||||
dir=$(dirname "$0")
|
||||
. "$dir/vif-common.sh"
|
||||
|
||||
+mac=$(xenstore_read_default "$XENBUS_PATH/mac" "")
|
||||
+if [ -z "$mac" ]
|
||||
+then
|
||||
+ log debug "No device details in $XENBUS_PATH, exiting."
|
||||
+ exit 0
|
||||
+fi
|
||||
+
|
||||
bridge=${bridge:-}
|
||||
bridge=$(xenstore_read_default "$XENBUS_PATH/bridge" "$bridge")
|
||||
|
25
vif-route.patch
Normal file
25
vif-route.patch
Normal file
@ -0,0 +1,25 @@
|
||||
References: bsc#985503
|
||||
|
||||
Index: xen-4.15.1-testing/tools/hotplug/Linux/vif-route
|
||||
===================================================================
|
||||
--- xen-4.15.1-testing.orig/tools/hotplug/Linux/vif-route
|
||||
+++ xen-4.15.1-testing/tools/hotplug/Linux/vif-route
|
||||
@@ -57,11 +57,13 @@ case "${type_if}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
-# If we've been given a list of IP addresses, then add routes from dom0 to
|
||||
-# the guest using those addresses.
|
||||
-for addr in ${ip} ; do
|
||||
- ${cmdprefix} ip route ${ipcmd} ${addr} dev ${dev} src ${main_ip} metric ${metric}
|
||||
-done
|
||||
+if [ "${ip}" ] && [ "${ipcmd}" ] ; then
|
||||
+ # If we've been given a list of IP addresses, then add routes from dom0 to
|
||||
+ # the guest using those addresses.
|
||||
+ for addr in ${ip} ; do
|
||||
+ ${cmdprefix} ip route ${ipcmd} ${addr} dev ${dev} src ${main_ip} metric ${metric}
|
||||
+ done
|
||||
+fi
|
||||
|
||||
handle_iptable
|
||||
|
16
x86-ioapic-ack-default.patch
Normal file
16
x86-ioapic-ack-default.patch
Normal file
@ -0,0 +1,16 @@
|
||||
Change default IO-APIC ack mode for single IO-APIC systems to old-style.
|
||||
|
||||
--- a/xen/arch/x86/io_apic.c
|
||||
+++ b/xen/arch/x86/io_apic.c
|
||||
@@ -2074,7 +2074,10 @@ void __init setup_IO_APIC(void)
|
||||
io_apic_irqs = ~PIC_IRQS;
|
||||
|
||||
printk("ENABLING IO-APIC IRQs\n");
|
||||
- printk(" -> Using %s ACK method\n", ioapic_ack_new ? "new" : "old");
|
||||
+ if (!directed_eoi_enabled && !ioapic_ack_forced) {
|
||||
+ ioapic_ack_new = (nr_ioapics > 1);
|
||||
+ printk(" -> Using %s ACK method\n", ioapic_ack_new ? "new" : "old");
|
||||
+ }
|
||||
|
||||
if (ioapic_ack_new) {
|
||||
ioapic_level_type.ack = irq_complete_move;
|
BIN
xen-4.18.0-testing-src.tar.bz2
(Stored with Git LFS)
Normal file
BIN
xen-4.18.0-testing-src.tar.bz2
(Stored with Git LFS)
Normal file
Binary file not shown.
13
xen-arch-kconfig-nr_cpus.patch
Normal file
13
xen-arch-kconfig-nr_cpus.patch
Normal file
@ -0,0 +1,13 @@
|
||||
Index: xen-4.18.0-testing/xen/arch/Kconfig
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/xen/arch/Kconfig
|
||||
+++ xen-4.18.0-testing/xen/arch/Kconfig
|
||||
@@ -7,7 +7,7 @@ config PHYS_ADDR_T_32
|
||||
config NR_CPUS
|
||||
int "Maximum number of CPUs"
|
||||
range 1 4095
|
||||
- default "256" if X86
|
||||
+ default "1024" if X86
|
||||
default "8" if ARM && RCAR3
|
||||
default "4" if ARM && QEMU
|
||||
default "4" if ARM && MPSOC
|
30
xen-destdir.patch
Normal file
30
xen-destdir.patch
Normal file
@ -0,0 +1,30 @@
|
||||
--- xen-4.18.0-testing/tools/xs-clients/Makefile.orig 2023-10-02 12:51:09.364766336 -0600
|
||||
+++ xen-4.18.0-testing/tools/xs-clients/Makefile 2023-10-02 12:53:09.360769196 -0600
|
||||
@@ -29,7 +29,7 @@ all: $(TARGETS)
|
||||
clients: xenstore $(CLIENTS) xenstore-control
|
||||
|
||||
$(CLIENTS): xenstore
|
||||
- ln -f xenstore $@
|
||||
+ ln -sf xenstore $@
|
||||
|
||||
xenstore: xenstore_client.o
|
||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ $(APPEND_LDFLAGS)
|
||||
@@ -54,7 +54,7 @@ install: all
|
||||
$(INSTALL_PROG) xenstore-control $(DESTDIR)$(bindir)
|
||||
$(INSTALL_PROG) xenstore $(DESTDIR)$(bindir)
|
||||
set -e ; for c in $(CLIENTS) ; do \
|
||||
- ln -f $(DESTDIR)$(bindir)/xenstore $(DESTDIR)$(bindir)/$${c} ; \
|
||||
+ ln -sf xenstore $(DESTDIR)$(bindir)/$${c} ; \
|
||||
done
|
||||
|
||||
.PHONY: uninstall
|
||||
--- xen-4.18.0-testing/tools/xenstored/Makefile.orig 2023-10-02 12:51:03.364766193 -0600
|
||||
+++ xen-4.18.0-testing/tools/xenstored/Makefile 2023-10-02 12:54:09.472770628 -0600
|
||||
@@ -37,6 +37,7 @@ TAGS:
|
||||
install: all
|
||||
$(INSTALL_DIR) $(DESTDIR)$(sbindir)
|
||||
$(INSTALL_PROG) xenstored $(DESTDIR)$(sbindir)
|
||||
+ $(INSTALL_DIR) $(DESTDIR)$(bindir)
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
16
xen-dom0-modules.service
Normal file
16
xen-dom0-modules.service
Normal file
@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Load dom0 backend drivers
|
||||
ConditionPathExists=/proc/xen
|
||||
Before=xenstored.service xen-watchdog.service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
Environment=PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin
|
||||
# dummy to have always one valid line
|
||||
ExecStart=-/usr/bin/env uname -a
|
||||
# modules listed in /usr/lib/modules.d/xen.conf
|
||||
# load them manually to avoid usage of system-modules-load.service
|
81
xen-supportconfig
Normal file
81
xen-supportconfig
Normal file
@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
#############################################################
|
||||
# Name: Supportconfig Plugin for Xen
|
||||
# Description: Gathers important troubleshooting information
|
||||
# about Xen and its tools
|
||||
#############################################################
|
||||
|
||||
# TODO:
|
||||
# - Anything needed for UEFI?
|
||||
#
|
||||
|
||||
RCFILE="/usr/lib/supportconfig/resources/supportconfig.rc"
|
||||
OF="output-xen.txt"
|
||||
|
||||
GRUB2_CONF_FILES="/etc/default/grub"
|
||||
XEN_CONF_FILES="/etc/xen/xl.conf /etc/sysconfig/xencommons /etc/sysconfig/xendomains"
|
||||
XEN_SERVICES="xencommons xendomains xen-watchdog"
|
||||
PERSISTENT_VM_CONF_FILES=""
|
||||
ACTIVE_VM_CONF_FILES=""
|
||||
XEN_LOG_FILES=""
|
||||
|
||||
if [ -s $RCFILE ]; then
|
||||
if ! source $RCFILE; then
|
||||
log_write $OF "ERROR: Initializing resource file: $RCFILE"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# if no xen package we are done
|
||||
rpm_verify $OF xen || exit 111
|
||||
|
||||
# if not a xen host (dom0) we are done
|
||||
log_write $OF "#==[ Checking if booted Xen ]=================================#"
|
||||
if [ ! -d /proc/xen ] || [ ! -e /proc/xen/capabilities ] || [ `cat /proc/xen/capabilities` != "control_d" ]; then
|
||||
log_write $OF "No"
|
||||
log_write $OF "Skipped"
|
||||
exit 0
|
||||
else
|
||||
log_write $OF "Yes"
|
||||
fi
|
||||
|
||||
# basic system information:
|
||||
log_cmd $OF "uname -r"
|
||||
for service in $XEN_SERVICES; do
|
||||
log_cmd $OF "systemctl status $service"
|
||||
log_cmd $OF "systemctl is-enabled $service"
|
||||
done
|
||||
log_cmd $OF "lscpu"
|
||||
log_cmd $OF "xl info --numa"
|
||||
log_cmd $OF "xl list"
|
||||
log_cmd $OF "xl pci-assignable-list"
|
||||
log_cmd $OF "xenstore-ls"
|
||||
log_cmd $OF "ps -ef | grep xen"
|
||||
# dump grub2-related conf files
|
||||
conf_files $OF "$GRUB2_CONF_FILES"
|
||||
# dump Xen-related conf files
|
||||
conf_files $OF "$XEN_CONF_FILES"
|
||||
|
||||
# detailed system info:
|
||||
log_cmd $OF "xl list --long"
|
||||
log_cmd $OF "xl dmesg"
|
||||
# network-related info often useful for debugging
|
||||
if [ systemctl is-enabled NetworkManager.service 2>&1 > /dev/null ]; then
|
||||
log_write $OF "NOTE: NetworkManager should not be enabled on a Xen host"
|
||||
fi
|
||||
log_cmd $OF "route -n"
|
||||
log_cmd $OF "arp -v"
|
||||
log_cmd $OF "ip link show type bridge"
|
||||
log_cmd $OF "bridge link show"
|
||||
# list contents of common config and image directories
|
||||
log_cmd $OF "ls -alR /etc/xen/vm/"
|
||||
log_cmd $OF "ls -alR /etc/xen/auto/"
|
||||
log_cmd $OF "ls -alR /var/lib/xen/images/"
|
||||
# dump VM-related conf files
|
||||
test -d /etc/xen/vm && PERSISTENT_VM_CONF_FILES=$(find -L /etc/xen/vm/ -type f | sort)
|
||||
conf_files $OF "$PERSISTENT_VM_CONF_FILES"
|
||||
test -d /var/lib/xen && ACTIVE_VM_CONF_FILES=$(find -L /var/lib/xen/userdata* -type f | sort)
|
||||
conf_files $OF "$ACTIVE_VM_CONF_FILES"
|
||||
# dump log files
|
||||
test -d /var/log/xen && XEN_LOG_FILES="$(find -L /var/log/xen/ -type f | grep 'log$' | sort)"
|
||||
log_files $OF 0 "$XEN_LOG_FILES"
|
58
xen.bug1026236.suse_vtsc_tolerance.patch
Normal file
58
xen.bug1026236.suse_vtsc_tolerance.patch
Normal file
@ -0,0 +1,58 @@
|
||||
suse_vtsc_tolerance=<val>
|
||||
Reference: bsc#1026236
|
||||
|
||||
To avoid emulation of vTSC after live migration or save/restore allow
|
||||
different clock frequency up to the specified value. If the frequency
|
||||
is within the allowed range TSC access by the domU will be performed
|
||||
at native speed. Otherwise TSC access will be emulated. It is up to
|
||||
the hostadmin to decide how much tolerance all running domUs can
|
||||
actually handle. The default is zero tolerance.
|
||||
|
||||
--- a/xen/arch/x86/time.c
|
||||
+++ b/xen/arch/x86/time.c
|
||||
@@ -47,6 +47,9 @@
|
||||
static char __initdata opt_clocksource[10];
|
||||
string_param("clocksource", opt_clocksource);
|
||||
|
||||
+static unsigned int __ro_after_init opt_suse_vtsc_tolerance;
|
||||
+integer_param("suse_vtsc_tolerance", opt_suse_vtsc_tolerance);
|
||||
+
|
||||
unsigned long __read_mostly cpu_khz; /* CPU clock frequency in kHz. */
|
||||
DEFINE_SPINLOCK(rtc_lock);
|
||||
unsigned long pit0_ticks;
|
||||
@@ -2720,6 +2723,8 @@ int tsc_set_info(struct domain *d,
|
||||
|
||||
switch ( tsc_mode )
|
||||
{
|
||||
+ bool disable_vtsc;
|
||||
+
|
||||
case XEN_CPUID_TSC_MODE_DEFAULT:
|
||||
case XEN_CPUID_TSC_MODE_ALWAYS_EMULATE:
|
||||
d->arch.vtsc_offset = get_s_time() - elapsed_nsec;
|
||||
@@ -2733,8 +2738,25 @@ int tsc_set_info(struct domain *d,
|
||||
* When a guest is created, gtsc_khz is passed in as zero, making
|
||||
* d->arch.tsc_khz == cpu_khz. Thus no need to check incarnation.
|
||||
*/
|
||||
+ disable_vtsc = d->arch.tsc_khz == cpu_khz;
|
||||
+
|
||||
+ if ( tsc_mode == XEN_CPUID_TSC_MODE_DEFAULT && !disable_vtsc &&
|
||||
+ opt_suse_vtsc_tolerance && is_hvm_domain(d) )
|
||||
+ {
|
||||
+ long khz_diff = ABS((long)cpu_khz - gtsc_khz);
|
||||
+
|
||||
+ disable_vtsc = khz_diff <= opt_suse_vtsc_tolerance;
|
||||
+
|
||||
+ printk(XENLOG_G_INFO "%pd: host has %lu kHz,"
|
||||
+ " domU expects %u kHz,"
|
||||
+ " difference of %ld is %s tolerance of %u\n",
|
||||
+ d, cpu_khz, gtsc_khz, khz_diff,
|
||||
+ disable_vtsc ? "within" : "outside",
|
||||
+ opt_suse_vtsc_tolerance);
|
||||
+ }
|
||||
+
|
||||
if ( tsc_mode == XEN_CPUID_TSC_MODE_DEFAULT && host_tsc_is_safe() &&
|
||||
- (d->arch.tsc_khz == cpu_khz ||
|
||||
+ (disable_vtsc ||
|
||||
(is_hvm_domain(d) &&
|
||||
hvm_get_tsc_scaling_ratio(d->arch.tsc_khz))) )
|
||||
{
|
31
xen.build-compare.doc_html.patch
Normal file
31
xen.build-compare.doc_html.patch
Normal file
@ -0,0 +1,31 @@
|
||||
The result of $(wildcard *) is random.
|
||||
Sort input files to reduce build-compare noise.
|
||||
|
||||
---
|
||||
docs/Makefile | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
Index: xen-4.18.0-testing/docs/Makefile
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/docs/Makefile
|
||||
+++ xen-4.18.0-testing/docs/Makefile
|
||||
@@ -192,7 +192,7 @@ uninstall: uninstall-man-pages uninstall
|
||||
|
||||
# Individual file build targets
|
||||
html/index.html: $(DOC_HTML) $(CURDIR)/gen-html-index INDEX
|
||||
- $(PERL) -w -- $(CURDIR)/gen-html-index -i INDEX html $(DOC_HTML)
|
||||
+ $(PERL) -w -- $(CURDIR)/gen-html-index -i INDEX html $(sort $(DOC_HTML))
|
||||
|
||||
html/%.txt: %.txt
|
||||
@$(INSTALL_DIR) $(@D)
|
||||
@@ -207,8 +207,8 @@ html/hypercall/%/index.html: $(CURDIR)/x
|
||||
$(INSTALL_DIR) $(@D)
|
||||
$(PERL) -w $(CURDIR)/xen-headers -O $(@D) \
|
||||
-T 'arch-$* - Xen public headers' \
|
||||
- $(patsubst %,-X arch-%,$(filter-out $*,$(DOC_ARCHES))) \
|
||||
- $(patsubst %,-X xen-%,$(filter-out $*,$(DOC_ARCHES))) \
|
||||
+ $(sort $(patsubst %,-X arch-%,$(filter-out $*,$(DOC_ARCHES)))) \
|
||||
+ $(sort $(patsubst %,-X xen-%,$(filter-out $*,$(DOC_ARCHES)))) \
|
||||
$(EXTRA_EXCLUDE) \
|
||||
$(XEN_ROOT)/xen include/public include/xen/errno.h
|
||||
|
14579
xen.changes
Normal file
14579
xen.changes
Normal file
File diff suppressed because it is too large
Load Diff
123
xen.libxl.dmmd.patch
Normal file
123
xen.libxl.dmmd.patch
Normal file
@ -0,0 +1,123 @@
|
||||
References: bsc#954872
|
||||
|
||||
---
|
||||
tools/libxl/libxl.c | 4 ++++
|
||||
tools/libxl/libxl_device.c | 3 ++-
|
||||
tools/libxl/libxl_dm.c | 34 +++++++++++++++++++++++++++++-----
|
||||
tools/libxl/libxlu_disk_l.l | 2 ++
|
||||
4 files changed, 37 insertions(+), 6 deletions(-)
|
||||
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_disk.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_disk.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_disk.c
|
||||
@@ -203,7 +203,7 @@ static int libxl__device_disk_setdefault
|
||||
return rc;
|
||||
}
|
||||
|
||||
-static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
|
||||
+int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
|
||||
const libxl_device_disk *disk,
|
||||
libxl__device *device)
|
||||
{
|
||||
@@ -372,6 +372,10 @@ static void device_disk_add(libxl__egc *
|
||||
rc = ERROR_FAIL;
|
||||
goto out;
|
||||
case LIBXL_DISK_BACKEND_QDISK:
|
||||
+ if (disk->script) {
|
||||
+ script = libxl__abs_path(gc, disk->script, libxl__xen_script_dir_path());
|
||||
+ flexarray_append_pair(back, "script", script);
|
||||
+ }
|
||||
flexarray_append(back, "params");
|
||||
flexarray_append(back, GCSPRINTF("%s:%s",
|
||||
libxl__device_disk_string_of_format(disk->format),
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_device.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_device.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_device.c
|
||||
@@ -333,7 +333,8 @@ static int disk_try_backend(disk_try_bac
|
||||
return 0;
|
||||
|
||||
case LIBXL_DISK_BACKEND_QDISK:
|
||||
- if (a->disk->script) goto bad_script;
|
||||
+ LOG(DEBUG, "Disk vdev=%s, uses script=%s on %s backend",
|
||||
+ a->disk->vdev, a->disk->script, libxl_disk_backend_to_string(backend));
|
||||
return backend;
|
||||
|
||||
case LIBXL_DISK_BACKEND_STANDALONE:
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_dm.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_dm.c
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_dm.c
|
||||
@@ -1197,6 +1197,30 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
+static void libxl__suse_node_to_path(libxl__gc *gc, int domid, const libxl_device_disk *dp, const char **pdev_path)
|
||||
+{
|
||||
+ libxl_ctx *ctx = libxl__gc_owner(gc);
|
||||
+ char *be_path, *node;
|
||||
+ libxl__device device;
|
||||
+ libxl_device_disk disk;
|
||||
+ int rc;
|
||||
+
|
||||
+ disk = *dp;
|
||||
+ rc = libxl__device_from_disk(gc, domid, &disk, &device);
|
||||
+ if (rc) {
|
||||
+ LIBXL__LOG(ctx, LIBXL__LOG_WARNING, "libxl__device_from_disk failed %d", rc);
|
||||
+ return;
|
||||
+ }
|
||||
+ be_path = libxl__device_backend_path(gc, &device);
|
||||
+
|
||||
+ node = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/node", be_path));
|
||||
+ if (!node)
|
||||
+ return;
|
||||
+
|
||||
+ LIBXL__LOG(ctx, LIBXL__LOG_WARNING, "replacing '%s' with '%s' from %s/node, just for qemu-xen", *pdev_path, node, be_path);
|
||||
+ *pdev_path = node;
|
||||
+}
|
||||
+
|
||||
static int libxl__build_device_model_args_new(libxl__gc *gc,
|
||||
const char *dm, int guest_domid,
|
||||
const libxl_domain_config *guest_config,
|
||||
@@ -1885,9 +1909,11 @@ static int libxl__build_device_model_arg
|
||||
libxl__device_disk_dev_number(disks[i].vdev, &disk, &part);
|
||||
const char *format;
|
||||
char *drive;
|
||||
- const char *target_path = NULL;
|
||||
+ const char *target_path = disks[i].pdev_path;
|
||||
int colo_mode;
|
||||
|
||||
+ libxl__suse_node_to_path(gc, guest_domid, disks + i, &target_path);
|
||||
+
|
||||
if (dev_number == -1) {
|
||||
LOGD(WARN, guest_domid, "unable to determine"" disk number for %s",
|
||||
disks[i].vdev);
|
||||
Index: xen-4.18.0-testing/tools/libs/util/libxlu_disk_l.l
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/util/libxlu_disk_l.l
|
||||
+++ xen-4.18.0-testing/tools/libs/util/libxlu_disk_l.l
|
||||
@@ -253,6 +253,8 @@ target=.* { STRIP(','); SAVESTRING("targ
|
||||
free(newscript);
|
||||
}
|
||||
|
||||
+dmmd:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
|
||||
+npiv:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
|
||||
tapdisk:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
|
||||
tap2?:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
|
||||
aio:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
|
||||
Index: xen-4.18.0-testing/tools/libs/light/libxl_internal.h
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/libs/light/libxl_internal.h
|
||||
+++ xen-4.18.0-testing/tools/libs/light/libxl_internal.h
|
||||
@@ -2073,6 +2073,10 @@ _hidden char *libxl__object_to_json(libx
|
||||
_hidden int libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool retore,
|
||||
libxl_domain_build_info *info);
|
||||
|
||||
+_hidden int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
|
||||
+ const libxl_device_disk *disk,
|
||||
+ libxl__device *device);
|
||||
+
|
||||
/* Calls poll() again - useful to check whether a signaled condition
|
||||
* is still true. Cannot fail. Returns currently-true revents. */
|
||||
_hidden short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events);
|
102
xen.sysconfig-fillup.patch
Normal file
102
xen.sysconfig-fillup.patch
Normal file
@ -0,0 +1,102 @@
|
||||
Fix xencommons for fillup
|
||||
|
||||
The usage in xen.spec is like this:
|
||||
%post tools
|
||||
%{fillup_only -n xencommons xencommons}
|
||||
|
||||
After fresh install, modify the key=value pairs as required, then
|
||||
upgrade the package, the sysconfig file is broken and changes are lost:
|
||||
|
||||
# rm /etc/sysconfig/xencommons
|
||||
# zypper in --oldpackage xen-tools-4.12.4_02-3.30.1
|
||||
# echo XENSTORETYPE=domain >> /etc/sysconfig/xencommons
|
||||
# echo XENSTORE_DOMAIN_SIZE=123 >> /etc/sysconfig/xencommons
|
||||
# zypper in --oldpackage xen-tools-4.12.4_04-3.33.1
|
||||
# diff -u /var/adm/fillup-templates/sysconfig.xencommons /etc/sysconfig/xencommons
|
||||
|
||||
Basically fillup removed all comments, and also the two added key=value lines.
|
||||
|
||||
Prevent this by defining all keys, with empty values, so that consumers
|
||||
of the values will continue to use the built-in defaults.
|
||||
Index: xen-4.16.0-testing/tools/hotplug/Linux/init.d/sysconfig.xencommons.in
|
||||
===================================================================
|
||||
--- xen-4.16.0-testing.orig/tools/hotplug/Linux/init.d/sysconfig.xencommons.in
|
||||
+++ xen-4.16.0-testing/tools/hotplug/Linux/init.d/sysconfig.xencommons.in
|
||||
@@ -3,7 +3,9 @@
|
||||
## Default: "none"
|
||||
#
|
||||
# Log xenconsoled messages (cf xl dmesg)
|
||||
-#XENCONSOLED_TRACE=[none|guest|hv|all]
|
||||
+# One of [none|guest|hv|all]
|
||||
+#
|
||||
+XENCONSOLED_TRACE=
|
||||
|
||||
## Type: string
|
||||
## Default: daemon
|
||||
@@ -16,10 +18,10 @@
|
||||
#
|
||||
# Changing this requires a reboot to take effect.
|
||||
#
|
||||
-#XENSTORETYPE=daemon
|
||||
+XENSTORETYPE=
|
||||
|
||||
## Type: string
|
||||
-## Default: xenstored
|
||||
+## Default: @XENSTORED@
|
||||
#
|
||||
# Select xenstore implementation, this can be either
|
||||
# of these below.
|
||||
@@ -30,7 +32,7 @@
|
||||
# * @sbindir@/xenstored
|
||||
#
|
||||
# Changing this requires a reboot to take effect.
|
||||
-#XENSTORED=@XENSTORED@
|
||||
+XENSTORED=
|
||||
|
||||
## Type: string
|
||||
## Default: unlimited
|
||||
@@ -57,9 +59,10 @@ XENSTORED_ARGS=
|
||||
## Type: string
|
||||
## Default: Not defined, tracing off
|
||||
#
|
||||
-# Log xenstored messages
|
||||
+# Log xenstored messages if a non-empty value is assigned.
|
||||
# Only evaluated if XENSTORETYPE is "daemon".
|
||||
-#XENSTORED_TRACE=[yes|on|1]
|
||||
+#
|
||||
+XENSTORED_TRACE=
|
||||
|
||||
## Type: integer
|
||||
## Default: 50
|
||||
@@ -75,14 +78,14 @@ XENSTORED_ARGS=
|
||||
#
|
||||
# xenstore domain kernel.
|
||||
# Only evaluated if XENSTORETYPE is "domain".
|
||||
-#XENSTORE_DOMAIN_KERNEL=@LIBEXEC@/boot/xenstore-stubdom.gz
|
||||
+XENSTORE_DOMAIN_KERNEL=
|
||||
|
||||
## Type: integer
|
||||
## Default: 8
|
||||
#
|
||||
# xenstore domain memory size in MiB.
|
||||
# Only evaluated if XENSTORETYPE is "domain".
|
||||
-#XENSTORE_DOMAIN_SIZE=8
|
||||
+XENSTORE_DOMAIN_SIZE=
|
||||
|
||||
## Type: string
|
||||
## Default: not set, no autoballooning of xenstore domain
|
||||
@@ -93,7 +96,7 @@ XENSTORED_ARGS=
|
||||
# - combination of both in form of <val>:<frac> (e.g. 8:1/100), resulting
|
||||
# value will be the higher of both specifications
|
||||
# Only evaluated if XENSTORETYPE is "domain".
|
||||
-#XENSTORE_MAX_DOMAIN_SIZE=
|
||||
+XENSTORE_MAX_DOMAIN_SIZE=
|
||||
|
||||
## Type: string
|
||||
## Default: ""
|
||||
@@ -106,4 +109,4 @@ XENSTORE_DOMAIN_ARGS=
|
||||
#QEMU_XEN=@qemu_xen_path@
|
||||
|
||||
# Dom0 UUID
|
||||
-#XEN_DOM0_UUID=00000000-0000-0000-0000-000000000000
|
||||
+XEN_DOM0_UUID=
|
137
xen2libvirt.py
Normal file
137
xen2libvirt.py
Normal file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Authors:
|
||||
# Jim Fehlig <jfehlig@suse.com>
|
||||
#
|
||||
# Read native Xen configuration format, convert to libvirt domXML, and
|
||||
# import (virsh define <xml>) into libvirt.
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import re
|
||||
from xml.etree import ElementTree
|
||||
|
||||
try:
|
||||
import libvirt
|
||||
except ImportError:
|
||||
print('Unable to import the libvirt module. Is libvirt-python installed?')
|
||||
sys.exit(1)
|
||||
|
||||
parser = argparse.ArgumentParser(description='Import Xen domain configuration into libvirt')
|
||||
parser.add_argument('-c', '--convert-only', help='Convert Xen domain configuration into libvirt domXML, but do not import into libvirt', action='store_true', dest='convert_only')
|
||||
parser.add_argument('-r', '--recursive', help='Operate recursivelly on all Xen domain configuration rooted at path', action='store_true')
|
||||
parser.add_argument('-f', '--format', help='Format of Xen domain configuration. Supported formats are xm and sexpr', choices=['xm', 'sexpr'], default=None)
|
||||
parser.add_argument('-v', '--verbose', help='Print information about the import process', action='store_true')
|
||||
parser.add_argument('path', help='Path to Xen domain configuration')
|
||||
|
||||
|
||||
def print_verbose(msg):
|
||||
if args.verbose:
|
||||
print(msg)
|
||||
|
||||
|
||||
def check_config(path, config):
|
||||
isbinary = os.system('file -b ' + path + ' | grep text > /dev/null')
|
||||
|
||||
if isbinary:
|
||||
print('Skipping %s (not a valid Xen configuration file)' % path)
|
||||
return 'unknown'
|
||||
|
||||
for line in config.splitlines():
|
||||
if len(line) == 0 or line.startswith('#'):
|
||||
continue
|
||||
if line.startswith('<domain'):
|
||||
# XML is not a supported conversion format
|
||||
break
|
||||
if line.startswith('(domain'):
|
||||
print('Found sexpr formatted file %s' % path)
|
||||
return 'sexpr'
|
||||
if '=' in line:
|
||||
print('Found xm formatted file %s' % path)
|
||||
return 'xm'
|
||||
break
|
||||
|
||||
print('Skipping %s (not a valid Xen configuration file)' % path)
|
||||
return 'unknown'
|
||||
|
||||
|
||||
def import_domain(conn, path, format=None, convert_only=False):
|
||||
|
||||
f = open(path, 'r')
|
||||
config = f.read()
|
||||
print_verbose('Xen domain configuration read from %s:\n %s' % (path, config))
|
||||
if format is None:
|
||||
format = check_config(path, config)
|
||||
|
||||
if format == 'sexpr':
|
||||
print_verbose('scrubbing domid from configuration')
|
||||
config = re.sub("\(domid [0-9]*\)", "", config)
|
||||
print_verbose('scrubbed sexpr:\n %s' % config)
|
||||
xml = conn.domainXMLFromNative('xen-sxpr', config, 0)
|
||||
elif format == 'xm':
|
||||
xml = conn.domainXMLFromNative('xen-xm', config, 0)
|
||||
else:
|
||||
# Return to continue on to next file (if recursive)
|
||||
return
|
||||
|
||||
f.close()
|
||||
|
||||
# domUloader is no longer available in SLES12, replace with pygrub
|
||||
tree = ElementTree.fromstring(xml)
|
||||
bl = tree.find('.//bootloader')
|
||||
if bl is not None and bl.text is not None and 'domUloader' in bl.text:
|
||||
bl.text = 'pygrub'
|
||||
xml = ElementTree.tostring(tree)
|
||||
|
||||
print_verbose('Successfully converted Xen domain configuration to '
|
||||
'libvirt domXML:\n %s' % xml)
|
||||
if convert_only:
|
||||
print(xml)
|
||||
else:
|
||||
print_verbose('Importing converted libvirt domXML into libvirt...')
|
||||
dom = conn.defineXML(xml.decode("utf-8"))
|
||||
if dom is None:
|
||||
print('Failed to define domain from converted domXML')
|
||||
sys.exit(1)
|
||||
print_verbose('domXML successfully imported into libvirt')
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
path = args.path
|
||||
|
||||
# Connect to libvirt
|
||||
conn = libvirt.open(None)
|
||||
if conn is None:
|
||||
print('Failed to open connection to the hypervisor')
|
||||
sys.exit(1)
|
||||
|
||||
if args.recursive:
|
||||
try:
|
||||
for root, dirs, files in os.walk(path):
|
||||
for name in files:
|
||||
abs_name = os.path.join(root, name)
|
||||
print_verbose('Processing file %s' % abs_name)
|
||||
import_domain(conn, abs_name, args.format, args.convert_only)
|
||||
except IOError:
|
||||
print('Failed to open/read path %s' % path)
|
||||
sys.exit(1)
|
||||
else:
|
||||
import_domain(conn, args.path, args.format, args.convert_only)
|
395
xen_maskcalc.py
Normal file
395
xen_maskcalc.py
Normal file
@ -0,0 +1,395 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Xen Mask Calculator - Calculate CPU masking information based on cpuid(1)
|
||||
# Copyright (C) 2017 Armando Vega
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
EAX1_MATCH = '0x00000001 0x00:'
|
||||
EAX7_MATCH = '0x00000007 0x00:'
|
||||
EXP_LINELN = 76
|
||||
|
||||
libxl_names_ecx1 = []
|
||||
libxl_names_edx1 = []
|
||||
libvirt_names_ecx1 = []
|
||||
libvirt_names_edx1 = []
|
||||
|
||||
libxl_names_ebx7 = []
|
||||
libxl_names_ecx7 = []
|
||||
libvirt_names_ebx7 = []
|
||||
libvirt_names_ecx7 = []
|
||||
|
||||
def fill_ecx1(bit, libxl, libvirt):
|
||||
if libxl_names_ecx1[bit]:
|
||||
print("ecx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_ecx1[bit], libvirt_names_ecx1[bit], libxl, libvirt))
|
||||
return
|
||||
libxl_names_ecx1[bit] = libxl
|
||||
libvirt_names_ecx1[bit] = libvirt
|
||||
|
||||
def fill_edx1(bit, libxl, libvirt):
|
||||
if libxl_names_edx1[bit]:
|
||||
print("edx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_edx1[bit], libvirt_names_edx1[bit], libxl, libvirt))
|
||||
return
|
||||
libxl_names_edx1[bit] = libxl
|
||||
libvirt_names_edx1[bit] = libvirt
|
||||
|
||||
def fill_ebx7(bit, libxl, libvirt):
|
||||
if libxl_names_ebx7[bit]:
|
||||
print("edx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_ebx7[bit], libvirt_names_ebx7[bit], libxl, libvirt))
|
||||
return
|
||||
libxl_names_ebx7[bit] = libxl
|
||||
libvirt_names_ebx7[bit] = libvirt
|
||||
|
||||
def fill_ecx7(bit, libxl, libvirt):
|
||||
if libxl_names_ecx7[bit]:
|
||||
print("ecx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_ecx7[bit], libvirt_names_ecx7[bit], libxl, libvirt))
|
||||
return
|
||||
libxl_names_ecx7[bit] = libxl
|
||||
libvirt_names_ecx7[bit] = libvirt
|
||||
|
||||
def fill_bit_names():
|
||||
for i in range(0,32):
|
||||
libxl_names_ecx1.append(None)
|
||||
libxl_names_edx1.append(None)
|
||||
libxl_names_ebx7.append(None)
|
||||
libxl_names_ecx7.append(None)
|
||||
libvirt_names_ecx1.append(None)
|
||||
libvirt_names_edx1.append(None)
|
||||
libvirt_names_ebx7.append(None)
|
||||
libvirt_names_ecx7.append(None)
|
||||
|
||||
fill_ecx1(0, "sse3", "pni")
|
||||
fill_ecx1(1, "pclmulqdq", "pclmuldq")
|
||||
fill_ecx1(2, "dtes64", "dtes64")
|
||||
fill_ecx1(3, "monitor", "monitor")
|
||||
fill_ecx1(4, "dscpl", "ds_cpl")
|
||||
fill_ecx1(5, "vmx", "vmx")
|
||||
fill_ecx1(6, "smx", "smx")
|
||||
fill_ecx1(7, "est", "est")
|
||||
fill_ecx1(8, "tm2", "tm2")
|
||||
fill_ecx1(9, "ssse3", "ssse3")
|
||||
fill_ecx1(10, "cntxid", "cid")
|
||||
fill_ecx1(12, "fma", "fma")
|
||||
fill_ecx1(13, "cmpxchg16", "cx16")
|
||||
fill_ecx1(14, "xtpr", "xtpr")
|
||||
fill_ecx1(15, "pdcm", "pdcm")
|
||||
fill_ecx1(17, "pcid", "pcid")
|
||||
fill_ecx1(18, "dca", "dca")
|
||||
fill_ecx1(19, "sse4_1", "sse4.1")
|
||||
fill_ecx1(20, "sse4_2", "sse4.2")
|
||||
fill_ecx1(21, "x2apic", "x2apic")
|
||||
fill_ecx1(22, "movbe", "movbe")
|
||||
fill_ecx1(23, "popcnt", "popcnt")
|
||||
fill_ecx1(24, "tsc-deadline", "tsc-deadline")
|
||||
fill_ecx1(25, "aes", "aes")
|
||||
fill_ecx1(26, "xsave", "xsave")
|
||||
fill_ecx1(27, "osxsave", "osxsave")
|
||||
fill_ecx1(28, "avx", "avx")
|
||||
fill_ecx1(29, "f16c", "f16c")
|
||||
fill_ecx1(30, "rdrand", "rdrand")
|
||||
fill_ecx1(31, "hypervisor", "hypervisor")
|
||||
|
||||
fill_edx1(0, "fpu", "fpu")
|
||||
fill_edx1(1, "vme", "vme")
|
||||
fill_edx1(2, "de", "de")
|
||||
fill_edx1(3, "pse", "pse")
|
||||
fill_edx1(4, "tsc", "tsc")
|
||||
fill_edx1(5, "msr", "msr")
|
||||
fill_edx1(6, "pae", "pae")
|
||||
fill_edx1(7, "mce", "mce")
|
||||
fill_edx1(8, "cmpxchg8", "cx8")
|
||||
fill_edx1(9, "apic", "apic")
|
||||
fill_edx1(11, "sysenter", "sep")
|
||||
fill_edx1(12, "mtrr", "mtrr")
|
||||
fill_edx1(13, "pge", "pge")
|
||||
fill_edx1(14, "mca", "mca")
|
||||
fill_edx1(15, "cmov", "cmov")
|
||||
fill_edx1(16, "pat", "pat")
|
||||
fill_edx1(17, "pse36", "pse36")
|
||||
fill_edx1(18, "psn", "pn")
|
||||
fill_edx1(19, "clfsh", "clflush")
|
||||
fill_edx1(21, "ds", "ds")
|
||||
fill_edx1(22, "acpi", "acpi")
|
||||
fill_edx1(23, "mmx", "mmx")
|
||||
fill_edx1(24, "fxsr", "fxsr")
|
||||
fill_edx1(25, "sse", "sse")
|
||||
fill_edx1(26, "sse2", "sse2")
|
||||
fill_edx1(27, "ss", "ss")
|
||||
fill_edx1(28, "htt", "ht")
|
||||
fill_edx1(29, "tm", "tm")
|
||||
fill_edx1(30, "ia64", "ia64")
|
||||
fill_edx1(31, "pbe", "pbe")
|
||||
|
||||
fill_ebx7(0, "fsgsbase", "fsgsbase")
|
||||
fill_ebx7(1, "tsc_adjust", "tsc_adjust")
|
||||
fill_ebx7(3, "bmi1", "bmi1")
|
||||
fill_ebx7(4, "hle", "hle")
|
||||
fill_ebx7(5, "avx2", "avx2")
|
||||
fill_ebx7(7, "smep", "smep")
|
||||
fill_ebx7(8, "bmi2", "bmi2")
|
||||
fill_ebx7(9, "erms", "erms")
|
||||
fill_ebx7(10, "invpcid", "invpcid")
|
||||
fill_ebx7(11, "rtm", "rtm")
|
||||
fill_ebx7(12, "cmt", "cmt")
|
||||
fill_ebx7(14, "mpx", "mpx")
|
||||
fill_ebx7(16, "avx512f", "avx512f")
|
||||
fill_ebx7(17, "avx512dq", "avx512dq")
|
||||
fill_ebx7(18, "rdseed", "rdseed")
|
||||
fill_ebx7(19, "adx", "adx")
|
||||
fill_ebx7(20, "smap", "smap")
|
||||
fill_ebx7(21, "avx512-ifma", "avx512-ifma")
|
||||
fill_ebx7(23, "clflushopt", "clflushopt")
|
||||
fill_ebx7(24, "clwb", "clwb")
|
||||
fill_ebx7(26, "avx512pf", "avx512pf")
|
||||
fill_ebx7(27, "avx512er", "avx512er")
|
||||
fill_ebx7(28, "avx512cd", "avx512cd")
|
||||
fill_ebx7(29, "sha", "sha")
|
||||
fill_ebx7(30, "avx512bw", "avx512bw")
|
||||
fill_ebx7(31, "avx512vl", "avx512vl")
|
||||
|
||||
fill_ecx7(0, "prefetchwt1", "prefetchwt1")
|
||||
fill_ecx7(1, "avx512-vbmi", "avx512-vbmi")
|
||||
fill_ecx7(2, "umip", "umip")
|
||||
fill_ecx7(3, "pku", "pku")
|
||||
fill_ecx7(4, "ospke", "ospke")
|
||||
fill_ecx7(6, "avx512-vbmi2", "avx512-vbmi2")
|
||||
fill_ecx7(8, "gfni", "gfni")
|
||||
fill_ecx7(9, "vaes", "vaes")
|
||||
fill_ecx7(10, "vpclmulqdq", "vpclmulqdq")
|
||||
fill_ecx7(11, "avx512-vnni", "avx512-vnni")
|
||||
fill_ecx7(12, "avx512-bitalg", "avx512-bitalg")
|
||||
fill_ecx7(14, "avx512-vpopcntdq", "avx512-vpopcntdq")
|
||||
fill_ecx7(22, "rdpid", "rdpid")
|
||||
fill_ecx7(25, "cldemote", "cldemote")
|
||||
|
||||
|
||||
def get_register_mask(regs):
|
||||
""" Take a list of register values and return the calculated mask """
|
||||
reg_n = len(regs)
|
||||
mask = ''
|
||||
for idx in range(32):
|
||||
counter = 0
|
||||
for reg in regs:
|
||||
counter += 1 if (reg & (1 << idx) > 0) else 0
|
||||
# if we have all 1s or all 0s we don't mask the bit
|
||||
if counter == reg_n or counter == 0:
|
||||
mask = mask + 'x'
|
||||
else:
|
||||
mask = mask + '0'
|
||||
# we calculated the mask in reverse, so we reverse it again
|
||||
return mask[::-1]
|
||||
|
||||
|
||||
def print_xl_masking_config(nodes):
|
||||
""" Take a dictionary of nodes containing their registers and print out CPUID masking configuration for xl """
|
||||
nomasking = 'x' * 32
|
||||
libxl = []
|
||||
libvirt = []
|
||||
eax1_ecx_regs = []
|
||||
eax1_edx_regs = []
|
||||
eax7_ebx_regs = []
|
||||
eax7_ecx_regs = []
|
||||
for node in nodes:
|
||||
eax1_ecx_regs.append(nodes[node]['eax1_ecx'])
|
||||
eax1_edx_regs.append(nodes[node]['eax1_edx'])
|
||||
eax7_ebx_regs.append(nodes[node]['eax7_ebx'])
|
||||
eax7_ecx_regs.append(nodes[node]['eax7_ecx'])
|
||||
# Get masks for the EAX1 and EAX7 registers
|
||||
eax1_ecx_mask = get_register_mask(eax1_ecx_regs)
|
||||
eax1_edx_mask = get_register_mask(eax1_edx_regs)
|
||||
eax7_ebx_mask = get_register_mask(eax7_ebx_regs)
|
||||
eax7_ecx_mask = get_register_mask(eax7_ecx_regs)
|
||||
# Build the xl CPUID config
|
||||
cpuid_config = 'cpuid = [\n "0x00000001:ecx=' + eax1_ecx_mask
|
||||
if eax1_edx_mask != nomasking:
|
||||
cpuid_config += ',edx=' + eax1_edx_mask
|
||||
cpuid_config += '",\n'
|
||||
cpuid_config += ' "0x00000007,0x00:ebx=' + eax7_ebx_mask
|
||||
if eax7_ecx_mask != nomasking:
|
||||
cpuid_config += ',ecx=' + eax7_ecx_mask
|
||||
cpuid_config += '"\n'
|
||||
cpuid_config += ']'
|
||||
print(cpuid_config)
|
||||
|
||||
bitnum = len(eax1_ecx_mask)
|
||||
while bitnum > 0:
|
||||
bitnum -= 1
|
||||
bitval = eax1_ecx_mask[len(eax1_ecx_mask) - 1 - bitnum]
|
||||
if bitval == "0" and libxl_names_ecx1[bitnum]:
|
||||
libxl.append(libxl_names_ecx1[bitnum] + "=0")
|
||||
libvirt.append(libvirt_names_ecx1[bitnum])
|
||||
|
||||
bitnum = len(eax1_edx_mask)
|
||||
while bitnum > 0:
|
||||
bitnum -= 1
|
||||
bitval = eax1_edx_mask[len(eax1_edx_mask) - 1 - bitnum]
|
||||
if bitval == "0" and libxl_names_edx1[bitnum]:
|
||||
libxl.append(libxl_names_edx1[bitnum] + "=0")
|
||||
libvirt.append(libvirt_names_edx1[bitnum])
|
||||
|
||||
bitnum = len(eax7_ebx_mask)
|
||||
while bitnum > 0:
|
||||
bitnum -= 1
|
||||
bitval = eax7_ebx_mask[len(eax7_ebx_mask) - 1 - bitnum]
|
||||
if bitval == "0" and libxl_names_ebx7[bitnum]:
|
||||
libxl.append(libxl_names_ebx7[bitnum] + "=0")
|
||||
libvirt.append(libvirt_names_ebx7[bitnum])
|
||||
|
||||
bitnum = len(eax7_ecx_mask)
|
||||
while bitnum > 0:
|
||||
bitnum -= 1
|
||||
bitval = eax7_ecx_mask[len(eax7_ecx_mask) - 1 - bitnum]
|
||||
if bitval == "0" and libxl_names_ecx7[bitnum]:
|
||||
libxl.append(libxl_names_ecx7[bitnum] + "=0")
|
||||
libvirt.append(libvirt_names_ecx7[bitnum])
|
||||
|
||||
if len(libxl) > 0:
|
||||
output = "cpuid = [ host"
|
||||
for i in libxl:
|
||||
output += "," + i
|
||||
output += " ]"
|
||||
print(output)
|
||||
|
||||
print("<domain>")
|
||||
print(" <cpu>")
|
||||
for i in libvirt:
|
||||
print(" <feature policy='optional' name='%s' />" % i)
|
||||
print(" </cpu>")
|
||||
print("</domain>")
|
||||
|
||||
|
||||
def print_verbose_masking_info(nodes):
|
||||
""" Take a dictionary of nodes containing their registers and print out verbose mask derivation information """
|
||||
eax1_ecx_regs = []
|
||||
eax1_edx_regs = []
|
||||
eax7_ebx_regs = []
|
||||
eax7_ecx_regs = []
|
||||
for node in nodes:
|
||||
eax1_ecx_regs.append(nodes[node]['eax1_ecx'])
|
||||
eax1_edx_regs.append(nodes[node]['eax1_edx'])
|
||||
eax7_ebx_regs.append(nodes[node]['eax7_ebx'])
|
||||
eax7_ecx_regs.append(nodes[node]['eax7_ecx'])
|
||||
|
||||
print("")
|
||||
print('== Detailed mask derivation info ==')
|
||||
print("")
|
||||
|
||||
print('EAX1 ECX registers:')
|
||||
for reg in eax1_ecx_regs:
|
||||
print('{0:032b}'.format(reg))
|
||||
print('================================')
|
||||
print(get_register_mask(eax1_ecx_regs))
|
||||
|
||||
print("")
|
||||
print('EAX1 EDX registers:')
|
||||
for reg in eax1_edx_regs:
|
||||
print('{0:032b}'.format(reg))
|
||||
print('================================')
|
||||
print(get_register_mask(eax1_edx_regs))
|
||||
|
||||
print("")
|
||||
print('EAX7,0 EBX registers:')
|
||||
for reg in eax7_ebx_regs:
|
||||
print('{0:032b}'.format(reg))
|
||||
print('================================')
|
||||
print(get_register_mask(eax7_ebx_regs))
|
||||
|
||||
print("")
|
||||
print('EAX7,0 ECX registers:')
|
||||
for reg in eax7_ecx_regs:
|
||||
print('{0:032b}'.format(reg))
|
||||
print('================================')
|
||||
print(get_register_mask(eax7_ecx_regs))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
epilog = """The individual 'node_files' are generated with 'cpuid -1r':
|
||||
server1~$ cpuid -1r > node1
|
||||
server2~$ cpuid -1r > node2
|
||||
server3~$ cpuid -1r > node3
|
||||
|
||||
~$ {0} node1 node2 node3
|
||||
|
||||
Use 'zypper install cpuid' to install the cpuid.rpm.
|
||||
|
||||
Note: Run 'cpuid' with NATIVE boot instead of dom0 to get the complete cpid value.
|
||||
Xen hides some bits from dom0!
|
||||
""".format(sys.argv[0])
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description='A utility that calculates a XEN CPUID difference mask',
|
||||
epilog=epilog
|
||||
)
|
||||
parser.add_argument('node_files', nargs='*', help='Filenames of XEN node CPUID outputs')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='Get detailed mask derivation information')
|
||||
args = parser.parse_args()
|
||||
if len(args.node_files) < 2:
|
||||
print('Need at least 2 files to do the comparison!')
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
fill_bit_names()
|
||||
nodes = dict()
|
||||
for node in args.node_files:
|
||||
if os.path.isfile(node):
|
||||
try:
|
||||
f = open(node)
|
||||
except IOError as e:
|
||||
print("I/O error({0}): {1}".format(e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
else:
|
||||
lines = [line.strip() for line in f]
|
||||
eax1 = ''
|
||||
eax7 = ''
|
||||
# try to match the lines containing interesting registers
|
||||
# EAX1 - Processor Info and Feature Bits
|
||||
# EAX7 - Extended features
|
||||
for line in lines:
|
||||
if line.startswith(EAX1_MATCH):
|
||||
eax1 = line
|
||||
elif line.startswith(EAX7_MATCH):
|
||||
eax7 = line
|
||||
# if we get garbled data we should probably just give up
|
||||
if len(eax1) < EXP_LINELN or len(eax7) < EXP_LINELN:
|
||||
print('ERROR: invalid data format in file : ' + node)
|
||||
sys.exit(1)
|
||||
|
||||
# check if we can actually parse the strings into integers
|
||||
try:
|
||||
eax1_ecx = int(eax1.split()[4].split('=')[1], 0)
|
||||
eax1_edx = int(eax1.split()[5].split('=')[1], 0)
|
||||
eax7_ebx = int(eax7.split()[3].split('=')[1], 0)
|
||||
eax7_ecx = int(eax7.split()[4].split('=')[1], 0)
|
||||
except ValueError:
|
||||
print('ERROR: invalid data format in file: ' + node)
|
||||
sys.exit(1)
|
||||
|
||||
nodes[node] = dict()
|
||||
nodes[node]['eax1_ecx'] = eax1_ecx
|
||||
nodes[node]['eax1_edx'] = eax1_edx
|
||||
nodes[node]['eax7_ebx'] = eax7_ebx
|
||||
nodes[node]['eax7_ecx'] = eax7_ecx
|
||||
f.close()
|
||||
else:
|
||||
print('File not found: ' + node)
|
||||
sys.exit(1)
|
||||
|
||||
print_xl_masking_config(nodes)
|
||||
if args.verbose:
|
||||
print_verbose_masking_info(nodes)
|
1
xenapiusers
Normal file
1
xenapiusers
Normal file
@ -0,0 +1 @@
|
||||
root
|
36
xencommons.service
Normal file
36
xencommons.service
Normal file
@ -0,0 +1,36 @@
|
||||
[Unit]
|
||||
Description=xencommons
|
||||
ConditionPathExists=/proc/xen/capabilities
|
||||
|
||||
# Avoid errors from systemd-modules-load.service
|
||||
Requires=xen-dom0-modules.service
|
||||
After=xen-dom0-modules.service
|
||||
|
||||
# Pull in all upstream service files
|
||||
Requires=proc-xen.mount
|
||||
After=proc-xen.mount
|
||||
Requires=xenstored.service
|
||||
After=xenstored.service
|
||||
Requires=xenconsoled.service
|
||||
After=xenconsoled.service
|
||||
Requires=xen-init-dom0.service
|
||||
After=xen-init-dom0.service
|
||||
Requires=xen-qemu-dom0-disk-backend.service
|
||||
After=xen-qemu-dom0-disk-backend.service
|
||||
|
||||
# Make sure network (for bridge) and remote mounts (for xendomains) are available ...
|
||||
After=network-online.target
|
||||
After=remote-fs.target
|
||||
|
||||
# ... for libvirt and xendomains
|
||||
Before=xendomains.service libvirtd.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
ExecStartPre=/bin/grep -q control_d /proc/xen/capabilities
|
||||
ExecStart=/usr/bin/xenstore-ls -f
|
||||
ExecStartPost=/bin/sh -c 'mv -vf /var/log/xen/xen-boot.log /var/log/xen/xen-boot.prev.log ; /usr/sbin/xl dmesg > /var/log/xen/xen-boot.log'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
27
xenconsole-no-multiple-connections.patch
Normal file
27
xenconsole-no-multiple-connections.patch
Normal file
@ -0,0 +1,27 @@
|
||||
Index: xen-4.18.0-testing/tools/console/client/main.c
|
||||
===================================================================
|
||||
--- xen-4.18.0-testing.orig/tools/console/client/main.c
|
||||
+++ xen-4.18.0-testing/tools/console/client/main.c
|
||||
@@ -101,6 +101,7 @@ static int get_pty_fd(struct xs_handle *
|
||||
* Assumes there is already a watch set in the store for this path. */
|
||||
{
|
||||
struct timeval tv;
|
||||
+ struct flock lock;
|
||||
fd_set watch_fdset;
|
||||
int xs_fd = xs_fileno(xs), pty_fd = -1;
|
||||
time_t start, now;
|
||||
@@ -124,6 +125,14 @@ static int get_pty_fd(struct xs_handle *
|
||||
pty_fd = open(pty_path, O_RDWR | O_NOCTTY);
|
||||
if (pty_fd == -1)
|
||||
warn("Could not open tty `%s'", pty_path);
|
||||
+ else {
|
||||
+ memset(&lock, 0, sizeof(lock));
|
||||
+ lock.l_type = F_WRLCK;
|
||||
+ lock.l_whence = SEEK_SET;
|
||||
+ if (fcntl(pty_fd, F_SETLK, &lock) != 0)
|
||||
+ err(errno, "Could not lock tty '%s'",
|
||||
+ pty_path);
|
||||
+ }
|
||||
}
|
||||
free(pty_path);
|
||||
}
|
674
xendomains-wait-disks.LICENSE
Normal file
674
xendomains-wait-disks.LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
28
xendomains-wait-disks.README.md
Normal file
28
xendomains-wait-disks.README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# xen-tools-xendomains-wait-disk
|
||||
|
||||
[xendomains.service](https://github.com/xen-project/xen/blob/RELEASE-4.13.0/tools/hotplug/Linux/systemd/xendomains.service.in) has problems
|
||||
with disks that appear only later in boot process (or even after booting is complete). This project creates a service that
|
||||
loops over all disks that domU will use and wait for them to appear.
|
||||
|
||||
xendomains-wait-disk.service launches a script that reads both /etc/xen/auto/ configurations and /var/lib/xen/save/ dumps.
|
||||
From those files, it extracts which disks are needed for all domU that will be started (respecting /etc/sysconfig/xendomains
|
||||
settings). After that, it simply loops waiting for those disks to appear. There is a timeout (5 min) configured in
|
||||
xendomains-wait-disk.service that prevents it to block booting process forever.
|
||||
|
||||
There are two known cases where this project is useful:
|
||||
|
||||
## degraded mdadm RAID
|
||||
|
||||
mdadm RAID are assembled by [udev rules](https://github.com/neilbrown/mdadm/blob/master/udev-md-raid-assembly.rules).
|
||||
However, it is only assembled when it is healthy. When a member is still missing, it starts a [timer](https://github.com/neilbrown/mdadm/blob/master/systemd/mdadm-last-resort%40.timer) that will try to assemble the RAID anyway after 30s, even if degraded. This timer does not block xendomains to be started. So, if a domU is depending on a MD RAID that is degraded (i.e. RAID 1 missing one disk), xendomains.service will be started before those 30s passed and that domU will fail.
|
||||
|
||||
An alternative solution would be to add extra hard dependencies to xendomains.service for each required disk (Require=xxx.device). However, this solution introduces another bigger problem. Before, if a single RAID is degraded, only the domU that depends on it will fail. With Require=xxx.device, xendomains will never start if
|
||||
a RAID could not be assembled even after 30s (i.e. RAID5 with two missing disks).
|
||||
|
||||
With xendomains-wait-disk.service, xendomains.service will be blocked up to 5 min waiting for those MD RAID used by domUs. If it fails, xendomains.service
|
||||
continues anyway.
|
||||
|
||||
## iSCSI disks
|
||||
|
||||
domU that uses iSCSI disk (mapped by host OS) also fails to start during boot. open-iscsi.service returns before it connect to the remote target and rescan
|
||||
iscsi disks. As in mdadm RAID case, xendomains.service is started and domU that depends on iSCSI disks will fail.
|
199
xendomains-wait-disks.sh
Normal file
199
xendomains-wait-disks.sh
Normal file
@ -0,0 +1,199 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Generates xendomains unit
|
||||
#
|
||||
|
||||
read_conf_from_file() {
|
||||
${sbindir}/xl create --quiet --dryrun --defconfig "$1"
|
||||
}
|
||||
|
||||
big2littleendian_32bit(){
|
||||
echo ${1:6:2}${1:4:2}${1:2:2}${1:0:2}
|
||||
}
|
||||
|
||||
read_hex() {
|
||||
local out_var=$1; shift
|
||||
local input=$1; shift
|
||||
local pos_var=$1; shift
|
||||
local length=$1; shift
|
||||
local hex=$(dd bs=1 skip=${!pos_var} count=$length status=none <$input | xxd -p -c$length -l$length)
|
||||
read -r $pos_var <<<"$((${!pos_var} + $length))"
|
||||
read -r $out_var <<<"$hex"
|
||||
}
|
||||
|
||||
hex2dec() {
|
||||
local hex=$1; shift
|
||||
local little_endian=$1; shift
|
||||
if $little_endian; then
|
||||
hex=$(big2littleendian_32bit $hex)
|
||||
fi
|
||||
echo $((0x$hex))
|
||||
}
|
||||
|
||||
read_conf_from_image(){
|
||||
local pos=0 length=0
|
||||
|
||||
local magic_header byte_order mandatory_flags optional_flags optional_data_len config_len config_json
|
||||
|
||||
read_hex magic_header $1 pos 32
|
||||
# "Xen saved domain, xl format\n \0 \r"
|
||||
if [ "$magic_header" != "58656e20736176656420646f6d61696e2c20786c20666f726d61740a2000200d" ]; then
|
||||
log $err "Unknown file format in $1. Wrong magic header: '0x$magic_header'"
|
||||
return 1
|
||||
fi
|
||||
|
||||
read_hex byte_order $1 pos 4
|
||||
case "$byte_order" in
|
||||
04030201) little_endian=true;;
|
||||
01020304) little_endian=false;;
|
||||
*) log $err "Unknown byte order 0x$byte_order in $1"; return 1;;
|
||||
esac
|
||||
|
||||
#define XL_MANDATORY_FLAG_JSON (1U << 0) /* config data is in JSON format */
|
||||
#define XL_MANDATORY_FLAG_STREAMv2 (1U << 1) /* stream is v2 */
|
||||
read_hex mandatory_flags $1 pos 4
|
||||
if [ "$(($(hex2dec $mandatory_flags $little_endian) & 0x3))" -ne 3 ]; then
|
||||
log $err "Unknown config format or stream version. Mandatory flags are 0x$mandatory_flag"
|
||||
return 1
|
||||
fi
|
||||
|
||||
read_hex optional_flags $1 pos 4
|
||||
read_hex optional_data_len $1 pos 4
|
||||
optional_data_len=$(hex2dec $optional_data_len $little_endian)
|
||||
|
||||
# I'll not use but saved memory dump will begin at $((pos+optional_data_len))
|
||||
read_hex config_len $1 pos 4
|
||||
config_len=$(hex2dec $config_len $little_endian)
|
||||
|
||||
# null terminated string
|
||||
read_hex config_json $1 pos $config_len
|
||||
xxd -p -r <<<"$config_json"
|
||||
}
|
||||
|
||||
log() {
|
||||
local msg_loglevel=$1; shift
|
||||
if [ "$msg_loglevel" -gt "$LOGLEVEL" ]; then
|
||||
return 0
|
||||
fi
|
||||
echo "$@" >&2
|
||||
}
|
||||
|
||||
|
||||
emerg=0; alert=1; crit=2; err=3
|
||||
warning=4; notice=5; info=6; debug=7
|
||||
LOGLEVEL=${LOGLEVEL:-4}
|
||||
if [ "$SYSTEMD_LOG_LEVEL" ]; then
|
||||
LOGLEVEL=${!SYSTEMD_LOG_LEVEL}
|
||||
fi
|
||||
log $debug "Using loglevel $LOGLEVEL"
|
||||
trap "log $err Error on \$LINENO: \$(caller)" ERR
|
||||
|
||||
log $debug "loading /etc/xen/scripts/hotplugpath.sh..."
|
||||
. /etc/xen/scripts/hotplugpath.sh
|
||||
|
||||
#log $debug "testing for ${sbindir}/xl..."
|
||||
#CMD=${sbindir}/xl
|
||||
#if ! $CMD list &> /dev/null; then
|
||||
# log $err "${sbindir}/xl list failed!"
|
||||
# log $err "$($CMD list &>&1)"
|
||||
# exit $?
|
||||
#fi
|
||||
#log $debug "${sbindir}/xl list OK!"
|
||||
|
||||
log $debug "loading /etc/sysconfig/xendomains..."
|
||||
XENDOM_CONFIG=/etc/sysconfig/xendomains
|
||||
if ! test -r $XENDOM_CONFIG; then
|
||||
echo "$XENDOM_CONFIG not existing" >&2;
|
||||
exit 6
|
||||
fi
|
||||
|
||||
. $XENDOM_CONFIG
|
||||
|
||||
doms_conf=()
|
||||
doms_restore=()
|
||||
doms_source=()
|
||||
|
||||
log $debug "Reading saved domains..."
|
||||
if [ "$XENDOMAINS_RESTORE" = "true" ] && [ -d "$XENDOMAINS_SAVE" ]; then
|
||||
for dom in $XENDOMAINS_SAVE/*; do
|
||||
log $debug "Trying $dom..."
|
||||
if ! [ -r $dom ] ; then
|
||||
log $debug "Not readable $dom..."
|
||||
continue
|
||||
fi
|
||||
|
||||
log $debug "Reading conf from $dom..."
|
||||
if ! dom_conf=$(read_conf_from_image $dom); then
|
||||
log $error "Cannot read conf from $dom"
|
||||
continue
|
||||
fi
|
||||
|
||||
log $debug "Adding $dom to the list"
|
||||
doms_conf+=("$dom_conf")
|
||||
doms_restore+=(true)
|
||||
doms_source+=("$dom")
|
||||
done
|
||||
fi
|
||||
|
||||
log $debug "Reading auto domains..."
|
||||
if [ -d "$XENDOMAINS_AUTO" ]; then
|
||||
for dom in $XENDOMAINS_AUTO/*; do
|
||||
log $debug "Trying $dom..."
|
||||
if ! [ -r $dom ] ; then
|
||||
log $debug "Not readable $dom..."
|
||||
continue
|
||||
fi
|
||||
|
||||
log $debug "Reading conf from $dom..."
|
||||
if ! dom_conf=$(read_conf_from_file $dom); then
|
||||
echo 123
|
||||
log $error "Cannot read conf from $dom"
|
||||
continue
|
||||
fi
|
||||
|
||||
log $debug "Adding $dom to the list"
|
||||
doms_conf+=("$dom_conf")
|
||||
doms_restore+=(false)
|
||||
doms_source+=("$dom")
|
||||
done
|
||||
fi
|
||||
|
||||
log $debug "We have ${#doms_conf[*]} to check"
|
||||
for i in ${!doms_conf[*]}; do
|
||||
log $debug "Doing dom $i..."
|
||||
|
||||
dom_conf="${doms_conf[i]}"
|
||||
dom_restore="${doms_restore[i]}"
|
||||
dom_source="${doms_source[i]}"
|
||||
|
||||
dom_name=$(sed -n 's/^.*(name \(.*\))$/\1/p;s/^.*"name": "\(.*\)",$/\1/p' <<<"$dom_conf")
|
||||
readarray -t required_disks <<<"$(sed -n -e '/^ "disks": \[/,/ \],/{ /"pdev_path":/ { s/.*"pdev_path": "//;s/".*//p } }' <<<"$dom_conf")"
|
||||
|
||||
log $debug "dom $i is named $dom_name..."
|
||||
for disk in "${required_disks[@]}"; do
|
||||
disk_control_var=control_$(tr -d -c '[a-zA-Z0-9_]' <<<"$disk")
|
||||
if [ "${!disk_control_var:-0}" -eq 1 ]; then
|
||||
log $debug "$disk for $dom_name is already being checked"
|
||||
continue
|
||||
fi
|
||||
declare $disk_control_var=1
|
||||
log $debug "waiting for $disk for $dom_name"
|
||||
(
|
||||
j=0 found_loglevel=$debug
|
||||
while true; do
|
||||
if [ -e "$disk" ]; then
|
||||
log $found_loglevel "disk $disk found (after $j seconds)"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$(( j++ % 5))" -eq 0 ]; then
|
||||
log $warning "still waiting for $disk for $dom_name..."
|
||||
found_loglevel=$warning
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
) &
|
||||
done
|
||||
done
|
||||
|
||||
wait
|
||||
log $debug "Exiting normally"
|
54
xenstore-run-in-studomain.patch
Normal file
54
xenstore-run-in-studomain.patch
Normal file
@ -0,0 +1,54 @@
|
||||
References: fate#323663 - Run Xenstore in stubdomain
|
||||
|
||||
--- a/tools/hotplug/Linux/init.d/sysconfig.xencommons.in
|
||||
+++ b/tools/hotplug/Linux/init.d/sysconfig.xencommons.in
|
||||
@@ -8,7 +8,7 @@
|
||||
XENCONSOLED_TRACE=
|
||||
|
||||
## Type: string
|
||||
-## Default: daemon
|
||||
+## Default: domain
|
||||
#
|
||||
# Select type of xentore service.
|
||||
#
|
||||
@@ -81,14 +81,14 @@ XENSTORED_TRACE=
|
||||
XENSTORE_DOMAIN_KERNEL=
|
||||
|
||||
## Type: integer
|
||||
-## Default: 8
|
||||
+## Default: 32
|
||||
#
|
||||
# xenstore domain memory size in MiB.
|
||||
# Only evaluated if XENSTORETYPE is "domain".
|
||||
XENSTORE_DOMAIN_SIZE=
|
||||
|
||||
## Type: string
|
||||
-## Default: not set, no autoballooning of xenstore domain
|
||||
+## Default: 1/100
|
||||
#
|
||||
# Maximum xenstore domain memory size. Can be specified as:
|
||||
# - plain integer value for max size in MiB
|
||||
--- a/tools/hotplug/Linux/launch-xenstore.in
|
||||
+++ b/tools/hotplug/Linux/launch-xenstore.in
|
||||
@@ -48,7 +48,7 @@ test_xenstore && exit 0
|
||||
|
||||
test -f @CONFIG_DIR@/@CONFIG_LEAF_DIR@/xencommons && . @CONFIG_DIR@/@CONFIG_LEAF_DIR@/xencommons
|
||||
|
||||
-[ "$XENSTORETYPE" = "" ] && XENSTORETYPE=daemon
|
||||
+[ "$XENSTORETYPE" = "" ] && XENSTORETYPE=domain
|
||||
|
||||
/bin/mkdir -p @XEN_RUN_DIR@
|
||||
|
||||
@@ -95,9 +95,10 @@ test -f @CONFIG_DIR@/@CONFIG_LEAF_DIR@/x
|
||||
[ "$XENSTORETYPE" = "domain" ] && {
|
||||
[ -z "$XENSTORE_DOMAIN_KERNEL" ] && XENSTORE_DOMAIN_KERNEL=@LIBEXEC@/boot/xenstore-stubdom.gz
|
||||
XENSTORE_DOMAIN_ARGS="$XENSTORE_DOMAIN_ARGS --kernel $XENSTORE_DOMAIN_KERNEL"
|
||||
- [ -z "$XENSTORE_DOMAIN_SIZE" ] && XENSTORE_DOMAIN_SIZE=8
|
||||
+ [ -z "$XENSTORE_DOMAIN_SIZE" ] && XENSTORE_DOMAIN_SIZE=32
|
||||
XENSTORE_DOMAIN_ARGS="$XENSTORE_DOMAIN_ARGS --memory $XENSTORE_DOMAIN_SIZE"
|
||||
- [ -z "$XENSTORE_MAX_DOMAIN_SIZE" ] || XENSTORE_DOMAIN_ARGS="$XENSTORE_DOMAIN_ARGS --maxmem $XENSTORE_MAX_DOMAIN_SIZE"
|
||||
+ [ -z "$XENSTORE_MAX_DOMAIN_SIZE" ] && XENSTORE_MAX_DOMAIN_SIZE="1/100"
|
||||
+ XENSTORE_DOMAIN_ARGS="$XENSTORE_DOMAIN_ARGS --maxmem $XENSTORE_MAX_DOMAIN_SIZE"
|
||||
|
||||
echo -n Starting $XENSTORE_DOMAIN_KERNEL...
|
||||
${LIBEXEC_BIN}/init-xenstore-domain $XENSTORE_DOMAIN_ARGS || exit 1
|
108
xenwatchdogd-restart.patch
Normal file
108
xenwatchdogd-restart.patch
Normal file
@ -0,0 +1,108 @@
|
||||
References: bsc#1178736
|
||||
|
||||
Allow restart of xenwatchdogd in case it terminated unexpectetly.
|
||||
Index: xen-4.14.0-testing/tools/misc/xenwatchdogd.c
|
||||
===================================================================
|
||||
--- xen-4.14.0-testing.orig/tools/misc/xenwatchdogd.c
|
||||
+++ xen-4.14.0-testing/tools/misc/xenwatchdogd.c
|
||||
@@ -9,12 +9,16 @@
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
+#include <libgen.h>
|
||||
+#include <syslog.h>
|
||||
|
||||
xc_interface *h;
|
||||
int id = 0;
|
||||
+static const char id_file[] = "/run/xenwatchdog_id.txt";
|
||||
|
||||
-void daemonize(void)
|
||||
+static void daemonize(const char *str)
|
||||
{
|
||||
+ const char *err_str = "";
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
err(1, "fork");
|
||||
@@ -23,7 +27,9 @@ void daemonize(void)
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
- umask(0);
|
||||
+#define err(x,s) do { err_str = (s); goto out; } while (0)
|
||||
+ openlog(str, LOG_CONS, LOG_DAEMON);
|
||||
+ umask(~(S_IRUSR|S_IWUSR));
|
||||
if (setsid() < 0)
|
||||
err(1, "setsid");
|
||||
if (chdir("/") < 0)
|
||||
@@ -34,6 +40,10 @@ void daemonize(void)
|
||||
err(1, "reopen stdout");
|
||||
if(freopen("/dev/null", "w", stderr) == NULL)
|
||||
err(1, "reopen stderr");
|
||||
+ return;
|
||||
+out:
|
||||
+ syslog(LOG_ERR, "%s: %m", err_str);
|
||||
+ exit(1);
|
||||
}
|
||||
|
||||
void catch_exit(int sig)
|
||||
@@ -47,18 +57,21 @@ void catch_usr1(int sig)
|
||||
{
|
||||
if (id)
|
||||
xc_watchdog(h, id, 0);
|
||||
+ unlink(id_file);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
+ FILE *f;
|
||||
int t, s;
|
||||
int ret;
|
||||
+ const char *err_str = "";
|
||||
|
||||
if (argc < 2)
|
||||
errx(1, "usage: %s <timeout> <sleep>", argv[0]);
|
||||
|
||||
- daemonize();
|
||||
+ daemonize(basename(argv[0]));
|
||||
|
||||
h = xc_interface_open(NULL, NULL, 0);
|
||||
if (h == NULL)
|
||||
@@ -86,9 +99,25 @@ int main(int argc, char **argv)
|
||||
if (signal(SIGUSR1, &catch_usr1) == SIG_ERR)
|
||||
err(1, "signal");
|
||||
|
||||
- id = xc_watchdog(h, 0, t);
|
||||
- if (id <= 0)
|
||||
- err(1, "xc_watchdog setup");
|
||||
+ f = fopen(id_file, "r");
|
||||
+ if (f) {
|
||||
+ if (fscanf(f, "%d", &id) != 1)
|
||||
+ id = -1;
|
||||
+ if (id <= 0)
|
||||
+ err(1, "xc_watchdog setup");
|
||||
+ syslog(LOG_INFO, "reusing id %d", id);
|
||||
+ fclose(f);
|
||||
+ } else {
|
||||
+ id = xc_watchdog(h, 0, t);
|
||||
+ syslog(LOG_INFO, "obtained id %d", id);
|
||||
+ if (id <= 0)
|
||||
+ err(1, "xc_watchdog setup");
|
||||
+ f = fopen(id_file, "w");
|
||||
+ if (f) {
|
||||
+ fprintf(f, "%d\n", id);
|
||||
+ fclose(f);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
for (;;) {
|
||||
sleep(s);
|
||||
@@ -96,4 +125,8 @@ int main(int argc, char **argv)
|
||||
if (ret != 0)
|
||||
err(1, "xc_watchdog");
|
||||
}
|
||||
+
|
||||
+out:
|
||||
+ syslog(LOG_ERR, "%s: %m", err_str);
|
||||
+ exit(1);
|
||||
}
|
13
xl-conf-default-bridge.patch
Normal file
13
xl-conf-default-bridge.patch
Normal file
@ -0,0 +1,13 @@
|
||||
Index: xen-4.14.0-testing/tools/examples/xl.conf
|
||||
===================================================================
|
||||
--- xen-4.14.0-testing.orig/tools/examples/xl.conf
|
||||
+++ xen-4.14.0-testing/tools/examples/xl.conf
|
||||
@@ -34,7 +34,7 @@
|
||||
#vif.default.script="vif-bridge"
|
||||
|
||||
# default bridge device to use with vif-bridge hotplug scripts
|
||||
-#vif.default.bridge="xenbr0"
|
||||
+vif.default.bridge="br0"
|
||||
|
||||
# Reserve a claim of memory when launching a guest. This guarantees immediate
|
||||
# feedback whether the guest can be launched due to memory exhaustion
|
13
xl-conf-disable-autoballoon.patch
Normal file
13
xl-conf-disable-autoballoon.patch
Normal file
@ -0,0 +1,13 @@
|
||||
Index: xen-4.14.0-testing/tools/examples/xl.conf
|
||||
===================================================================
|
||||
--- xen-4.14.0-testing.orig/tools/examples/xl.conf
|
||||
+++ xen-4.14.0-testing/tools/examples/xl.conf
|
||||
@@ -7,7 +7,7 @@
|
||||
# Control whether dom0 is ballooned down when xen doesn't have enough
|
||||
# free memory to create a domain. "auto" means only balloon if dom0
|
||||
# starts with all the host's memory.
|
||||
-#autoballoon="auto"
|
||||
+autoballoon="off"
|
||||
|
||||
# full path of the lockfile used by xl during domain creation
|
||||
#lockfile="/var/lock/xl"
|
180
xl-save-pc.patch
Normal file
180
xl-save-pc.patch
Normal file
@ -0,0 +1,180 @@
|
||||
References: bug#1176189
|
||||
|
||||
Usage of xl save -p|-c will suspend the domU.
|
||||
As a result the monitoring xl process with get a LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN/LIBXL_SHUTDOWN_REASON_SUSPEND event.
|
||||
This will cause it to exit because it does not know the -p/-c flags were used to keep the domU active.
|
||||
As a result the final shutdown will not destroy the domU.
|
||||
|
||||
Write a flag to xenstore to let the monitoring process know about the usage of -p/-c.
|
||||
Remove the flag once the suspend is done.
|
||||
|
||||
Recognize the flag in the monitoring process.
|
||||
Keep going if the flag is seen.
|
||||
Watch again for @releaseDomain events.
|
||||
Keep going if the event type and shutdown reason remains the same.
|
||||
---
|
||||
tools/xl/Makefile | 3 ++-
|
||||
tools/xl/xl.h | 1 +
|
||||
tools/xl/xl_saverestore.c | 15 ++++++++++++
|
||||
tools/xl/xl_vmcontrol.c | 48 +++++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 66 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/tools/xl/Makefile
|
||||
+++ b/tools/xl/Makefile
|
||||
@@ -26,6 +26,7 @@ XL_OBJS += xl_vmcontrol.o xl_saverestore
|
||||
XL_OBJS += xl_vdispl.o xl_vsnd.o xl_vkb.o
|
||||
|
||||
$(XL_OBJS): CFLAGS += $(CFLAGS_libxentoollog)
|
||||
+$(XL_OBJS): CFLAGS += $(CFLAGS_libxenstore)
|
||||
$(XL_OBJS): CFLAGS += $(CFLAGS_XL)
|
||||
$(XL_OBJS): CFLAGS += -include $(XEN_ROOT)/tools/config.h # libxl_json.h needs it.
|
||||
|
||||
@@ -33,7 +34,7 @@ $(XL_OBJS): CFLAGS += -include $(XEN_ROO
|
||||
all: xl
|
||||
|
||||
xl: $(XL_OBJS)
|
||||
- $(CC) $(LDFLAGS) -o $@ $(XL_OBJS) $(LDLIBS_libxenutil) $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) -lyajl $(APPEND_LDFLAGS)
|
||||
+ $(CC) $(LDFLAGS) -o $@ $(XL_OBJS) $(LDLIBS_libxenutil) $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) $(LDLIBS_libxenstore) -lyajl $(APPEND_LDFLAGS)
|
||||
|
||||
.PHONY: install
|
||||
install: all
|
||||
--- a/tools/xl/xl.h
|
||||
+++ b/tools/xl/xl.h
|
||||
@@ -303,6 +303,7 @@ typedef enum {
|
||||
DOMAIN_RESTART_SOFT_RESET, /* Soft reset should be performed */
|
||||
} domain_restart_type;
|
||||
|
||||
+#define XL_SAVE_PAUSE_CHECKPOINT "suse-xl-save-pc"
|
||||
extern void printf_info_sexp(int domid, libxl_domain_config *d_config, FILE *fh);
|
||||
extern void apply_global_affinity_masks(libxl_domain_type type,
|
||||
libxl_bitmap *vcpu_affinity_array,
|
||||
--- a/tools/xl/xl_saverestore.c
|
||||
+++ b/tools/xl/xl_saverestore.c
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
+#include <xenstore.h>
|
||||
#include <libxl.h>
|
||||
#include <libxl_utils.h>
|
||||
#include <libxlutil.h>
|
||||
@@ -127,6 +128,8 @@ static int save_domain(uint32_t domid, i
|
||||
const char *filename, int checkpoint,
|
||||
int leavepaused, const char *override_config_file)
|
||||
{
|
||||
+ struct xs_handle *xsh = NULL;
|
||||
+ char path[80];
|
||||
int fd;
|
||||
uint8_t *config_data;
|
||||
int config_len;
|
||||
@@ -144,12 +147,24 @@ static int save_domain(uint32_t domid, i
|
||||
fprintf(stderr, "Failed to open temp file %s for writing\n", filename);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
+ if (leavepaused || checkpoint)
|
||||
+ {
|
||||
+ snprintf(path, sizeof(path), "/libxl/%u/" XL_SAVE_PAUSE_CHECKPOINT, domid);
|
||||
+ xsh = xs_open(0);
|
||||
+ if (xsh)
|
||||
+ xs_write(xsh, XBT_NULL, path, leavepaused ? "p" : "c", 1);
|
||||
+ }
|
||||
|
||||
save_domain_core_writeconfig(fd, filename, config_data, config_len);
|
||||
|
||||
int rc = libxl_domain_suspend_suse(ctx, domid, fd, &props, NULL);
|
||||
close(fd);
|
||||
|
||||
+ if (xsh) {
|
||||
+ xs_rm(xsh, XBT_NULL, path);
|
||||
+ xs_close(xsh);
|
||||
+ }
|
||||
+
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to save domain, resuming domain\n");
|
||||
libxl_domain_resume(ctx, domid, 1, 0);
|
||||
--- a/tools/xl/xl_vmcontrol.c
|
||||
+++ b/tools/xl/xl_vmcontrol.c
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
+#include <xenstore.h>
|
||||
#include <libxl.h>
|
||||
#include <libxl_utils.h>
|
||||
#include <libxlutil.h>
|
||||
@@ -668,6 +669,10 @@ int create_domain(struct domain_create *
|
||||
int migrate_fd = dom_info->migrate_fd;
|
||||
bool config_in_json;
|
||||
|
||||
+ libxl_event_type type = 0;
|
||||
+ uint8_t shutdown_reason = 0;
|
||||
+ bool is_in_suspend = false;
|
||||
+
|
||||
int i;
|
||||
int need_daemon = daemonize;
|
||||
int ret, rc;
|
||||
@@ -1034,6 +1039,24 @@ start:
|
||||
ret = domain_wait_event(domid, &event);
|
||||
if (ret) goto out;
|
||||
|
||||
+ if (is_in_suspend) {
|
||||
+ if ( type == event->type && event->u.domain_shutdown.shutdown_reason == shutdown_reason) {
|
||||
+ struct timespec req = { .tv_nsec = 123456789, };
|
||||
+ libxl_evdisable_domain_death(ctx, deathw);
|
||||
+ deathw = NULL;
|
||||
+ ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw);
|
||||
+ if (ret) goto out;
|
||||
+ libxl_event_free(ctx, event);
|
||||
+ LOG("Domain %u still suspended", domid);
|
||||
+ nanosleep(&req, NULL);
|
||||
+ continue;
|
||||
+ }
|
||||
+ is_in_suspend = false;
|
||||
+ LOG("Domain %u left suspend state", domid);
|
||||
+ }
|
||||
+ type = event->type;
|
||||
+ shutdown_reason = event->u.domain_shutdown.shutdown_reason;
|
||||
+
|
||||
switch (event->type) {
|
||||
|
||||
case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
|
||||
@@ -1095,14 +1118,39 @@ start:
|
||||
goto start;
|
||||
|
||||
case DOMAIN_RESTART_NONE:
|
||||
+ {
|
||||
+ struct xs_handle *xsh = xs_open(0);
|
||||
+
|
||||
+ if (xsh) {
|
||||
+ char path[80];
|
||||
+ unsigned int len = 0;
|
||||
+ char *val;
|
||||
+
|
||||
+ snprintf(path, sizeof(path), "/libxl/%u/" XL_SAVE_PAUSE_CHECKPOINT, domid);
|
||||
+ val = xs_read(xsh, XBT_NULL, path, &len);
|
||||
+ xs_close(xsh);
|
||||
+ LOG("Got %p '%s' from %s, len %u", val, val ?:"", path, len);
|
||||
+ free(val);
|
||||
+ if (val)
|
||||
+ {
|
||||
+ is_in_suspend = true;
|
||||
+ libxl_evdisable_domain_death(ctx, deathw);
|
||||
+ deathw = NULL;
|
||||
+ ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw);
|
||||
+ if (ret) goto out;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
LOG("Done. Exiting now");
|
||||
libxl_event_free(ctx, event);
|
||||
ret = 0;
|
||||
goto out;
|
||||
+ }
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
+ break;
|
||||
|
||||
case LIBXL_EVENT_TYPE_DOMAIN_DEATH:
|
||||
LOG("Domain %u has been destroyed.", domid);
|
Loading…
Reference in New Issue
Block a user