aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2024-06-15 09:16:05 -0700
committerRichard Henderson <richard.henderson@linaro.org>2024-06-15 09:16:05 -0700
commitb23acd3e1fd35a9a8452e793d7a0b0e8cc39714a (patch)
tree705860d7ed0b32e655b2bff80ace8b21a1ec275e
parent046a64b9801343e2e89eef10c7a48eec8d8c0d4f (diff)
parentc519caa825f5eba6e204bed5a464df167a5421d0 (diff)
downloadqemu-b23acd3e1fd35a9a8452e793d7a0b0e8cc39714a.zip
qemu-b23acd3e1fd35a9a8452e793d7a0b0e8cc39714a.tar.gz
qemu-b23acd3e1fd35a9a8452e793d7a0b0e8cc39714a.tar.bz2
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>
-rw-r--r--docs/devel/migration/features.rst2
-rw-r--r--docs/devel/migration/qpl-compression.rst260
-rw-r--r--docs/devel/migration/uadk-compression.rst144
-rw-r--r--hw/core/qdev-properties-system.c2
-rw-r--r--meson.build22
-rw-r--r--meson_options.txt4
-rw-r--r--migration/meson.build2
-rw-r--r--migration/multifd-qpl.c762
-rw-r--r--migration/multifd-uadk.c369
-rw-r--r--migration/multifd-zlib.c7
-rw-r--r--migration/multifd-zstd.c8
-rw-r--r--migration/multifd.c22
-rw-r--r--migration/multifd.h6
-rw-r--r--qapi/migration.json11
-rw-r--r--scripts/meson-buildoptions.sh6
-rw-r--r--tests/migration/Makefile2
-rw-r--r--tests/migration/migration-test.h1
-rw-r--r--tests/migration/ppc64/Makefile15
-rw-r--r--tests/migration/ppc64/a-b-kernel.S66
-rw-r--r--tests/migration/ppc64/a-b-kernel.h42
-rw-r--r--tests/qtest/boot-serial-test.c2
-rw-r--r--tests/qtest/libqos/libqos-spapr.h7
-rw-r--r--tests/qtest/migration-test.c100
-rw-r--r--tests/qtest/ppc-util.h19
-rw-r--r--tests/qtest/prom-env-test.c2
-rw-r--r--tests/qtest/pxe-test.c2
26 files changed, 1816 insertions, 69 deletions
diff --git a/docs/devel/migration/features.rst b/docs/devel/migration/features.rst
index d5ca7b8..58f8fd9 100644
--- a/docs/devel/migration/features.rst
+++ b/docs/devel/migration/features.rst
@@ -12,3 +12,5 @@ Migration has plenty of features to support different use cases.
virtio
mapped-ram
CPR
+ qpl-compression
+ uadk-compression
diff --git a/docs/devel/migration/qpl-compression.rst b/docs/devel/migration/qpl-compression.rst
new file mode 100644
index 0000000..990992d
--- /dev/null
+++ b/docs/devel/migration/qpl-compression.rst
@@ -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
diff --git a/docs/devel/migration/uadk-compression.rst b/docs/devel/migration/uadk-compression.rst
new file mode 100644
index 0000000..3f73345
--- /dev/null
+++ b/docs/devel/migration/uadk-compression.rst
@@ -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.
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index d79d6f4..f13350b 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -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,
diff --git a/meson.build b/meson.build
index 3a2f126..97e00d6 100644
--- a/meson.build
+++ b/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}
diff --git a/meson_options.txt b/meson_options.txt
index 4c1583e..7a79dd8 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -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',
diff --git a/migration/meson.build b/migration/meson.build
index bdc3244..5ce2acb4 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -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',
diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c
new file mode 100644
index 0000000..9265098
--- /dev/null
+++ b/migration/multifd-qpl.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);
diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c
new file mode 100644
index 0000000..d12353f
--- /dev/null
+++ b/migration/multifd-uadk.c
@@ -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 = &param;
+
+ 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);
diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c
index 737a964..2ced694 100644
--- a/migration/multifd-zlib.c
+++ b/migration/multifd-zlib.c
@@ -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;
}
/**
diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c
index 256858d..ca17b7e 100644
--- a/migration/multifd-zstd.c
+++ b/migration/multifd-zstd.c
@@ -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;
}
/**
diff --git a/migration/multifd.c b/migration/multifd.c
index f317bff..d82885f 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -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;
diff --git a/migration/multifd.h b/migration/multifd.h
index c9d9b09..0ecd6f4 100644
--- a/migration/multifd.h
+++ b/migration/multifd.h
@@ -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)
diff --git a/qapi/migration.json b/qapi/migration.json
index a351fd3..470f746 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -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:
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 6ce5a8b..58d49a4 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -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
}
diff --git a/tests/migration/Makefile b/tests/migration/Makefile
index 13e99b1..2c5ee28 100644
--- a/tests/migration/Makefile
+++ b/tests/migration/Makefile
@@ -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 = ../..
diff --git a/tests/migration/migration-test.h b/tests/migration/migration-test.h
index 68512c0..194df7d 100644
--- a/tests/migration/migration-test.h
+++ b/tests/migration/migration-test.h
@@ -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)
diff --git a/tests/migration/ppc64/Makefile b/tests/migration/ppc64/Makefile
new file mode 100644
index 0000000..a3a2d98
--- /dev/null
+++ b/tests/migration/ppc64/Makefile
@@ -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
diff --git a/tests/migration/ppc64/a-b-kernel.S b/tests/migration/ppc64/a-b-kernel.S
new file mode 100644
index 0000000..0613a8d
--- /dev/null
+++ b/tests/migration/ppc64/a-b-kernel.S
@@ -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
diff --git a/tests/migration/ppc64/a-b-kernel.h b/tests/migration/ppc64/a-b-kernel.h
new file mode 100644
index 0000000..673317e
--- /dev/null
+++ b/tests/migration/ppc64/a-b-kernel.h
@@ -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
+};
+
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c
index df389ad..3b92fa5 100644
--- a/tests/qtest/boot-serial-test.c
+++ b/tests/qtest/boot-serial-test.c
@@ -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 */
diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h
index e4483c1..a446276 100644
--- a/tests/qtest/libqos/libqos-spapr.h
+++ b/tests/qtest/libqos/libqos-spapr.h
@@ -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
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index b7e3406..0dccb4b 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -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);
diff --git a/tests/qtest/ppc-util.h b/tests/qtest/ppc-util.h
new file mode 100644
index 0000000..f68ee93
--- /dev/null
+++ b/tests/qtest/ppc-util.h
@@ -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 */
diff --git a/tests/qtest/prom-env-test.c b/tests/qtest/prom-env-test.c
index 39ccb59..1470510 100644
--- a/tests/qtest/prom-env-test.c
+++ b/tests/qtest/prom-env-test.c
@@ -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
diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c
index e4b4822..a3f900f 100644
--- a/tests/qtest/pxe-test.c
+++ b/tests/qtest/pxe-test.c
@@ -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"