mirror of https://github.com/xemu-project/xemu.git
Migration pull request
- Nick's reenabling of ppc64 tests + speed improvements - Yuan's IAA/QPL compression support for multifd - Shameer's UADK compression support for multifd -----BEGIN PGP SIGNATURE----- iQJEBAABCAAuFiEEqhtIsKIjJqWkw2TPx5jcdBvsMZ0FAmZseDcQHGZhcm9zYXNA c3VzZS5kZQAKCRDHmNx0G+wxncT/D/0RkSBDyY7Mg+WLIUkbXBFKxnCwpiDiub4K FsesQfTU8IBLTHSkAMeTipZ8MMg1odfTcB6CCzRpXdJ4V07UGCxKEV77WftiomUm bA/FmkvQRQh2iuEESV+6ciomvI33085TuZLguMQCsER1gv3BPCVjLZ3n7/oTm9MD IdLJx9x5vLKLgT1pfHJt0x9joER77Vk7JN97fuHHvcWBlUnZ1vsmWf3ZQnnWLJNf bg5TSlmxV1x/iGJh0GDIVyZHgBJ1jWKA7qONHxACP4mF14WFCVaQ8DYS+yL6Ggs3 vAdOjTECE7kAbb6zk33NoZ8GO39xzrGTvYoxOGEnOCB8pco/dHyr01mdiH/NM+uF +OTymQhO8LqJ1VGPvkDfQy2CZmb7DbkER5Y/0zBPaUJCjqNlEQUoq5UfCJDPp5Am u5e29QQLWA1j4rsIA7L4HUP8KEuJrnANMSGaomJIjbR/rbLXwb0k5Fr9DL4J4bIu z6e+SMrY+0SMAmx5u9WG7HhVTw8yvZM1PnrvCvFGX35nNB0VJ0//lejLGNOXjcXm QZcytlkyKeLwn6mRJWCWlasbW07/lNegNRqBP394awFtG8OYKDgrHfTtxtJcLIiK buLmZezuI4XVPA2WxmK+viCAfPTukpnoLaQr1yxGH22VThqwjfcEyAHQFccSvY3y F3n9dtwpUQ== =HAv2 -----END PGP SIGNATURE----- Merge tag 'migration-20240614-pull-request' of https://gitlab.com/farosas/qemu into staging Migration pull request - Nick's reenabling of ppc64 tests + speed improvements - Yuan's IAA/QPL compression support for multifd - Shameer's UADK compression support for multifd # -----BEGIN PGP SIGNATURE----- # # iQJEBAABCAAuFiEEqhtIsKIjJqWkw2TPx5jcdBvsMZ0FAmZseDcQHGZhcm9zYXNA # c3VzZS5kZQAKCRDHmNx0G+wxncT/D/0RkSBDyY7Mg+WLIUkbXBFKxnCwpiDiub4K # FsesQfTU8IBLTHSkAMeTipZ8MMg1odfTcB6CCzRpXdJ4V07UGCxKEV77WftiomUm # bA/FmkvQRQh2iuEESV+6ciomvI33085TuZLguMQCsER1gv3BPCVjLZ3n7/oTm9MD # IdLJx9x5vLKLgT1pfHJt0x9joER77Vk7JN97fuHHvcWBlUnZ1vsmWf3ZQnnWLJNf # bg5TSlmxV1x/iGJh0GDIVyZHgBJ1jWKA7qONHxACP4mF14WFCVaQ8DYS+yL6Ggs3 # vAdOjTECE7kAbb6zk33NoZ8GO39xzrGTvYoxOGEnOCB8pco/dHyr01mdiH/NM+uF # +OTymQhO8LqJ1VGPvkDfQy2CZmb7DbkER5Y/0zBPaUJCjqNlEQUoq5UfCJDPp5Am # u5e29QQLWA1j4rsIA7L4HUP8KEuJrnANMSGaomJIjbR/rbLXwb0k5Fr9DL4J4bIu # z6e+SMrY+0SMAmx5u9WG7HhVTw8yvZM1PnrvCvFGX35nNB0VJ0//lejLGNOXjcXm # QZcytlkyKeLwn6mRJWCWlasbW07/lNegNRqBP394awFtG8OYKDgrHfTtxtJcLIiK # buLmZezuI4XVPA2WxmK+viCAfPTukpnoLaQr1yxGH22VThqwjfcEyAHQFccSvY3y # F3n9dtwpUQ== # =HAv2 # -----END PGP SIGNATURE----- # gpg: Signature made Fri 14 Jun 2024 10:04:55 AM PDT # gpg: using RSA key AA1B48B0A22326A5A4C364CFC798DC741BEC319D # gpg: issuer "farosas@suse.de" # gpg: Good signature from "Fabiano Rosas <farosas@suse.de>" [unknown] # gpg: aka "Fabiano Almeida Rosas <fabiano.rosas@suse.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: AA1B 48B0 A223 26A5 A4C3 64CF C798 DC74 1BEC 319D * tag 'migration-20240614-pull-request' of https://gitlab.com/farosas/qemu: tests/migration-test: add uadk compression test migration/multifd: Switch to no compression when no hardware support migration/multifd: Add UADK based compression and decompression migration/multifd: Add UADK initialization migration/multifd: add uadk compression framework configure: Add uadk option docs/migration: add uadk compression feature tests/migration-test: add qpl compression test migration/multifd: implement qpl compression and decompression migration/multifd: implement initialization of qpl compression migration/multifd: add qpl compression method configure: add --enable-qpl build option migration/multifd: put IOV initialization into compression method docs/migration: add qpl compression feature tests/qtest/migration-test: Use custom asm bios for ppc64 tests/qtest/migration-test: Enable on ppc64 TCG tests/qtest/migration-test: Quieten ppc64 QEMU warnings tests/qtest: Move common define from libqos-spapr.h to new ppc-util.h Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
b23acd3e1f
|
@ -12,3 +12,5 @@ Migration has plenty of features to support different use cases.
|
|||
virtio
|
||||
mapped-ram
|
||||
CPR
|
||||
qpl-compression
|
||||
uadk-compression
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
===============
|
||||
QPL Compression
|
||||
===============
|
||||
The Intel Query Processing Library (Intel ``QPL``) is an open-source library to
|
||||
provide compression and decompression features and it is based on deflate
|
||||
compression algorithm (RFC 1951).
|
||||
|
||||
The ``QPL`` compression relies on Intel In-Memory Analytics Accelerator(``IAA``)
|
||||
and Shared Virtual Memory(``SVM``) technology, they are new features supported
|
||||
from Intel 4th Gen Intel Xeon Scalable processors, codenamed Sapphire Rapids
|
||||
processor(``SPR``).
|
||||
|
||||
For more ``QPL`` introduction, please refer to `QPL Introduction
|
||||
<https://intel.github.io/qpl/documentation/introduction_docs/introduction.html>`_
|
||||
|
||||
QPL Compression Framework
|
||||
=========================
|
||||
|
||||
::
|
||||
|
||||
+----------------+ +------------------+
|
||||
| MultiFD Thread | |accel-config tool |
|
||||
+-------+--------+ +--------+---------+
|
||||
| |
|
||||
| |
|
||||
|compress/decompress |
|
||||
+-------+--------+ | Setup IAA
|
||||
| QPL library | | Resources
|
||||
+-------+---+----+ |
|
||||
| | |
|
||||
| +-------------+-------+
|
||||
| Open IAA |
|
||||
| Devices +-----+-----+
|
||||
| |idxd driver|
|
||||
| +-----+-----+
|
||||
| |
|
||||
| |
|
||||
| +-----+-----+
|
||||
+-----------+IAA Devices|
|
||||
Submit jobs +-----------+
|
||||
via enqcmd
|
||||
|
||||
|
||||
QPL Build And Installation
|
||||
--------------------------
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$git clone --recursive https://github.com/intel/qpl.git qpl
|
||||
$mkdir qpl/build
|
||||
$cd qpl/build
|
||||
$cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DQPL_LIBRARY_TYPE=SHARED ..
|
||||
$sudo cmake --build . --target install
|
||||
|
||||
For more details about ``QPL`` installation, please refer to `QPL Installation
|
||||
<https://intel.github.io/qpl/documentation/get_started_docs/installation.html>`_
|
||||
|
||||
IAA Device Management
|
||||
---------------------
|
||||
|
||||
The number of ``IAA`` devices will vary depending on the Xeon product model.
|
||||
On a ``SPR`` server, there can be a maximum of 8 ``IAA`` devices, with up to
|
||||
4 devices per socket.
|
||||
|
||||
By default, all ``IAA`` devices are disabled and need to be configured and
|
||||
enabled by users manually.
|
||||
|
||||
Check the number of devices through the following command
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
#lspci -d 8086:0cfe
|
||||
6a:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
6f:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
74:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
79:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
e7:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
ec:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
f1:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
f6:02.0 System peripheral: Intel Corporation Device 0cfe
|
||||
|
||||
IAA Device Configuration And Enabling
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``accel-config`` tool is used to enable ``IAA`` devices and configure
|
||||
``IAA`` hardware resources(work queues and engines). One ``IAA`` device
|
||||
has 8 work queues and 8 processing engines, multiple engines can be assigned
|
||||
to a work queue via ``group`` attribute.
|
||||
|
||||
For ``accel-config`` installation, please refer to `accel-config installation
|
||||
<https://github.com/intel/idxd-config>`_
|
||||
|
||||
One example of configuring and enabling an ``IAA`` device.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
#accel-config config-engine iax1/engine1.0 -g 0
|
||||
#accel-config config-engine iax1/engine1.1 -g 0
|
||||
#accel-config config-engine iax1/engine1.2 -g 0
|
||||
#accel-config config-engine iax1/engine1.3 -g 0
|
||||
#accel-config config-engine iax1/engine1.4 -g 0
|
||||
#accel-config config-engine iax1/engine1.5 -g 0
|
||||
#accel-config config-engine iax1/engine1.6 -g 0
|
||||
#accel-config config-engine iax1/engine1.7 -g 0
|
||||
#accel-config config-wq iax1/wq1.0 -g 0 -s 128 -p 10 -b 1 -t 128 -m shared -y user -n app1 -d user
|
||||
#accel-config enable-device iax1
|
||||
#accel-config enable-wq iax1/wq1.0
|
||||
|
||||
.. note::
|
||||
IAX is an early name for IAA
|
||||
|
||||
- The ``IAA`` device index is 1, use ``ls -lh /sys/bus/dsa/devices/iax*``
|
||||
command to query the ``IAA`` device index.
|
||||
|
||||
- 8 engines and 1 work queue are configured in group 0, so all compression jobs
|
||||
submitted to this work queue can be processed by all engines at the same time.
|
||||
|
||||
- Set work queue attributes including the work mode, work queue size and so on.
|
||||
|
||||
- Enable the ``IAA1`` device and work queue 1.0
|
||||
|
||||
.. note::
|
||||
|
||||
Set work queue mode to shared mode, since ``QPL`` library only supports
|
||||
shared mode
|
||||
|
||||
For more detailed configuration, please refer to `IAA Configuration Samples
|
||||
<https://github.com/intel/idxd-config/tree/stable/Documentation/accfg>`_
|
||||
|
||||
IAA Unit Test
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
- Enabling ``IAA`` devices for Xeon platform, please refer to `IAA User Guide
|
||||
<https://www.intel.com/content/www/us/en/content-details/780887/intel-in-memory-analytics-accelerator-intel-iaa.html>`_
|
||||
|
||||
- ``IAA`` device driver is Intel Data Accelerator Driver (idxd), it is
|
||||
recommended that the minimum version of Linux kernel is 5.18.
|
||||
|
||||
- Add ``"intel_iommu=on,sm_on"`` parameter to kernel command line
|
||||
for ``SVM`` feature enabling.
|
||||
|
||||
Here is an easy way to verify ``IAA`` device driver and ``SVM`` with `iaa_test
|
||||
<https://github.com/intel/idxd-config/tree/stable/test>`_
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
#./test/iaa_test
|
||||
[ info] alloc wq 0 shared size 128 addr 0x7f26cebe5000 batch sz 0xfffffffe xfer sz 0x80000000
|
||||
[ info] test noop: tflags 0x1 num_desc 1
|
||||
[ info] preparing descriptor for noop
|
||||
[ info] Submitted all noop jobs
|
||||
[ info] verifying task result for 0x16f7e20
|
||||
[ info] test with op 0 passed
|
||||
|
||||
|
||||
IAA Resources Allocation For Migration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There is no ``IAA`` resource configuration parameters for migration and
|
||||
``accel-config`` tool configuration cannot directly specify the ``IAA``
|
||||
resources used for migration.
|
||||
|
||||
The multifd migration with ``QPL`` compression method will use all work
|
||||
queues that are enabled and shared mode.
|
||||
|
||||
.. note::
|
||||
|
||||
Accessing IAA resources requires ``sudo`` command or ``root`` privileges
|
||||
by default. Administrators can modify the IAA device node ownership
|
||||
so that QEMU can use IAA with specified user permissions.
|
||||
|
||||
For example
|
||||
|
||||
#chown -R qemu /dev/iax
|
||||
|
||||
Shared Virtual Memory(SVM) Introduction
|
||||
=======================================
|
||||
|
||||
An ability for an accelerator I/O device to operate in the same virtual
|
||||
memory space of applications on host processors. It also implies the
|
||||
ability to operate from pageable memory, avoiding functional requirements
|
||||
to pin memory for DMA operations.
|
||||
|
||||
When using ``SVM`` technology, users do not need to reserve memory for the
|
||||
``IAA`` device and perform pin memory operation. The ``IAA`` device can
|
||||
directly access data using the virtual address of the process.
|
||||
|
||||
For more ``SVM`` technology, please refer to
|
||||
`Shared Virtual Addressing (SVA) with ENQCMD
|
||||
<https://docs.kernel.org/next/x86/sva.html>`_
|
||||
|
||||
|
||||
How To Use QPL Compression In Migration
|
||||
=======================================
|
||||
|
||||
1 - Installation of ``QPL`` library and ``accel-config`` library if using IAA
|
||||
|
||||
2 - Configure and enable ``IAA`` devices and work queues via ``accel-config``
|
||||
|
||||
3 - Build ``QEMU`` with ``--enable-qpl`` parameter
|
||||
|
||||
E.g. configure --target-list=x86_64-softmmu --enable-kvm ``--enable-qpl``
|
||||
|
||||
4 - Enable ``QPL`` compression during migration
|
||||
|
||||
Set ``migrate_set_parameter multifd-compression qpl`` when migrating, the
|
||||
``QPL`` compression does not support configuring the compression level, it
|
||||
only supports one compression level.
|
||||
|
||||
The Difference Between QPL And ZLIB
|
||||
===================================
|
||||
|
||||
Although both ``QPL`` and ``ZLIB`` are based on the deflate compression
|
||||
algorithm, and ``QPL`` can support the header and tail of ``ZLIB``, ``QPL``
|
||||
is still not fully compatible with the ``ZLIB`` compression in the migration.
|
||||
|
||||
``QPL`` only supports 4K history buffer, and ``ZLIB`` is 32K by default.
|
||||
``ZLIB`` compresses data that ``QPL`` may not decompress correctly and
|
||||
vice versa.
|
||||
|
||||
``QPL`` does not support the ``Z_SYNC_FLUSH`` operation in ``ZLIB`` streaming
|
||||
compression, current ``ZLIB`` implementation uses ``Z_SYNC_FLUSH``, so each
|
||||
``multifd`` thread has a ``ZLIB`` streaming context, and all page compression
|
||||
and decompression are based on this stream. ``QPL`` cannot decompress such data
|
||||
and vice versa.
|
||||
|
||||
The introduction for ``Z_SYNC_FLUSH``, please refer to `Zlib Manual
|
||||
<https://www.zlib.net/manual.html>`_
|
||||
|
||||
The Best Practices
|
||||
==================
|
||||
When user enables the IAA device for ``QPL`` compression, it is recommended
|
||||
to add ``-mem-prealloc`` parameter to the destination boot parameters. This
|
||||
parameter can avoid the occurrence of I/O page fault and reduce the overhead
|
||||
of IAA compression and decompression.
|
||||
|
||||
The example of booting with ``-mem-prealloc`` parameter
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$qemu-system-x86_64 --enable-kvm -cpu host --mem-prealloc ...
|
||||
|
||||
|
||||
An example about I/O page fault measurement of destination without
|
||||
``-mem-prealloc``, the ``svm_prq`` indicates the number of I/O page fault
|
||||
occurrences and processing time.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
#echo 1 > /sys/kernel/debug/iommu/intel/dmar_perf_latency
|
||||
#echo 2 > /sys/kernel/debug/iommu/intel/dmar_perf_latency
|
||||
#echo 3 > /sys/kernel/debug/iommu/intel/dmar_perf_latency
|
||||
#echo 4 > /sys/kernel/debug/iommu/intel/dmar_perf_latency
|
||||
#cat /sys/kernel/debug/iommu/intel/dmar_perf_latency
|
||||
IOMMU: dmar18 Register Base Address: c87fc000
|
||||
<0.1us 0.1us-1us 1us-10us 10us-100us 100us-1ms 1ms-10ms >=10ms min(us) max(us) average(us)
|
||||
inv_iotlb 0 286 123 0 0 0 0 0 1 0
|
||||
inv_devtlb 0 276 133 0 0 0 0 0 2 0
|
||||
inv_iec 0 0 0 0 0 0 0 0 0 0
|
||||
svm_prq 0 0 25206 364 395 0 0 1 556 9
|
|
@ -0,0 +1,144 @@
|
|||
=========================================================
|
||||
User Space Accelerator Development Kit (UADK) Compression
|
||||
=========================================================
|
||||
UADK is a general-purpose user space accelerator framework that uses shared
|
||||
virtual addressing (SVA) to provide a unified programming interface for
|
||||
hardware acceleration of cryptographic and compression algorithms.
|
||||
|
||||
UADK includes Unified/User-space-access-intended Accelerator Framework (UACCE),
|
||||
which enables hardware accelerators from different vendors that support SVA to
|
||||
adapt to UADK.
|
||||
|
||||
Currently, HiSilicon Kunpeng hardware accelerators have been registered with
|
||||
UACCE. Through the UADK framework, users can run cryptographic and compression
|
||||
algorithms using hardware accelerators instead of CPUs, freeing up CPU
|
||||
computing power and improving computing performance.
|
||||
|
||||
https://github.com/Linaro/uadk/tree/master/docs
|
||||
|
||||
UADK Framework
|
||||
==============
|
||||
UADK consists of UACCE, vendors' drivers, and an algorithm layer. UADK requires
|
||||
the hardware accelerator to support SVA, and the operating system to support
|
||||
IOMMU and SVA. Hardware accelerators from different vendors are registered as
|
||||
different character devices with UACCE by using kernel-mode drivers of the
|
||||
vendors. A user can access the hardware accelerators by performing user-mode
|
||||
operations on the character devices.
|
||||
|
||||
::
|
||||
|
||||
+----------------------------------+
|
||||
| apps |
|
||||
+----+------------------------+----+
|
||||
| |
|
||||
| |
|
||||
+-------+--------+ +-------+-------+
|
||||
| scheduler | | alg libraries |
|
||||
+-------+--------+ +-------+-------+
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| +--------+------+
|
||||
| | vendor drivers|
|
||||
| +-+-------------+
|
||||
| |
|
||||
| |
|
||||
+--+------------------+--+
|
||||
| libwd |
|
||||
User +----+-------------+-----+
|
||||
--------------------------------------------------
|
||||
Kernel +--+-----+ +------+
|
||||
| uacce | | smmu |
|
||||
+---+----+ +------+
|
||||
|
|
||||
+---+------------------+
|
||||
| vendor kernel driver |
|
||||
+----------------------+
|
||||
--------------------------------------------------
|
||||
+----------------------+
|
||||
| HW Accelerators |
|
||||
+----------------------+
|
||||
|
||||
UADK Installation
|
||||
-----------------
|
||||
Build UADK
|
||||
^^^^^^^^^^
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://github.com/Linaro/uadk.git
|
||||
cd uadk
|
||||
mkdir build
|
||||
./autogen.sh
|
||||
./configure --prefix=$PWD/build
|
||||
make
|
||||
make install
|
||||
|
||||
Without --prefix, UADK will be installed to /usr/local/lib by default.
|
||||
If get error:"cannot find -lnuma", please install the libnuma-dev
|
||||
|
||||
Run pkg-config libwd to ensure env is setup correctly
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* export PKG_CONFIG_PATH=$PWD/build/lib/pkgconfig
|
||||
* pkg-config libwd --cflags --libs
|
||||
-I/usr/local/include -L/usr/local/lib -lwd
|
||||
|
||||
* export PKG_CONFIG_PATH is required on demand.
|
||||
Not required if UADK is installed to /usr/local/lib
|
||||
|
||||
UADK Host Kernel Requirements
|
||||
-----------------------------
|
||||
User needs to make sure that ``UACCE`` is already supported in Linux kernel.
|
||||
The kernel version should be at least v5.9 with SVA (Shared Virtual
|
||||
Addressing) enabled.
|
||||
|
||||
Kernel Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``UACCE`` could be built as module or built-in.
|
||||
|
||||
Here's an example to enable UACCE with hardware accelerator in HiSilicon
|
||||
Kunpeng platform.
|
||||
|
||||
* CONFIG_IOMMU_SVA_LIB=y
|
||||
* CONFIG_ARM_SMMU=y
|
||||
* CONFIG_ARM_SMMU_V3=y
|
||||
* CONFIG_ARM_SMMU_V3_SVA=y
|
||||
* CONFIG_PCI_PASID=y
|
||||
* CONFIG_UACCE=y
|
||||
* CONFIG_CRYPTO_DEV_HISI_QM=y
|
||||
* CONFIG_CRYPTO_DEV_HISI_ZIP=y
|
||||
|
||||
Make sure all these above kernel configurations are selected.
|
||||
|
||||
Accelerator dev node permissions
|
||||
--------------------------------
|
||||
Harware accelerators(eg: HiSilicon Kunpeng Zip accelerator) gets registered to
|
||||
UADK and char devices are created in dev directory. In order to access resources
|
||||
on hardware accelerator devices, write permission should be provided to user.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ sudo chmod 777 /dev/hisi_zip-*
|
||||
|
||||
How To Use UADK Compression In QEMU Migration
|
||||
---------------------------------------------
|
||||
* Make sure UADK is installed as above
|
||||
* Build ``QEMU`` with ``--enable-uadk`` parameter
|
||||
|
||||
E.g. configure --target-list=aarch64-softmmu --enable-kvm ``--enable-uadk``
|
||||
|
||||
* Enable ``UADK`` compression during migration
|
||||
|
||||
Set ``migrate_set_parameter multifd-compression uadk``
|
||||
|
||||
Since UADK uses Shared Virtual Addressing(SVA) and device access virtual memory
|
||||
directly it is possible that SMMUv3 may enounter page faults while walking the
|
||||
IO page tables. This may impact the performance. In order to mitigate this,
|
||||
please make sure to specify ``-mem-prealloc`` parameter to the destination VM
|
||||
boot parameters.
|
||||
|
||||
Though both UADK and ZLIB are based on the deflate compression algorithm, UADK
|
||||
is not fully compatible with ZLIB. Hence, please make sure to use ``uadk`` on
|
||||
both source and destination during migration.
|
|
@ -659,7 +659,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = {
|
|||
const PropertyInfo qdev_prop_multifd_compression = {
|
||||
.name = "MultiFDCompression",
|
||||
.description = "multifd_compression values, "
|
||||
"none/zlib/zstd",
|
||||
"none/zlib/zstd/qpl/uadk",
|
||||
.enum_table = &MultiFDCompression_lookup,
|
||||
.get = qdev_propinfo_get_enum,
|
||||
.set = qdev_propinfo_set_enum,
|
||||
|
|
22
meson.build
22
meson.build
|
@ -1201,6 +1201,24 @@ if not get_option('zstd').auto() or have_block
|
|||
required: get_option('zstd'),
|
||||
method: 'pkg-config')
|
||||
endif
|
||||
qpl = not_found
|
||||
if not get_option('qpl').auto() or have_system
|
||||
qpl = dependency('qpl', version: '>=1.5.0',
|
||||
required: get_option('qpl'),
|
||||
method: 'pkg-config')
|
||||
endif
|
||||
uadk = not_found
|
||||
if not get_option('uadk').auto() or have_system
|
||||
libwd = dependency('libwd', version: '>=2.6',
|
||||
required: get_option('uadk'),
|
||||
method: 'pkg-config')
|
||||
libwd_comp = dependency('libwd_comp', version: '>=2.6',
|
||||
required: get_option('uadk'),
|
||||
method: 'pkg-config')
|
||||
if libwd.found() and libwd_comp.found()
|
||||
uadk = declare_dependency(dependencies: [libwd, libwd_comp])
|
||||
endif
|
||||
endif
|
||||
virgl = not_found
|
||||
|
||||
have_vhost_user_gpu = have_tools and host_os == 'linux' and pixman.found()
|
||||
|
@ -2333,6 +2351,8 @@ config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
|
|||
config_host_data.set('CONFIG_STATX', has_statx)
|
||||
config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id)
|
||||
config_host_data.set('CONFIG_ZSTD', zstd.found())
|
||||
config_host_data.set('CONFIG_QPL', qpl.found())
|
||||
config_host_data.set('CONFIG_UADK', uadk.found())
|
||||
config_host_data.set('CONFIG_FUSE', fuse.found())
|
||||
config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found())
|
||||
config_host_data.set('CONFIG_SPICE_PROTOCOL', spice_protocol.found())
|
||||
|
@ -4446,6 +4466,8 @@ summary_info += {'snappy support': snappy}
|
|||
summary_info += {'bzip2 support': libbzip2}
|
||||
summary_info += {'lzfse support': liblzfse}
|
||||
summary_info += {'zstd support': zstd}
|
||||
summary_info += {'Query Processing Library support': qpl}
|
||||
summary_info += {'UADK Library support': uadk}
|
||||
summary_info += {'NUMA host support': numa}
|
||||
summary_info += {'capstone': capstone}
|
||||
summary_info += {'libpmem support': libpmem}
|
||||
|
|
|
@ -259,6 +259,10 @@ option('xkbcommon', type : 'feature', value : 'auto',
|
|||
description: 'xkbcommon support')
|
||||
option('zstd', type : 'feature', value : 'auto',
|
||||
description: 'zstd compression support')
|
||||
option('qpl', type : 'feature', value : 'auto',
|
||||
description: 'Query Processing Library support')
|
||||
option('uadk', type : 'feature', value : 'auto',
|
||||
description: 'UADK Library support')
|
||||
option('fuse', type: 'feature', value: 'auto',
|
||||
description: 'FUSE block device export')
|
||||
option('fuse_lseek', type : 'feature', value : 'auto',
|
||||
|
|
|
@ -39,6 +39,8 @@ endif
|
|||
|
||||
system_ss.add(when: rdma, if_true: files('rdma.c'))
|
||||
system_ss.add(when: zstd, if_true: files('multifd-zstd.c'))
|
||||
system_ss.add(when: qpl, if_true: files('multifd-qpl.c'))
|
||||
system_ss.add(when: uadk, if_true: files('multifd-uadk.c'))
|
||||
|
||||
specific_ss.add(when: 'CONFIG_SYSTEM_ONLY',
|
||||
if_true: files('ram.c',
|
||||
|
|
|
@ -0,0 +1,762 @@
|
|||
/*
|
||||
* Multifd qpl compression accelerator implementation
|
||||
*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Yuan Liu<yuan1.liu@intel.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-types-migration.h"
|
||||
#include "exec/ramblock.h"
|
||||
#include "multifd.h"
|
||||
#include "qpl/qpl.h"
|
||||
|
||||
/* Maximum number of retries to resubmit a job if IAA work queues are full */
|
||||
#define MAX_SUBMIT_RETRY_NUM (3)
|
||||
|
||||
typedef struct {
|
||||
/* the QPL hardware path job */
|
||||
qpl_job *job;
|
||||
/* indicates if fallback to software path is required */
|
||||
bool fallback_sw_path;
|
||||
/* output data from the software path */
|
||||
uint8_t *sw_output;
|
||||
/* output data length from the software path */
|
||||
uint32_t sw_output_len;
|
||||
} QplHwJob;
|
||||
|
||||
typedef struct {
|
||||
/* array of hardware jobs, the number of jobs equals the number pages */
|
||||
QplHwJob *hw_jobs;
|
||||
/* the QPL software job for the slow path and software fallback */
|
||||
qpl_job *sw_job;
|
||||
/* the number of pages that the QPL needs to process at one time */
|
||||
uint32_t page_num;
|
||||
/* array of compressed page buffers */
|
||||
uint8_t *zbuf;
|
||||
/* array of compressed page lengths */
|
||||
uint32_t *zlen;
|
||||
/* the status of the hardware device */
|
||||
bool hw_avail;
|
||||
} QplData;
|
||||
|
||||
/**
|
||||
* check_hw_avail: check if IAA hardware is available
|
||||
*
|
||||
* If the IAA hardware does not exist or is unavailable,
|
||||
* the QPL hardware job initialization will fail.
|
||||
*
|
||||
* Returns true if IAA hardware is available, otherwise false.
|
||||
*
|
||||
* @job_size: indicates the hardware job size if hardware is available
|
||||
*/
|
||||
static bool check_hw_avail(uint32_t *job_size)
|
||||
{
|
||||
qpl_path_t path = qpl_path_hardware;
|
||||
uint32_t size = 0;
|
||||
qpl_job *job;
|
||||
|
||||
if (qpl_get_job_size(path, &size) != QPL_STS_OK) {
|
||||
return false;
|
||||
}
|
||||
assert(size > 0);
|
||||
job = g_malloc0(size);
|
||||
if (qpl_init_job(path, job) != QPL_STS_OK) {
|
||||
g_free(job);
|
||||
return false;
|
||||
}
|
||||
g_free(job);
|
||||
*job_size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_free_sw_job: clean up software job
|
||||
*
|
||||
* Free the software job resources.
|
||||
*
|
||||
* @qpl: pointer to the QplData structure
|
||||
*/
|
||||
static void multifd_qpl_free_sw_job(QplData *qpl)
|
||||
{
|
||||
assert(qpl);
|
||||
if (qpl->sw_job) {
|
||||
qpl_fini_job(qpl->sw_job);
|
||||
g_free(qpl->sw_job);
|
||||
qpl->sw_job = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_free_jobs: clean up hardware jobs
|
||||
*
|
||||
* Free all hardware job resources.
|
||||
*
|
||||
* @qpl: pointer to the QplData structure
|
||||
*/
|
||||
static void multifd_qpl_free_hw_job(QplData *qpl)
|
||||
{
|
||||
assert(qpl);
|
||||
if (qpl->hw_jobs) {
|
||||
for (int i = 0; i < qpl->page_num; i++) {
|
||||
qpl_fini_job(qpl->hw_jobs[i].job);
|
||||
g_free(qpl->hw_jobs[i].job);
|
||||
qpl->hw_jobs[i].job = NULL;
|
||||
}
|
||||
g_free(qpl->hw_jobs);
|
||||
qpl->hw_jobs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_init_sw_job: initialize a software job
|
||||
*
|
||||
* Use the QPL software path to initialize a job
|
||||
*
|
||||
* @qpl: pointer to the QplData structure
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_qpl_init_sw_job(QplData *qpl, Error **errp)
|
||||
{
|
||||
qpl_path_t path = qpl_path_software;
|
||||
uint32_t size = 0;
|
||||
qpl_job *job = NULL;
|
||||
qpl_status status;
|
||||
|
||||
status = qpl_get_job_size(path, &size);
|
||||
if (status != QPL_STS_OK) {
|
||||
error_setg(errp, "qpl_get_job_size failed with error %d", status);
|
||||
return -1;
|
||||
}
|
||||
job = g_malloc0(size);
|
||||
status = qpl_init_job(path, job);
|
||||
if (status != QPL_STS_OK) {
|
||||
error_setg(errp, "qpl_init_job failed with error %d", status);
|
||||
g_free(job);
|
||||
return -1;
|
||||
}
|
||||
qpl->sw_job = job;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_init_jobs: initialize hardware jobs
|
||||
*
|
||||
* Use the QPL hardware path to initialize jobs
|
||||
*
|
||||
* @qpl: pointer to the QplData structure
|
||||
* @size: the size of QPL hardware path job
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static void multifd_qpl_init_hw_job(QplData *qpl, uint32_t size, Error **errp)
|
||||
{
|
||||
qpl_path_t path = qpl_path_hardware;
|
||||
qpl_job *job = NULL;
|
||||
qpl_status status;
|
||||
|
||||
qpl->hw_jobs = g_new0(QplHwJob, qpl->page_num);
|
||||
for (int i = 0; i < qpl->page_num; i++) {
|
||||
job = g_malloc0(size);
|
||||
status = qpl_init_job(path, job);
|
||||
/* the job initialization should succeed after check_hw_avail */
|
||||
assert(status == QPL_STS_OK);
|
||||
qpl->hw_jobs[i].job = job;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_init: initialize QplData structure
|
||||
*
|
||||
* Allocate and initialize a QplData structure
|
||||
*
|
||||
* Returns a QplData pointer on success or NULL on error
|
||||
*
|
||||
* @num: the number of pages
|
||||
* @size: the page size
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static QplData *multifd_qpl_init(uint32_t num, uint32_t size, Error **errp)
|
||||
{
|
||||
uint32_t job_size = 0;
|
||||
QplData *qpl;
|
||||
|
||||
qpl = g_new0(QplData, 1);
|
||||
qpl->page_num = num;
|
||||
if (multifd_qpl_init_sw_job(qpl, errp) != 0) {
|
||||
g_free(qpl);
|
||||
return NULL;
|
||||
}
|
||||
qpl->hw_avail = check_hw_avail(&job_size);
|
||||
if (qpl->hw_avail) {
|
||||
multifd_qpl_init_hw_job(qpl, job_size, errp);
|
||||
}
|
||||
qpl->zbuf = g_malloc0(size * num);
|
||||
qpl->zlen = g_new0(uint32_t, num);
|
||||
return qpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_deinit: clean up QplData structure
|
||||
*
|
||||
* Free jobs, buffers and the QplData structure
|
||||
*
|
||||
* @qpl: pointer to the QplData structure
|
||||
*/
|
||||
static void multifd_qpl_deinit(QplData *qpl)
|
||||
{
|
||||
if (qpl) {
|
||||
multifd_qpl_free_sw_job(qpl);
|
||||
multifd_qpl_free_hw_job(qpl);
|
||||
g_free(qpl->zbuf);
|
||||
g_free(qpl->zlen);
|
||||
g_free(qpl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_send_setup: set up send side
|
||||
*
|
||||
* Set up the channel with QPL compression.
|
||||
*
|
||||
* Returns 0 on success or -1 on error
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_qpl_send_setup(MultiFDSendParams *p, Error **errp)
|
||||
{
|
||||
QplData *qpl;
|
||||
|
||||
qpl = multifd_qpl_init(p->page_count, p->page_size, errp);
|
||||
if (!qpl) {
|
||||
return -1;
|
||||
}
|
||||
p->compress_data = qpl;
|
||||
|
||||
/*
|
||||
* the page will be compressed independently and sent using an IOV. The
|
||||
* additional two IOVs are used to store packet header and compressed data
|
||||
* length
|
||||
*/
|
||||
p->iov = g_new0(struct iovec, p->page_count + 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_send_cleanup: clean up send side
|
||||
*
|
||||
* Close the channel and free memory.
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static void multifd_qpl_send_cleanup(MultiFDSendParams *p, Error **errp)
|
||||
{
|
||||
multifd_qpl_deinit(p->compress_data);
|
||||
p->compress_data = NULL;
|
||||
g_free(p->iov);
|
||||
p->iov = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_prepare_job: prepare the job
|
||||
*
|
||||
* Set the QPL job parameters and properties.
|
||||
*
|
||||
* @job: pointer to the qpl_job structure
|
||||
* @is_compression: indicates compression and decompression
|
||||
* @input: pointer to the input data buffer
|
||||
* @input_len: the length of the input data
|
||||
* @output: pointer to the output data buffer
|
||||
* @output_len: the length of the output data
|
||||
*/
|
||||
static void multifd_qpl_prepare_job(qpl_job *job, bool is_compression,
|
||||
uint8_t *input, uint32_t input_len,
|
||||
uint8_t *output, uint32_t output_len)
|
||||
{
|
||||
job->op = is_compression ? qpl_op_compress : qpl_op_decompress;
|
||||
job->next_in_ptr = input;
|
||||
job->next_out_ptr = output;
|
||||
job->available_in = input_len;
|
||||
job->available_out = output_len;
|
||||
job->flags = QPL_FLAG_FIRST | QPL_FLAG_LAST | QPL_FLAG_OMIT_VERIFY;
|
||||
/* only supports compression level 1 */
|
||||
job->level = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_prepare_comp_job: prepare the compression job
|
||||
*
|
||||
* Set the compression job parameters and properties.
|
||||
*
|
||||
* @job: pointer to the qpl_job structure
|
||||
* @input: pointer to the input data buffer
|
||||
* @output: pointer to the output data buffer
|
||||
* @size: the page size
|
||||
*/
|
||||
static void multifd_qpl_prepare_comp_job(qpl_job *job, uint8_t *input,
|
||||
uint8_t *output, uint32_t size)
|
||||
{
|
||||
/*
|
||||
* Set output length to less than the page size to force the job to
|
||||
* fail in case it compresses to a larger size. We'll send that page
|
||||
* without compression and skip the decompression operation on the
|
||||
* destination.
|
||||
*/
|
||||
multifd_qpl_prepare_job(job, true, input, size, output, size - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_prepare_decomp_job: prepare the decompression job
|
||||
*
|
||||
* Set the decompression job parameters and properties.
|
||||
*
|
||||
* @job: pointer to the qpl_job structure
|
||||
* @input: pointer to the input data buffer
|
||||
* @len: the length of the input data
|
||||
* @output: pointer to the output data buffer
|
||||
* @size: the page size
|
||||
*/
|
||||
static void multifd_qpl_prepare_decomp_job(qpl_job *job, uint8_t *input,
|
||||
uint32_t len, uint8_t *output,
|
||||
uint32_t size)
|
||||
{
|
||||
multifd_qpl_prepare_job(job, false, input, len, output, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_fill_iov: fill in the IOV
|
||||
*
|
||||
* Fill in the QPL packet IOV
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @data: pointer to the IOV data
|
||||
* @len: The length of the IOV data
|
||||
*/
|
||||
static void multifd_qpl_fill_iov(MultiFDSendParams *p, uint8_t *data,
|
||||
uint32_t len)
|
||||
{
|
||||
p->iov[p->iovs_num].iov_base = data;
|
||||
p->iov[p->iovs_num].iov_len = len;
|
||||
p->iovs_num++;
|
||||
p->next_packet_size += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_fill_packet: fill the compressed page into the QPL packet
|
||||
*
|
||||
* Fill the compressed page length and IOV into the QPL packet
|
||||
*
|
||||
* @idx: The index of the compressed length array
|
||||
* @p: Params for the channel being used
|
||||
* @data: pointer to the compressed page buffer
|
||||
* @len: The length of the compressed page
|
||||
*/
|
||||
static void multifd_qpl_fill_packet(uint32_t idx, MultiFDSendParams *p,
|
||||
uint8_t *data, uint32_t len)
|
||||
{
|
||||
QplData *qpl = p->compress_data;
|
||||
|
||||
qpl->zlen[idx] = cpu_to_be32(len);
|
||||
multifd_qpl_fill_iov(p, data, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_submit_job: submit a job to the hardware
|
||||
*
|
||||
* Submit a QPL hardware job to the IAA device
|
||||
*
|
||||
* Returns true if the job is submitted successfully, otherwise false.
|
||||
*
|
||||
* @job: pointer to the qpl_job structure
|
||||
*/
|
||||
static bool multifd_qpl_submit_job(qpl_job *job)
|
||||
{
|
||||
qpl_status status;
|
||||
uint32_t num = 0;
|
||||
|
||||
retry:
|
||||
status = qpl_submit_job(job);
|
||||
if (status == QPL_STS_QUEUES_ARE_BUSY_ERR) {
|
||||
if (num < MAX_SUBMIT_RETRY_NUM) {
|
||||
num++;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
return (status == QPL_STS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_compress_pages_slow_path: compress pages using slow path
|
||||
*
|
||||
* Compress the pages using software. If compression fails, the uncompressed
|
||||
* page will be sent.
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
*/
|
||||
static void multifd_qpl_compress_pages_slow_path(MultiFDSendParams *p)
|
||||
{
|
||||
QplData *qpl = p->compress_data;
|
||||
uint32_t size = p->page_size;
|
||||
qpl_job *job = qpl->sw_job;
|
||||
uint8_t *zbuf = qpl->zbuf;
|
||||
uint8_t *buf;
|
||||
|
||||
for (int i = 0; i < p->pages->normal_num; i++) {
|
||||
buf = p->pages->block->host + p->pages->offset[i];
|
||||
multifd_qpl_prepare_comp_job(job, buf, zbuf, size);
|
||||
if (qpl_execute_job(job) == QPL_STS_OK) {
|
||||
multifd_qpl_fill_packet(i, p, zbuf, job->total_out);
|
||||
} else {
|
||||
/* send the uncompressed page */
|
||||
multifd_qpl_fill_packet(i, p, buf, size);
|
||||
}
|
||||
zbuf += size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_compress_pages: compress pages
|
||||
*
|
||||
* Submit the pages to the IAA hardware for compression. If hardware
|
||||
* compression fails, it falls back to software compression. If software
|
||||
* compression also fails, the uncompressed page is sent.
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
*/
|
||||
static void multifd_qpl_compress_pages(MultiFDSendParams *p)
|
||||
{
|
||||
QplData *qpl = p->compress_data;
|
||||
MultiFDPages_t *pages = p->pages;
|
||||
uint32_t size = p->page_size;
|
||||
QplHwJob *hw_job;
|
||||
uint8_t *buf;
|
||||
uint8_t *zbuf;
|
||||
|
||||
for (int i = 0; i < pages->normal_num; i++) {
|
||||
buf = pages->block->host + pages->offset[i];
|
||||
zbuf = qpl->zbuf + (size * i);
|
||||
hw_job = &qpl->hw_jobs[i];
|
||||
multifd_qpl_prepare_comp_job(hw_job->job, buf, zbuf, size);
|
||||
if (multifd_qpl_submit_job(hw_job->job)) {
|
||||
hw_job->fallback_sw_path = false;
|
||||
} else {
|
||||
/*
|
||||
* The IAA work queue is full, any immediate subsequent job
|
||||
* submission is likely to fail, sending the page via the QPL
|
||||
* software path at this point gives us a better chance of
|
||||
* finding the queue open for the next pages.
|
||||
*/
|
||||
hw_job->fallback_sw_path = true;
|
||||
multifd_qpl_prepare_comp_job(qpl->sw_job, buf, zbuf, size);
|
||||
if (qpl_execute_job(qpl->sw_job) == QPL_STS_OK) {
|
||||
hw_job->sw_output = zbuf;
|
||||
hw_job->sw_output_len = qpl->sw_job->total_out;
|
||||
} else {
|
||||
hw_job->sw_output = buf;
|
||||
hw_job->sw_output_len = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < pages->normal_num; i++) {
|
||||
buf = pages->block->host + pages->offset[i];
|
||||
zbuf = qpl->zbuf + (size * i);
|
||||
hw_job = &qpl->hw_jobs[i];
|
||||
if (hw_job->fallback_sw_path) {
|
||||
multifd_qpl_fill_packet(i, p, hw_job->sw_output,
|
||||
hw_job->sw_output_len);
|
||||
continue;
|
||||
}
|
||||
if (qpl_wait_job(hw_job->job) == QPL_STS_OK) {
|
||||
multifd_qpl_fill_packet(i, p, zbuf, hw_job->job->total_out);
|
||||
} else {
|
||||
/* send the uncompressed page */
|
||||
multifd_qpl_fill_packet(i, p, buf, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_send_prepare: prepare data to be able to send
|
||||
*
|
||||
* Create a compressed buffer with all the pages that we are going to
|
||||
* send.
|
||||
*
|
||||
* Returns 0 on success or -1 on error
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_qpl_send_prepare(MultiFDSendParams *p, Error **errp)
|
||||
{
|
||||
QplData *qpl = p->compress_data;
|
||||
uint32_t len = 0;
|
||||
|
||||
if (!multifd_send_prepare_common(p)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The first IOV is used to store the compressed page lengths */
|
||||
len = p->pages->normal_num * sizeof(uint32_t);
|
||||
multifd_qpl_fill_iov(p, (uint8_t *) qpl->zlen, len);
|
||||
if (qpl->hw_avail) {
|
||||
multifd_qpl_compress_pages(p);
|
||||
} else {
|
||||
multifd_qpl_compress_pages_slow_path(p);
|
||||
}
|
||||
|
||||
out:
|
||||
p->flags |= MULTIFD_FLAG_QPL;
|
||||
multifd_send_fill_packet(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_recv_setup: set up receive side
|
||||
*
|
||||
* Create the compressed channel and buffer.
|
||||
*
|
||||
* Returns 0 on success or -1 on error
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_qpl_recv_setup(MultiFDRecvParams *p, Error **errp)
|
||||
{
|
||||
QplData *qpl;
|
||||
|
||||
qpl = multifd_qpl_init(p->page_count, p->page_size, errp);
|
||||
if (!qpl) {
|
||||
return -1;
|
||||
}
|
||||
p->compress_data = qpl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_recv_cleanup: set up receive side
|
||||
*
|
||||
* Close the channel and free memory.
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
*/
|
||||
static void multifd_qpl_recv_cleanup(MultiFDRecvParams *p)
|
||||
{
|
||||
multifd_qpl_deinit(p->compress_data);
|
||||
p->compress_data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_process_and_check_job: process and check a QPL job
|
||||
*
|
||||
* Process the job and check whether the job output length is the
|
||||
* same as the specified length
|
||||
*
|
||||
* Returns true if the job execution succeeded and the output length
|
||||
* is equal to the specified length, otherwise false.
|
||||
*
|
||||
* @job: pointer to the qpl_job structure
|
||||
* @is_hardware: indicates whether the job is a hardware job
|
||||
* @len: Specified output length
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static bool multifd_qpl_process_and_check_job(qpl_job *job, bool is_hardware,
|
||||
uint32_t len, Error **errp)
|
||||
{
|
||||
qpl_status status;
|
||||
|
||||
status = (is_hardware ? qpl_wait_job(job) : qpl_execute_job(job));
|
||||
if (status != QPL_STS_OK) {
|
||||
error_setg(errp, "qpl job failed with error %d", status);
|
||||
return false;
|
||||
}
|
||||
if (job->total_out != len) {
|
||||
error_setg(errp, "qpl decompressed len %u, expected len %u",
|
||||
job->total_out, len);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_decompress_pages_slow_path: decompress pages using slow path
|
||||
*
|
||||
* Decompress the pages using software
|
||||
*
|
||||
* Returns 0 on success or -1 on error
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_qpl_decompress_pages_slow_path(MultiFDRecvParams *p,
|
||||
Error **errp)
|
||||
{
|
||||
QplData *qpl = p->compress_data;
|
||||
uint32_t size = p->page_size;
|
||||
qpl_job *job = qpl->sw_job;
|
||||
uint8_t *zbuf = qpl->zbuf;
|
||||
uint8_t *addr;
|
||||
uint32_t len;
|
||||
|
||||
for (int i = 0; i < p->normal_num; i++) {
|
||||
len = qpl->zlen[i];
|
||||
addr = p->host + p->normal[i];
|
||||
/* the page is uncompressed, load it */
|
||||
if (len == size) {
|
||||
memcpy(addr, zbuf, size);
|
||||
zbuf += size;
|
||||
continue;
|
||||
}
|
||||
multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size);
|
||||
if (!multifd_qpl_process_and_check_job(job, false, size, errp)) {
|
||||
return -1;
|
||||
}
|
||||
zbuf += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_qpl_decompress_pages: decompress pages
|
||||
*
|
||||
* Decompress the pages using the IAA hardware. If hardware
|
||||
* decompression fails, it falls back to software decompression.
|
||||
*
|
||||
* Returns 0 on success or -1 on error
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_qpl_decompress_pages(MultiFDRecvParams *p, Error **errp)
|
||||
{
|
||||
QplData *qpl = p->compress_data;
|
||||
uint32_t size = p->page_size;
|
||||
uint8_t *zbuf = qpl->zbuf;
|
||||
uint8_t *addr;
|
||||
uint32_t len;
|
||||
qpl_job *job;
|
||||
|
||||
for (int i = 0; i < p->normal_num; i++) {
|
||||
addr = p->host + p->normal[i];
|
||||
len = qpl->zlen[i];
|
||||
/* the page is uncompressed if received length equals the page size */
|
||||
if (len == size) {
|
||||
memcpy(addr, zbuf, size);
|
||||
zbuf += size;
|
||||
continue;
|
||||
}
|
||||
|
||||
job = qpl->hw_jobs[i].job;
|
||||
multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size);
|
||||
if (multifd_qpl_submit_job(job)) {
|
||||
qpl->hw_jobs[i].fallback_sw_path = false;
|
||||
} else {
|
||||
/*
|
||||
* The IAA work queue is full, any immediate subsequent job
|
||||
* submission is likely to fail, sending the page via the QPL
|
||||
* software path at this point gives us a better chance of
|
||||
* finding the queue open for the next pages.
|
||||
*/
|
||||
qpl->hw_jobs[i].fallback_sw_path = true;
|
||||
job = qpl->sw_job;
|
||||
multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size);
|
||||
if (!multifd_qpl_process_and_check_job(job, false, size, errp)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
zbuf += len;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p->normal_num; i++) {
|
||||
/* ignore pages that have already been processed */
|
||||
if (qpl->zlen[i] == size || qpl->hw_jobs[i].fallback_sw_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
job = qpl->hw_jobs[i].job;
|
||||
if (!multifd_qpl_process_and_check_job(job, true, size, errp)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* multifd_qpl_recv: read the data from the channel into actual pages
|
||||
*
|
||||
* Read the compressed buffer, and uncompress it into the actual
|
||||
* pages.
|
||||
*
|
||||
* Returns 0 on success or -1 on error
|
||||
*
|
||||
* @p: Params for the channel being used
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp)
|
||||
{
|
||||
QplData *qpl = p->compress_data;
|
||||
uint32_t in_size = p->next_packet_size;
|
||||
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
|
||||
uint32_t len = 0;
|
||||
uint32_t zbuf_len = 0;
|
||||
int ret;
|
||||
|
||||
if (flags != MULTIFD_FLAG_QPL) {
|
||||
error_setg(errp, "multifd %u: flags received %x flags expected %x",
|
||||
p->id, flags, MULTIFD_FLAG_QPL);
|
||||
return -1;
|
||||
}
|
||||
multifd_recv_zero_page_process(p);
|
||||
if (!p->normal_num) {
|
||||
assert(in_size == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read compressed page lengths */
|
||||
len = p->normal_num * sizeof(uint32_t);
|
||||
assert(len < in_size);
|
||||
ret = qio_channel_read_all(p->c, (void *) qpl->zlen, len, errp);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
for (int i = 0; i < p->normal_num; i++) {
|
||||
qpl->zlen[i] = be32_to_cpu(qpl->zlen[i]);
|
||||
assert(qpl->zlen[i] <= p->page_size);
|
||||
zbuf_len += qpl->zlen[i];
|
||||
}
|
||||
|
||||
/* read compressed pages */
|
||||
assert(in_size == len + zbuf_len);
|
||||
ret = qio_channel_read_all(p->c, (void *) qpl->zbuf, zbuf_len, errp);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (qpl->hw_avail) {
|
||||
return multifd_qpl_decompress_pages(p, errp);
|
||||
}
|
||||
return multifd_qpl_decompress_pages_slow_path(p, errp);
|
||||
}
|
||||
|
||||
static MultiFDMethods multifd_qpl_ops = {
|
||||
.send_setup = multifd_qpl_send_setup,
|
||||
.send_cleanup = multifd_qpl_send_cleanup,
|
||||
.send_prepare = multifd_qpl_send_prepare,
|
||||
.recv_setup = multifd_qpl_recv_setup,
|
||||
.recv_cleanup = multifd_qpl_recv_cleanup,
|
||||
.recv = multifd_qpl_recv,
|
||||
};
|
||||
|
||||
static void multifd_qpl_register(void)
|
||||
{
|
||||
multifd_register_ops(MULTIFD_COMPRESSION_QPL, &multifd_qpl_ops);
|
||||
}
|
||||
|
||||
migration_init(multifd_qpl_register);
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Multifd UADK compression accelerator implementation
|
||||
*
|
||||
* Copyright (c) 2024 Huawei Technologies R & D (UK) Ltd
|
||||
*
|
||||
* Authors:
|
||||
* Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "exec/ramblock.h"
|
||||
#include "migration.h"
|
||||
#include "multifd.h"
|
||||
#include "options.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "uadk/wd_comp.h"
|
||||
#include "uadk/wd_sched.h"
|
||||
|
||||
struct wd_data {
|
||||
handle_t handle;
|
||||
uint8_t *buf;
|
||||
uint32_t *buf_hdr;
|
||||
};
|
||||
|
||||
static bool uadk_hw_init(void)
|
||||
{
|
||||
char alg[] = "zlib";
|
||||
int ret;
|
||||
|
||||
ret = wd_comp_init2(alg, SCHED_POLICY_RR, TASK_HW);
|
||||
if (ret && ret != -WD_EEXIST) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static struct wd_data *multifd_uadk_init_sess(uint32_t count,
|
||||
uint32_t page_size,
|
||||
bool compress, Error **errp)
|
||||
{
|
||||
struct wd_comp_sess_setup ss = {0};
|
||||
struct sched_params param = {0};
|
||||
uint32_t size = count * page_size;
|
||||
struct wd_data *wd;
|
||||
|
||||
wd = g_new0(struct wd_data, 1);
|
||||
|
||||
if (uadk_hw_init()) {
|
||||
ss.alg_type = WD_ZLIB;
|
||||
if (compress) {
|
||||
ss.op_type = WD_DIR_COMPRESS;
|
||||
/* Add an additional page for handling output > input */
|
||||
size += page_size;
|
||||
} else {
|
||||
ss.op_type = WD_DIR_DECOMPRESS;
|
||||
}
|
||||
/* We use default level 1 compression and 4K window size */
|
||||
param.type = ss.op_type;
|
||||
ss.sched_param = ¶m;
|
||||
|
||||
wd->handle = wd_comp_alloc_sess(&ss);
|
||||
if (!wd->handle) {
|
||||
error_setg(errp, "multifd: failed wd_comp_alloc_sess");
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* For CI test use */
|
||||
warn_report_once("UADK hardware not available. Switch to no compression mode");
|
||||
}
|
||||
|
||||
wd->buf = g_try_malloc(size);
|
||||
if (!wd->buf) {
|
||||
error_setg(errp, "multifd: out of mem for uadk buf");
|
||||
goto out_free_sess;
|
||||
}
|
||||
wd->buf_hdr = g_new0(uint32_t, count);
|
||||
return wd;
|
||||
|
||||
out_free_sess:
|
||||
if (wd->handle) {
|
||||
wd_comp_free_sess(wd->handle);
|
||||
}
|
||||
out:
|
||||
wd_comp_uninit2();
|
||||
g_free(wd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void multifd_uadk_uninit_sess(struct wd_data *wd)
|
||||
{
|
||||
if (wd->handle) {
|
||||
wd_comp_free_sess(wd->handle);
|
||||
}
|
||||
wd_comp_uninit2();
|
||||
g_free(wd->buf);
|
||||
g_free(wd->buf_hdr);
|
||||
g_free(wd);
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_uadk_send_setup: setup send side
|
||||
*
|
||||
* Returns 0 for success or -1 for error
|
||||
*
|
||||
* @p: Params for the channel that we are using
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_uadk_send_setup(MultiFDSendParams *p, Error **errp)
|
||||
{
|
||||
struct wd_data *wd;
|
||||
|
||||
wd = multifd_uadk_init_sess(p->page_count, p->page_size, true, errp);
|
||||
if (!wd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->compress_data = wd;
|
||||
assert(p->iov == NULL);
|
||||
/*
|
||||
* Each page will be compressed independently and sent using an IOV. The
|
||||
* additional two IOVs are used to store packet header and compressed data
|
||||
* length
|
||||
*/
|
||||
|
||||
p->iov = g_new0(struct iovec, p->page_count + 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_uadk_send_cleanup: cleanup send side
|
||||
*
|
||||
* Close the channel and return memory.
|
||||
*
|
||||
* @p: Params for the channel that we are using
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp)
|
||||
{
|
||||
struct wd_data *wd = p->compress_data;
|
||||
|
||||
multifd_uadk_uninit_sess(wd);
|
||||
p->compress_data = NULL;
|
||||
}
|
||||
|
||||
static inline void prepare_next_iov(MultiFDSendParams *p, void *base,
|
||||
uint32_t len)
|
||||
{
|
||||
p->iov[p->iovs_num].iov_base = (uint8_t *)base;
|
||||
p->iov[p->iovs_num].iov_len = len;
|
||||
p->next_packet_size += len;
|
||||
p->iovs_num++;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_uadk_send_prepare: prepare data to be able to send
|
||||
*
|
||||
* Create a compressed buffer with all the pages that we are going to
|
||||
* send.
|
||||
*
|
||||
* Returns 0 for success or -1 for error
|
||||
*
|
||||
* @p: Params for the channel that we are using
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp)
|
||||
{
|
||||
struct wd_data *uadk_data = p->compress_data;
|
||||
uint32_t hdr_size;
|
||||
uint8_t *buf = uadk_data->buf;
|
||||
int ret = 0;
|
||||
|
||||
if (!multifd_send_prepare_common(p)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
hdr_size = p->pages->normal_num * sizeof(uint32_t);
|
||||
/* prepare the header that stores the lengths of all compressed data */
|
||||
prepare_next_iov(p, uadk_data->buf_hdr, hdr_size);
|
||||
|
||||
for (int i = 0; i < p->pages->normal_num; i++) {
|
||||
struct wd_comp_req creq = {
|
||||
.op_type = WD_DIR_COMPRESS,
|
||||
.src = p->pages->block->host + p->pages->offset[i],
|
||||
.src_len = p->page_size,
|
||||
.dst = buf,
|
||||
/* Set dst_len to double the src in case compressed out >= page_size */
|
||||
.dst_len = p->page_size * 2,
|
||||
};
|
||||
|
||||
if (uadk_data->handle) {
|
||||
ret = wd_do_comp_sync(uadk_data->handle, &creq);
|
||||
if (ret || creq.status) {
|
||||
error_setg(errp, "multifd %u: failed compression, ret %d status %d",
|
||||
p->id, ret, creq.status);
|
||||
return -1;
|
||||
}
|
||||
if (creq.dst_len < p->page_size) {
|
||||
uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len);
|
||||
prepare_next_iov(p, buf, creq.dst_len);
|
||||
buf += creq.dst_len;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Send raw data if no UADK hardware or if compressed out >= page_size.
|
||||
* We might be better off sending raw data if output is slightly less
|
||||
* than page_size as well because at the receive end we can skip the
|
||||
* decompression. But it is tricky to find the right number here.
|
||||
*/
|
||||
if (!uadk_data->handle || creq.dst_len >= p->page_size) {
|
||||
uadk_data->buf_hdr[i] = cpu_to_be32(p->page_size);
|
||||
prepare_next_iov(p, p->pages->block->host + p->pages->offset[i],
|
||||
p->page_size);
|
||||
buf += p->page_size;
|
||||
}
|
||||
}
|
||||
out:
|
||||
p->flags |= MULTIFD_FLAG_UADK;
|
||||
multifd_send_fill_packet(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_uadk_recv_setup: setup receive side
|
||||
*
|
||||
* Create the compressed channel and buffer.
|
||||
*
|
||||
* Returns 0 for success or -1 for error
|
||||
*
|
||||
* @p: Params for the channel that we are using
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_uadk_recv_setup(MultiFDRecvParams *p, Error **errp)
|
||||
{
|
||||
struct wd_data *wd;
|
||||
|
||||
wd = multifd_uadk_init_sess(p->page_count, p->page_size, false, errp);
|
||||
if (!wd) {
|
||||
return -1;
|
||||
}
|
||||
p->compress_data = wd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_uadk_recv_cleanup: cleanup receive side
|
||||
*
|
||||
* Close the channel and return memory.
|
||||
*
|
||||
* @p: Params for the channel that we are using
|
||||
*/
|
||||
static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p)
|
||||
{
|
||||
struct wd_data *wd = p->compress_data;
|
||||
|
||||
multifd_uadk_uninit_sess(wd);
|
||||
p->compress_data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* multifd_uadk_recv: read the data from the channel into actual pages
|
||||
*
|
||||
* Read the compressed buffer, and uncompress it into the actual
|
||||
* pages.
|
||||
*
|
||||
* Returns 0 for success or -1 for error
|
||||
*
|
||||
* @p: Params for the channel that we are using
|
||||
* @errp: pointer to an error
|
||||
*/
|
||||
static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp)
|
||||
{
|
||||
struct wd_data *uadk_data = p->compress_data;
|
||||
uint32_t in_size = p->next_packet_size;
|
||||
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
|
||||
uint32_t hdr_len = p->normal_num * sizeof(uint32_t);
|
||||
uint32_t data_len = 0;
|
||||
uint8_t *buf = uadk_data->buf;
|
||||
int ret = 0;
|
||||
|
||||
if (flags != MULTIFD_FLAG_UADK) {
|
||||
error_setg(errp, "multifd %u: flags received %x flags expected %x",
|
||||
p->id, flags, MULTIFD_FLAG_ZLIB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
multifd_recv_zero_page_process(p);
|
||||
if (!p->normal_num) {
|
||||
assert(in_size == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read compressed data lengths */
|
||||
assert(hdr_len < in_size);
|
||||
ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr,
|
||||
hdr_len, errp);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p->normal_num; i++) {
|
||||
uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]);
|
||||
data_len += uadk_data->buf_hdr[i];
|
||||
assert(uadk_data->buf_hdr[i] <= p->page_size);
|
||||
}
|
||||
|
||||
/* read compressed data */
|
||||
assert(in_size == hdr_len + data_len);
|
||||
ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p->normal_num; i++) {
|
||||
struct wd_comp_req creq = {
|
||||
.op_type = WD_DIR_DECOMPRESS,
|
||||
.src = buf,
|
||||
.src_len = uadk_data->buf_hdr[i],
|
||||
.dst = p->host + p->normal[i],
|
||||
.dst_len = p->page_size,
|
||||
};
|
||||
|
||||
if (uadk_data->buf_hdr[i] == p->page_size) {
|
||||
memcpy(p->host + p->normal[i], buf, p->page_size);
|
||||
buf += p->page_size;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlikely(!uadk_data->handle)) {
|
||||
error_setg(errp, "multifd %u: UADK HW not available for decompression",
|
||||
p->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = wd_do_comp_sync(uadk_data->handle, &creq);
|
||||
if (ret || creq.status) {
|
||||
error_setg(errp, "multifd %u: failed decompression, ret %d status %d",
|
||||
p->id, ret, creq.status);
|
||||
return -1;
|
||||
}
|
||||
if (creq.dst_len != p->page_size) {
|
||||
error_setg(errp, "multifd %u: decompressed length error", p->id);
|
||||
return -1;
|
||||
}
|
||||
buf += uadk_data->buf_hdr[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MultiFDMethods multifd_uadk_ops = {
|
||||
.send_setup = multifd_uadk_send_setup,
|
||||
.send_cleanup = multifd_uadk_send_cleanup,
|
||||
.send_prepare = multifd_uadk_send_prepare,
|
||||
.recv_setup = multifd_uadk_recv_setup,
|
||||
.recv_cleanup = multifd_uadk_recv_cleanup,
|
||||
.recv = multifd_uadk_recv,
|
||||
};
|
||||
|
||||
static void multifd_uadk_register(void)
|
||||
{
|
||||
multifd_register_ops(MULTIFD_COMPRESSION_UADK, &multifd_uadk_ops);
|
||||
}
|
||||
migration_init(multifd_uadk_register);
|
|
@ -70,6 +70,10 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp)
|
|||
goto err_free_zbuff;
|
||||
}
|
||||
p->compress_data = z;
|
||||
|
||||
/* Needs 2 IOVs, one for packet header and one for compressed data */
|
||||
p->iov = g_new0(struct iovec, 2);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_zbuff:
|
||||
|
@ -101,6 +105,9 @@ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp)
|
|||
z->buf = NULL;
|
||||
g_free(p->compress_data);
|
||||
p->compress_data = NULL;
|
||||
|
||||
g_free(p->iov);
|
||||
p->iov = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,7 +52,6 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp)
|
|||
struct zstd_data *z = g_new0(struct zstd_data, 1);
|
||||
int res;
|
||||
|
||||
p->compress_data = z;
|
||||
z->zcs = ZSTD_createCStream();
|
||||
if (!z->zcs) {
|
||||
g_free(z);
|
||||
|
@ -77,6 +76,10 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp)
|
|||
error_setg(errp, "multifd %u: out of memory for zbuff", p->id);
|
||||
return -1;
|
||||
}
|
||||
p->compress_data = z;
|
||||
|
||||
/* Needs 2 IOVs, one for packet header and one for compressed data */
|
||||
p->iov = g_new0(struct iovec, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -98,6 +101,9 @@ static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp)
|
|||
z->zbuff = NULL;
|
||||
g_free(p->compress_data);
|
||||
p->compress_data = NULL;
|
||||
|
||||
g_free(p->iov);
|
||||
p->iov = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -137,6 +137,13 @@ static int nocomp_send_setup(MultiFDSendParams *p, Error **errp)
|
|||
p->write_flags |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY;
|
||||
}
|
||||
|
||||
if (multifd_use_packets()) {
|
||||
/* We need one extra place for the packet header */
|
||||
p->iov = g_new0(struct iovec, p->page_count + 1);
|
||||
} else {
|
||||
p->iov = g_new0(struct iovec, p->page_count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -150,6 +157,8 @@ static int nocomp_send_setup(MultiFDSendParams *p, Error **errp)
|
|||
*/
|
||||
static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp)
|
||||
{
|
||||
g_free(p->iov);
|
||||
p->iov = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -228,6 +237,7 @@ static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp)
|
|||
*/
|
||||
static int nocomp_recv_setup(MultiFDRecvParams *p, Error **errp)
|
||||
{
|
||||
p->iov = g_new0(struct iovec, p->page_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -240,6 +250,8 @@ static int nocomp_recv_setup(MultiFDRecvParams *p, Error **errp)
|
|||
*/
|
||||
static void nocomp_recv_cleanup(MultiFDRecvParams *p)
|
||||
{
|
||||
g_free(p->iov);
|
||||
p->iov = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -783,8 +795,6 @@ static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp)
|
|||
p->packet_len = 0;
|
||||
g_free(p->packet);
|
||||
p->packet = NULL;
|
||||
g_free(p->iov);
|
||||
p->iov = NULL;
|
||||
multifd_send_state->ops->send_cleanup(p, errp);
|
||||
|
||||
return *errp == NULL;
|
||||
|
@ -1179,11 +1189,6 @@ bool multifd_send_setup(void)
|
|||
p->packet = g_malloc0(p->packet_len);
|
||||
p->packet->magic = cpu_to_be32(MULTIFD_MAGIC);
|
||||
p->packet->version = cpu_to_be32(MULTIFD_VERSION);
|
||||
|
||||
/* We need one extra place for the packet header */
|
||||
p->iov = g_new0(struct iovec, page_count + 1);
|
||||
} else {
|
||||
p->iov = g_new0(struct iovec, page_count);
|
||||
}
|
||||
p->name = g_strdup_printf("multifdsend_%d", i);
|
||||
p->page_size = qemu_target_page_size();
|
||||
|
@ -1353,8 +1358,6 @@ static void multifd_recv_cleanup_channel(MultiFDRecvParams *p)
|
|||
p->packet_len = 0;
|
||||
g_free(p->packet);
|
||||
p->packet = NULL;
|
||||
g_free(p->iov);
|
||||
p->iov = NULL;
|
||||
g_free(p->normal);
|
||||
p->normal = NULL;
|
||||
g_free(p->zero);
|
||||
|
@ -1602,7 +1605,6 @@ int multifd_recv_setup(Error **errp)
|
|||
p->packet = g_malloc0(p->packet_len);
|
||||
}
|
||||
p->name = g_strdup_printf("multifdrecv_%d", i);
|
||||
p->iov = g_new0(struct iovec, page_count);
|
||||
p->normal = g_new0(ram_addr_t, page_count);
|
||||
p->zero = g_new0(ram_addr_t, page_count);
|
||||
p->page_count = page_count;
|
||||
|
|
|
@ -34,12 +34,14 @@ MultiFDRecvData *multifd_get_recv_data(void);
|
|||
/* Multifd Compression flags */
|
||||
#define MULTIFD_FLAG_SYNC (1 << 0)
|
||||
|
||||
/* We reserve 3 bits for compression methods */
|
||||
#define MULTIFD_FLAG_COMPRESSION_MASK (7 << 1)
|
||||
/* We reserve 4 bits for compression methods */
|
||||
#define MULTIFD_FLAG_COMPRESSION_MASK (0xf << 1)
|
||||
/* we need to be compatible. Before compression value was 0 */
|
||||
#define MULTIFD_FLAG_NOCOMP (0 << 1)
|
||||
#define MULTIFD_FLAG_ZLIB (1 << 1)
|
||||
#define MULTIFD_FLAG_ZSTD (2 << 1)
|
||||
#define MULTIFD_FLAG_QPL (4 << 1)
|
||||
#define MULTIFD_FLAG_UADK (8 << 1)
|
||||
|
||||
/* This value needs to be a multiple of qemu_target_page_size() */
|
||||
#define MULTIFD_PACKET_SIZE (512 * 1024)
|
||||
|
|
|
@ -554,11 +554,20 @@
|
|||
#
|
||||
# @zstd: use zstd compression method.
|
||||
#
|
||||
# @qpl: use qpl compression method. Query Processing Library(qpl) is
|
||||
# based on the deflate compression algorithm and use the Intel
|
||||
# In-Memory Analytics Accelerator(IAA) accelerated compression
|
||||
# and decompression. (Since 9.1)
|
||||
#
|
||||
# @uadk: use UADK library compression method. (Since 9.1)
|
||||
#
|
||||
# Since: 5.0
|
||||
##
|
||||
{ 'enum': 'MultiFDCompression',
|
||||
'data': [ 'none', 'zlib',
|
||||
{ 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] }
|
||||
{ 'name': 'zstd', 'if': 'CONFIG_ZSTD' },
|
||||
{ 'name': 'qpl', 'if': 'CONFIG_QPL' },
|
||||
{ 'name': 'uadk', 'if': 'CONFIG_UADK' } ] }
|
||||
|
||||
##
|
||||
# @MigMode:
|
||||
|
|
|
@ -220,6 +220,8 @@ meson_options_help() {
|
|||
printf "%s\n" ' Xen PCI passthrough support'
|
||||
printf "%s\n" ' xkbcommon xkbcommon support'
|
||||
printf "%s\n" ' zstd zstd compression support'
|
||||
printf "%s\n" ' qpl Query Processing Library support'
|
||||
printf "%s\n" ' uadk UADK Library support'
|
||||
}
|
||||
_meson_option_parse() {
|
||||
case $1 in
|
||||
|
@ -558,6 +560,10 @@ _meson_option_parse() {
|
|||
--disable-xkbcommon) printf "%s" -Dxkbcommon=disabled ;;
|
||||
--enable-zstd) printf "%s" -Dzstd=enabled ;;
|
||||
--disable-zstd) printf "%s" -Dzstd=disabled ;;
|
||||
--enable-qpl) printf "%s" -Dqpl=enabled ;;
|
||||
--disable-qpl) printf "%s" -Dqpl=disabled ;;
|
||||
--enable-uadk) printf "%s" -Duadk=enabled ;;
|
||||
--disable-uadk) printf "%s" -Duadk=disabled ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# See the COPYING file in the top-level directory.
|
||||
#
|
||||
|
||||
TARGET_LIST = i386 aarch64 s390x
|
||||
TARGET_LIST = i386 aarch64 s390x ppc64
|
||||
|
||||
SRC_PATH = ../..
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
/* PPC */
|
||||
#define PPC_TEST_MEM_START (1 * 1024 * 1024)
|
||||
#define PPC_TEST_MEM_END (100 * 1024 * 1024)
|
||||
#define PPC_H_PUT_TERM_CHAR 0x58
|
||||
|
||||
/* ARM */
|
||||
#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
.PHONY: all clean
|
||||
all: a-b-kernel.h
|
||||
|
||||
a-b-kernel.h: ppc64.kernel
|
||||
echo "$$__note" > $@
|
||||
xxd -i $< | sed -e 's/.*int.*//' >> $@
|
||||
|
||||
ppc64.kernel: ppc64.elf
|
||||
$(CROSS_PREFIX)objcopy -O binary -S $< $@
|
||||
|
||||
ppc64.elf: a-b-kernel.S
|
||||
$(CROSS_PREFIX)gcc -static -o $@ -nostdlib -Wl,--build-id=none $<
|
||||
|
||||
clean:
|
||||
$(RM) *.kernel *.elf
|
|
@ -0,0 +1,66 @@
|
|||
#
|
||||
# Copyright (c) 2024 IBM, Inc
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
#include "../migration-test.h"
|
||||
|
||||
.section .text
|
||||
|
||||
.macro print ch
|
||||
li %r3,PPC_H_PUT_TERM_CHAR
|
||||
li %r4,0
|
||||
li %r5,1
|
||||
li %r6,\ch
|
||||
sldi %r6,%r6,56
|
||||
sc 1
|
||||
.endm
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
. = 0x100
|
||||
/*
|
||||
* Enter 64-bit mode. Not necessary because the test uses 32-bit
|
||||
* addresses, but those constants could easily be changed and break
|
||||
* in 32-bit mode.
|
||||
*/
|
||||
mfmsr %r9
|
||||
li %r10,-1
|
||||
rldimi %r9,%r10,63,0
|
||||
mtmsrd %r9
|
||||
|
||||
/*
|
||||
* Set up test memory region. Non-volatiles are used because the
|
||||
* hcall can clobber regs.
|
||||
* r20 - start address
|
||||
* r21 - number of pages
|
||||
*/
|
||||
lis %r20,PPC_TEST_MEM_START@h
|
||||
ori %r20,%r20,PPC_TEST_MEM_START@l
|
||||
lis %r9,PPC_TEST_MEM_END@h
|
||||
ori %r9,%r9,PPC_TEST_MEM_END@l
|
||||
subf %r21,%r20,%r9
|
||||
li %r10,TEST_MEM_PAGE_SIZE
|
||||
divd %r21,%r21,%r10
|
||||
|
||||
print 'A'
|
||||
|
||||
li %r3,0
|
||||
mr %r9,%r20
|
||||
mtctr %r21
|
||||
1: stb %r3,0(%r9)
|
||||
addi %r9,%r9,TEST_MEM_PAGE_SIZE
|
||||
bdnz 1b
|
||||
|
||||
loop:
|
||||
mr %r9,%r20
|
||||
mtctr %r21
|
||||
1: lbz %r3,0(%r9)
|
||||
addi %r3,%r3,1
|
||||
stb %r3,0(%r9)
|
||||
addi %r9,%r9,TEST_MEM_PAGE_SIZE
|
||||
bdnz 1b
|
||||
|
||||
print 'B'
|
||||
b loop
|
|
@ -0,0 +1,42 @@
|
|||
/* This file is automatically generated from the assembly file in
|
||||
* tests/migration/ppc64. Edit that file and then run "make all"
|
||||
* inside tests/migration to update, and then remember to send both
|
||||
* the header and the assembler differences in your patch submission.
|
||||
*/
|
||||
unsigned char ppc64_kernel[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x7d, 0x20, 0x00, 0xa6, 0x39, 0x40, 0xff, 0xff,
|
||||
0x79, 0x49, 0xf8, 0x0e, 0x7d, 0x20, 0x01, 0x64, 0x3e, 0x80, 0x00, 0x10,
|
||||
0x62, 0x94, 0x00, 0x00, 0x3d, 0x20, 0x06, 0x40, 0x61, 0x29, 0x00, 0x00,
|
||||
0x7e, 0xb4, 0x48, 0x50, 0x39, 0x40, 0x10, 0x00, 0x7e, 0xb5, 0x53, 0xd2,
|
||||
0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, 0x38, 0xa0, 0x00, 0x01,
|
||||
0x38, 0xc0, 0x00, 0x41, 0x78, 0xc6, 0xc1, 0xc6, 0x44, 0x00, 0x00, 0x22,
|
||||
0x38, 0x60, 0x00, 0x00, 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6,
|
||||
0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, 0x42, 0x00, 0xff, 0xf8,
|
||||
0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, 0x88, 0x69, 0x00, 0x00,
|
||||
0x38, 0x63, 0x00, 0x01, 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00,
|
||||
0x42, 0x00, 0xff, 0xf0, 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00,
|
||||
0x38, 0xa0, 0x00, 0x01, 0x38, 0xc0, 0x00, 0x42, 0x78, 0xc6, 0xc1, 0xc6,
|
||||
0x44, 0x00, 0x00, 0x22, 0x4b, 0xff, 0xff, 0xcc
|
||||
};
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
#include "ppc-util.h"
|
||||
|
||||
static const uint8_t bios_avr[] = {
|
||||
0x88, 0xe0, /* ldi r24, 0x08 */
|
||||
|
|
|
@ -9,11 +9,4 @@ QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...)
|
|||
G_GNUC_PRINTF(1, 2);
|
||||
void qtest_spapr_shutdown(QOSState *qs);
|
||||
|
||||
/* List of capabilities needed to silence warnings with TCG */
|
||||
#define PSERIES_DEFAULT_CAPABILITIES \
|
||||
"cap-cfpc=broken," \
|
||||
"cap-sbbc=broken," \
|
||||
"cap-ibs=broken," \
|
||||
"cap-ccf-assist=off,"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "chardev/char.h"
|
||||
#include "crypto/tlscredspsk.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "ppc-util.h"
|
||||
|
||||
#include "migration-helpers.h"
|
||||
#include "tests/migration/migration-test.h"
|
||||
|
@ -127,6 +128,7 @@ static char *bootpath;
|
|||
*/
|
||||
#include "tests/migration/i386/a-b-bootblock.h"
|
||||
#include "tests/migration/aarch64/a-b-kernel.h"
|
||||
#include "tests/migration/ppc64/a-b-kernel.h"
|
||||
#include "tests/migration/s390x/a-b-bios.h"
|
||||
|
||||
static void bootfile_create(char *dir, bool suspend_me)
|
||||
|
@ -146,10 +148,8 @@ static void bootfile_create(char *dir, bool suspend_me)
|
|||
content = s390x_elf;
|
||||
len = sizeof(s390x_elf);
|
||||
} else if (strcmp(arch, "ppc64") == 0) {
|
||||
/*
|
||||
* sane architectures can be programmed at the boot prompt
|
||||
*/
|
||||
return;
|
||||
content = ppc64_kernel;
|
||||
len = sizeof(ppc64_kernel);
|
||||
} else if (strcmp(arch, "aarch64") == 0) {
|
||||
content = aarch64_kernel;
|
||||
len = sizeof(aarch64_kernel);
|
||||
|
@ -180,29 +180,10 @@ static void wait_for_serial(const char *side)
|
|||
{
|
||||
g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
|
||||
FILE *serialfile = fopen(serialpath, "r");
|
||||
const char *arch = qtest_get_arch();
|
||||
int started = (strcmp(side, "src_serial") == 0 &&
|
||||
strcmp(arch, "ppc64") == 0) ? 0 : 1;
|
||||
|
||||
do {
|
||||
int readvalue = fgetc(serialfile);
|
||||
|
||||
if (!started) {
|
||||
/* SLOF prints its banner before starting test,
|
||||
* to ignore it, mark the start of the test with '_',
|
||||
* ignore all characters until this marker
|
||||
*/
|
||||
switch (readvalue) {
|
||||
case '_':
|
||||
started = 1;
|
||||
break;
|
||||
case EOF:
|
||||
fseek(serialfile, 0, SEEK_SET);
|
||||
usleep(1000);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
switch (readvalue) {
|
||||
case 'A':
|
||||
/* Fine */
|
||||
|
@ -214,8 +195,6 @@ static void wait_for_serial(const char *side)
|
|||
return;
|
||||
|
||||
case EOF:
|
||||
started = (strcmp(side, "src_serial") == 0 &&
|
||||
strcmp(arch, "ppc64") == 0) ? 0 : 1;
|
||||
fseek(serialfile, 0, SEEK_SET);
|
||||
usleep(1000);
|
||||
break;
|
||||
|
@ -736,13 +715,11 @@ static int test_migrate_start(QTestState **from, QTestState **to,
|
|||
memory_size = "256M";
|
||||
start_address = PPC_TEST_MEM_START;
|
||||
end_address = PPC_TEST_MEM_END;
|
||||
arch_source = g_strdup_printf("-prom-env 'use-nvramrc?=true' -prom-env "
|
||||
"'nvramrc=hex .\" _\" begin %x %x "
|
||||
"do i c@ 1 + i c! 1000 +loop .\" B\" 0 "
|
||||
"until'", end_address, start_address);
|
||||
machine_alias = "pseries";
|
||||
machine_opts = "vsmt=8";
|
||||
arch_opts = g_strdup("-nodefaults");
|
||||
arch_opts = g_strdup_printf(
|
||||
"-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " "
|
||||
"-bios %s", bootpath);
|
||||
} else if (strcmp(arch, "aarch64") == 0) {
|
||||
memory_size = "150M";
|
||||
machine_alias = "virt";
|
||||
|
@ -2661,6 +2638,23 @@ test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from,
|
|||
}
|
||||
#endif /* CONFIG_ZSTD */
|
||||
|
||||
#ifdef CONFIG_QPL
|
||||
static void *
|
||||
test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from,
|
||||
QTestState *to)
|
||||
{
|
||||
return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl");
|
||||
}
|
||||
#endif /* CONFIG_QPL */
|
||||
#ifdef CONFIG_UADK
|
||||
static void *
|
||||
test_migrate_precopy_tcp_multifd_uadk_start(QTestState *from,
|
||||
QTestState *to)
|
||||
{
|
||||
return test_migrate_precopy_tcp_multifd_start_common(from, to, "uadk");
|
||||
}
|
||||
#endif /* CONFIG_UADK */
|
||||
|
||||
static void test_multifd_tcp_uri_none(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
|
@ -2741,6 +2735,28 @@ static void test_multifd_tcp_zstd(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_QPL
|
||||
static void test_multifd_tcp_qpl(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.listen_uri = "defer",
|
||||
.start_hook = test_migrate_precopy_tcp_multifd_qpl_start,
|
||||
};
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UADK
|
||||
static void test_multifd_tcp_uadk(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.listen_uri = "defer",
|
||||
.start_hook = test_migrate_precopy_tcp_multifd_uadk_start,
|
||||
};
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
static void *
|
||||
test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from,
|
||||
|
@ -3452,19 +3468,9 @@ int main(int argc, char **argv)
|
|||
#endif
|
||||
|
||||
/*
|
||||
* On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG
|
||||
* is touchy due to race conditions on dirty bits (especially on PPC for
|
||||
* some reason)
|
||||
*/
|
||||
if (g_str_equal(arch, "ppc64") &&
|
||||
(!has_kvm || access("/sys/module/kvm_hv", F_OK))) {
|
||||
g_test_message("Skipping tests: kvm_hv not available");
|
||||
goto test_add_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to ppc64, s390x seems to be touchy with TCG, so disable it
|
||||
* there until the problems are resolved
|
||||
* On s390x with TCG, migration is observed to hang due to the 'pending'
|
||||
* state of the flic interrupt controller not being migrated or
|
||||
* reconstructed post-migration. Disable it until the problem is resolved.
|
||||
*/
|
||||
if (g_str_equal(arch, "s390x") && !has_kvm) {
|
||||
g_test_message("Skipping tests: s390x host with KVM is required");
|
||||
|
@ -3626,6 +3632,14 @@ int main(int argc, char **argv)
|
|||
migration_test_add("/migration/multifd/tcp/plain/zstd",
|
||||
test_multifd_tcp_zstd);
|
||||
#endif
|
||||
#ifdef CONFIG_QPL
|
||||
migration_test_add("/migration/multifd/tcp/plain/qpl",
|
||||
test_multifd_tcp_qpl);
|
||||
#endif
|
||||
#ifdef CONFIG_UADK
|
||||
migration_test_add("/migration/multifd/tcp/plain/uadk",
|
||||
test_multifd_tcp_uadk);
|
||||
#endif
|
||||
#ifdef CONFIG_GNUTLS
|
||||
migration_test_add("/migration/multifd/tcp/tls/psk/match",
|
||||
test_multifd_tcp_tls_psk_match);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PowerPC misc useful things
|
||||
*
|
||||
* Copyright (c) 2024, IBM Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PPC_UTIL_H
|
||||
#define PPC_UTIL_H
|
||||
|
||||
/* List of capabilities needed to silence warnings with TCG */
|
||||
#define PSERIES_DEFAULT_CAPABILITIES \
|
||||
"cap-cfpc=broken," \
|
||||
"cap-sbbc=broken," \
|
||||
"cap-ibs=broken," \
|
||||
"cap-ccf-assist=off,"
|
||||
|
||||
#endif /* PPC_UTIL_H */
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
#include "ppc-util.h"
|
||||
|
||||
#define MAGIC 0xcafec0de
|
||||
#define ADDRESS 0x4000
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <glib/gstdio.h>
|
||||
#include "libqtest.h"
|
||||
#include "boot-sector.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
#include "ppc-util.h"
|
||||
|
||||
#define NETNAME "net0"
|
||||
|
||||
|
|
Loading…
Reference in New Issue