Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support
@ 2026-07-24 16:13 Anthony Krowiak
  2026-07-24 16:13 ` [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
                   ` (14 more replies)
  0 siblings, 15 replies; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

This patch series implements live guest migration support for KVM guests
with s390 AP (Adjunct Processor) devices passed through via the VFIO
mediated device framework.

Background
~~~~~~~~~~

The vfio-ap device driver differs from typical VFIO device drivers in that
it does not virtualize a physical device. Instead, it manages AP
configuration metadata identifying the AP adapters, domains, and control
domains to which a guest will be granted access. These AP resources are
configured by assigning them to a vfio-ap mediated device via its sysfs
assignment interfaces. When the fd for the VFIO device is opened by
userspace, the vfio_ap device driver sets the guest's AP configuration
from the metadata stored with the mediated device. As such, the AP devices
are not accessed directly through the vfio_ap driver, so the driver has no
internal AP device state to migrate. What it does migrate is the AP
configuration metadata of the source guest.

Implementation Approach
~~~~~~~~~~~~~~~~~~~~~~~

This series implements the VFIO migration protocol using the STOP_COPY
migration flow. The key aspects are:

1. On transition of the migration state from STOP to STOP_COPY
   - The vfio_ap device driver creates a filestream for userspace to use to
     read the guest's AP configuration from the mdev

2. During the STOP_COPY phase
   - Userspace uses the filestream created in #1 to read the source guest's
     AP configuration
   - The vfio_ap device driver copies the source guest's AP configuration
     information to userspace

3. On transition of the migration state from STOP to RESUMING
   - The vfio_ap device driver creates a filestream for userspace to use to
     write the source guest's AP configuration information so it can be
     restored to the mdev on the destination host.

4. During the RESUMING phase
   - Userspace uses the filestream created in #3 to send the source guest's
     AP configuration information to the vfio_ap device driver on the
     destination host.
   - The vfio_ap device driver first verifies the source guest's AP
     configuration is compatible with the destination host's.
   - The driver restores AP configuration to the mdev on the destination
     host which automatically hot plugs the AP resources identified
     therein.

5. Documentation
   - Add live guest migration chapter to vfio-ap.rst

Compatibility Validation
~~~~~~~~~~~~~~~~~~~~~~~~

The series includes comprehensive validation to ensure source and
destination AP configurations are compatible. For each queue, the following
characteristics must match:

- AP type (target must be same or newer than source)
- Installed facilities (APSC, APQKM, AP4KC, SLCF)
- Operating mode (CCA, Accelerator, XCP)
- APXA facility setting
- Classification (native vs stateless functions)
- Queue usability (binding/associated state)

When incompatibilities are detected, migration fails with detailed error
messages identifying the specific queue and characteristic that caused
the failure.

Configuration Management
~~~~~~~~~~~~~~~~~~~~~~~~

This implementation does not prevent configuration changes during
migration. Configuration stability is an orchestration-layer
responsibility, consistent with other VFIO device types. The driver's
role is to validate configurations and provide clear diagnostics when
incompatibilities are detected, enabling orchestration tools to implement
appropriate policies.

QEMU patches exploiting this series:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
https://lore.kernel.org/qemu-devel/20260409141352.997844-1-akrowiak@linux.ibm.com/

Change log v4 => v5:
~~~~~~~~~~~~~~~~~~~
Patch 3: Functions to initialize/release vfio device migration data
* Fixed error path bug in vfio_ap_mdev_set_kvm function; reset pqap hook
  pointer
* Do not initialize vfio_device migration_flags and mig_ops for SE guests

Patch 4: Reset migration state in VFIO_DEVICE_RESET ioctl handler
* Added missing vfio_ap_release_mig_files() function

Patch 5: Callback to get/set vfio device mig state during guest migration
* Move mutex_unlock(&matrix_dev->mdevs_lock) before call to 
  vfio_put_device function in the vfio_ap_mdev_probe function to avoid
  deadlock situation
 
Patch 6: Transition guest migration state from STOP to STOP_COPY
* Acquire matrix_dev->mdevs_lock for duration of vfio_ap_release_mig_file
  callback
  
Patch 7: File ops called to save the vfio device migration state
* Change data type of ret in vfio_ap_stop_copy function to ssize_t to 
  accommodate negtive return codes.
  
Patch 9:  Add method to set a new guest AP configuration
* Removed call to vfio_ap_mdev_link_queue in 
  vfio_ap_mdev_unlink_fr_queues() function 
* Refactored vfio_ap_set_new_guest_config function to correct some bugs

Patch 10 - File ops called to resume the vfio device migration
* Fixed a corrupted kernel-doc comment in vfio_ap_private.h where a
  function signature was accidentally concatenated onto the @node field
  description of struct ap_matrix_mdev.
* Fixed do_post_copy_validation() to evaluate queues_available() and
  control_domains_available() independently rather than short-circuiting
  with ||, ensuring all availability errors are logged before returning so
  operators see the complete set of problems in a single migration attempt
* Fixed reallocate_ap_config() to use check_add_overflow() when computing
  the new buffer size for sub-header partial chunks, consistent with
  overflow checks used elsewhere in the file.
* Fixed verify_ap_configs_are_compatible() to return -EINVAL instead of
  -EFAULT for hardware incompatibility, as -EFAULT conventionally signals
  a bad userspace address rather than a compatibility failure.
* Removed a spurious blank line before return -EIO in the default: branch
  of get_hardware_info_for_queue() that was inconsistent with the style of
  the surrounding error paths.
* Clarified the QINFO_DATA_MASK comment to correctly describe the
  asymmetric semantics for the classification bits (upgrade permitted,
  downgrade rejected) and AP type (less-than-or-equal, not strict 
  equality), and to clarify that the BS bits must be zero on both sides
  rather than merely equal.
* Added a mig_data NULL check immediately after 
  get_update_locks_for_mdev() in vfio_ap_resuming_write() to guard against
  the migration file descriptor being closed concurrently while mdevs_lock
  was dropped for the copy_from_user(). get_update_locks_for_mdev()
  reacquires mdevs_lock as its final step, making the check safe at that
  point without introducing a separate preliminary lock acquisition that
  would deadlock.

Patch 15: Add live guest migration chapter to vfio-ap.rst
* Fixed the facilities bit range description from "Bits 0-3" to "Bits 0-2"
  to eliminate the overlap with the adapter modes range that begins at bit
  3.

* Fixed a typo: "Destinaton host" (appeared twice, in the "AP queues not 
  available" and "control domains not available" troubleshooting tables).

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Anthony Krowiak (15):
  s390/vfio-ap: Provide function to get the number of queues assigned to
    mdev
  s390/vfio-ap: Data structures for facilitating vfio device migration
  s390/vfio-ap: Functions to initialize/release vfio device migration
    data
  s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler
  s390-vfio-ap: Callback to get/set vfio device mig state during guest
    migration
  s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY
  s390/vfio-ap: File ops called to save the vfio device migration state
  s390/vfio-ap: Transition device migration state from STOP to RESUMING
  s390/vfio-ap: Add method to set a new guest AP configuration
  s390/vfio-ap: File ops called to resume the vfio device migration
  s390/vfio-ap: Transition device migration state to STOP
  s390/vfio-ap: Transition device migration state from STOP to RUNNING
    and vice versa
  s390/vfio-ap: Callback to get the size of data to be migrated during
    guest migration
  s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute
  s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst

 Documentation/arch/s390/vfio-ap.rst     |  616 +++++++--
 drivers/s390/crypto/Makefile            |    2 +-
 drivers/s390/crypto/vfio_ap_drv.c       |    4 +-
 drivers/s390/crypto/vfio_ap_migration.c | 1560 +++++++++++++++++++++++
 drivers/s390/crypto/vfio_ap_ops.c       |  305 +++--
 drivers/s390/crypto/vfio_ap_private.h   |   73 ++
 6 files changed, 2351 insertions(+), 209 deletions(-)
 create mode 100644 drivers/s390/crypto/vfio_ap_migration.c

-- 
2.53.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 17:36   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Provides a function that returns the number of queues that are or will be
passed through to a guest when the mdev is attached to a guest.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_ops.c     | 14 ++++++++++++++
 drivers/s390/crypto/vfio_ap_private.h |  2 ++
 2 files changed, 16 insertions(+)

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 44b3a1dcc1b3..71d3e2c2e07c 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -2098,6 +2098,20 @@ static void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)
 	release_update_locks_for_mdev(matrix_mdev);
 }
 
+int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix)
+{
+	unsigned long apid, apqi;
+	int num_queues = 0;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	for_each_set_bit_inv(apid, ap_matrix->apm, AP_DEVICES)
+		for_each_set_bit_inv(apqi, ap_matrix->aqm, AP_DOMAINS)
+			num_queues++;
+
+	return num_queues;
+}
+
 static int vfio_ap_mdev_get_device_info(unsigned long arg)
 {
 	unsigned long minsz;
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index 9bff666b0b35..9677e49554d7 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -152,6 +152,8 @@ struct vfio_ap_queue {
 	struct work_struct reset_work;
 };
 
+int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix);
+
 int vfio_ap_mdev_register(void);
 void vfio_ap_mdev_unregister(void);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
  2026-07-24 16:13 ` [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 17:28   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Creates the data structures used to facilitate state transitions during
vfio device migration.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/Makefile            |  2 +-
 drivers/s390/crypto/vfio_ap_migration.c | 72 +++++++++++++++++++++++++
 drivers/s390/crypto/vfio_ap_private.h   |  5 ++
 3 files changed, 78 insertions(+), 1 deletion(-)
 create mode 100644 drivers/s390/crypto/vfio_ap_migration.c

diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index e83c6603c858..20f29184825a 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -34,5 +34,5 @@ pkey-uv-objs := pkey_uv.o
 obj-$(CONFIG_PKEY_UV) += pkey-uv.o
 
 # adjunct processor matrix
-vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o
+vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o vfio_ap_migration.o
 obj-$(CONFIG_VFIO_AP) += vfio_ap.o
diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
new file mode 100644
index 000000000000..1b0856aacb69
--- /dev/null
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Drives vfio_ap mdev migration.
+ *
+ * Copyright IBM Corp. 2025
+ */
+#include "vfio_ap_private.h"
+
+/**
+ * vfio_ap_migration_file
+ *
+ * This object is used for chunk processing of multiple reads and writes of
+ * AP configuration information.
+ *
+ * @filp:	file stream used to read or write AP configuration data
+ * @ap_config:	object used to store AP configuration data between read or write
+ *		calls
+ * @config_sz:	the size (in bytes) of @ap_config
+ */
+struct vfio_ap_migration_file {
+	struct file		*filp;
+	struct vfio_ap_config	*ap_config;
+	unsigned long		config_sz;
+};
+
+/**
+ * vfio_ap_migration_data
+ *
+ * Manages the migration state for the VFIO device that maintains the AP
+ * configuration of the guest being migrated.
+ *
+ * @mig_state:		the current migration state
+ * @resuming_mig_file:	the object used to restore the state of the vfio-ap
+ *			device the destination host:
+ * @stop_copy_mig_file: the object used to store the AP configuration of the
+ *			source guest for transfer to the destination host.
+ */
+struct vfio_ap_migration_data {
+	enum vfio_device_mig_state	mig_state;
+	struct vfio_ap_migration_file	resuming_mig_file;
+	struct vfio_ap_migration_file	stop_copy_mig_file;
+};
+
+/**
+ * vfio_ap_queue_info - the information for an AP queue
+ *
+ * @data: contains the queue information returned in GR2 from the PQAP(TAPQ)
+ *	  command
+ * @apqn: the APQN of the queue
+ * @reserved: padding to ensure consistent structure size across platforms
+ */
+struct vfio_ap_queue_info {
+	u64 data;
+	u16 apqn;
+	u8  reserved[6];
+};
+
+/**
+ * vfio_ap_config - the guest's AP configuration
+ *
+ * @num_queues:	the number of queues passed through to the guest
+ * @reserved:	padding to ensure proper alignment of @cntl_domains
+ * @adm:	bitmap specifying the control domains in the AP configuration
+ * @qinfo:	an array of vfio_ap_queue_info objects, each specifying the
+ *		queue information for a queue passed through to the guest
+ */
+struct vfio_ap_config {
+	u32				num_queues;
+	u8				reserved[4];
+	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
+	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
+};
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index 9677e49554d7..2b542648964b 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -91,6 +91,9 @@ struct ap_queue_table {
 	DECLARE_HASHTABLE(queues, 8);
 };
 
+/* Forward declaration for migration data structure */
+struct vfio_ap_migration_data;
+
 /**
  * struct ap_matrix_mdev - Contains the data associated with a matrix mediated
  *			   device.
@@ -110,6 +113,7 @@ struct ap_queue_table {
  * @aqm_add:	bitmap of APQIs added to the host's AP configuration
  * @adm_add:	bitmap of control domain numbers added to the host's AP
  *		configuration
+ * @mig_data:  vfio device migration data
  */
 struct ap_matrix_mdev {
 	struct vfio_device vdev;
@@ -125,6 +129,7 @@ struct ap_matrix_mdev {
 	DECLARE_BITMAP(apm_add, AP_DEVICES);
 	DECLARE_BITMAP(aqm_add, AP_DOMAINS);
 	DECLARE_BITMAP(adm_add, AP_DOMAINS);
+	struct vfio_ap_migration_data *mig_data;
 };
 
 /**
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
  2026-07-24 16:13 ` [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
  2026-07-24 16:13 ` [PATCH v5 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 17:35   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 04/15] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Provides the functions that initialize and release the data structures
used during live guest migration:

* vfio_ap_init_migration_capabilities

  Sets the migration flags and vfio_migration_ops structure into the
  vfio_device object when the mdev is probed.

* vfio_ap_init_migration_data

  Allocates and initializes the object used to maintain the state of the
  VFIO migration. It is called when the VFIO device is opened.

* vfio_ap_release_migration_data

  Frees the memory of the object used to maintain the VFIO migration state.
  It is called when the VFIO device release callback is invoked and when
  the VFIO device is closed.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 80 +++++++++++++++++++++++++
 drivers/s390/crypto/vfio_ap_ops.c       | 60 ++++++++++++++++---
 drivers/s390/crypto/vfio_ap_private.h   |  4 ++
 3 files changed, 136 insertions(+), 8 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 1b0856aacb69..9187cfe26da7 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -4,6 +4,7 @@
  *
  * Copyright IBM Corp. 2025
  */
+#include <linux/file.h>
 #include "vfio_ap_private.h"
 
 /**
@@ -70,3 +71,82 @@ struct vfio_ap_config {
 	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
 	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
 };
+
+static struct file *vfio_ap_set_state(struct vfio_device *vdev,
+				      enum vfio_device_mig_state  new_state)
+{
+	return NULL;
+}
+
+static int vfio_ap_get_state(struct vfio_device *vdev,
+			     enum vfio_device_mig_state  *current_state)
+{
+	return -EOPNOTSUPP;
+}
+
+static int vfio_ap_get_data_size(struct vfio_device *vdev,
+				 unsigned long *stop_copy_length)
+{
+	return -EOPNOTSUPP;
+}
+
+static const struct vfio_migration_ops vfio_ap_migration_ops = {
+	.migration_set_state = vfio_ap_set_state,
+	.migration_get_state = vfio_ap_get_state,
+	.migration_get_data_size = vfio_ap_get_data_size,
+};
+
+/**
+ * vfio_ap_init_migrations_capabilities - initialize migration capabilities
+ *
+ * @matrix_mdev: pointer to object containing the mdev state
+ */
+void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev)
+{
+	if (ap_is_se_guest())
+		return;
+
+	matrix_mdev->vdev.migration_flags = VFIO_MIGRATION_STOP_COPY;
+	matrix_mdev->vdev.mig_ops = &vfio_ap_migration_ops;
+}
+
+/**
+ * vfio_ap_init_migration_data - initialize migration data and functions
+ *
+ * @matrix_mdev: pointer to object containing the mdev state
+ *
+ * Return: zero if initialization is successful; otherwise, returns a error.
+ */
+int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct vfio_ap_migration_data *mig_data;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	mig_data = kzalloc_obj(struct vfio_ap_migration_data, GFP_KERNEL);
+	if (!mig_data)
+		return -ENOMEM;
+
+	mig_data->mig_state = VFIO_DEVICE_STATE_RUNNING;
+	matrix_mdev->mig_data = mig_data;
+
+	return 0;
+}
+
+/**
+ * vfio_ap_release_migration_data: reclaim private migration data
+ *
+ * @vdev: pointer to the mdev
+ */
+void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev)
+{
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (!matrix_mdev->mig_data)
+		return;
+
+	kfree(matrix_mdev->mig_data->resuming_mig_file.ap_config);
+	kfree(matrix_mdev->mig_data->stop_copy_mig_file.ap_config);
+	kfree(matrix_mdev->mig_data);
+	matrix_mdev->mig_data = NULL;
+}
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 71d3e2c2e07c..47aca908701d 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -775,18 +775,30 @@ static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev,
 
 static int vfio_ap_mdev_init_dev(struct vfio_device *vdev)
 {
-	struct ap_matrix_mdev *matrix_mdev =
-		container_of(vdev, struct ap_matrix_mdev, vdev);
+	struct ap_matrix_mdev *matrix_mdev;
 
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
 	matrix_mdev->mdev = to_mdev_device(vdev->dev);
 	vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix);
 	matrix_mdev->pqap_hook = handle_pqap;
 	vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->shadow_apcb);
 	hash_init(matrix_mdev->qtable.queues);
+	mutex_unlock(&matrix_dev->mdevs_lock);
 
 	return 0;
 }
 
+static void vfio_ap_mdev_release_dev(struct vfio_device *vdev)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	vfio_ap_release_migration_data(matrix_mdev);
+	mutex_unlock(&matrix_dev->mdevs_lock);
+}
+
 static int vfio_ap_mdev_probe(struct mdev_device *mdev)
 {
 	struct ap_matrix_mdev *matrix_mdev;
@@ -797,18 +809,29 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)
 	if (IS_ERR(matrix_mdev))
 		return PTR_ERR(matrix_mdev);
 
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	/*
+	 * Migration capabilities must be initialized before calling
+	 * vfio_register_emulated_iommu_dev; otherwise, the VFIO core
+	 * will see mig_ops as NULL during the registration. This could
+	 * prevent the VFIO core from properly setting up migration
+	 * infrastructure like debugfs entries.
+	 */
+	vfio_ap_init_migration_capabilities(matrix_mdev);
+
 	ret = vfio_register_emulated_iommu_dev(&matrix_mdev->vdev);
 	if (ret)
 		goto err_put_vdev;
 	matrix_mdev->req_trigger = NULL;
 	matrix_mdev->cfg_chg_trigger = NULL;
 	dev_set_drvdata(&mdev->dev, matrix_mdev);
-	mutex_lock(&matrix_dev->mdevs_lock);
 	list_add(&matrix_mdev->node, &matrix_dev->mdev_list);
 	mutex_unlock(&matrix_dev->mdevs_lock);
 	return 0;
 
 err_put_vdev:
+	mutex_unlock(&matrix_dev->mdevs_lock);
 	vfio_put_device(&matrix_mdev->vdev);
 	return ret;
 }
@@ -2052,19 +2075,39 @@ static int vfio_ap_mdev_reset_qlist(struct list_head *qlist)
 
 static int vfio_ap_mdev_open_device(struct vfio_device *vdev)
 {
-	struct ap_matrix_mdev *matrix_mdev =
-		container_of(vdev, struct ap_matrix_mdev, vdev);
+	struct ap_matrix_mdev *matrix_mdev;
+	int ret;
 
 	if (!vdev->kvm)
 		return -EINVAL;
 
-	return vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm);
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	ret = vfio_ap_init_migration_data(matrix_mdev);
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	if (ret)
+		return ret;
+
+	ret = vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm);
+	if (ret) {
+		/* Clean up migration data on failure */
+		mutex_lock(&matrix_dev->mdevs_lock);
+		vfio_ap_release_migration_data(matrix_mdev);
+		mutex_unlock(&matrix_dev->mdevs_lock);
+	}
+
+	return ret;
 }
 
 static void vfio_ap_mdev_close_device(struct vfio_device *vdev)
 {
-	struct ap_matrix_mdev *matrix_mdev =
-		container_of(vdev, struct ap_matrix_mdev, vdev);
+	struct ap_matrix_mdev *matrix_mdev;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	vfio_ap_release_migration_data(matrix_mdev);
+	mutex_unlock(&matrix_dev->mdevs_lock);
 
 	vfio_ap_mdev_unset_kvm(matrix_mdev);
 }
@@ -2374,6 +2417,7 @@ static const struct attribute_group vfio_queue_attr_group = {
 
 static const struct vfio_device_ops vfio_ap_matrix_dev_ops = {
 	.init = vfio_ap_mdev_init_dev,
+	.release = vfio_ap_mdev_release_dev,
 	.open_device = vfio_ap_mdev_open_device,
 	.close_device = vfio_ap_mdev_close_device,
 	.ioctl = vfio_ap_mdev_ioctl,
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index 2b542648964b..a2a713f93674 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -172,4 +172,8 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *new_config_info,
 void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
 			      struct ap_config_info *old_config_info);
 
+void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev);
+int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev);
+void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev);
+
 #endif /* _VFIO_AP_PRIVATE_H_ */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 04/15] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (2 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 17:43   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 05/15] s390-vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

According to the VFIO migration specifications, userspace must issue a
VFIO_DEVICE_RESET ioctl if a vfio device enters an error state or fails a
state transition to recover the device and return its migration state to
RUNNING. This patch adds a function that is called when a VFIO_DEVICE_RESET
is issued that releases the migration files and frees the storage allocated
for maintaining the migration state.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 54 +++++++++++++++++++++++++
 drivers/s390/crypto/vfio_ap_ops.c       |  4 ++
 drivers/s390/crypto/vfio_ap_private.h   |  1 +
 3 files changed, 59 insertions(+)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 9187cfe26da7..45557f433652 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -150,3 +150,57 @@ void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev)
 	kfree(matrix_mdev->mig_data);
 	matrix_mdev->mig_data = NULL;
 }
+
+static void vfio_ap_release_mig_files(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct vfio_ap_migration_data *mig_data;
+
+	/*
+	 * The fput call does not call .release synchronously while the
+	 * mdevs_lock mutex is held, so there is no problem with incurring a
+	 * deadlock situation if fput is executed in this function.
+	 */
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	mig_data = matrix_mdev->mig_data;
+	if (!mig_data)
+		return;
+
+	if (mig_data->stop_copy_mig_file.filp) {
+		fput(mig_data->stop_copy_mig_file.filp);
+		mig_data->stop_copy_mig_file.filp = NULL;
+	}
+
+	kfree(mig_data->stop_copy_mig_file.ap_config);
+	mig_data->stop_copy_mig_file.ap_config = NULL;
+	mig_data->stop_copy_mig_file.config_sz = 0;
+
+	if (mig_data->resuming_mig_file.filp) {
+		fput(mig_data->resuming_mig_file.filp);
+		mig_data->resuming_mig_file.filp = NULL;
+	}
+
+	kfree(mig_data->resuming_mig_file.ap_config);
+	mig_data->resuming_mig_file.ap_config = NULL;
+	mig_data->resuming_mig_file.config_sz = 0;
+}
+
+/**
+ * vfio_ap_reset_migration_state - Reset the vfio-ap migration state
+ *
+ * @matrix_mdev: pointer to the object maintaining the vfio-ap device state
+ *
+ * Called during VFIO_DEVICE_RESET to clean up any active migration
+ * state and reset the device to RUNNING state as required by the VFIO
+ * migration specification.
+ */
+void vfio_ap_reset_migration_state(struct ap_matrix_mdev *matrix_mdev)
+{
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (!matrix_mdev->mig_data)
+		return;
+
+	vfio_ap_release_mig_files(matrix_mdev);
+	matrix_mdev->mig_data->mig_state = VFIO_DEVICE_STATE_RUNNING;
+}
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 47aca908701d..678bdd4c82e9 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -2332,6 +2332,10 @@ static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev,
 		break;
 	case VFIO_DEVICE_RESET:
 		ret = vfio_ap_mdev_reset_queues(matrix_mdev);
+		if (ret == 0) {
+			/* Reset migration state per VFIO migration spec */
+			vfio_ap_reset_migration_state(matrix_mdev);
+		}
 		break;
 	case VFIO_DEVICE_GET_IRQ_INFO:
 		ret = vfio_ap_get_irq_info(arg);
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index a2a713f93674..1fbdfcce5a11 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -175,5 +175,6 @@ void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
 void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev);
 int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev);
 void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev);
+void vfio_ap_reset_migration_state(struct ap_matrix_mdev *matrix_mdev);
 
 #endif /* _VFIO_AP_PRIVATE_H_ */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 05/15] s390-vfio-ap: Callback to get/set vfio device mig state during guest migration
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (3 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 04/15] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 17:47   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Implements two callbacks related to the current state of a vfio-ap device:
* The callback that sets a new migration state of a vfio-ap device during
  live migration of guests with pass-through access to AP devices. This
  callback is mandatory for VFIO_DEVICE_FEATURE_MIGRATION support.

  The function pointer for this callback is specified via the
  'migration_set_state' field of the 'vfio_migration_ops' structure
  which is stored with the VFIO device when the 'vfio_device'
  structure representing the mediated device is initialized.

* The callback that returns the current vfio device migration state during
  live migration of guests with pass-through access to AP devices.

  The function pointer for this callback is specified via the
  'migration_get_state' field of the 'vfio_migration_ops' structure
  which is stored with the VFIO device when the 'vfio_device'
  structure representing the mediated device is initialized.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 130 +++++++++++++++++++++++-
 1 file changed, 128 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 45557f433652..17cabd7541e7 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -72,16 +72,141 @@ struct vfio_ap_config {
 	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
 };
 
+static struct file *
+vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
+			    enum vfio_device_mig_state new_state)
+{
+	struct vfio_ap_migration_data *mig_data;
+	enum vfio_device_mig_state cur_state;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+	mig_data = matrix_mdev->mig_data;
+	cur_state = mig_data->mig_state;
+	dev_dbg(matrix_mdev->vdev.dev, "%s: %d -> %d\n", __func__, cur_state,
+		new_state);
+
+	if (cur_state == VFIO_DEVICE_STATE_STOP &&
+	    new_state == VFIO_DEVICE_STATE_STOP_COPY) {
+		/* TODO */
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
+	if (cur_state == VFIO_DEVICE_STATE_STOP &&
+	    new_state == VFIO_DEVICE_STATE_RESUMING) {
+		/* TODO */
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
+	if ((cur_state == VFIO_DEVICE_STATE_RESUMING &&
+	     new_state == VFIO_DEVICE_STATE_STOP) ||
+	    (cur_state == VFIO_DEVICE_STATE_STOP_COPY &&
+	     new_state == VFIO_DEVICE_STATE_STOP)) {
+		/* TODO */
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
+	if ((cur_state == VFIO_DEVICE_STATE_STOP &&
+	     new_state == VFIO_DEVICE_STATE_RUNNING) ||
+	    (cur_state == VFIO_DEVICE_STATE_RUNNING &&
+	     new_state == VFIO_DEVICE_STATE_STOP)) {
+		/* TODO */
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
+	/* vfio_mig_get_next_state() does not use arcs other than the above */
+	WARN_ON(true);
+
+	return ERR_PTR(-EINVAL);
+}
+
 static struct file *vfio_ap_set_state(struct vfio_device *vdev,
 				      enum vfio_device_mig_state  new_state)
 {
-	return NULL;
+	int ret;
+	struct file *filp = NULL;
+	struct ap_matrix_mdev *matrix_mdev;
+	enum vfio_device_mig_state next_state;
+	struct vfio_ap_migration_data *mig_data;
+
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	if (ap_is_se_guest()) {
+		dev_err_once(matrix_mdev->vdev.dev,
+			     "Migration not allowed from or to a Secure Execution guest\n");
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ERR_PTR(-EPERM);
+	}
+
+	mig_data = matrix_mdev->mig_data;
+
+	/*
+	 * The mig_data pointer is set in the vfio_ap_init_migration_data
+	 * function which is called when the vfio-ap device fd is opened.
+	 * Since the implicit pre-open state is RUNNING, a request to set
+	 * RUNNING is a no-op. Any other state transition is invalid before
+	 * open_device.
+	 */
+	if (!mig_data) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		if (new_state == VFIO_DEVICE_STATE_RUNNING)
+			return NULL;
+		return ERR_PTR(-ENODEV);
+	}
+
+	dev_dbg(vdev->dev, "%s -> %d\n", __func__, new_state);
+
+	while (mig_data->mig_state != VFIO_DEVICE_STATE_ERROR &&
+	       mig_data->mig_state != new_state) {
+		ret = vfio_mig_get_next_state(vdev, mig_data->mig_state,
+					      new_state, &next_state);
+		if (ret) {
+			filp = ERR_PTR(ret);
+			break;
+		}
+
+		filp = vfio_ap_transition_to_state(matrix_mdev, next_state);
+		if (IS_ERR(filp))
+			break;
+
+		mig_data->mig_state = next_state;
+
+		if (WARN_ON(filp && new_state != next_state)) {
+			fput(filp);
+			filp = ERR_PTR(-EINVAL);
+			break;
+		}
+	}
+
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	return filp;
 }
 
 static int vfio_ap_get_state(struct vfio_device *vdev,
 			     enum vfio_device_mig_state  *current_state)
 {
-	return -EOPNOTSUPP;
+	struct ap_matrix_mdev *matrix_mdev;
+	struct vfio_ap_migration_data *mig_data;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	mig_data =  matrix_mdev->mig_data;
+
+	/*
+	 * The mig_data pointer is set in the vfio_ap_init_migration_data
+	 * function which is called when the vfio-ap device fd is opened.
+	 * If mig_data is NULL, report RUNNING as the implicit pre-open state
+	 * so userspace doesn't need to perform any state transition before the
+	 * device becomes active.
+	 */
+	*current_state = (mig_data) ? mig_data->mig_state :
+				      VFIO_DEVICE_STATE_RUNNING;
+
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	return 0;
 }
 
 static int vfio_ap_get_data_size(struct vfio_device *vdev,
@@ -103,6 +228,7 @@ static const struct vfio_migration_ops vfio_ap_migration_ops = {
  */
 void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev)
 {
+	/* Live guest migration is not supported for SE guests */
 	if (ap_is_se_guest())
 		return;
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (4 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 05/15] s390-vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 17:50   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 07/15] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

The transition of the guest migration state from VFIO_DEVICE_STATE_STOP to
VFIO_DEVICE_STATE_STOP_COPY begins the process of saving the
vfio device state. The vfio device state is comprised of the guest's
AP configuration which specifies the adapters, domains and control
domains to which the guest will be given access.

To begin the process of saving the vfio device state, the VFIO AP device
driver opens a file stream that will be used to read the source guest's
AP configuration.

Note:
This commit introduces the file operations structure for migration state
files with a stub implementation of the read operation. The complete
implementation is provided in the subsequent commit.

This incremental approach allows for easier review of the migration
infrastructure setup separate from the actual implementation logic.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 78 ++++++++++++++++++++++++-
 1 file changed, 76 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 17cabd7541e7..e566250a88ea 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -4,6 +4,7 @@
  *
  * Copyright IBM Corp. 2025
  */
+#include <linux/anon_inodes.h>
 #include <linux/file.h>
 #include "vfio_ap_private.h"
 
@@ -72,6 +73,67 @@ struct vfio_ap_config {
 	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
 };
 
+static void
+vfio_ap_release_stop_copy_file(struct vfio_ap_migration_data *mig_data)
+{
+	kfree(mig_data->stop_copy_mig_file.ap_config);
+	mig_data->stop_copy_mig_file.ap_config = NULL;
+	mig_data->stop_copy_mig_file.config_sz = 0;
+	mig_data->stop_copy_mig_file.filp = NULL;
+}
+
+static ssize_t
+vfio_ap_stop_copy_read(struct file *, char __user *, size_t, loff_t *)
+{
+	/* TODO */
+	return -EOPNOTSUPP;
+}
+
+static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+	int ret = 0;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	matrix_mdev = filp->private_data;
+	if (!matrix_mdev || !matrix_mdev->mig_data) {
+		ret = -ENODEV;
+		goto done;
+	}
+
+	if (filp == matrix_mdev->mig_data->stop_copy_mig_file.filp)
+		vfio_ap_release_stop_copy_file(matrix_mdev->mig_data);
+	else
+		ret = -ENOENT;
+
+done:
+	mutex_unlock(&matrix_dev->mdevs_lock);
+	return ret;
+}
+
+static const struct file_operations vfio_ap_stop_copy_fops = {
+	.owner = THIS_MODULE,
+	.read = vfio_ap_stop_copy_read,
+	.compat_ioctl = compat_ptr_ioctl,
+	.release = vfio_ap_release_mig_file,
+};
+
+static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,
+					     const struct file_operations *fops,
+					     int flags)
+{
+	struct file *filp;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	filp = anon_inode_getfile("vfio_ap_mig_file", fops, matrix_mdev, flags);
+	if (!IS_ERR(filp))
+		stream_open(filp->f_inode, filp);
+
+	return filp;
+}
+
 static struct file *
 vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 			    enum vfio_device_mig_state new_state)
@@ -85,10 +147,22 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 	dev_dbg(matrix_mdev->vdev.dev, "%s: %d -> %d\n", __func__, cur_state,
 		new_state);
 
+	/*
+	 * Begins the process of saving the vfio device state by creating and
+	 * returning a streaming data_fd to be used to read out the internal
+	 * state of the vfio-ap device on the source host.
+	 */
 	if (cur_state == VFIO_DEVICE_STATE_STOP &&
 	    new_state == VFIO_DEVICE_STATE_STOP_COPY) {
-		/* TODO */
-		return ERR_PTR(-EOPNOTSUPP);
+		struct file *filp = vfio_ap_open_file_stream(matrix_mdev,
+							     &vfio_ap_stop_copy_fops,
+							     O_RDONLY);
+		if (IS_ERR(filp))
+			return ERR_CAST(filp);
+
+		mig_data->stop_copy_mig_file.filp = filp;
+
+		return filp;
 	}
 
 	if (cur_state == VFIO_DEVICE_STATE_STOP &&
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 07/15] s390/vfio-ap: File ops called to save the vfio device migration state
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (5 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:04   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Implements the read callback function that was added to the
file_operations structure for the file created to save the state of the
vfio-ap device when the migration state transitioned from STOP to
to the STOP_COPY state.

This function copies the guest's AP configuration information to
userspace. The information copied is comprised of the APQN of each queue
device passed through to the guest along with its hardware information.
This state data will be transferred to the vfio_ap device driver on the
destination host when the state is transitioned to RESUMING.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 278 +++++++++++++++++++++++-
 1 file changed, 271 insertions(+), 7 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index e566250a88ea..0d949ec205de 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -82,13 +82,6 @@ vfio_ap_release_stop_copy_file(struct vfio_ap_migration_data *mig_data)
 	mig_data->stop_copy_mig_file.filp = NULL;
 }
 
-static ssize_t
-vfio_ap_stop_copy_read(struct file *, char __user *, size_t, loff_t *)
-{
-	/* TODO */
-	return -EOPNOTSUPP;
-}
-
 static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 {
 	struct ap_matrix_mdev *matrix_mdev;
@@ -112,6 +105,277 @@ static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 	return ret;
 }
 
+/**
+ * validate_stop_copy_read_parms: Validate the input parameters to the
+ *                                vfio_ap_stop_copy_read function
+ *
+ * @matrix_mdev: The object device containing the state to be read
+ * @filp: Pointer to the file stream used to read the vfio-ap device state
+ * @pos:  The file offset from which to start reading data
+ * @len:  The length of the data to be read
+ *
+ * Verify the following:
+ * - @filp private data is an ap_matrix_mdev instance
+ * - @filp is the instance opened when state transitioned from STOP to STOP_COPY
+ * - @pos + @len does not cause integer overflow
+ *
+ * Returns: 0 if the parameters pass validation; otherwise returns an error
+ */
+static int validate_stop_copy_read_parms(struct file *filp, loff_t *pos,
+					 size_t len)
+{
+	struct vfio_ap_migration_data *mig_data;
+	struct ap_matrix_mdev *matrix_mdev;
+	loff_t total_len;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (check_add_overflow((loff_t)len, *pos, &total_len))
+		return -EIO;
+
+	matrix_mdev = filp->private_data;
+
+	if (!matrix_mdev || !matrix_mdev->mig_data)
+		return -ENODEV;
+
+	mig_data = matrix_mdev->mig_data;
+
+	if (mig_data->stop_copy_mig_file.filp != filp)
+		return -EINVAL;
+
+	return 0;
+}
+
+static size_t vfio_ap_config_size(struct ap_matrix_mdev *matrix_mdev,
+				  int *num_queues)
+{
+	size_t qinfo_size;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	*num_queues = vfio_ap_mdev_get_num_queues(&matrix_mdev->shadow_apcb);
+	qinfo_size = *num_queues * sizeof(struct vfio_ap_queue_info);
+
+	return qinfo_size + sizeof(struct vfio_ap_config);
+}
+
+static int get_hardware_info_for_queue(const char *mdev_name,
+				       struct ap_tapq_hwinfo *hwinfo,
+				       unsigned long apqn)
+{
+	struct ap_queue_status status;
+
+	status = ap_tapq(apqn, hwinfo);
+
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+	case AP_RESPONSE_RESET_IN_PROGRESS:
+	case AP_RESPONSE_DECONFIGURED:
+	case AP_RESPONSE_CHECKSTOPPED:
+	case AP_RESPONSE_BUSY:
+		/* For all these RCs the tapq info should be available */
+		return 0;
+	case AP_RESPONSE_Q_NOT_AVAIL:
+		pr_err("vfio_ap_mdev %s: Failed to get hwinfo for queue %02lx.%04lx: TAPQ rc=%d",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
+		       status.response_code);
+		return -ENODEV;
+	default:
+		/*
+		 * Without a pending async error, the tapq info should be
+		 * available
+		 */
+		if (status.async)
+			return 0;
+
+		pr_err("vfio_ap_mdev %s:Failed to get hwinfo for queue %02lx.%04lx: TAPQ rc=%d",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
+		       status.response_code);
+		return -EIO;
+	}
+
+	return -EINVAL;
+}
+
+static int vfio_ap_store_queue_info(const char *mdev_name,
+				    struct vfio_ap_config *ap_config)
+{
+	struct ap_tapq_hwinfo source_hwinfo;
+	unsigned long num_queues;
+	int ret;
+
+	/*
+	 * ap_tapq() is a hardware instruction that may take time to complete.
+	 * It must be called without mdevs_lock held to avoid blocking other
+	 * mdevs. The apqn list was already snapshotted into ap_config->qinfo[]
+	 * by the caller under the lock.
+	 */
+	for (num_queues = 0; num_queues < ap_config->num_queues; num_queues++) {
+		ret = get_hardware_info_for_queue(mdev_name, &source_hwinfo,
+						  ap_config->qinfo[num_queues].apqn);
+		if (ret)
+			return ret;
+
+		ap_config->qinfo[num_queues].data = source_hwinfo.value;
+	}
+
+	return 0;
+}
+
+static int
+vfio_ap_get_config(struct ap_matrix_mdev *matrix_mdev)
+{
+	unsigned long *apm, *aqm, apid, apqi, num_queues;
+	struct vfio_ap_config *ap_configuration;
+	const char *mdev_name;
+	size_t ap_config_size;
+	int ret;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	ap_config_size = vfio_ap_config_size(matrix_mdev, (int *)&num_queues);
+
+	ap_configuration = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);
+	if (!ap_configuration)
+		return -ENOMEM;
+
+	/*
+	 * Snapshot the APQN list from shadow_apcb under the lock so that
+	 * ap_tapq() calls in vfio_ap_store_queue_info() can happen without it.
+	 */
+	apm = matrix_mdev->shadow_apcb.apm;
+	aqm = matrix_mdev->shadow_apcb.aqm;
+	num_queues = 0;
+	for_each_set_bit_inv(apid, apm, AP_DEVICES) {
+		for_each_set_bit_inv(apqi, aqm, AP_DOMAINS) {
+			ap_configuration->qinfo[num_queues].apqn =
+				AP_MKQID(apid, apqi);
+			num_queues += 1;
+		}
+	}
+	ap_configuration->num_queues = num_queues;
+	memcpy(ap_configuration->adm, matrix_mdev->shadow_apcb.adm,
+	       sizeof(ap_configuration->adm));
+	mdev_name = dev_name(matrix_mdev->vdev.dev);
+
+	/**
+	 * Release the global mdevs_lock which guards access to all mdevs.
+	 * Storing the queue information could take a while if there are a
+	 * large number of queues.
+	 */
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	ret = vfio_ap_store_queue_info(mdev_name, ap_configuration);
+	if (ret) {
+		kfree(ap_configuration);
+		mutex_lock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	/* Retake the mdevs lock so we can safely make updates to the mdev */
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	/*
+	 * Verify mig_data is still valid - mdevs_lock was dropped, so the
+	 * device could have been closed concurrently.
+	 */
+	if (!matrix_mdev->mig_data) {
+		kfree(ap_configuration);
+		return -ENODEV;
+	}
+
+	matrix_mdev->mig_data->stop_copy_mig_file.ap_config = ap_configuration;
+	matrix_mdev->mig_data->stop_copy_mig_file.config_sz = ap_config_size;
+
+	return 0;
+}
+
+static ssize_t vfio_ap_stop_copy_read(struct file *filp, char __user *buf,
+				      size_t len, loff_t *pos)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+	struct vfio_ap_config *ap_config;
+	ssize_t ret = 0;
+	size_t ap_config_size;
+
+	/*
+	 * When userspace calls read() with an explicit offset (pread), pos is
+	 * non-NULL and the function rejects it with -ESPIPE (illegal seek). For
+	 * normal read() calls, pos is NULL, so we'll use the file's internal
+	 * position filp->f_pos
+	 */
+	if (pos)
+		return -ESPIPE;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	pos = &filp->f_pos;
+
+	ret = validate_stop_copy_read_parms(filp, pos, len);
+	if (ret) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	matrix_mdev = filp->private_data;
+	if (!matrix_mdev->mig_data->stop_copy_mig_file.ap_config) {
+		ret = vfio_ap_get_config(matrix_mdev);
+		if (ret) {
+			mutex_unlock(&matrix_dev->mdevs_lock);
+			return ret;
+		}
+	}
+
+	ap_config_size = matrix_mdev->mig_data->stop_copy_mig_file.config_sz;
+
+	/*
+	 * If the position exceeds the size of the AP configuration data,
+	 * then indicate EOF; otherwise calculate the length of the data to
+	 * read such that a buffer overrun is prevented.
+	 */
+	if (*pos >= ap_config_size)
+		len = 0;
+	else
+		len = min_t(size_t, ap_config_size - *pos, len);
+
+	/* If we've reached an EOF condition, let the caller know */
+	if (len == 0) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return 0;
+	}
+
+	/*
+	 * Allocate ap_config and copy the content of the object caching the
+	 * AP configuration data between calls to it so we can give up the
+	 * global mdevs_lock while copying the data to userspace.
+	 */
+	ap_config = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);
+	if (!ap_config) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return -ENOMEM;
+	}
+
+	memcpy(ap_config, matrix_mdev->mig_data->stop_copy_mig_file.ap_config,
+	       ap_config_size);
+
+	/*
+	 * Give up the global mdevs_lock while copying data to the user; it
+	 * might be a long running operation and we don't want to prevent access
+	 * to other mdevs for an inordinate amount of time.
+	 */
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	if (copy_to_user(buf, (char *)ap_config + *pos, len)) {
+		kfree(ap_config);
+		return -EFAULT;
+	}
+
+	kfree(ap_config);
+	*pos += len;
+
+	return len;
+}
+
 static const struct file_operations vfio_ap_stop_copy_fops = {
 	.owner = THIS_MODULE,
 	.read = vfio_ap_stop_copy_read,
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (6 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 07/15] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:06   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 09/15] s390/vfio-ap: Add method to set a new guest AP configuration Anthony Krowiak
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Transitioning from VFIO_DEVICE_STATE_STOP to VFIO_DEVICE_STATE_RESUMING
starts a process of restoring the device state of the vfio device on the
target system. To prepare for restoring the device state, open a file
stream to receive the guest's AP configuration saved when the device state
on the source system transitioned from VFIO_DEVICE_STATE_STOP to
VFIO_DEVICE_STATE_STOP_COPY.

Note:
This commit introduces the file operations structure for migration state
files with a stub implementation of the write operation. The complete
implementation is provided in the subsequent commit.

This incremental approach allows for easier review of the migration
infrastructure setup separate from the actual implementation logic.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 51 ++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 0d949ec205de..4a489a7a05ff 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -82,6 +82,15 @@ vfio_ap_release_stop_copy_file(struct vfio_ap_migration_data *mig_data)
 	mig_data->stop_copy_mig_file.filp = NULL;
 }
 
+static void
+vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_data)
+{
+	kfree(mig_data->resuming_mig_file.ap_config);
+	mig_data->resuming_mig_file.ap_config = NULL;
+	mig_data->resuming_mig_file.config_sz = 0;
+	mig_data->resuming_mig_file.filp = NULL;
+}
+
 static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 {
 	struct ap_matrix_mdev *matrix_mdev;
@@ -97,6 +106,8 @@ static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 
 	if (filp == matrix_mdev->mig_data->stop_copy_mig_file.filp)
 		vfio_ap_release_stop_copy_file(matrix_mdev->mig_data);
+	else if (filp == matrix_mdev->mig_data->resuming_mig_file.filp)
+		vfio_ap_release_resuming_file(matrix_mdev->mig_data);
 	else
 		ret = -ENOENT;
 
@@ -398,6 +409,32 @@ static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,
 	return filp;
 }
 
+static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,
+				      size_t len, loff_t *pos)
+{
+	/* TODO */
+	return -EOPNOTSUPP;
+}
+
+static const struct file_operations vfio_ap_resume_fops = {
+	.owner = THIS_MODULE,
+	.write = vfio_ap_resuming_write,
+	.release = vfio_ap_release_mig_file,
+};
+
+static struct file *vfio_ap_resuming_init(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct vfio_ap_migration_data *mig_data;
+	struct file *filp;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	mig_data = matrix_mdev->mig_data;
+	filp = vfio_ap_open_file_stream(matrix_mdev, &vfio_ap_resume_fops, O_WRONLY);
+
+	return filp;
+}
+
 static struct file *
 vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 			    enum vfio_device_mig_state new_state)
@@ -429,10 +466,20 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 		return filp;
 	}
 
+	/*
+	 * Begins the process of restoring the vfio device state by creating and
+	 * returning a streaming data_fd to be used to read in the internal
+	 * state of the vfio-ap device on the destination host.
+	 */
 	if (cur_state == VFIO_DEVICE_STATE_STOP &&
 	    new_state == VFIO_DEVICE_STATE_RESUMING) {
-		/* TODO */
-		return ERR_PTR(-EOPNOTSUPP);
+		struct file *filp = vfio_ap_resuming_init(matrix_mdev);
+
+		if (IS_ERR(filp))
+			return ERR_CAST(filp);
+
+		mig_data->resuming_mig_file.filp = filp;
+		return filp;
 	}
 
 	if ((cur_state == VFIO_DEVICE_STATE_RESUMING &&
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 09/15] s390/vfio-ap: Add method to set a new guest AP configuration
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (7 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:10   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 10/15] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Adds a new vfio_ap_set_new_config function to set a guest's AP
configuration. This is needed in order to set the state of the mdev when
it is migrated from a remote host system during the RESUMING phase.

Key changes:
* Refactored code from the ap_config_store function - handles changes to
  the sysfs ap_config attribute - into a new, non-static function which
  is callable from the ap_config_store function as well as the live guest
  migration code.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_ops.c     | 227 ++++++++++++++------------
 drivers/s390/crypto/vfio_ap_private.h |  61 +++++++
 2 files changed, 188 insertions(+), 100 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 678bdd4c82e9..454bb9fe1971 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -81,53 +81,6 @@ static inline void release_update_locks_for_kvm(struct kvm *kvm)
 	mutex_unlock(&matrix_dev->guests_lock);
 }
 
-/**
- * get_update_locks_for_mdev: Acquire the locks required to dynamically update a
- *			      KVM guest's APCB in the proper order.
- *
- * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
- *		 configuration data to use to update a KVM guest's APCB.
- *
- * The proper locking order is:
- * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM
- *			       guest's APCB.
- * 2. matrix_mdev->kvm->lock:  required to update a guest's APCB
- * 3. matrix_dev->mdevs_lock:  required to access data stored in a matrix_mdev
- *
- * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
- *	 lock will not be taken.
- */
-static inline void get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
-{
-	mutex_lock(&matrix_dev->guests_lock);
-	if (matrix_mdev && matrix_mdev->kvm)
-		mutex_lock(&matrix_mdev->kvm->lock);
-	mutex_lock(&matrix_dev->mdevs_lock);
-}
-
-/**
- * release_update_locks_for_mdev: Release the locks used to dynamically update a
- *				  KVM guest's APCB in the proper order.
- *
- * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
- *		 configuration data to use to update a KVM guest's APCB.
- *
- * The proper unlocking order is:
- * 1. matrix_dev->mdevs_lock
- * 2. matrix_mdev->kvm->lock
- * 3. matrix_dev->guests_lock
- *
- * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
- *	 lock will not be released.
- */
-static inline void release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
-{
-	mutex_unlock(&matrix_dev->mdevs_lock);
-	if (matrix_mdev && matrix_mdev->kvm)
-		mutex_unlock(&matrix_mdev->kvm->lock);
-	mutex_unlock(&matrix_dev->guests_lock);
-}
-
 /**
  * get_update_locks_by_apqn: Find the mdev to which an APQN is assigned and
  *			     acquire the locks required to update the APCB of
@@ -642,8 +595,7 @@ static int handle_pqap(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
-static void vfio_ap_matrix_init(struct ap_config_info *info,
-				struct ap_matrix *matrix)
+void vfio_ap_matrix_init(struct ap_config_info *info, struct ap_matrix *matrix)
 {
 	matrix->apm_max = info->apxa ? info->na : 63;
 	matrix->aqm_max = info->apxa ? info->nd : 15;
@@ -875,7 +827,7 @@ static void vfio_ap_mdev_unlink_fr_queues(struct ap_matrix_mdev *matrix_mdev)
 			q = vfio_ap_mdev_get_queue(matrix_mdev,
 						   AP_MKQID(apid, apqi));
 			if (q)
-				q->matrix_mdev = NULL;
+				vfio_ap_mdev_link_queue(matrix_mdev, q);
 		}
 	}
 }
@@ -1014,19 +966,20 @@ static void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev,
 	unsigned long apqi;
 
 	for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, AP_DOMAINS)
-		vfio_ap_mdev_link_apqn(matrix_mdev,
-				       AP_MKQID(apid, apqi));
+		vfio_ap_mdev_link_apqn(matrix_mdev, AP_MKQID(apid, apqi));
 }
 
-static void collect_queues_to_reset(struct ap_matrix_mdev *matrix_mdev,
-				    unsigned long apid,
-				    struct list_head *qlist)
+static void collect_queues_by_apid(struct ap_matrix_mdev *matrix_mdev,
+				   unsigned long apid,
+				   struct list_head *qlist)
 {
 	struct vfio_ap_queue *q;
 	unsigned long  apqi;
 
 	for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm, AP_DOMAINS) {
-		q = vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi));
+		q = matrix_mdev ?
+				vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi)) :
+				vfio_ap_find_queue(AP_MKQID(apid, apqi));
 		if (q)
 			list_add_tail(&q->reset_qnode, qlist);
 	}
@@ -1038,7 +991,7 @@ static void reset_queues_for_apid(struct ap_matrix_mdev *matrix_mdev,
 	struct list_head qlist;
 
 	INIT_LIST_HEAD(&qlist);
-	collect_queues_to_reset(matrix_mdev, apid, &qlist);
+	collect_queues_by_apid(matrix_mdev, apid, &qlist);
 	vfio_ap_mdev_reset_qlist(&qlist);
 }
 
@@ -1054,7 +1007,7 @@ static int reset_queues_for_apids(struct ap_matrix_mdev *matrix_mdev,
 	INIT_LIST_HEAD(&qlist);
 
 	for_each_set_bit_inv(apid, apm_reset, AP_DEVICES)
-		collect_queues_to_reset(matrix_mdev, apid, &qlist);
+		collect_queues_by_apid(matrix_mdev, apid, &qlist);
 
 	return vfio_ap_mdev_reset_qlist(&qlist);
 }
@@ -1725,54 +1678,100 @@ static void ap_matrix_copy(struct ap_matrix *dst, struct ap_matrix *src)
 	bitmap_copy(dst->adm, src->adm, AP_DOMAINS);
 }
 
-static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr,
-			       const char *buf, size_t count)
+static void get_removed_matrixes(struct ap_matrix *m_removed,
+				 struct ap_matrix *m_old,
+				 struct ap_matrix *m_new)
 {
-	struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
-	struct ap_matrix m_new, m_old, m_added, m_removed;
+	bitmap_andnot(m_removed->apm, m_old->apm, m_new->apm, AP_DEVICES);
+	bitmap_andnot(m_removed->aqm, m_old->aqm, m_new->aqm, AP_DOMAINS);
+	bitmap_andnot(m_removed->adm, m_old->adm, m_new->adm, AP_DOMAINS);
+}
+
+static void get_added_matrixes(struct ap_matrix *m_added,
+			       struct ap_matrix *m_old,
+			       struct ap_matrix *m_new)
+{
+	bitmap_andnot(m_added->apm, m_new->apm, m_old->apm, AP_DEVICES);
+	bitmap_andnot(m_added->aqm, m_new->aqm, m_old->aqm, AP_DOMAINS);
+	bitmap_andnot(m_added->adm, m_new->adm, m_old->adm, AP_DOMAINS);
+}
+
+static int validate_new_state(struct ap_matrix_mdev *matrix_mdev)
+{
+	int rc;
+
+	/* Ensure new state is valid, else undo new state */
+	rc = vfio_ap_mdev_validate_masks(matrix_mdev);
+	if (rc)
+		return rc;
+
+	rc = ap_matrix_overflow_check(matrix_mdev);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static void link_new_queues(struct ap_matrix_mdev *matrix_mdev,
+			    struct ap_matrix *m_added)
+{
+	unsigned long apid, apqi;
+
+	for_each_set_bit_inv(apid, m_added->apm, AP_DEVICES)
+		vfio_ap_mdev_link_adapter(matrix_mdev, apid);
+
+	for_each_set_bit_inv(apqi, m_added->aqm, AP_DOMAINS)
+		vfio_ap_mdev_link_domain(matrix_mdev, apqi);
+}
+
+/**
+ * vfio_ap_set_new_guest_config:
+ *
+ * Set a new AP configuration for a guest.
+ *
+ * @matrix_mdev: Object used to maintain the AP configuration for a guest
+ * @m_new:		 Object used to set the new AP configuration
+ *
+ * Returns: zero (0) if the new AP configuration is successfully set; otherwise,
+ *	    returns an error:
+ *
+ *	    ~ EADDRNOTAVAIL One or more APQNs are reserved for host use
+ *	    ~ EADDRINUSE    One or more APQNs are assigned to another mdev
+ *	    ~ ENODEV	    An adapter, domain or control domain in the new
+ *			    AP configuration exceeds the max architected value
+ */
+int vfio_ap_set_new_guest_config(struct ap_matrix_mdev *matrix_mdev,
+				 struct ap_matrix *m_new)
+{
+	struct ap_matrix m_old, m_old_shadow, m_added, m_removed;
 	DECLARE_BITMAP(apm_filtered, AP_DEVICES);
-	unsigned long newbit;
-	char *newbuf, *rest;
-	int rc = count;
 	bool do_update;
+	int rc;
 
-	newbuf = kstrndup(buf, AP_CONFIG_STRLEN, GFP_KERNEL);
-	if (!newbuf)
-		return -ENOMEM;
-	rest = newbuf;
+	lockdep_assert_held(&ap_attr_mutex);
+	assert_has_update_locks_for_mdev(matrix_mdev);
 
-	mutex_lock(&ap_attr_mutex);
-	get_update_locks_for_mdev(matrix_mdev);
-
-	/* Save old state */
+	/* Save the old state */
 	ap_matrix_copy(&m_old, &matrix_mdev->matrix);
-	if (parse_bitmap(&rest, m_new.apm, AP_DEVICES) ||
-	    parse_bitmap(&rest, m_new.aqm, AP_DOMAINS) ||
-	    parse_bitmap(&rest, m_new.adm, AP_DOMAINS)) {
-		rc = -EINVAL;
-		goto out;
-	}
+	ap_matrix_copy(&m_old_shadow, &matrix_mdev->shadow_apcb);
 
-	bitmap_andnot(m_removed.apm, m_old.apm, m_new.apm, AP_DEVICES);
-	bitmap_andnot(m_removed.aqm, m_old.aqm, m_new.aqm, AP_DOMAINS);
-	bitmap_andnot(m_added.apm, m_new.apm, m_old.apm, AP_DEVICES);
-	bitmap_andnot(m_added.aqm, m_new.aqm, m_old.aqm, AP_DOMAINS);
+	/*
+	 * Get the adapters, domains and control domains added and/or removed
+	 * from the existing configuration
+	 */
+	get_removed_matrixes(&m_removed, &m_old, m_new);
+	get_added_matrixes(&m_added, &m_old, m_new);
 
 	/* Need new bitmaps in matrix_mdev for validation */
-	ap_matrix_copy(&matrix_mdev->matrix, &m_new);
+	ap_matrix_copy(&matrix_mdev->matrix, m_new);
 
 	/* Ensure new state is valid, else undo new state */
-	rc = vfio_ap_mdev_validate_masks(matrix_mdev);
-	if (rc) {
-		ap_matrix_copy(&matrix_mdev->matrix, &m_old);
-		goto out;
-	}
-	rc = ap_matrix_overflow_check(matrix_mdev);
+	rc = validate_new_state(matrix_mdev);
 	if (rc) {
 		ap_matrix_copy(&matrix_mdev->matrix, &m_old);
-		goto out;
+		ap_matrix_copy(&matrix_mdev->shadow_apcb, &m_old_shadow);
+		return rc;
 	}
-	rc = count;
 
 	/* Need old bitmaps in matrix_mdev for unplug/unlink */
 	ap_matrix_copy(&matrix_mdev->matrix, &m_old);
@@ -1782,14 +1781,10 @@ static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr
 	vfio_ap_mdev_hot_unplug_domains(matrix_mdev, m_removed.aqm);
 
 	/* Need new bitmaps in matrix_mdev for linking new adapters/domains */
-	ap_matrix_copy(&matrix_mdev->matrix, &m_new);
-
-	/* Link newly added adapters */
-	for_each_set_bit_inv(newbit, m_added.apm, AP_DEVICES)
-		vfio_ap_mdev_link_adapter(matrix_mdev, newbit);
+	ap_matrix_copy(&matrix_mdev->matrix, m_new);
 
-	for_each_set_bit_inv(newbit, m_added.aqm, AP_DOMAINS)
-		vfio_ap_mdev_link_domain(matrix_mdev, newbit);
+	/* Link queues associated with the newly added adapters and domains */
+	link_new_queues(matrix_mdev, &m_added);
 
 	/* filter resources not bound to vfio-ap */
 	do_update = vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered);
@@ -1800,7 +1795,39 @@ static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr
 		vfio_ap_mdev_update_guest_apcb(matrix_mdev);
 		reset_queues_for_apids(matrix_mdev, apm_filtered);
 	}
-out:
+
+	return 0;
+}
+
+static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
+	struct ap_matrix m_new;
+	char *newbuf, *rest;
+	ssize_t rc;
+
+	newbuf = kstrndup(buf, AP_CONFIG_STRLEN, GFP_KERNEL);
+	if (!newbuf)
+		return -ENOMEM;
+	rest = newbuf;
+
+	mutex_lock(&ap_attr_mutex);
+	get_update_locks_for_mdev(matrix_mdev);
+
+	if (parse_bitmap(&rest, m_new.apm, AP_DEVICES) ||
+	    parse_bitmap(&rest, m_new.aqm, AP_DOMAINS) ||
+	    parse_bitmap(&rest, m_new.adm, AP_DOMAINS)) {
+		kfree(newbuf);
+		release_update_locks_for_mdev(matrix_mdev);
+		mutex_unlock(&ap_attr_mutex);
+		return -EINVAL;
+	}
+
+	rc = vfio_ap_set_new_guest_config(matrix_mdev, &m_new);
+	if (!rc)
+		rc = count;
+
 	release_update_locks_for_mdev(matrix_mdev);
 	mutex_unlock(&ap_attr_mutex);
 	kfree(newbuf);
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index 1fbdfcce5a11..150dfce8a674 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -157,6 +157,62 @@ struct vfio_ap_queue {
 	struct work_struct reset_work;
 };
 
+/**
+ * get_update_locks_for_mdev: Acquire the locks required to dynamically update a
+ *			      KVM guest's APCB in the proper order.
+ *
+ * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
+ *		 configuration data to use to update a KVM guest's APCB.
+ *
+ * The proper locking order is:
+ * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM
+ *			       guest's APCB.
+ * 2. matrix_mdev->kvm->lock:  required to update a guest's APCB
+ * 3. matrix_dev->mdevs_lock:  required to access data stored in a matrix_mdev
+ *
+ * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
+ *	 lock will not be taken.
+ */
+static inline void get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
+{
+	mutex_lock(&matrix_dev->guests_lock);
+	if (matrix_mdev && matrix_mdev->kvm)
+		mutex_lock(&matrix_mdev->kvm->lock);
+	mutex_lock(&matrix_dev->mdevs_lock);
+}
+
+/**
+ * release_update_locks_for_mdev: Release the locks used to dynamically update a
+ *				  KVM guest's APCB in the proper order.
+ *
+ * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
+ *		 configuration data to use to update a KVM guest's APCB.
+ *
+ * The proper unlocking order is:
+ * 1. matrix_dev->mdevs_lock
+ * 2. matrix_mdev->kvm->lock
+ * 3. matrix_dev->guests_lock
+ *
+ * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
+ *	 lock will not be released.
+ */
+static inline void release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
+{
+	mutex_unlock(&matrix_dev->mdevs_lock);
+	if (matrix_mdev && matrix_mdev->kvm)
+		mutex_unlock(&matrix_mdev->kvm->lock);
+	mutex_unlock(&matrix_dev->guests_lock);
+}
+
+static inline void
+assert_has_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
+{
+	lockdep_assert_held(&matrix_dev->guests_lock);
+	if (matrix_mdev && matrix_mdev->kvm)
+		lockdep_assert_held(&matrix_mdev->kvm->lock);
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+}
+
 int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix);
 
 int vfio_ap_mdev_register(void);
@@ -172,9 +228,14 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *new_config_info,
 void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
 			      struct ap_config_info *old_config_info);
 
+void vfio_ap_matrix_init(struct ap_config_info *info, struct ap_matrix *matrix);
+
 void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev);
 int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev);
 void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev);
 void vfio_ap_reset_migration_state(struct ap_matrix_mdev *matrix_mdev);
 
+int vfio_ap_set_new_guest_config(struct ap_matrix_mdev *matrix_mdev,
+				 struct ap_matrix *m_new);
+
 #endif /* _VFIO_AP_PRIVATE_H_ */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 10/15] s390/vfio-ap: File ops called to resume the vfio device migration
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (8 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 09/15] s390/vfio-ap: Add method to set a new guest AP configuration Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:17   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 11/15] s390/vfio-ap: Transition device migration state to STOP Anthony Krowiak
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Implements the 'write' callback function that was added to the
'file_operations' structure for the file stream created to restore the
state of the vfio-ap device on the destination system when the migration
state transitioned from STOP to RESUMING

The write callback retrieves the vfio device migration state saved to the
file stream created when the vfio device state was transitioned from
STOP to STOP_COPY. The saved state contains the source guest's AP
configuration information. This data is copied from the userspace buffer
passed to the 'write' callback and stored in the vfio_ap_config structure
used to set the state of the vfio-ap device on the destination host. If the
source guest's AP configuration is compatible with the AP configuration on
the destination host, it will be hot plugged into the destination guest.

In order for the source guest's and destination host's AP configurations
to be considered compatible:

* Each APQN in the source guest's AP configuration must also be in the
  destination host's AP configuration

* Each matching APQN in the destination host's AP configuration must be
  bound to the vfio_ap device driver

* Each matching APQN in the destination host's AP configuration must
  reference a queue device with compatible hardware:

  - The source and destination queues must have the same facilities
    installed:
    ~ APSC facility
    ~ APQKM facility
    ~ AP4KC facility

  - The source and destination queues must have the same mode:
    ~ Coprocessor-mode
    ~ Accelerator-mode
    ~ XCP-mode

  - The source and destination queues must have the same APXA facility
    setting
    ~ If the APXA facility is installed on source queue, it must also
      be installed on the destination queue and vice versa

  - The source and destination queues must have a compatible
    classification setting. If the source queue has full native card
    function, then the destination queue must also have full native
    card function. If the source queue has stateless functions, then
    the destination queue can have stateless functions or full native card
    function because the latter includes the stateless functions.

  - The binding and associated state for both the source and destination
    queues must indicate that the queue is usable for all messages
    (i.e., BS bits equal to 00).

  - The AP type of the destination queue must be the same as or newer than
    the source queue (backward compatibility)

Note: The get_hardware_info_for_queue function that was created in
      a previous patch was modified to take a mediated device name rather
      than an ap_matrix_mdev object because that is what is needed for
      this patch so the function can be executed without holding the
      matrix_dev->mdevs_lock.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 830 +++++++++++++++++++++++-
 1 file changed, 826 insertions(+), 4 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 4a489a7a05ff..7a9a8f63a76d 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -8,6 +8,54 @@
 #include <linux/file.h>
 #include "vfio_ap_private.h"
 
+/*
+ * Masks the fields of the queue information returned from the PQAP(TAPQ)
+ * command. In order to migrate a guest, it's AP configuration must be
+ * compatible with AP configuration assigned to the target guest's mdev.
+ * This mask is used to verify that the queue information for each source and
+ * target queue is compatible.
+ *
+ * The following bits must match for the source device and the corresponding
+ * destination device:
+ * -------------------------------------------------------------------------
+ * S bit 0: APSC  facility installed
+ * M bit 1: APQKM facility installed
+ * C bit 2: AP4KC facility installed
+ * Mode bits 3-5:
+ *     D bit 3: CCA-mode facility
+ *     A bit 4: accelerator-mode facility
+ *     X bit 5: XCP-mode facility
+ * N  bit 6: APXA facility installed
+ * SL bit 7: SLCF facility installed
+ *
+ * Either bit 8 or bit 9 will be set. If bit 8 is set for the source device,
+ * then it must also be set for the corresponding destination device:
+ * -------------------------------------------------------------------------
+ * Classification (functional capabilities) bits 8-16
+ *     bit 8: Native card function
+ *     bit 9: Only stateless functions
+ *
+ * The BS bits must be set to 0 for both the source and corresponding
+ * destination device:
+ * -------------------------------------------------------------------------
+ * BS bits 16-17:
+ *
+ * The AP type of the source device must be less than or equal to that of
+ * the corresponding destination device:
+ * -------------------------------------------------------------------------
+ * AP Type bits 32-40:
+ */
+#define QINFO_DATA_MASK		0xffffc000ff000000
+
+/*
+ * Masks the bit that indicates whether full native card function is available
+ * from the 8 bits specifying the functional capabilities of a queue
+ */
+#define CLASSIFICATION_NATIVE_FCN_MASK		0x80
+
+/* The maximum number of queues that can be installed in an s390 system */
+#define MAX_AP_QUEUES				(AP_DEVICES * AP_DOMAINS)
+
 /**
  * vfio_ap_migration_file
  *
@@ -204,8 +252,6 @@ static int get_hardware_info_for_queue(const char *mdev_name,
 		       status.response_code);
 		return -EIO;
 	}
-
-	return -EINVAL;
 }
 
 static int vfio_ap_store_queue_info(const char *mdev_name,
@@ -409,11 +455,787 @@ static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,
 	return filp;
 }
 
+static int validate_resuming_write_parms(struct file *filp,
+					 size_t len, loff_t *pos)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+	loff_t total_len;
+	int ret;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	matrix_mdev = filp->private_data;
+	if (!matrix_mdev || !matrix_mdev->mig_data) {
+		ret = -ENODEV;
+		goto done;
+	}
+
+	if (filp != matrix_mdev->mig_data->resuming_mig_file.filp) {
+		ret = -ENXIO;
+		goto done;
+	}
+
+	if (*pos < 0) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (check_add_overflow((loff_t)len, *pos, &total_len)) {
+		ret = -ERANGE;
+		goto done;
+	}
+
+	/*
+	 * If the ap_config has not yet been allocated and the file position
+	 * indicates this is not the first write, or the ap_config has been allocated
+	 * but the file position indicates this is the first write, then this is an
+	 * error condition.
+	 */
+	if ((!matrix_mdev->mig_data->resuming_mig_file.ap_config && *pos != 0) ||
+	    (matrix_mdev->mig_data->resuming_mig_file.ap_config && *pos == 0)) {
+		ret = -EFAULT;
+
+		goto done;
+	}
+
+	ret = 0;
+
+done:
+	return ret;
+}
+
+static ssize_t calculate_ap_config_size(unsigned int num_queues)
+{
+	size_t qinfo_size;
+
+	if (num_queues > MAX_AP_QUEUES)
+		return -EINVAL;
+
+	qinfo_size = num_queues * sizeof(struct vfio_ap_queue_info);
+	return qinfo_size + sizeof(struct vfio_ap_config);
+}
+
+/**
+ * allocate_ap_config:
+ *
+ * Allocate storage for the source guest's AP configuration data sent from
+ * userspace.
+ *
+ * @ap_config:	The location in which to store the pointer to the storage
+ *		allocated for the AP configuration data.
+ * @buf:	The userspace buffer containing some or all of the source
+ *		guest's AP configuration data
+ * @len:	The number of bytes of data to copy from @buf
+ *
+ * Returns:	The number of bytes of storage allocated for the config data or
+ *		an error:
+ *
+ *
+ *		-EIO: failed to copy data from @buf
+ *		-EINVAL: the number of queues specified exceeds the max allowed
+ *		-ENOMEM: the allocation of storage failed
+ */
+static ssize_t allocate_ap_config(struct vfio_ap_config **ap_config,
+				  const char __user *buf, size_t len)
+{
+	struct vfio_ap_config tmp_ap_config;
+	ssize_t config_size;
+	size_t copy_size;
+
+	/*
+	 * If the length of the data sent exceeds the size of the vfio_ap_config
+	 * structure, then we will copy enough data from userspace to get the
+	 * number of queues which we can use to allocate enough space all of
+	 * the queue information.
+	 */
+	copy_size = min(len, sizeof(tmp_ap_config));
+
+	if (copy_from_user(&tmp_ap_config, buf, copy_size))
+		return -EIO;
+
+	/*
+	 * If the length of data sent includes the number of queues
+	 * in the AP configuration, then calculate its size; otherwise
+	 * set config_size to the length of data sent.
+	 */
+	if (len >= sizeof(struct vfio_ap_config)) {
+		config_size = calculate_ap_config_size(tmp_ap_config.num_queues);
+
+		/* If the calculation returned an error */
+		if (config_size < 0)
+			return config_size;
+	} else {
+		config_size = len;
+	}
+
+	*ap_config = kzalloc(config_size, GFP_KERNEL_ACCOUNT);
+	if (!*ap_config)
+		return -ENOMEM;
+
+	return config_size;
+}
+
+static ssize_t reallocate_ap_config(struct vfio_ap_migration_file *mig_file,
+				    struct vfio_ap_config **new_ap_config,
+				    size_t len)
+{
+	struct vfio_ap_config *cur_ap_config;
+	unsigned long cur_cfg_sz;
+	ssize_t new_cfg_sz;
+
+	cur_ap_config = mig_file->ap_config;
+	cur_cfg_sz = mig_file->config_sz;
+
+	/*
+	 * If the current configuration size is greater than the
+	 * size of a vfio_ap_config structure (i.e., contains the num_queues
+	 * field), then there should already be enough storage allocated
+	 * to store the source guest's AP configuration. Let's verify that the
+	 * amount of storage allocated is what we expect based on the number of
+	 * vfio_ap_queue_info objects that must be stored.
+	 */
+	if (cur_cfg_sz >= sizeof(struct vfio_ap_config)) {
+		new_cfg_sz = calculate_ap_config_size(cur_ap_config->num_queues);
+		if (cur_cfg_sz != new_cfg_sz)
+			return -EINVAL;
+	} else {
+		if (check_add_overflow(cur_cfg_sz, len, &new_cfg_sz))
+			return -ERANGE;
+	}
+
+	*new_ap_config = kzalloc(new_cfg_sz, GFP_KERNEL_ACCOUNT);
+	if (!*new_ap_config)
+		return -ENOMEM;
+
+	memcpy(*new_ap_config, cur_ap_config, cur_cfg_sz);
+
+	return new_cfg_sz;
+}
+
+/**
+ * qdev_is_bound_to_vfio_ap:
+ *
+ * Query to determine whether a queue with the specified APQN is available on
+ * the host system and bound to the vfio_ap device driver.
+ *
+ * @apqn: The APQN of the queue device being queried
+ *
+ * Returns: True if there is a queue device with the specified @apqn installed
+ *	    in the system and is bound to the vfio_ap device driver; otherwise,
+ *	    returns false.
+ */
+static bool qdev_is_bound_to_vfio_ap(unsigned int apqn)
+{
+	struct ap_queue *queue;
+	bool is_bound = true;
+
+	queue = ap_get_qdev(apqn);
+	if (!queue)
+		return false;
+
+	if (queue->ap_dev.device.driver != &matrix_dev->vfio_ap_drv->driver)
+		is_bound = false;
+
+	put_device(&queue->ap_dev.device);
+
+	return is_bound;
+}
+
+/**
+ * queues_available:
+ *
+ * Query whether each queue from the source guest's AP configuration is
+ * available and bound to the vfio_ap device driver; if not, log an error
+ * message.
+ *
+ * @mdev_name:	   The mdev name to use in error messages
+ * @source_config: The object specifying the source guest's AP configuration
+ *
+ * Returns: true if each queue identified in @source_config is available and
+ *	    bound to the vfio_ap device driver; otherwise, returns false.
+ */
+static bool queues_available(const char *mdev_name,
+			     struct vfio_ap_config *source_config)
+{
+	unsigned long apqn;
+	bool ret = true;
+
+	for (int i = 0; i < source_config->num_queues; i++) {
+		apqn = source_config->qinfo[i].apqn;
+
+		/*
+		 * Find the queue device bound to the vfio_ap device driver. If it is
+		 * not found, log an error and continue so users see all problems
+		 * at once, not one-at-a-time through retries of the migration.
+		 */
+		if (!qdev_is_bound_to_vfio_ap(apqn)) {
+			pr_err("vfio_ap_mdev %s: Queue %02lx.%04lx not available to vfio_ap driver on target host\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+			ret = false;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * control_domains_available
+ *
+ * Query whether each control domain specified in the source guest's AP
+ * configuration is installed in the host system.
+ *
+ * @mdev_name:		The name of the mdev to use when logging messages
+ * @source_config:	The object specifying the source guest's AP config
+ *
+ * Returns:	True if each control domain is installed; otherwise, logs an
+ *		error message for each unavailable control domain and returns
+ *		false.
+ */
+static bool control_domains_available(const char *mdev_name,
+				      struct vfio_ap_config *source_config)
+{
+	unsigned long domain_num;
+	bool available = true;
+
+	for_each_set_bit_inv(domain_num, (unsigned long *)source_config->adm,
+			     AP_DOMAINS) {
+		if (!ap_test_config_ctrl_domain(domain_num)) {
+			pr_err("vfio_ap_mdev: %s: Control domain %04lx not available on the destination host",
+			       mdev_name, domain_num);
+			available = false;
+		}
+	}
+
+	return available;
+}
+
+static void report_facilities_compatibility(const char *mdev_name,
+					    unsigned long apqn,
+					    struct ap_tapq_hwinfo *src_hwinfo,
+					    struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->apsc != target_hwinfo->apsc) {
+		if (src_hwinfo->apsc) {
+			pr_err("vfio_ap_mdev %s: APSC facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: APSC facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: APSC facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s APSC facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->mex4k != target_hwinfo->mex4k) {
+		if (src_hwinfo->mex4k) {
+			pr_err("vfio_ap_mdev %s: mex4k facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: mex4k facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: mex4k facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: mex4k facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->crt4k != target_hwinfo->crt4k) {
+		if (src_hwinfo->crt4k) {
+			pr_err("vfio_ap_mdev %s: crt4k facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: crt4k facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: crt4k facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: crt4k facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_mode_compatibility(const char *mdev_name,
+				      unsigned long apqn,
+				      struct ap_tapq_hwinfo *src_hwinfo,
+				      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->cca != target_hwinfo->cca) {
+		if (src_hwinfo->cca) {
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode  facility not installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode  facility installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->accel != target_hwinfo->accel) {
+		if (src_hwinfo->accel) {
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility not installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility not installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->ep11 != target_hwinfo->ep11) {
+		if (src_hwinfo->ep11) {
+			pr_err("vfio_ap_mdev %s: XCP-mode facility installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: XCP-mode facility not installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: XCP-mode facility not installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: XCP-mode facility installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_apxa_compatibility(const char *mdev_name,
+				      unsigned long apqn,
+				      struct ap_tapq_hwinfo *src_hwinfo,
+				      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->apxa != target_hwinfo->apxa) {
+		if (src_hwinfo->apxa) {
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_slcf_compatibility(const char *mdev_name,
+				      unsigned long apqn,
+				      struct ap_tapq_hwinfo *src_hwinfo,
+				      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->slcf != target_hwinfo->slcf) {
+		if (src_hwinfo->slcf) {
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) available in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) not available in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) not available in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) available in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_bs_compatibility(const char *mdev_name,
+				    unsigned long apqn,
+				    struct ap_tapq_hwinfo *src_hwinfo,
+				    struct ap_tapq_hwinfo *target_hwinfo)
+{
+	/*
+	 * The BS field on both the source and destination must be 0, so if one
+	 * of them is not, then report an error.
+	 */
+	if (src_hwinfo->bs || target_hwinfo->bs) {
+		pr_err("vfio_ap_mdev %s: Bind/associate state for source (%01x) and target (%01x) queue %02lx.%04lx must be 0\n",
+		       mdev_name, src_hwinfo->bs, target_hwinfo->bs,
+		       AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+	}
+}
+
+static void report_aptype_compatibility(const char *mdev_name,
+					unsigned long apqn,
+					struct ap_tapq_hwinfo *src_hwinfo,
+					struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->at > target_hwinfo->at) {
+		pr_err("vfio_ap_mdev %s: AP type of source (%02x) not compatible with target (%02x)\n",
+		       mdev_name, src_hwinfo->at, target_hwinfo->at);
+	}
+}
+
+static bool classes_compatible(struct ap_tapq_hwinfo *src_hwinfo,
+			       struct ap_tapq_hwinfo *target_hwinfo)
+{
+	unsigned long src_native, target_native;
+
+	src_native = src_hwinfo->class & CLASSIFICATION_NATIVE_FCN_MASK;
+	target_native = target_hwinfo->class & CLASSIFICATION_NATIVE_FCN_MASK;
+
+	/*
+	 * If the source queue has full native card function and the
+	 * target queue has only stateless functions available, then
+	 * there may be instructions that will not execute on the
+	 * target queue. This shall be reported as an error.
+	 *
+	 * If the source queue has only stateless card functions and the
+	 * target queue has full native card function available, then
+	 * we are okay because the target queue can run all stateless card
+	 * functions.
+	 */
+	return (src_native != target_native) ? !src_native : true;
+}
+
+static void report_class_compatibility(const char *mdev_name,
+				       unsigned long apqn,
+				       struct ap_tapq_hwinfo *src_hwinfo,
+				       struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (!classes_compatible(src_hwinfo, target_hwinfo)) {
+		pr_err("vfio_ap_mdev %s: Full native card function available on source queue %02lx.%04lx\n",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+		pr_err("vfio_ap_mdev %s: Only stateless functions available on target queue %02lx.%04lx\n",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+	}
+}
+
+/*
+ * Log a device error reporting that migration failed due to queue
+ * incompatibilities followed by a device error for each incompatible feature.
+ */
+static void report_qinfo_incompatibilities(const char *mdev_name,
+					   unsigned long apqn,
+					   struct ap_tapq_hwinfo *src_hwinfo,
+					   struct ap_tapq_hwinfo *target_hwinfo)
+{
+	pr_err("vfio_ap_mdev %s: Migration failed: Source and target queue (%02lx.%04lx) not compatible\n",
+	       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+	report_facilities_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_mode_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_apxa_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_slcf_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_aptype_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_bs_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_class_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+}
+
+/**
+ * queue_hardware_info_is_compatible:
+ *
+ * Verify whether the hardware information for a source queue is compatible with
+ * the hardware info for the corresponding queue on this system.
+ *
+ * In order to be compatible, the hardware information for each queue must
+ * meet the following requirements:
+ *
+ * 1. The hardware facilities bits much match
+ * 2. The AP type of the source queue must be the same as or older than that
+ *    of the target queue (target is backwards compatible)
+ * 3. The classification bits must indicate:
+ *    - Both queues have full native card function or both have stateless
+ *      functions available
+ *    - If the classification bits don't match, then the only acceptable
+ *      configuration is stateless functions for the source queue and
+ *      full native function for the target queue
+ * 4. The BS bits for both queues must be 0 (Queue usable for all messages
+ *    supported by the adapter)
+ *
+ * @mdev_name:	The mdev name to use in error messages
+ * @apqn:	The APQN for the queues
+ * @src_hwinfo: The hardware info for the source queue
+ * @target_hwinfo: The hardware info for the corresponding queue on this system
+ *
+ * Returns: true if the hardware info for the two queues is compatible;
+ *          otherwise, returns false.
+ */
+static bool queue_hardware_info_is_compatible(const char *mdev_name,
+					      unsigned long apqn,
+					      struct ap_tapq_hwinfo *src_hwinfo,
+					      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	unsigned long src_bits, target_bits;
+
+	src_bits = src_hwinfo->value & QINFO_DATA_MASK;
+	target_bits = target_hwinfo->value & QINFO_DATA_MASK;
+
+	/* If all bits match the queues are compatible */
+	if (src_bits == target_bits &&
+	    (src_hwinfo->bs == 0 && target_hwinfo->bs == 0))
+		return true;
+
+	if (src_hwinfo->apsc  == target_hwinfo->apsc     &&
+	    src_hwinfo->mex4k == target_hwinfo->mex4k    &&
+	    src_hwinfo->crt4k == target_hwinfo->crt4k    &&
+	    src_hwinfo->cca   == target_hwinfo->cca      &&
+	    src_hwinfo->accel == target_hwinfo->accel    &&
+	    src_hwinfo->ep11  == target_hwinfo->ep11     &&
+	    src_hwinfo->slcf  == target_hwinfo->slcf     &&
+	    src_hwinfo->apxa  == target_hwinfo->apxa     &&
+	    src_hwinfo->at    <= target_hwinfo->at       &&
+	    classes_compatible(src_hwinfo, target_hwinfo) &&
+	    (src_hwinfo->bs == 0 && target_hwinfo->bs == 0))
+		return true;
+
+	report_qinfo_incompatibilities(mdev_name, apqn, src_hwinfo, target_hwinfo);
+
+	return false;
+}
+
+/**
+ * verify_ap_configs_are_compatible:
+ *
+ * Verifies that the queues in the source guest's AP configuration are
+ * compatible with the corresponding queues on this system.
+ *
+ * @mdev_name:	   The mdev name to use in error messages
+ * @source_config: The object specifying the source guest's AP configuration
+ *
+ * Returns: an error indicating either a failure to retrieve a queue's
+ *			hardware information or one or more source queues are not
+ *			compatible with the corresponding queue on this system; otherwise,
+ *			returns zero to indicate compatibility.
+ */
+static int verify_ap_configs_are_compatible(const char *mdev_name,
+					    struct vfio_ap_config *source_config)
+{
+	struct ap_tapq_hwinfo src_hwinfo, dest_hwinfo;
+	unsigned long apqn;
+	int ret = 0, rc;
+
+	for (int i = 0; i < source_config->num_queues; i++) {
+		apqn = source_config->qinfo[i].apqn;
+
+		/*
+		 * If we can't get the hardware info for a particular queue, then let's
+		 * capture the function return code and continue so we can log all
+		 * errors to aid in debugging of migration.
+		 */
+		rc = get_hardware_info_for_queue(mdev_name, &dest_hwinfo, apqn);
+		if (rc) {
+			ret = rc;
+			continue;
+		}
+
+		src_hwinfo.value =  source_config->qinfo[i].data;
+
+		if (!queue_hardware_info_is_compatible(mdev_name, apqn,
+						       &src_hwinfo,
+						       &dest_hwinfo))
+			ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int do_post_copy_validation(const char *mdev_name,
+				   struct vfio_ap_config *source_config)
+{
+	if (!queues_available(mdev_name, source_config))
+		return -ENODEV;
+
+	if (!control_domains_available(mdev_name, source_config))
+		return -ENODEV;
+
+	return verify_ap_configs_are_compatible(mdev_name, source_config);
+}
+
+/**
+ * setup_ap_matrix_from_ap_config:
+ *
+ * Set the bits corresponding to the adapters, domains and control domains
+ * from the source guest's AP configuration into an ap_matrix object to be
+ * used to update the destination guest to run on this host.
+ *
+ * @ap_config:		The source guest's AP configuration
+ * @guest_matrix:	The object to be used to update the destination guest's
+ *			AP configuration
+ */
+static void setup_ap_matrix_from_ap_config(struct vfio_ap_config *ap_config,
+					   struct ap_matrix *guest_matrix)
+{
+	struct ap_config_info host_config_info = { 0 };
+	unsigned long apid, apqi, *guest_adm;
+	struct vfio_ap_queue_info qinfo;
+
+	ap_qci(&host_config_info);
+	vfio_ap_matrix_init(&host_config_info, guest_matrix);
+
+	for (int i = 0; i < ap_config->num_queues; i++) {
+		qinfo = ap_config->qinfo[i];
+		apid = AP_QID_CARD(qinfo.apqn);
+		apqi = AP_QID_QUEUE(qinfo.apqn);
+
+		if (!test_bit_inv(apid, guest_matrix->apm))
+			set_bit_inv(apid, guest_matrix->apm);
+		if (!test_bit_inv(apqi, guest_matrix->aqm))
+			set_bit_inv(apqi, guest_matrix->aqm);
+	}
+
+	guest_adm = (unsigned long *)ap_config->adm;
+	for_each_set_bit_inv(apqi, guest_adm, AP_DOMAINS) {
+		if (!test_bit_inv(apqi, guest_matrix->adm))
+			set_bit_inv(apqi, guest_matrix->adm);
+	}
+}
+
 static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,
 				      size_t len, loff_t *pos)
 {
-	/* TODO */
-	return -EOPNOTSUPP;
+	struct vfio_ap_migration_data *mig_data;
+	struct ap_matrix_mdev *matrix_mdev;
+	struct vfio_ap_config *ap_config;
+	struct ap_matrix guest_matrix;
+	ssize_t ret = 0, cfg_sz;
+	const char *mdev_name;
+
+	/*
+	 * When userspace calls write() with an explicit offset (pwrite), pos is
+	 * non-NULL and the function rejects it with -ESPIPE (illegal seek). For
+	 * normal write() calls, pos is NULL, so we'll use the file's internal
+	 * position filp->f_pos
+	 */
+	if (pos)
+		return -ESPIPE;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	pos = &filp->f_pos;
+
+	ret = validate_resuming_write_parms(filp, len, pos);
+	if (ret) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	matrix_mdev = filp->private_data;
+	mig_data = matrix_mdev->mig_data;
+	mdev_name = dev_name(matrix_mdev->vdev.dev);
+
+	/*
+	 * If this is the first write operation, then allocate storage for the
+	 * AP configuration information; otherwise, reallocate the
+	 * struct vfio_ap_config object used to store the AP configuration data
+	 * sent from userspace.
+	 */
+	if (*pos == 0) {
+		ret = allocate_ap_config(&ap_config, buf, len);
+
+		/* If the allocation failed, we'll return the error */
+		if (ret < 0) {
+			mutex_unlock(&matrix_dev->mdevs_lock);
+			return ret;
+		}
+
+		cfg_sz = ret;
+	} else {
+		ret = reallocate_ap_config(&mig_data->resuming_mig_file,
+					   &ap_config, len);
+		if (ret < 0) {
+			mutex_unlock(&matrix_dev->mdevs_lock);
+			return ret;
+		}
+
+		cfg_sz = ret;
+	}
+
+	if (*pos + len > cfg_sz) {
+		ret = -EIO;
+		kfree(ap_config);
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	/*
+	 * We don't want to lock all mdevs while copying data from userspace so
+	 * we don't block all other mdevs in case the I/O takes a long time.
+	 * From here on out we don't need this lock because we are not
+	 * accessing the matrix_mdev until we need to get the update
+	 * locks to set the new destination guest's AP configuration in which
+	 * case this lock will be taken then.
+	 */
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	if (copy_from_user((char *)ap_config + *pos, buf, len)) {
+		ret = -EIO;
+		kfree(ap_config);
+
+		return ret;
+	}
+
+	/* Check if we've completed writing the entire configuration */
+	if (*pos + len == cfg_sz) {
+		/* Acquire locks required to update the guest's AP config */
+		mutex_lock(&ap_attr_mutex);
+		get_update_locks_for_mdev(matrix_mdev);
+
+		/*
+		 * Verify the device wasn't closed while mdevs_lock was dropped
+		 * for the copy_from_user above. get_update_locks_for_mdev()
+		 * reacquires mdevs_lock, so the check is safe here.
+		 */
+		if (!matrix_mdev->mig_data) {
+			release_update_locks_for_mdev(matrix_mdev);
+			mutex_unlock(&ap_attr_mutex);
+			kfree(ap_config);
+			return -ENODEV;
+		}
+
+		ret = do_post_copy_validation(mdev_name, ap_config);
+		if (ret < 0) {
+			kfree(ap_config);
+			release_update_locks_for_mdev(matrix_mdev);
+			mutex_unlock(&ap_attr_mutex);
+			return ret;
+		}
+
+		setup_ap_matrix_from_ap_config(ap_config, &guest_matrix);
+
+		ret = vfio_ap_set_new_guest_config(matrix_mdev, &guest_matrix);
+
+		release_update_locks_for_mdev(matrix_mdev);
+		mutex_unlock(&ap_attr_mutex);
+
+		if (ret) {
+			kfree(ap_config);
+			return ret;
+		}
+	}
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	kfree(mig_data->resuming_mig_file.ap_config);
+	mig_data->resuming_mig_file.ap_config = ap_config;
+	mig_data->resuming_mig_file.config_sz = cfg_sz;
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	ret = len;
+	*pos += len;
+
+	return ret;
 }
 
 static const struct file_operations vfio_ap_resume_fops = {
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 11/15] s390/vfio-ap: Transition device migration state to STOP
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (9 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 10/15] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:26   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 12/15] s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa Anthony Krowiak
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Typically at this point, interrupt processing for the device, DMA and other
changes to the device's external state must be prohibited. For VFIO AP,
there is no physical DMA device virtualized by the vfio-ap device . It
merely manages the guest's AP configuration that identifies the devices
passed through to the guest, but does not have access to any of them. Those
AP devices are passed through and controlled exclusively by the SIE program
used to start the guest. There  only thing to do at this point is return
NULL to the caller since the file streams and associated data will get
cleaned up as a result of the VFIO migration framework releasing the files.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 7a9a8f63a76d..21cd9810a18c 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -1304,12 +1304,17 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 		return filp;
 	}
 
+	/*
+	 * Terminates the data transfer session of the vfio-ap device state
+	 * between the source and target hosts. Since the vfio-ap device does
+	 * not virtualize a DMA device, there is no internal device state to
+	 * incorporate into the vfio-ap device on the target.
+	 */
 	if ((cur_state == VFIO_DEVICE_STATE_RESUMING &&
 	     new_state == VFIO_DEVICE_STATE_STOP) ||
 	    (cur_state == VFIO_DEVICE_STATE_STOP_COPY &&
 	     new_state == VFIO_DEVICE_STATE_STOP)) {
-		/* TODO */
-		return ERR_PTR(-EOPNOTSUPP);
+		return NULL;
 	}
 
 	if ((cur_state == VFIO_DEVICE_STATE_STOP &&
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 12/15] s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (10 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 11/15] s390/vfio-ap: Transition device migration state to STOP Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:29   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 13/15] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

The transition from STOP to RUNNING and vice versa indicates that
the vfio device is operating normally and migration is not
currently taking place. In this case, just set the new state.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 21cd9810a18c..8876338d2898 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -1317,12 +1317,17 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 		return NULL;
 	}
 
+	/*
+	 * These states indicate migration has either not been initiated or
+	 * has completed and the vfio-ap device is operating normally.Since the
+	 * vfio-ap device does not virtualize a DMA device, there is no internal
+	 * device state to incorporate into the vfio-ap device on the target.
+	 */
 	if ((cur_state == VFIO_DEVICE_STATE_STOP &&
 	     new_state == VFIO_DEVICE_STATE_RUNNING) ||
 	    (cur_state == VFIO_DEVICE_STATE_RUNNING &&
 	     new_state == VFIO_DEVICE_STATE_STOP)) {
-		/* TODO */
-		return ERR_PTR(-EOPNOTSUPP);
+		return NULL;
 	}
 
 	/* vfio_mig_get_next_state() does not use arcs other than the above */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 13/15] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (11 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 12/15] s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:27   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 14/15] s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute Anthony Krowiak
  2026-07-24 16:13 ` [PATCH v5 15/15] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Callback to get the estimated data length of the data that will be required
to complete reading of the vfio device's internal state. It's mandatory for
VFIO_DEVICE_FEATURE_MIGRATION migration support.

The function pointer for this callback is specified via the
'migration_get_data_size' field of the 'vfio_migration_ops' structure
which is stored with the VFIO device when the 'vfio_device'
structure representing the mediated device is initialized.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 8876338d2898..20b519f0898f 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -1429,7 +1429,18 @@ static int vfio_ap_get_state(struct vfio_device *vdev,
 static int vfio_ap_get_data_size(struct vfio_device *vdev,
 				 unsigned long *stop_copy_length)
 {
-	return -EOPNOTSUPP;
+	struct ap_matrix_mdev *matrix_mdev;
+	size_t qinfo_sz;
+	int num_queues;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	num_queues = vfio_ap_mdev_get_num_queues(&matrix_mdev->shadow_apcb);
+	qinfo_sz = num_queues * sizeof(struct vfio_ap_queue_info);
+	*stop_copy_length = qinfo_sz + sizeof(struct vfio_ap_config);
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	return 0;
 }
 
 static const struct vfio_migration_ops vfio_ap_migration_ops = {
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 14/15] s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (12 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 13/15] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:30   ` sashiko-bot
  2026-07-24 16:13 ` [PATCH v5 15/15] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Add the 'migratable' feature to the features provided by the sysfs
/sys/devices/vfio_ap/matrix/feature attribute to indicate that migration
of vfio devices is supported.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_drv.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/s390/crypto/vfio_ap_drv.c b/drivers/s390/crypto/vfio_ap_drv.c
index 8e69ed286bb9..4e973736522b 100644
--- a/drivers/s390/crypto/vfio_ap_drv.c
+++ b/drivers/s390/crypto/vfio_ap_drv.c
@@ -26,9 +26,11 @@ MODULE_LICENSE("GPL v2");
 struct ap_matrix_dev *matrix_dev;
 debug_info_t *vfio_ap_dbf_info;
 
+#define FEATURES "guest_matrix hotplug ap_config migratable"
+
 static ssize_t features_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	return sysfs_emit(buf, "guest_matrix hotplug ap_config\n");
+	return sysfs_emit(buf, "%s\n", FEATURES);
 }
 static DEVICE_ATTR_RO(features);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 15/15] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst
  2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (13 preceding siblings ...)
  2026-07-24 16:13 ` [PATCH v5 14/15] s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute Anthony Krowiak
@ 2026-07-24 16:13 ` Anthony Krowiak
  2026-07-24 18:38   ` sashiko-bot
  14 siblings, 1 reply; 31+ messages in thread
From: Anthony Krowiak @ 2026-07-24 16:13 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, borntraeger, mjrosato, pasic, alex, kwankhede, fiuczy,
	pbonzini, frankja, imbrenda, agordeev, hca, gor

Add a 'Live Guest Migration' chapter to the
Documentation/arch/s390/vfio-ap.rst document to describe the details for
initiating live guest migration for a guest to which AP adapters, domains
and control domains have been passed through.

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 Documentation/arch/s390/vfio-ap.rst | 616 +++++++++++++++++++++++-----
 1 file changed, 517 insertions(+), 99 deletions(-)

diff --git a/Documentation/arch/s390/vfio-ap.rst b/Documentation/arch/s390/vfio-ap.rst
index ac0c07f76ddd..02d7cbe5e479 100644
--- a/Documentation/arch/s390/vfio-ap.rst
+++ b/Documentation/arch/s390/vfio-ap.rst
@@ -1012,7 +1012,7 @@ guest_matrix dyn ap_config
 
 the following features are advertised:
 
----------------+---------------------------------------------------------------+
++--------------+---------------------------------------------------------------+
 | Flag         | Description                                                   |
 +==============+===============================================================+
 | guest_matrix | guest_matrix attribute exists. It reports the matrix of       |
@@ -1021,105 +1021,523 @@ the following features are advertised:
 +--------------+---------------------------------------------------------------+
 | dyn          | Indicates hot plug/unplug of AP adapters, domains and control |
 |              | domains for a guest to which the mdev is attached.            |
-+------------+-----------------------------------------------------------------+
++--------------+---------------------------------------------------------------+
 | ap_config    | ap_config interface for one-shot modifications to mdev config |
 +--------------+---------------------------------------------------------------+
+| migratable   | Indicates that live guest migration is supported for guests   |
+|              | to which crypto devices are passed through                    |
++--------------+---------------------------------------------------------------+
 
-Limitations
-===========
-Live guest migration is not supported for guests using AP devices without
-intervention by a system administrator. Before a KVM guest can be migrated,
-the vfio_ap mediated device must be removed. Unfortunately, it can not be
-removed manually (i.e., echo 1 > /sys/devices/vfio_ap/matrix/$UUID/remove) while
-the mdev is in use by a KVM guest. If the guest is being emulated by QEMU,
-its mdev can be hot unplugged from the guest in one of two ways:
-
-1. If the KVM guest was started with libvirt, you can hot unplug the mdev via
-   the following commands:
-
-      virsh detach-device <guestname> <path-to-device-xml>
-
-      For example, to hot unplug mdev 62177883-f1bb-47f0-914d-32a22e3a8804 from
-      the guest named 'my-guest':
-
-         virsh detach-device my-guest ~/config/my-guest-hostdev.xml
-
-            The contents of my-guest-hostdev.xml:
-
-.. code-block:: xml
-
-            <hostdev mode='subsystem' type='mdev' managed='no' model='vfio-ap'>
-              <source>
-                <address uuid='62177883-f1bb-47f0-914d-32a22e3a8804'/>
-              </source>
-            </hostdev>
-
-
-      virsh qemu-monitor-command <guest-name> --hmp "device-del <device-id>"
-
-      For example, to hot unplug the vfio_ap mediated device identified on the
-      qemu command line with 'id=hostdev0' from the guest named 'my-guest':
-
-.. code-block:: sh
-
-         virsh qemu-monitor-command my-guest --hmp "device_del hostdev0"
-
-2. A vfio_ap mediated device can be hot unplugged by attaching the qemu monitor
-   to the guest and using the following qemu monitor command:
-
-      (QEMU) device-del id=<device-id>
-
-      For example, to hot unplug the vfio_ap mediated device that was specified
-      on the qemu command line with 'id=hostdev0' when the guest was started:
-
-         (QEMU) device-del id=hostdev0
-
-After live migration of the KVM guest completes, an AP configuration can be
-restored to the KVM guest by hot plugging a vfio_ap mediated device on the target
-system into the guest in one of two ways:
-
-1. If the KVM guest was started with libvirt, you can hot plug a matrix mediated
-   device into the guest via the following virsh commands:
-
-   virsh attach-device <guestname> <path-to-device-xml>
-
-      For example, to hot plug mdev 62177883-f1bb-47f0-914d-32a22e3a8804 into
-      the guest named 'my-guest':
-
-         virsh attach-device my-guest ~/config/my-guest-hostdev.xml
-
-            The contents of my-guest-hostdev.xml:
-
-.. code-block:: xml
-
-            <hostdev mode='subsystem' type='mdev' managed='no' model='vfio-ap'>
-              <source>
-                <address uuid='62177883-f1bb-47f0-914d-32a22e3a8804'/>
-              </source>
-            </hostdev>
-
-
-   virsh qemu-monitor-command <guest-name> --hmp \
-   "device_add vfio-ap,sysfsdev=<path-to-mdev>,id=<device-id>"
-
-      For example, to hot plug the vfio_ap mediated device
-      62177883-f1bb-47f0-914d-32a22e3a8804 into the guest named 'my-guest' with
-      device-id hostdev0:
-
-      virsh qemu-monitor-command my-guest --hmp \
-      "device_add vfio-ap,\
-      sysfsdev=/sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804,\
-      id=hostdev0"
-
-2. A vfio_ap mediated device can be hot plugged by attaching the qemu monitor
-   to the guest and using the following qemu monitor command:
-
-      (qemu) device_add "vfio-ap,sysfsdev=<path-to-mdev>,id=<device-id>"
-
-      For example, to plug the vfio_ap mediated device
-      62177883-f1bb-47f0-914d-32a22e3a8804 into the guest with the device-id
-      hostdev0:
+Live Guest Migration
+====================
+The VFIO AP mediated device is not used to provide userspace with direct
+access to device memory as is the case with other devices that use the VFIO
+framework for device pass-through. Instead, it manages AP
+configuration metadata identifying the adapters, domains, and control
+domains to which a guest will be granted access. These AP resources are
+configured by assigning them to a vfio-ap mediated device via its sysfs
+assignment interfaces. When the guest is started, the vfio_ap device driver
+sets the guest's AP configuration from the metadata stored with the mediated
+device. The AP devices are not accessed directly through the vfio_ap driver,
+so the driver does not migrate the state of the AP devices themselves.
+Instead, it migrates the AP configuration metadata. The vfio_ap device
+driver on the destination host will then ensure that the AP configuration
+metadata migrated from the source host is compatible with the AP
+configuration available on the destination host and if
+so, will set the destination guest's AP configuration accordingly.
 
-         (QEMU) device-add "vfio-ap,\
-         sysfsdev=/sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804,\
-         id=hostdev0"
+To be considered compatible, the AP configuration for the destination host must
+meet these requirements:
+
+* Each AP adapter, domain and control domain number assigned to the source
+  guest's AP configuration must be in the destination host's AP
+  configuration. This can be verified as follows:
+
+  * On the source host, display the AP configuration of the source guest
+    with the following two commands:
+
+    .. code-block:: bash
+
+       cat /sys/devices/vfio_ap/matrix/$UUID/guest_matrix
+       cat /sys/devices/vfio_ap/matrix/$UUID/control_domains
+
+    .. note::
+       Replace ``$UUID`` with the actual UUID of your mediated device.
+
+       The ``guest_matrix`` lists the APQNs of the queue devices assigned
+       to the mediated device; for example:
+
+       .. code-block:: bash
+
+          00.0033
+          00.003a
+
+       The ``control_domains`` lists the domain numbers of the control
+       domains; for example:
+
+       .. code-block:: bash
+
+          0033
+          003a
+
+  * Display the AP configuration of the destination host with the following
+    two commands:
+
+    .. code-block:: bash
+
+       lszcrypt -V
+       cat /sys/bus/ap/ap_control_domain_mask
+
+    .. note::
+       The ``lszcrypt -V`` command lists the AP cards (adapters) and queue
+       devices on the host system; for example:
+
+       .. code-block:: bash
+
+          =========== ===== ============ ==========
+          CARD.DOMAIN TYPE  MODE         DRIVER
+          =========== ===== ============ ==========
+          00          CEX5C CCA-Coproc   cex4card
+          00.0033     CEX5C CCA-Coproc   vfio_ap
+          00.003a     CEX5C CCA-Coproc   vfio_ap
+          =========== ===== ============ ==========
+
+       Each bit in the ap_control_domain_mask corresponds to
+       domain numbers 0-255. If a bit is set to one, then
+       the control domain is available on the source host. For
+       example:
+
+       .. code-block:: bash
+
+          0x0000000000001020000000000000000000000000000000000000000000000000
+
+       This mask indicates that AP control domains 51 (0x33) and 58 (0x3a) are
+       in the AP configuration of the host system.
+
+  * Verify the configurations:
+
+    * Each APQN in the guest's sysfs ``guest_matrix`` file must match an
+      APQN returned from the ``lszcrypt -V`` command on the destination host. It
+      is important to note the driver to which the queue is bound which
+      is displayed in the ``driver`` column. Queues not bound to the vfio_ap
+      driver are not available to the destination guest. Even if the APQNs are
+      available to the driver, that does not guarantee they can be used by the
+      guest being migrated; for example, migration will fail if any of these APQNs
+      are assigned to a mediated device other than the one being migrated.
+
+    * Each domain number from the source guest's sysfs
+      ``control_domains`` attribute must correspond to a bit set to one in
+      the destination host's ``ap_control_domain_mask``.
+
+
+* Each APQN assigned to the source guest and destination guest must
+  reference a queue device with compatible hardware capabilities:
+
+  +--------------+---------------------------------------------------------------+
+  | Hardware     | Description                                                   |
+  | Capabilities |                                                               |
+  +==============+===============================================================+
+  | [1]          | * AP special command facility (APSC)                          |
+  | Facilities   | * AP 4096-bit ME PKU commands facility (AP4KM)                |
+  |              | * AP 4096-bit CRT PKU commands (AP4KC)                        |
+  +--------------+---------------------------------------------------------------+
+  | [1] Mode     | * CCA-mode                                                    |
+  |              | * Accelerator-mode                                            |
+  |              | * XCP-mode (EP11)                                             |
+  +--------------+---------------------------------------------------------------+
+  | [1] AP       |   APXA installed                                              |
+  | extended     |                                                               |
+  | addressing   |                                                               |
+  +--------------+---------------------------------------------------------------+
+  | [1] Command  |   Command filtering available                                 |
+  | filtering    |                                                               |
+  +--------------+---------------------------------------------------------------+
+  | [2]          | * Full native card function                                   |
+  | Functional   | * Only stateless functions                                    |
+  | capabilities |                                                               |
+  +--------------+---------------------------------------------------------------+
+  | [3] AP type  | * 10 (0x0a): CEX4                                             |
+  |              | * 11 (0x0b): CEX5                                             |
+  |              | * 12 (0x0c)  CEX6                                             |
+  |              | * 13 (0x0d)  CEX7                                             |
+  |              | * 14 (0x0e)  CEX8                                             |
+  +--------------+---------------------------------------------------------------+
+
+  +-----------------------------------------------------------------------------+
+  | Table Keys                                                                  |
+  +=============================================================================+
+  |                                                                             |
+  | [1]        The hardware capability must be the same for both the source     |
+  |            and destination queue device.                                    |
+  |                                                                             |
+  | [2]        The source and destination queues must either both have          |
+  |            full native card function or both have stateless functions.      |
+  |            If the functional capabilities don't match, then the source      |
+  |            queue can have stateless capabilities since full native card     |
+  |            function can handle stateless functions; otherwise, migration    |
+  |            will fail.                                                       |
+  |                                                                             |
+  | [3]        The AP type on the source and destination guests can             |
+  |            differ if the queue passed through to the target guest is a      |
+  |            newer model (backwards compatible)                               |
+  +-----------------------------------------------------------------------------+
+
+  * To verify the hardware capabilities are compatible:
+
+    * On both the source and destination hosts, display the hardware capabilities
+      for each AP adapter in the AP configuration for that host:
+
+      .. code-block:: bash
+
+         cat /sys/bus/ap/devices/card$APID/ap_functions
+
+      .. note::
+         The ``$APID`` is the two-character adapter number in hexadecimal format;
+         for example, ``card02`` or ``card1f``. The ``/sys/bus/ap/devices`` directory
+         also lists the APQNs of the queue devices installed in the respective
+         host system with the first two characters being the APID; for example:
+         ``02.0004`` or ``00.0033`` .
+
+    * Verify the hardware capabilities for each AP adapter device on the source
+      and destination host are compatible. The ``ap_functions`` attribute is a
+      bitmask. The bits in the mask read from left to right starting with bit 0.
+      Each bit that is set to one indicates the corresponding hardware capability
+      is installed:
+
+      * Bits 0-2 are the facilities bits. Each bit value must match for the AP
+        devices on the source and destination systems. The values indicate the
+        following:
+
+        * bit-0: APSC is installed
+        * bit-1: AP4KM is installed
+        * bit-2: AP4KC is installed
+
+      * Bits 3-5 specify the adapter Modes. Only one of these bits will be set to
+        one and must be the same for both the source and destination adapter
+        devices:
+
+        * bit-3: CCA-mode
+        * bit-4: Accelerator-mode
+        * bit-5: XCP-mode (EP11)
+
+      * Bit 6 specifies whether APXA is installed and must be the same for both
+        the source and destination adapter devices.
+
+      * Bit 7 specifies whether command filtering is installed
+        and must be the same for both the source and destination adapter devices.
+
+      * Bits 8-15 are the classification bits. There are only two relevant
+        bits and only one of them will be set to one. For the source and destination
+        adapter devices to be considered compatible:
+
+        * Bit 8 may be set to 1 on both hosts (full native card function)
+        * Bit 9 may be set to 1 on both hosts (only stateless functions available)
+        * Bit 9 may be set to 1 on the source host and bit 8 may be set to 1 on
+          the destination host
+
+      * Bits 16-17 are the binding state bits which must be 00
+
+  * To verify the AP types are compatible:
+
+    * On both the source and destination hosts, display the hardware type
+      for each AP adapter in the AP configuration for that host to ensure the
+      source and destination adapter types are the same, or the source type is
+      less than the destination type::
+
+       cat /sys/bus/ap/devices/card$APID/hwtype
+
+      .. note::
+         The ``$APID`` is the two-character adapter number in hexadecimal format;
+         for example, ``card02`` or ``card1f``.
+
+Setting up for live guest migration on the destination host
+-----------------------------------------------------------
+To set up a host as the destination for live guest migration, do the
+following:
+
+1. Create a mediated device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Create a mediated device with the same name (UUID) as the mediated device
+used to supply the AP configuration to the source guest. For example, if
+the mediated device on the source guest is named
+62177883-f1bb-47f0-914d-32a22e3a8804:
+
+.. code-block:: bash
+
+   echo 62177883-f1bb-47f0-914d-32a22e3a8804 > \
+     /sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/create
+
+2. Reserve adapters and domains for the vfio_ap device driver's use
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Reserve the same adapters, and domains passed through to the source guest
+for the vfio_ap device driver on the destination host by clearing the
+adapter numbers from the ``/sys/bus/ap/apmask`` and/or the domain numbers
+from the ``/sys/bus/ap/aqmask`` files.
+
+For example, suppose adapters 0, 20 (0x14) and 22 (0x16) as well as
+domains 51 (0x33) and 58 (0x3a) are passed through to the source guest. To
+ensure they can be passed through to the destination guest, they must be
+made available to the vfio_ap device driver on the destination host. To
+make them available, do one or both of the following on the destination host:
+
+.. code-block:: bash
+
+   echo -0,-0x14,-22 > /sys/bus/ap/apmask
+   echo -0,-0x33,-0x3a > /sys/bus/ap/aqmask
+
+.. note::
+
+    * It is not necessary to reserve both the adapters and domains.
+      Reserving an adapter implicitly allows the vfio_ap driver to assign
+      each of the domains available on the system along with the reserved
+      adapters to a guest's AP configuration and vice versa.
+
+    * The Cartesian product of the adapters and domains reserved for the
+      vfio_ap device driver identifies the APQNs of the AP queue devices to be
+      bound to the vfio_ap device driver. If an APQN does not reference a
+      queue device installed in the system at the time of live guest migration,
+      the migration will fail.
+
+    * It is not necessary to configure the destination mediated device with
+      adapters, domains and control domains because the source guest's AP
+      configuration will overlay the destination guest's during migration.
+
+    * To verify that the queue devices are reserved for the vfio_ap
+      device driver, issue the ``lszcrypt -V`` command and you should
+      see ``vfio_ap`` in the ``DRIVER`` column of the output.
+
+Live guest migration failures due to AP configuration errors
+------------------------------------------------------------
+This section describes the errors that may occur during live guest migration
+when the AP configuration of the source guest and destination host are not
+compatible and where to look for problem determination.
+
+The destination host is missing the mediated device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This error occurs when a mediated device with the same name (UUID) as the
+mediated device being migrated has not been created on the destination host.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: device not found: mediated device '$UUID' not           |
+|              | found                                                          |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | initiating migration                                           |
+|              | qemu-system-s390x: Sibling indicated error 1                   |
++--------------+----------------------------------------------------------------+
+
+.. note::
+   $UUID is the UUID of the mediated device being migrated from the source host.
+
+
+One or more queues are not bound to the vfio_ap driver on the destination host
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The queue devices passed through to the source guest are not installed in the
+destination host or are not bound to its vfio_ap device driver.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: No such device: Failed to load vmstate version_id: 1,  |
+|              | ret: -19                                                       |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Kernel log:  | vfio_ap_mdev $UUID: Queue $APID.$APQI not available to         |
+|              | vfio_ap driver on target host                                  |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | shutting down, reason=failed                                   |
+|              | qemu-system-s390x: terminating on signal $SIGNAL_NUM from      |
+|              | pid $PID $DAEMON                                               |
++--------------+----------------------------------------------------------------+
+
+
+One or more control domains are not available on the destination host
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The control domains passed through to the source guest are not installed on the
+destination host.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: No such device: Failed to load vmstate version_id: 1,  |
+|              | ret: -19                                                       |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Kernel log:  | vfio_ap_mdev $UUID: Control domain $DOMAIN_NUM not available   |
+|              | on the destination host                                        |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | shutting down, reason=failed                                   |
+|              | qemu-system-s390x: terminating on signal $SIGNAL_NUM from      |
+|              | pid $PID $DAEMON                                               |
++--------------+----------------------------------------------------------------+
+
+
+.. note::
+   * $UUID is the UUID of the mediated device being migrated from the source host.
+   * $APID.$APQI is adapter number and queue index (domain number) comprising the
+     $APQN (for example, 00.0033) of the queue device that is not available.
+   * $SIGNAL_NUM is the signal number that caused the QEMU process to terminate.
+   * $PID is the process ID of the QEMU process on the destination host.
+   * $DAEMON is the name of the QEMU daemon process.
+   * This message will be logged for each queue for which this error is detected.
+
+Failure to get hardware information for a queue device on destination host
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The vfio_ap device driver on the destination host will execute an AP instruction
+to get the hardware information for a queue device to perform queue compatibility
+checks. The instruction can fail for a number of reasons in which case the
+migration will be terminated.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: failed to get hardware information for queue device    |
+|              | $APQN                                                          |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | vfio_ap_mdev $UUID: migration failed: Failed to get hwinfo for |
+|              | queue $APQN on target host: rc=$RESPONSE-CODE                  |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | shutting down, reason=failed                                   |
+|              | qemu-system-s390x: terminating on signal $SIGNAL-ID from pid   |
+|              | $PID $DAEMON                                                   |
++--------------+----------------------------------------------------------------+
+
+.. note::
+   * $UUID is the UUID of the mediated device being migrated from the source host.
+   * $APQN (for example, 00.0033) of the queue device that is not compatible.
+   * $RESPONSE_CODE is the response code from the AP instruction.
+   * $SIGNAL_NUM is the signal number of the signal that caused the QEMU process
+     to terminate.
+   * $PID is the process ID of the QEMU process on the destination host.
+   * $DAEMON is the name of the QEMU daemon process.
+   * This message will be logged for each queue for which this error is detected.
+
+The AP configurations of the source guest and destination host not compatible
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The functional capabilities or facilities available on one or more of the queues
+passed through to the source guest are not compatible with the queue device with
+the same APQN on the destination system (see the Hardware Capabilities table above)
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: No such device: Failed to load vmstate version_id: 1,  |
+|              | ret: -19                                                       |
++--------------+----------------------------------------------------------------+
+| Kernel log:  | N/A                                                            |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | *initiating migration*                                         |
+|              |                                                                |
+|              | *qemu-system-s390x: Sibling indicated error 1*                 |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | N/A                                                            |
++--------------+----------------------------------------------------------------+
+| Kernel log:  | *vfio_ap_mdev $UUID: Migration failed: Source and target       |
+|              | queue ($APQN) not compatible*                                  |
+|              |                                                                |
+|              | The message above will be followed by one or more messages     |
+|              | enumerating the incompatible features; for example:            |
+|              |                                                                |
+|              | ``vfio_ap_mdev $UUID: APSC facility installed in source queue  |
+|              | $APQN``                                                        |
+|              | ``vfio_ap_mdev $UUID: APSC facility not installed in target    |
+|              | queue $APQN``                                                  |
+|              |                                                                |
+|              | ``AP type of source ($APTYPE1) not compatible with target      |
+|              | ($APTYPE2)`` ...                                               |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | *initiating migration*                                         |
+|              |                                                                |
+|              | *qemu-system-s390x: error while loading state section id ...*  |
+|              |                                                                |
+|              | *shutting down, reason=failed*                                 |
+|              |                                                                |
+|              | *terminating on signal $SIGNAL-ID from pid $PID $DAEMON*       |
++--------------+----------------------------------------------------------------+
+
+.. note::
+   * $UUID is the UUID of the mediated device being migrated from the source host.
+   * $APQN (for example, 00.0033) of the queue device that is incompatible.
+   * $APTYPE1 is the type of the source queue device.
+   * $APTYPE2 is the type of the target queue device.
+   * $SIGNAL-ID is the signal number of the signal that caused the QEMU process
+     to terminate.
+   * $PROCESS-ID is the process ID of the QEMU process on the source host.
+   * $PID is the process ID of the QEMU process on the destination host.
+   * $DAEMON is the name of the QEMU daemon process.
+   * Each follow-on message logging the incompatibility will be logged for each
+     error detected for the specified queue device.
+
+AP Configuration Management
+---------------------------
+The AP configurations of the source guest and destination host must be kept
+in synchronization or live guest migration will likely fail due to potential
+incompatibilities introduced by such changes. In particular, changing adapter
+or domain reservations via the sysfs ``apmask`` or ``aqmask`` attributes on
+either the source or destination host without coordinating such changes between
+the two hosts -especially while a migration is taking place - can result in
+migration failures.
+
+Configuration stability is an orchestration-layer or system administrator
+responsibility, which is consistent with other VFIO device types. The vfio_ap
+device driver will validate source and destination configurations and provide
+clear diagnostics when incompatibilities are detected, enabling orchestration
+tools to implement appropriate policies; for example, blocking migration when
+such changes are to be made.
+
+Master Key administration
+--------------------------
+It is imperative that s390 Common Cryptographic Architecture (CCA) master key
+administration be performed on both the source and destination AP
+devices to synchronize the key values prior to allowing live guest
+migration. If the master keys do not match, then crypto applications that
+rely on secure keys wrapped by a CCA master key will fail when the guest on
+which they are running is migrated to the destination host. Again, this is
+an orchestration-layer or system administrator responsibility and migration must
+be blocked while master key administration is being performed.
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration
  2026-07-24 16:13 ` [PATCH v5 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
@ 2026-07-24 17:28   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 17:28 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev, kvm, linux-s390

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Incorrect kernel-doc formatting and field name mismatches will cause documentation build warnings.
--

--- Patch [2]: [PATCH v5 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration ---
commit c31e0e68ee7918227073261c8d3c179cdb56d4da
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Data structures for facilitating vfio device migration

Creates the data structures used to facilitate state transitions during
vfio device migration.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> new file mode 100644
> index 0000000000000..1b0856aacb697
> --- /dev/null
> +++ b/drivers/s390/crypto/vfio_ap_migration.c
> @@ -0,0 +1,72 @@
[ ... ]
> +/**
> + * vfio_ap_migration_file
       ^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: Low]
Is this missing the "struct " keyword and a short description for
struct vfio_ap_migration_file?

Building the documentation might trigger a kernel-doc warning here.

> + *
> + * This object is used for chunk processing of multiple reads and writes of
> + * AP configuration information.
[ ... ]
> +struct vfio_ap_migration_file {
> +	struct file		*filp;
> +	struct vfio_ap_config	*ap_config;
> +	unsigned long		config_sz;
> +};
> +
> +/**
> + * vfio_ap_migration_data
       ^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: Low]
Could this kernel-doc block for struct vfio_ap_migration_data also be
missing the "struct " keyword?

> + *
> + * Manages the migration state for the VFIO device that maintains the AP
> + * configuration of the guest being migrated.
[ ... ]
> +struct vfio_ap_migration_data {
> +	enum vfio_device_mig_state	mig_state;
> +	struct vfio_ap_migration_file	resuming_mig_file;
> +	struct vfio_ap_migration_file	stop_copy_mig_file;
> +};
[ ... ]
> +/**
> + * vfio_ap_config - the guest's AP configuration
> + *
> + * @num_queues:	the number of queues passed through to the guest
> + * @reserved:	padding to ensure proper alignment of @cntl_domains
                                                              ^^^^^^^^^^^^^

[Severity: Low]
Does this documentation for @reserved incorrectly reference a nonexistent
cntl_domains field in struct vfio_ap_config?

The struct defines the field as adm, so this might result in a field name
mismatch warning during documentation build.

> + * @adm:	bitmap specifying the control domains in the AP configuration
> + * @qinfo:	an array of vfio_ap_queue_info objects, each specifying the
> + *		queue information for a queue passed through to the guest
> + */
> +struct vfio_ap_config {
> +	u32				num_queues;
> +	u8				reserved[4];
> +	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
> +	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
> +};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=2

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data
  2026-07-24 16:13 ` [PATCH v5 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
@ 2026-07-24 17:35   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 17:35 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Heiko Carstens, linux-s390, kvm, Vasily Gorbik, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] ABBA deadlock (circular locking dependency) between `matrix_dev->mdevs_lock` and the VFIO core's `dev_set->lock`.

Pre-existing issues:
- [Critical] Failing to attach a second mdev to a KVM instance corrupts `kvm->arch.crypto.pqap_hook`, leading to a Use-After-Free.
--

--- Patch [3]: [PATCH v5 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data ---
commit ab0b2af7d9f1c49f7cea7a7d2157bcc5a0bb9b7a
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Functions to initialize/release vfio device migration data

Provides the functions that initialize and release the data structures
used during live guest migration:

> diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
> index 71d3e2c2e07c6..47aca908701d1 100644
> --- a/drivers/s390/crypto/vfio_ap_ops.c
> +++ b/drivers/s390/crypto/vfio_ap_ops.c
[ ... ]
> @@ -797,18 +809,29 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)
>  	if (IS_ERR(matrix_mdev))
>  		return PTR_ERR(matrix_mdev);
>  
> +	mutex_lock(&matrix_dev->mdevs_lock);
> +
> +	/*
> +	 * Migration capabilities must be initialized before calling
> +	 * vfio_register_emulated_iommu_dev; otherwise, the VFIO core
> +	 * will see mig_ops as NULL during the registration. This could
> +	 * prevent the VFIO core from properly setting up migration
> +	 * infrastructure like debugfs entries.
> +	 */
> +	vfio_ap_init_migration_capabilities(matrix_mdev);
> +
>  	ret = vfio_register_emulated_iommu_dev(&matrix_mdev->vdev);

[Severity: High]
Does taking mdevs_lock before calling vfio_register_emulated_iommu_dev()
introduce an ABBA deadlock?

In vfio_ap_mdev_probe(), we now hold mdevs_lock when calling
vfio_register_emulated_iommu_dev(), which takes dev_set->lock inside the VFIO
core via vfio_assign_device_set().

However, in vfio_ap_mdev_open_device() below, which is called by the VFIO core
with dev_set->lock already held, we now acquire mdevs_lock.

Will this reverse lock ordering trigger a lockdep circular dependency splat
when userspace probes and opens the device?

[ ... ]
> @@ -2052,19 +2075,39 @@ static int vfio_ap_mdev_reset_qlist(struct list_head *qlist)
>  
>  static int vfio_ap_mdev_open_device(struct vfio_device *vdev)
>  {
> -	struct ap_matrix_mdev *matrix_mdev =
> -		container_of(vdev, struct ap_matrix_mdev, vdev);
> +	struct ap_matrix_mdev *matrix_mdev;
> +	int ret;
>  
>  	if (!vdev->kvm)
>  		return -EINVAL;
>  
> -	return vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm);
> +	mutex_lock(&matrix_dev->mdevs_lock);
> +	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
> +	ret = vfio_ap_init_migration_data(matrix_mdev);
> +	mutex_unlock(&matrix_dev->mdevs_lock);
> +
> +	if (ret)
> +		return ret;
> +
> +	ret = vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm);

[Severity: Critical]
This is a pre-existing issue, but I noticed that if vfio_ap_mdev_set_kvm()
fails (for instance, returning -EPERM because another mdev is already attached
to this KVM), does it leave kvm->arch.crypto.pqap_hook pointing to the new
mdev's pqap_hook?

Looking at vfio_ap_mdev_set_kvm() in drivers/s390/crypto/vfio_ap_ops.c, it
unconditionally overwrites KVM's pqap_hook before checking if another mdev is
already attached:

	down_write(&kvm->arch.crypto.pqap_hook_rwsem);
	kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
	up_write(&kvm->arch.crypto.pqap_hook_rwsem);
	
	get_update_locks_for_kvm(kvm);
	
	list_for_each_entry(m, &matrix_dev->mdev_list, node) {
		if (m != matrix_mdev && m->kvm == kvm) {
			release_update_locks_for_kvm(kvm);
			return -EPERM;
		}
	}

If this fails with -EPERM, the pointer is not restored. If this new mdev is
subsequently closed and freed, could a malicious guest executing a PQAP
instruction trigger a use-after-free in the host when handle_pqap()
dereferences this freed pointer?

> +	if (ret) {
> +		/* Clean up migration data on failure */
> +		mutex_lock(&matrix_dev->mdevs_lock);
> +		vfio_ap_release_migration_data(matrix_mdev);
> +		mutex_unlock(&matrix_dev->mdevs_lock);
> +	}
> +
> +	return ret;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=3

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev
  2026-07-24 16:13 ` [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
@ 2026-07-24 17:36   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 17:36 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Vasily Gorbik, Heiko Carstens, kvm, linux-s390, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Extremely inefficient nested bitmask iteration to count the number of queues.
--

--- Patch [1]: [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev ---
commit c5c27f83fbf7c245f4954e58e37786067ff32658
Author: Anthony Krowiak <akrowiak@linux.ibm.com>
s390/vfio-ap: Provide function to get the number of queues assigned to mdev

Provides a function that returns the number of queues that are or will be
passed through to a guest when the mdev is attached to a guest.

> diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
> index 44b3a1dcc1b36..71d3e2c2e07c6 100644
> --- a/drivers/s390/crypto/vfio_ap_ops.c
> +++ b/drivers/s390/crypto/vfio_ap_ops.c

[ ... ]

> @@ -2098,6 +2098,20 @@ static void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)
>  	release_update_locks_for_mdev(matrix_mdev);
>  }
>  
> +int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix)
> +{
> +	unsigned long apid, apqi;
> +	int num_queues = 0;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	for_each_set_bit_inv(apid, ap_matrix->apm, AP_DEVICES)
> +		for_each_set_bit_inv(apqi, ap_matrix->aqm, AP_DOMAINS)
> +			num_queues++;

[Severity: Medium]
Is this nested iteration the most efficient way to calculate the total number
of queues?

On s390, find_first_bit_inv and find_next_bit_inv are external function calls.
If a significant number of bits are set in both masks, this loop will make
thousands of function calls simply to count bits, resulting in an O(N*M)
operation instead of O(1).

Could we instead multiply the weights of the two bitmaps directly without loops?

return bitmap_weight(ap_matrix->apm, AP_DEVICES) *
       bitmap_weight(ap_matrix->aqm, AP_DOMAINS);

> +
> +	return num_queues;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=1

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 04/15] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler
  2026-07-24 16:13 ` [PATCH v5 04/15] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
@ 2026-07-24 17:43   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 17:43 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Alexander Gordeev, Heiko Carstens, linux-s390, kvm, Vasily Gorbik

> According to the VFIO migration specifications, userspace must issue a
> VFIO_DEVICE_RESET ioctl if a vfio device enters an error state or fails a
> state transition to recover the device and return its migration state to
> RUNNING. This patch adds a function that is called when a VFIO_DEVICE_RESET
> is issued that releases the migration files and frees the storage allocated
> for maintaining the migration state.
> 
> Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=4


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 05/15] s390-vfio-ap: Callback to get/set vfio device mig state during guest migration
  2026-07-24 16:13 ` [PATCH v5 05/15] s390-vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
@ 2026-07-24 17:47   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 17:47 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Alexander Gordeev, kvm, Vasily Gorbik, Heiko Carstens, linux-s390

> Implements two callbacks related to the current state of a vfio-ap device:
> * The callback that sets a new migration state of a vfio-ap device during
>   live migration of guests with pass-through access to AP devices. This
>   callback is mandatory for VFIO_DEVICE_FEATURE_MIGRATION support.
> 
>   The function pointer for this callback is specified via the
>   'migration_set_state' field of the 'vfio_migration_ops' structure
>   which is stored with the VFIO device when the 'vfio_device'
>   structure representing the mediated device is initialized.
> 
> * The callback that returns the current vfio device migration state during
>   live migration of guests with pass-through access to AP devices.
> 
>   The function pointer for this callback is specified via the
>   'migration_get_state' field of the 'vfio_migration_ops' structure
>   which is stored with the VFIO device when the 'vfio_device'
>   structure representing the mediated device is initialized.
> 
> Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=5


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY
  2026-07-24 16:13 ` [PATCH v5 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
@ 2026-07-24 17:50   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 17:50 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, kvm

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The migration file descriptor holds a raw pointer to `ap_matrix_mdev` without acquiring a reference to the VFIO device, causing a Use-After-Free when the file is closed if the device was removed.
- [High] Missing `get_file()` when saving the migration file pointer to the internal state, leading to premature file destruction and Use-After-Free.
--

--- Patch [6]: [PATCH v5 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY ---
commit d0aca043e8f77cdca50116119638c000987fae0d
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY
    
This commit begins the process of saving the vfio device state by transitioning
from STOP to STOP_COPY. It introduces a stub implementation of the read
operation for migration state files.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 17cabd7541e75..e566250a88ea3 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c
[ ... ]
> @@ -72,6 +73,67 @@ struct vfio_ap_config {
>  	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
>  };
>  
[ ... ]
> +static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
> +{
> +	struct ap_matrix_mdev *matrix_mdev;
> +	int ret = 0;
> +
> +	mutex_lock(&matrix_dev->mdevs_lock);
> +
> +	matrix_mdev = filp->private_data;
> +	if (!matrix_mdev || !matrix_mdev->mig_data) {
> +		ret = -ENODEV;
> +		goto done;
> +	}
[ ... ]
> +static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,
> +					     const struct file_operations *fops,
> +					     int flags)
> +{
> +	struct file *filp;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	filp = anon_inode_getfile("vfio_ap_mig_file", fops, matrix_mdev, flags);

[Severity: High]
Does this code need to take a reference to the underlying VFIO device?

If the mediated device is unbound or removed, matrix_mdev could be freed.
When the migration file descriptor is subsequently closed,
vfio_ap_release_mig_file() dereferences filp->private_data, which might
result in a use-after-free on the freed matrix_mdev.

> +	if (!IS_ERR(filp))
> +		stream_open(filp->f_inode, filp);
> +
> +	return filp;
> +}
[ ... ]
> @@ -85,10 +147,22 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
>  	dev_dbg(matrix_mdev->vdev.dev, "%s: %d -> %d\n", __func__, cur_state,
>  		new_state);
>  
> +	/*
> +	 * Begins the process of saving the vfio device state by creating and
> +	 * returning a streaming data_fd to be used to read out the internal
> +	 * state of the vfio-ap device on the source host.
> +	 */
>  	if (cur_state == VFIO_DEVICE_STATE_STOP &&
>  	    new_state == VFIO_DEVICE_STATE_STOP_COPY) {
> -		/* TODO */
> -		return ERR_PTR(-EOPNOTSUPP);
> +		struct file *filp = vfio_ap_open_file_stream(matrix_mdev,
> +							     &vfio_ap_stop_copy_fops,
> +							     O_RDONLY);
> +		if (IS_ERR(filp))
> +			return ERR_CAST(filp);
> +
> +		mig_data->stop_copy_mig_file.filp = filp;
> +
> +		return filp;
>  	}

[Severity: High]
Should get_file() be called before saving the file pointer to the internal
state?

The VFIO core installs the returned file pointer into the user's FD table,
which consumes the only reference to the file. If the driver later drops its
saved reference during a device reset (e.g., via a call to fput() in
vfio_ap_release_mig_files()), the file could be prematurely destroyed while the
userspace FD table still points to it.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=6

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 07/15] s390/vfio-ap: File ops called to save the vfio device migration state
  2026-07-24 16:13 ` [PATCH v5 07/15] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
@ 2026-07-24 18:04   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:04 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: kvm, linux-s390, Vasily Gorbik, Alexander Gordeev, Heiko Carstens

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Concurrent `read()` calls trigger a TOCTOU race condition on unprotected `filp->f_pos`, leading to data corruption and a heap out-of-bounds read.
- [High] A TOCTOU race condition in `vfio_ap_get_config()` permanently leaks the `ap_configuration` buffer during concurrent reads.
- [High] `__counted_by` bounds violation panic due to accessing a flexible array before initializing its count.
- [High] Use-After-Free of `matrix_mdev` because the migration file descriptor does not hold a reference to the VFIO device.
- [Medium] Resource exhaustion and high allocation failure probability due to order-8 memory allocation on every read chunk.
--

--- Patch [7]: [PATCH v5 07/15] s390/vfio-ap: File ops called to save the vfio device migration state ---
commit ea39a12de8ce91ab4c204140075df7f484f9c8f8
Author: Anthony Krowiak <akrowiak@linux.ibm.com>
Subject: s390/vfio-ap: File ops called to save the vfio device migration state

This commit implements the read callback for the migration stop-copy state
to transfer the guest's AP configuration and queue information to userspace.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index e566250a88ea3..0d949ec205de6 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> +static int validate_stop_copy_read_parms(struct file *filp, loff_t *pos,
> +					 size_t len)
> +{
> +	struct vfio_ap_migration_data *mig_data;
> +	struct ap_matrix_mdev *matrix_mdev;
> +	loff_t total_len;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	if (check_add_overflow((loff_t)len, *pos, &total_len))
> +		return -EIO;
> +
> +	matrix_mdev = filp->private_data;
> +
> +	if (!matrix_mdev || !matrix_mdev->mig_data)
> +		return -ENODEV;

[Severity: High]
Does the migration file descriptor hold a reference to the VFIO device?

If userspace closes the main VFIO device file descriptor but keeps the
migration file descriptor open, the VFIO core could unregister and free the
vfio_device and the embedding matrix_mdev. A subsequent read() on the
migration file would then dereference the freed matrix_mdev here.

[ ... ]

> +static int
> +vfio_ap_get_config(struct ap_matrix_mdev *matrix_mdev)
> +{
> +	unsigned long *apm, *aqm, apid, apqi, num_queues;
> +	struct vfio_ap_config *ap_configuration;
> +	const char *mdev_name;
> +	size_t ap_config_size;
> +	int ret;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	ap_config_size = vfio_ap_config_size(matrix_mdev, (int *)&num_queues);
> +
> +	ap_configuration = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);
> +	if (!ap_configuration)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Snapshot the APQN list from shadow_apcb under the lock so that
> +	 * ap_tapq() calls in vfio_ap_store_queue_info() can happen without it.
> +	 */
> +	apm = matrix_mdev->shadow_apcb.apm;
> +	aqm = matrix_mdev->shadow_apcb.aqm;
> +	num_queues = 0;
> +	for_each_set_bit_inv(apid, apm, AP_DEVICES) {
> +		for_each_set_bit_inv(apqi, aqm, AP_DOMAINS) {
> +			ap_configuration->qinfo[num_queues].apqn =
> +				AP_MKQID(apid, apqi);
> +			num_queues += 1;
> +		}
> +	}
> +	ap_configuration->num_queues = num_queues;

[Severity: High]
Will this trigger a __counted_by bounds check panic?

The struct vfio_ap_config uses __counted_by(num_queues) for the qinfo
flexible array. Since kzalloc() initializes num_queues to 0, writing to
qinfo[num_queues] before assigning ap_configuration->num_queues = num_queues
causes compiler-inserted bounds checks to evaluate the access against a
count of 0.

> +	memcpy(ap_configuration->adm, matrix_mdev->shadow_apcb.adm,
> +	       sizeof(ap_configuration->adm));
> +	mdev_name = dev_name(matrix_mdev->vdev.dev);
> +
> +	/**
> +	 * Release the global mdevs_lock which guards access to all mdevs.
> +	 * Storing the queue information could take a while if there are a
> +	 * large number of queues.
> +	 */
> +	mutex_unlock(&matrix_dev->mdevs_lock);
> +
> +	ret = vfio_ap_store_queue_info(mdev_name, ap_configuration);
> +	if (ret) {
> +		kfree(ap_configuration);
> +		mutex_lock(&matrix_dev->mdevs_lock);
> +		return ret;
> +	}
> +
> +	/* Retake the mdevs lock so we can safely make updates to the mdev */
> +	mutex_lock(&matrix_dev->mdevs_lock);
> +
> +	/*
> +	 * Verify mig_data is still valid - mdevs_lock was dropped, so the
> +	 * device could have been closed concurrently.
> +	 */
> +	if (!matrix_mdev->mig_data) {
> +		kfree(ap_configuration);
> +		return -ENODEV;
> +	}
> +
> +	matrix_mdev->mig_data->stop_copy_mig_file.ap_config = ap_configuration;

[Severity: High]
Does dropping mdevs_lock allow concurrent readers to leak ap_configuration?

In vfio_ap_stop_copy_read(), if stop_copy_mig_file.ap_config is NULL,
it calls this function. Because mdevs_lock is temporarily dropped here for
the hardware query, a concurrent read() could also observe that ap_config is
NULL, enter this function, and allocate a second buffer.

When the lock is reacquired, both threads will sequentially overwrite
stop_copy_mig_file.ap_config, permanently leaking the previous buffer.

[ ... ]

> +static ssize_t vfio_ap_stop_copy_read(struct file *filp, char __user *buf,
> +				      size_t len, loff_t *pos)
> +{
> +	struct ap_matrix_mdev *matrix_mdev;
> +	struct vfio_ap_config *ap_config;
> +	ssize_t ret = 0;
> +	size_t ap_config_size;

[ ... ]

> +	/*
> +	 * Allocate ap_config and copy the content of the object caching the
> +	 * AP configuration data between calls to it so we can give up the
> +	 * global mdevs_lock while copying the data to userspace.
> +	 */
> +	ap_config = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);

[Severity: Medium]
Is it safe to allocate up to 1MB using kzalloc() on every read syscall?

ap_config_size can exceed 1MB, requiring a contiguous order-8 allocation.
Requesting this on every read() regardless of the requested chunk length
is likely to fail with -ENOMEM on fragmented systems, breaking migration.
It may also allow userspace to cause memory allocator thrashing by issuing
small, repetitive reads.

> +	if (!ap_config) {
> +		mutex_unlock(&matrix_dev->mdevs_lock);
> +		return -ENOMEM;
> +	}
> +
> +	memcpy(ap_config, matrix_mdev->mig_data->stop_copy_mig_file.ap_config,
> +	       ap_config_size);

[Severity: High]
Will this memcpy() also trigger a __counted_by panic?

Since ap_config was just allocated with kzalloc(), its num_queues field is 0.
The memcpy() relies on __struct_size(), which will evaluate only to the base
structure size without the flexible array elements, causing a buffer
overflow panic during the copy.

> +
> +	/*
> +	 * Give up the global mdevs_lock while copying data to the user; it
> +	 * might be a long running operation and we don't want to prevent access
> +	 * to other mdevs for an inordinate amount of time.
> +	 */
> +	mutex_unlock(&matrix_dev->mdevs_lock);
> +
> +	if (copy_to_user(buf, (char *)ap_config + *pos, len)) {
> +		kfree(ap_config);
> +		return -EFAULT;
> +	}
> +
> +	kfree(ap_config);
> +	*pos += len;

[Severity: High]
Can concurrent read() calls cause an out-of-bounds heap read here?

Because the file is opened with FMODE_STREAM, VFS does not serialize reads
with f_pos_lock. The read length is calculated based on *pos under
mdevs_lock, but the lock is dropped before copy_to_user() and the offset
increment (*pos += len).

A concurrent read() can calculate its length based on an outdated *pos,
wait for the lock, and then read from ap_config + *pos after the first
thread has advanced *pos. This would allow reading past the end of the
ap_config buffer, leaking adjacent kernel memory to userspace.

> +
> +	return len;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=7

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING
  2026-07-24 16:13 ` [PATCH v5 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
@ 2026-07-24 18:06   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:06 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Alexander Gordeev, linux-s390, Vasily Gorbik, Heiko Carstens, kvm

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] Use-After-Free of `matrix_mdev` in the migration file release handler if the migration file descriptor outlives the VFIO device.
- [Critical] Missing file reference acquisition (`get_file`) leads to unbalanced `fput()` and Use-After-Free of `struct file`.
- [Low] Unused variable `mig_data` in `vfio_ap_resuming_init`.
--

--- Patch [8]: [PATCH v5 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING ---
commit 94d16087e429b1f41655bfa5ac0aacdda266732c
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Transition device migration state from STOP to RESUMING
    
Transitioning from VFIO_DEVICE_STATE_STOP to VFIO_DEVICE_STATE_RESUMING
starts a process of restoring the device state of the vfio device on the
target system. To prepare for restoring the device state, open a file
stream to receive the guest's AP configuration saved when the device state
on the source system transitioned from VFIO_DEVICE_STATE_STOP to
VFIO_DEVICE_STATE_STOP_COPY.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 0d949ec205de6..4a489a7a05ff1 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> @@ -398,6 +409,32 @@ static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,
>  	return filp;
>  }
>  
> +static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,
> +				      size_t len, loff_t *pos)
> +{
> +	/* TODO */
> +	return -EOPNOTSUPP;
> +}
> +
> +static const struct file_operations vfio_ap_resume_fops = {
> +	.owner = THIS_MODULE,
> +	.write = vfio_ap_resuming_write,
> +	.release = vfio_ap_release_mig_file,
> +};

[Severity: Critical]
If the migration file descriptor outlives the VFIO device, could this cause a
use-after-free?

When a process closes the main device file descriptor, the VFIO device
reference count drops to 0, which triggers device release and frees the
matrix_mdev structure.

If the process retains the migration file descriptor and subsequently closes
it, the VFS invokes this .release callback (vfio_ap_release_mig_file()).
This extracts the freed matrix_mdev from filp->private_data and dereferences
it:

vfio_ap_release_mig_file() {
    ...
    matrix_mdev = filp->private_data;
    if (!matrix_mdev || !matrix_mdev->mig_data) {
    ...
}

Does vfio_ap_open_file_stream() need to take a reference on the VFIO device
when creating the file descriptor to prevent this?

> +static struct file *vfio_ap_resuming_init(struct ap_matrix_mdev *matrix_mdev)
> +{
> +	struct vfio_ap_migration_data *mig_data;
> +	struct file *filp;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	mig_data = matrix_mdev->mig_data;

[Severity: Low]
This isn't a bug, but the mig_data variable is assigned here and never
used. Can it be removed?

> +	filp = vfio_ap_open_file_stream(matrix_mdev, &vfio_ap_resume_fops, O_WRONLY);
> +
> +	return filp;
> +}

[ ... ]

> @@ -429,10 +466,20 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
>  		return filp;
>  	}
>  
> +	/*
> +	 * Begins the process of restoring the vfio device state by creating and
> +	 * returning a streaming data_fd to be used to read in the internal
> +	 * state of the vfio-ap device on the destination host.
> +	 */
>  	if (cur_state == VFIO_DEVICE_STATE_STOP &&
>  	    new_state == VFIO_DEVICE_STATE_RESUMING) {
> -		/* TODO */
> -		return ERR_PTR(-EOPNOTSUPP);
> +		struct file *filp = vfio_ap_resuming_init(matrix_mdev);
> +
> +		if (IS_ERR(filp))
> +			return ERR_CAST(filp);
> +
> +		mig_data->resuming_mig_file.filp = filp;
> +		return filp;
>  	}

[Severity: Critical]
Does caching this file pointer without acquiring an additional reference
create a risk of a use-after-free on the struct file?

When the VFIO core installs the file into the userspace file descriptor
table via fd_install(), ownership of the single existing file reference
transfers to userspace.

If userspace issues a VFIO_DEVICE_RESET ioctl on the main device,
vfio_ap_reset_migration_state() calls vfio_ap_release_mig_files(), which
appears to execute fput() on the cached file pointer:

vfio_ap_release_mig_files() {
    ...
    if (mig_data->resuming_mig_file.filp) {
        fput(mig_data->resuming_mig_file.filp);
        mig_data->resuming_mig_file.filp = NULL;
    }
    ...
}

This would incorrectly drop the file's reference count to 0 and destroy the
struct file object while it remains in the userspace fdtable. Any subsequent
read, write, or close syscall by userspace on the migration fd would then
dereference the freed struct file.

Should this call get_file(filp) before caching the pointer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=8

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 09/15] s390/vfio-ap: Add method to set a new guest AP configuration
  2026-07-24 16:13 ` [PATCH v5 09/15] s390/vfio-ap: Add method to set a new guest AP configuration Anthony Krowiak
@ 2026-07-24 18:10   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:10 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Alexander Gordeev, Heiko Carstens, Vasily Gorbik, kvm, linux-s390

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Critical] vfio_ap_mdev_unlink_fr_queues incorrectly links instead of unlinking queues, causing a Use-After-Free when an mdev is removed.

Pre-existing issues:
- [Critical] Queues filtered from the guest APCB do not have their matrix_mdev pointer cleared during hot unplug, leading to a dangling pointer UAF.
--

--- Patch [9]: [PATCH v5 09/15] s390/vfio-ap: Add method to set a new guest AP configuration ---
commit c2bb55ba8d50e1fc1f6fb1459cd3cfcecf5e78d5
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Add method to set a new guest AP configuration
    
Adds a new vfio_ap_set_new_config function to set a guest's AP
configuration. This is needed in order to set the state of the mdev when
it is migrated from a remote host system during the RESUMING phase.

> diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
> index 678bdd4c82e95..454bb9fe19716 100644
> --- a/drivers/s390/crypto/vfio_ap_ops.c
> +++ b/drivers/s390/crypto/vfio_ap_ops.c

[ ... ]

> @@ -875,7 +827,7 @@ static void vfio_ap_mdev_unlink_fr_queues(struct ap_matrix_mdev *matrix_mdev)
>  			q = vfio_ap_mdev_get_queue(matrix_mdev,
>  						   AP_MKQID(apid, apqi));
>  			if (q)
> -				q->matrix_mdev = NULL;
> +				vfio_ap_mdev_link_queue(matrix_mdev, q);

[Severity: Critical]
Is it intended to link the queue here rather than nullifying the pointer?

When an admin removes an AP mdev device, vfio_ap_mdev_remove() calls
vfio_ap_mdev_unlink_fr_queues() immediately before freeing the matrix_mdev.

Since this replaces the pointer nullification (q->matrix_mdev = NULL) with
a call to vfio_ap_mdev_link_queue(), which acts as a no-op if the queue is
already linked, does this leave the physical queues with dangling pointers
to the freed mdev?

>  		}
>  	}
>  }

[ ... ]

> @@ -1725,54 +1678,100 @@ static void ap_matrix_copy(struct ap_matrix *dst, struct ap_matrix *src)

[ ... ]

> +int vfio_ap_set_new_guest_config(struct ap_matrix_mdev *matrix_mdev,
> +				 struct ap_matrix *m_new)
> +{

[ ... ]

> +	/* Need old bitmaps in matrix_mdev for unplug/unlink */
> +	ap_matrix_copy(&matrix_mdev->matrix, &m_old);
> +
> +	/* Unlink removed adapters/domains */
> +	vfio_ap_mdev_hot_unplug_adapters(matrix_mdev, m_removed.apm);

[Severity: Critical]
This is a pre-existing issue, but can this lead to a use-after-free for
queues that are filtered from the guest APCB?

If an admin unassigns an adapter that was filtered from shadow_apcb,
vfio_ap_mdev_hot_unplug_adapters() unlinks it from the hash table but skips
adding the filtered queue to qlist (checked in vfio_ap_mdev_unlink_adapter).

Because it is never added to qlist, the cleanup loop in
vfio_ap_mdev_hot_unplug_adapters():

list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode) {
        vfio_ap_unlink_mdev_fr_queue(q);
        list_del(&q->reset_qnode);
}

completely misses this queue. This leaves q->matrix_mdev pointing to the
mdev. Since the adapter is cleared from matrix_mdev->matrix.apm, a later
mdev destruction would skip cleaning it up, leaving a dangling pointer.

> +	vfio_ap_mdev_hot_unplug_domains(matrix_mdev, m_removed.aqm);
> +
> +	/* Need new bitmaps in matrix_mdev for linking new adapters/domains */
> +	ap_matrix_copy(&matrix_mdev->matrix, m_new);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=9

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 10/15] s390/vfio-ap: File ops called to resume the vfio device migration
  2026-07-24 16:13 ` [PATCH v5 10/15] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
@ 2026-07-24 18:17   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:17 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Vasily Gorbik, kvm, linux-s390, Alexander Gordeev, Heiko Carstens

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] Uninitialized stack variable `guest_matrix` applied to guest configuration.
- [High] Truncated write bypasses bounds check leading to massive out-of-bounds reads.
- [High] Double-fetch of `num_queues` leads to validation bypass (TOCTOU).
- [High] Zero-length write yields ZERO_SIZE_PTR and causes an immediate NULL/invalid pointer dereference.
- [High] Un-serialized writes to the migration stream file lead to heap overflows and memory leaks.
- [High] Use-After-Free of `mig_data` due to concurrent device closure.
- [High] Prolonged execution of slow hardware instructions while holding massive global and KVM lock chains.
- [Medium] High-order memory allocation for `ap_config` breaks live migration reliability.
--

--- Patch [10]: [PATCH v5 10/15] s390/vfio-ap: File ops called to resume the vfio device migration ---
commit e61d7c6d952455d8c5221b566f8371c207901817
Author: Anthony Krowiak <akrowiak@linux.ibm.com>
Subject: s390/vfio-ap: File ops called to resume the vfio device migration

This commit implements the write callback function to restore the state of
the vfio-ap device during migration. It reads the source guest's AP
configuration from userspace and validates it against the destination host.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 4a489a7a05ff1..7a9a8f63a76d3 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> @@ -538,24 +538,36 @@ static ssize_t allocate_ap_config(struct vfio_ap_config **ap_config,
>  				  const char __user *buf, size_t len)
>  {
>  	struct vfio_ap_config tmp_ap_config;
>  	ssize_t config_size;
>  	size_t copy_size;
>  
>  	/*
>  	 * If the length of the data sent exceeds the size of the vfio_ap_config
>  	 * structure, then we will copy enough data from userspace to get the
>  	 * number of queues which we can use to allocate enough space all of
>  	 * the queue information.
>  	 */
>  	copy_size = min(len, sizeof(tmp_ap_config));
>  
>  	if (copy_from_user(&tmp_ap_config, buf, copy_size))
>  		return -EIO;
>  
>  	/*
>  	 * If the length of data sent includes the number of queues
>  	 * in the AP configuration, then calculate its size; otherwise
>  	 * set config_size to the length of data sent.
>  	 */
>  	if (len >= sizeof(struct vfio_ap_config)) {
>  		config_size = calculate_ap_config_size(tmp_ap_config.num_queues);
>  
>  		/* If the calculation returned an error */
>  		if (config_size < 0)
>  			return config_size;
>  	} else {
>  		config_size = len;
>  	}

[Severity: High]
If userspace provides a small length (e.g., 4 bytes), does this bypass the
bounds check? It looks like config_size is set to len, meaning a tiny buffer
is allocated, but later validation loops rely on the unchecked num_queues 
field from this undersized buffer, which could result in a massive 
out-of-bounds heap read.

>  
>  	*ap_config = kzalloc(config_size, GFP_KERNEL_ACCOUNT);

[Severity: Medium]
Since MAX_AP_QUEUES allows up to 65,536 elements, the size calculated by
calculate_ap_config_size() could exceed 1MB. Could this cause legitimate live
migrations to fail if a contiguous order-8 or order-9 physical allocation
cannot be satisfied by kzalloc() due to memory fragmentation? Would it be
safer to use kvzalloc() here to allow falling back to vmalloc?

[Severity: High]
If a zero-length write occurs, len will be 0 and config_size will be set to 0.
Passing 0 to kzalloc() returns ZERO_SIZE_PTR. Because the completion check 
in vfio_ap_resuming_write() (0 + 0 == 0) succeeds immediately, would this
ZERO_SIZE_PTR be passed directly to do_post_copy_validation(), causing an
immediate kernel panic when it attempts to read source_config->num_queues?

[ ... ]

> @@ -1079,22 +1079,30 @@ static void setup_ap_matrix_from_ap_config(struct vfio_ap_config *ap_config,
>  					   struct ap_matrix *guest_matrix)
>  {
>  	struct ap_config_info host_config_info = { 0 };
>  	unsigned long apid, apqi, *guest_adm;
>  	struct vfio_ap_queue_info qinfo;
>  
>  	ap_qci(&host_config_info);
>  	vfio_ap_matrix_init(&host_config_info, guest_matrix);
>  
>  	for (int i = 0; i < ap_config->num_queues; i++) {
>  		qinfo = ap_config->qinfo[i];
>  		apid = AP_QID_CARD(qinfo.apqn);
>  		apqi = AP_QID_QUEUE(qinfo.apqn);
>  
>  		if (!test_bit_inv(apid, guest_matrix->apm))
>  			set_bit_inv(apid, guest_matrix->apm);

[Severity: High]
Does vfio_ap_matrix_init() in drivers/s390/crypto/vfio_ap_ops.c zero out the
apm, aqm, and adm bitmaps in guest_matrix? It appears to only initialize 
scalar max values. Since guest_matrix is declared on the stack in 
vfio_ap_resuming_write() without initialization, using set_bit_inv() here
could leave stack garbage in the bitmaps, potentially granting the destination
guest access to arbitrary unassigned queues.

[ ... ]

> @@ -1113,87 +1121,95 @@ static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,
>  				      size_t len, loff_t *pos)
>  {
>  	struct vfio_ap_migration_data *mig_data;
>  	struct ap_matrix_mdev *matrix_mdev;
>  	struct vfio_ap_config *ap_config;
>  	struct ap_matrix guest_matrix;
>  	ssize_t ret = 0, cfg_sz;
>  	const char *mdev_name;

[ ... ]

>  	/*
>  	 * We don't want to lock all mdevs while copying data from userspace so
>  	 * we don't block all other mdevs in case the I/O takes a long time.
>  	 * From here on out we don't need this lock because we are not
>  	 * accessing the matrix_mdev until we need to get the update
>  	 * locks to set the new destination guest's AP configuration in which
>  	 * case this lock will be taken then.
>  	 */
>  	mutex_unlock(&matrix_dev->mdevs_lock);
>  
>  	if (copy_from_user((char *)ap_config + *pos, buf, len)) {

[Severity: High]
Since copy_from_user() fetches the entire configuration from the same userspace
buffer pointer a second time, could a concurrent userspace thread modify
num_queues between the initial fetch in allocate_ap_config() and this one?
This could overwrite the validated num_queues value with an arbitrarily large
number, bypassing the sizing logic and leading to an out-of-bounds read later.

>  		ret = -EIO;
>  		kfree(ap_config);
>  
>  		return ret;
>  	}
>  
>  	/* Check if we've completed writing the entire configuration */
>  	if (*pos + len == cfg_sz) {
>  		/* Acquire locks required to update the guest's AP config */
>  		mutex_lock(&ap_attr_mutex);
>  		get_update_locks_for_mdev(matrix_mdev);
>  
>  		/*
>  		 * Verify the device wasn't closed while mdevs_lock was dropped
>  		 * for the copy_from_user above. get_update_locks_for_mdev()
>  		 * reacquires mdevs_lock, so the check is safe here.
>  		 */
>  		if (!matrix_mdev->mig_data) {
>  			release_update_locks_for_mdev(matrix_mdev);
>  			mutex_unlock(&ap_attr_mutex);
>  			kfree(ap_config);
>  			return -ENODEV;
>  		}
>  
>  		ret = do_post_copy_validation(mdev_name, ap_config);

[Severity: High]
The commit message mentions that get_hardware_info_for_queue() was modified 
so it can be executed without holding matrix_dev->mdevs_lock. However, 
do_post_copy_validation() iterates over up to 65,536 elements and calls 
get_hardware_info_for_queue(), which executes the slow ap_tapq() hardware 
instruction. Is it safe to execute this lengthy loop while holding 
ap_attr_mutex, matrix_dev->guests_lock, the KVM lock, and mdevs_lock? Could
this result in severe latency spikes or soft lockups?

[ ... ]

>  	mutex_lock(&matrix_dev->mdevs_lock);
>  	kfree(mig_data->resuming_mig_file.ap_config);
>  	mig_data->resuming_mig_file.ap_config = ap_config;
>  	mig_data->resuming_mig_file.config_sz = cfg_sz;
>  	mutex_unlock(&matrix_dev->mdevs_lock);

[Severity: High]
Could mig_data point to freed memory here? The mig_data pointer was cached at
the start of this function before dropping mdevs_lock. If the device was 
closed concurrently while the lock was dropped for copy_from_user(), wouldn't
this code unconditionally dereference the stale mig_data pointer, leading to a
use-after-free?

>  
>  	ret = len;
>  	*pos += len;

[Severity: High]
Because the migration file is opened with stream_open(), the VFS does not
serialize write() calls with f_pos_lock. Given that mdevs_lock is dropped 
during the copy_from_user() call above, could concurrent writes see the same
shared *pos offset? This seems like it would cause multiple threads to copy
data over the same region, and eventually leak the newly allocated ap_config
buffer when they race to reassign mig_data->resuming_mig_file.ap_config.

>  
>  	return ret;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=10

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 11/15] s390/vfio-ap: Transition device migration state to STOP
  2026-07-24 16:13 ` [PATCH v5 11/15] s390/vfio-ap: Transition device migration state to STOP Anthony Krowiak
@ 2026-07-24 18:26   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:26 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: linux-s390, kvm, Vasily Gorbik, Heiko Carstens, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [High] Transitioning out of data transfer states (STOP_COPY -> STOP or RESUMING -> STOP) returns NULL without cleaning up the active migration file state, causing stale data reuse and memory leaks.

Pre-existing issues:
- [Critical] Migration file descriptors lack a reference count to `matrix_mdev`, allowing a Use-After-Free if the device is unbound while the file is open.
- [Critical] Calling `fput()` on a migration file stream without holding an additional reference causes a structural Use-After-Free of the file object.
- [Critical] Writing fewer than `sizeof(struct vfio_ap_config)` bytes to the resuming FD bypasses validation and causes an Out-Of-Bounds Read kernel panic.
--

--- Patch [11]: [PATCH v5 11/15] s390/vfio-ap: Transition device migration state to STOP ---
commit 6eb426fca5901497387e866972eaf6e2157cc2bb
Author: Anthony Krowiak <akrowiak@linux.ibm.com>
s390/vfio-ap: Transition device migration state to STOP

This commit modifies the vfio-ap driver to support transitioning to the STOP
state during migration. It updates the state machine to return NULL instead
of -EOPNOTSUPP when transitioning out of the STOP_COPY or RESUMING states.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 7a9a8f63a76d3..21cd9810a18c4 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c
> @@ -1304,12 +1304,17 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
>  		return filp;
>  	}
>  
> +	/*
> +	 * Terminates the data transfer session of the vfio-ap device state
> +	 * between the source and target hosts. Since the vfio-ap device does
> +	 * not virtualize a DMA device, there is no internal device state to
> +	 * incorporate into the vfio-ap device on the target.
> +	 */
>  	if ((cur_state == VFIO_DEVICE_STATE_RESUMING &&
>  	     new_state == VFIO_DEVICE_STATE_STOP) ||
>  	    (cur_state == VFIO_DEVICE_STATE_STOP_COPY &&
>  	     new_state == VFIO_DEVICE_STATE_STOP)) {
> -		/* TODO */
> -		return ERR_PTR(-EOPNOTSUPP);
> +		return NULL;

[Severity: High]
Does returning NULL here without calling vfio_ap_release_mig_files() leak the
active migration file state?

If the migration is aborted and a new one starts before userspace closes the
old file descriptor, the new session overwrites the filp pointer but does not
free ap_config. Will the new read operation then see a non-NULL ap_config and
silently reuse the stale configuration snapshot from the aborted migration?

>  	}
>  
>  	if ((cur_state == VFIO_DEVICE_STATE_STOP &&

[Severity: Critical]
This is a pre-existing issue, but does creating the migration file descriptor
without incrementing the VFIO device's reference count allow a use-after-free?

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_open_file_stream() {
    ...
	filp = anon_inode_getfile("vfio_ap_mig_file", fops, matrix_mdev, flags);
    ...
}

If a user holds the migration file descriptor open and unbinds the device via
sysfs, vfio_unregister_group_dev() can complete because the migration file
descriptor holds no reference to the device. Any future read, write, or close
on the file descriptor will dereference the freed matrix_mdev:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_release_mig_file() {
    ...
	matrix_mdev = filp->private_data;
	if (!matrix_mdev || !matrix_mdev->mig_data) {
    ...
}

[Severity: Critical]
This is a pre-existing issue, but does calling fput() here cause a structural
use-after-free of the file object?

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_release_mig_files() {
    ...
	if (mig_data->stop_copy_mig_file.filp) {
		fput(mig_data->stop_copy_mig_file.filp);
		mig_data->stop_copy_mig_file.filp = NULL;
	}
    ...
}

Since vfio_ap_open_file_stream() allocates the file using anon_inode_getfile()
with a refcount of 1, and this single reference is consumed by the VFIO core
when it installs the file into the userspace file descriptor table, the driver
does not own a reference to the file.

If the device state is reset and fput() is called, it artificially drops the
refcount to 0, freeing the struct file while userspace still holds the file
descriptor open.

[Severity: Critical]
This is a pre-existing issue, but can writing fewer than
sizeof(struct vfio_ap_config) bytes to the resuming file descriptor bypass
validation and cause an out-of-bounds read?

If userspace writes a small payload (e.g., 4 bytes), allocate_ap_config()
allocates those 4 bytes directly:

drivers/s390/crypto/vfio_ap_migration.c:allocate_ap_config() {
    ...
	} else {
		config_size = len;
	}

	*ap_config = kzalloc(config_size, GFP_KERNEL_ACCOUNT);
    ...
}

This allows the write handler's completion check to instantly pass:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_resuming_write() {
    ...
	/* Check if we've completed writing the entire configuration */
	if (*pos + len == cfg_sz) {
		/* Acquire locks required to update the guest's AP config */
		...
		ret = do_post_copy_validation(mdev_name, ap_config);
    ...
}

The validation will then read a user-controlled num_queues value from the
undersized buffer and loop over the qinfo array, reading heap memory
drastically out of bounds:

drivers/s390/crypto/vfio_ap_migration.c:queues_available() {
    ...
	for (int i = 0; i < source_config->num_queues; i++) {
		apqn = source_config->qinfo[i].apqn;
    ...
}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=11

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 13/15] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration
  2026-07-24 16:13 ` [PATCH v5 13/15] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
@ 2026-07-24 18:27   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:27 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Heiko Carstens, linux-s390, kvm, Alexander Gordeev, Vasily Gorbik

> Callback to get the estimated data length of the data that will be required
> to complete reading of the vfio device's internal state. It's mandatory for
> VFIO_DEVICE_FEATURE_MIGRATION migration support.
> 
> The function pointer for this callback is specified via the
> 'migration_get_data_size' field of the 'vfio_migration_ops' structure
> which is stored with the VFIO device when the 'vfio_device'
> structure representing the mediated device is initialized.
> 
> Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=13


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 12/15] s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa
  2026-07-24 16:13 ` [PATCH v5 12/15] s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa Anthony Krowiak
@ 2026-07-24 18:29   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:29 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: kvm, linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Critical] TOCTOU data race on filp->f_pos in migration stream read/write operations causing heap out-of-bounds access.
- [High] Transitioning from RUNNING to STOP state fails to quiesce AP hardware queues, violating the VFIO migration protocol.
- [High] Unbalanced fput() in vfio_ap_release_mig_files() causes a Use-After-Free of the migration struct file.
- [High] Use-After-Free of `matrix_mdev` via migration data file descriptor.
- [Medium] Stale migration data due to failure to clear ap_config across repeated STOP_COPY or RESUMING state transitions.
- [Low] Missing space in the newly added comment.
--

--- Patch [12]: [PATCH v5 12/15] s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa ---
commit d2d58b5a535e0fa830f65967da774eb5f768e3ae
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa

This commit handles the transition between STOP and RUNNING migration states
by setting the new state without returning an error. It assumes the device is
operating normally and that no internal state needs to be incorporated on the
target since vfio-ap does not virtualize a DMA device.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 21cd9810a18c4..8876338d28980 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c
> @@ -1317,12 +1317,17 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
>  		return NULL;
>  	}
>  
> +	/*
> +	 * These states indicate migration has either not been initiated or
> +	 * has completed and the vfio-ap device is operating normally.Since the

[Severity: Low]
This isn't a bug, but there is a missing space after "normally." in the comment.

> +	 * vfio-ap device does not virtualize a DMA device, there is no internal
> +	 * device state to incorporate into the vfio-ap device on the target.
> +	 */
>  	if ((cur_state == VFIO_DEVICE_STATE_STOP &&
>  	     new_state == VFIO_DEVICE_STATE_RUNNING) ||
>  	    (cur_state == VFIO_DEVICE_STATE_RUNNING &&
>  	     new_state == VFIO_DEVICE_STATE_STOP)) {
> -		/* TODO */
> -		return ERR_PTR(-EOPNOTSUPP);
> +		return NULL;
>  	}

[Severity: High]
Does this return from the RUNNING to STOP transition arc leave the physical
AP hardware queues active?

The VFIO migration specification mandates that a device must be fully quiesced
in the STOP state, meaning no DMA, no asynchronous execution, and no interrupts.

Since this arc simply returns NULL instead of quiescing the device, could
pending asynchronous hardware operations continue and write to guest memory
after QEMU considers the device stopped?

While reviewing this patch, I noticed a few related regressions in the
migration file handling.

[Severity: High]
Can an unbalanced fput() in vfio_ap_release_mig_files() cause a use-after-free
of the migration file?

When anon_inode_getfile() creates the migration stream, it returns a file with
a refcount of 1. If userspace triggers a reset while the migration file stream
is open, the driver executes this fput() without having taken its own reference
via get_file():

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_release_mig_files() {
        ...
        if (mig_data->stop_copy_mig_file.filp) {
                fput(mig_data->stop_copy_mig_file.filp);
                mig_data->stop_copy_mig_file.filp = NULL;
        }
        ...
}

Does this drop the userspace-owned reference to 0, freeing the file while the
user's file descriptor is still open?

[Severity: Medium]
Could this logic result in migrating stale device state?

If userspace transitions the device STOP_COPY -> STOP -> STOP_COPY while
keeping the old migration stream file descriptor open, ap_config is not cleared
because the file release callback hasn't fired:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_stop_copy_read() {
        ...
        matrix_mdev = filp->private_data;
        if (!matrix_mdev->mig_data->stop_copy_mig_file.ap_config) {
                ret = vfio_ap_get_config(matrix_mdev);
        ...
}

Will the next STOP_COPY transition skip fetching the updated hardware state and
silently serve the stale configuration?

[Severity: High]
Is there a possibility of a use-after-free of matrix_mdev via the migration
file descriptor?

The migration file descriptor is created without taking a reference to the
vfio_device:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_open_file_stream() {
        ...
        filp = anon_inode_getfile("vfio_ap_mig_file", fops, matrix_mdev, flags);
        ...
}

If userspace keeps the migration file descriptor open but closes the VFIO
device file descriptor, matrix_mdev and mig_data are freed. A subsequent read
or write on the migration file descriptor would dereference them:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_stop_copy_read() {
        ...
        matrix_mdev = filp->private_data;
        if (!matrix_mdev->mig_data->stop_copy_mig_file.ap_config) {
        ...
}

Does the driver need to tie the lifetime of the migration file descriptor to
the vfio_device?

[Severity: Critical]
Does this code allow concurrent modifications to cause an out-of-bounds access
on the kernel heap?

Because the migration file is opened with stream_open(), the VFS sets
FMODE_STREAM and bypasses f_pos_lock, allowing concurrent I/O syscalls.

In vfio_ap_stop_copy_read(), pos is assigned to &filp->f_pos and validated
under mdevs_lock. The lock is then dropped before dereferencing pos during
the copy:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_stop_copy_read() {
        ...
        pos = &filp->f_pos;
        ret = validate_stop_copy_read_parms(filp, pos, len);
        ...
        mutex_unlock(&matrix_dev->mdevs_lock);

        if (copy_to_user(buf, (char *)ap_config + *pos, len)) {
        ...
}

If another thread advances filp->f_pos after the bounds check but before the
copy, could copy_to_user() read past the end of the ap_config buffer?

A similar issue appears during the write operation:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_resuming_write() {
        ...
        mutex_unlock(&matrix_dev->mdevs_lock);

        if (copy_from_user((char *)ap_config + *pos, buf, len)) {
        ...
}

Could this lead to copy_from_user() writing past the buffer if filp->f_pos is
advanced concurrently?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=12

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 14/15] s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute
  2026-07-24 16:13 ` [PATCH v5 14/15] s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute Anthony Krowiak
@ 2026-07-24 18:30   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:30 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: Vasily Gorbik, linux-s390, Alexander Gordeev, Heiko Carstens, kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The newly added 'migratable' feature is not documented in the Driver Features table, and the Limitations section explicitly states migration is not supported.
--

--- Patch [14]: [PATCH v5 14/15] s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute ---
commit c8392bd6bcb1403c66327e9551bbba5ecd4a167d
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute

This commit adds the 'migratable' feature to the sysfs features attribute
for vfio_ap. This exposes the feature to userspace, indicating that
migration of vfio devices is supported.

> diff --git a/drivers/s390/crypto/vfio_ap_drv.c b/drivers/s390/crypto/vfio_ap_drv.c
> index 8e69ed286bb9c..4e973736522bc 100644
> --- a/drivers/s390/crypto/vfio_ap_drv.c
> +++ b/drivers/s390/crypto/vfio_ap_drv.c
> @@ -26,9 +26,11 @@ MODULE_LICENSE("GPL v2");
>  struct ap_matrix_dev *matrix_dev;
>  debug_info_t *vfio_ap_dbf_info;
>  
> +#define FEATURES "guest_matrix hotplug ap_config migratable"

[Severity: Low]
Since this adds the 'migratable' feature, shouldn't
Documentation/arch/s390/vfio-ap.rst be updated to reflect this capability?

Looking at that file, the "Driver Features" table appears to be missing an
entry for this newly added 'migratable' flag.

Additionally, the "Limitations" section in the same document explicitly
states: "Live guest migration is not supported for guests using AP devices
without intervention by a system administrator."

Does this patch series render that limitation obsolete? If so, should that
section be updated or removed to avoid confusing users?

> +
>  static ssize_t features_show(struct device *dev, struct device_attribute *attr, char *buf)
>  {
> -	return sysfs_emit(buf, "guest_matrix hotplug ap_config\n");
> +	return sysfs_emit(buf, "%s\n", FEATURES);
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=14

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v5 15/15] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst
  2026-07-24 16:13 ` [PATCH v5 15/15] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
@ 2026-07-24 18:38   ` sashiko-bot
  0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-07-24 18:38 UTC (permalink / raw)
  To: Anthony Krowiak
  Cc: kvm, Alexander Gordeev, Vasily Gorbik, linux-s390, Heiko Carstens

> Add a 'Live Guest Migration' chapter to the
> Documentation/arch/s390/vfio-ap.rst document to describe the details for
> initiating live guest migration for a guest to which AP adapters, domains
> and control domains have been passed through.
> 
> Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724161351.1802644-1-akrowiak@linux.ibm.com?part=15


^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2026-07-24 18:38 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 16:13 [PATCH v5 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
2026-07-24 16:13 ` [PATCH v5 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
2026-07-24 17:36   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
2026-07-24 17:28   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
2026-07-24 17:35   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 04/15] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
2026-07-24 17:43   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 05/15] s390-vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
2026-07-24 17:47   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
2026-07-24 17:50   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 07/15] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
2026-07-24 18:04   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
2026-07-24 18:06   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 09/15] s390/vfio-ap: Add method to set a new guest AP configuration Anthony Krowiak
2026-07-24 18:10   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 10/15] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
2026-07-24 18:17   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 11/15] s390/vfio-ap: Transition device migration state to STOP Anthony Krowiak
2026-07-24 18:26   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 12/15] s390/vfio-ap: Transition device migration state from STOP to RUNNING and vice versa Anthony Krowiak
2026-07-24 18:29   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 13/15] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
2026-07-24 18:27   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 14/15] s390/vfio-ap: Add 'migratable' feature to sysfs 'features' attribute Anthony Krowiak
2026-07-24 18:30   ` sashiko-bot
2026-07-24 16:13 ` [PATCH v5 15/15] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
2026-07-24 18:38   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox