Linux s390 Architecture development
 help / color / mirror / Atom feed
* [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support
@ 2026-11-02 15:14 Anthony Krowiak
  2026-11-02 15:14 ` [PATCH v11 01/14] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
                   ` (13 more replies)
  0 siblings, 14 replies; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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 controls390/vfio-ap: Add live guest migration support
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 v10 => v11:
~~~~~~~~~~~~~~~~~~~~~
Patch 02/14: Data structures for facilitating vfio device migration
* struct vfio_ap_migration_file
  ~ Added @matrix_mdev back-pointer to the owning ap_matrix_mdev so
    read/write file ops can reach mdev state without storing it
    directly in filp->private_data
  ~ Added @lock mutex to serialize concurrent read/write operations
    and ensure f_pos is only advanced after a successful
    copy_to_user()

Patch 03/14: Functions to initialize/release vfio device migration data
* vfio_ap_init_migration_data()
  ~ Initialize @matrix_mdev back-pointer and @lock for both
    stop_copy_mig_file and resuming_mig_file embedded in mig_data
* vfio_ap_release_migration_data()
  ~ Destroy @lock for both stop_copy_mig_file and resuming_mig_file
    before freeing mig_data; moved from per-session release
    functions vfio_ap_release_stop_copy_file() and
    vfio_ap_release_resuming_file() which are called at the end
    of each migration session, not at final object lifetime

Patch 05/14: Callback to get/set vfio device mig state during guest migration
* vfio_ap_mdev_set_kvm()
  ~ vdev->kvm changed from struct kvm * to struct file * following
    upstream KVM API change; file_to_kvm_s390() now called to get
    the struct kvm *
  ~ kvm_get_kvm() / kvm_put_kvm() removed; file reference holds
    KVM lifetime

Patch 06/14: Transition guest migration state from STOP to STOP_COPY
* vfio_ap_transition_to_state()
  ~ Pass &mig_data->stop_copy_mig_file to vfio_ap_open_file_stream()
    instead of matrix_mdev; filp->private_data is now the
    vfio_ap_migration_file pointer
* vfio_ap_open_file_stream()
  ~ Changed first parameter from ap_matrix_mdev * to
    vfio_ap_migration_file *; uses mig_file->matrix_mdev internally
* vfio_ap_release_mig_file()
  ~ filp->private_data is now vfio_ap_migration_file; reach
    matrix_mdev via mig_file->matrix_mdev

Patch 07/14: File ops called to save the vfio device migration state
* validate_stop_copy_read_parms()
  ~ Added mig_file parameter; validates filp against mig_file->filp
    instead of mig_data->stop_copy_mig_file.filp
  ~ Added lockdep_assert_held(&mig_file->lock)
  ~ Removed config_sz overflow check that was masking the broken
    EOF logic (see vfio_ap_stop_copy_read() fix below)
* vfio_ap_stop_copy_read()
  ~ filp->private_data cast to vfio_ap_migration_file *; matrix_mdev
    reached via mig_file->matrix_mdev
  ~ Acquire mig_file->lock before mdevs_lock and hold across
    copy_to_user() to serialize concurrent readers and protect f_pos
  ~ *pos advanced only after copy_to_user() succeeds; mutex_unlock
    added on -EFAULT error path
  ~ if (pos) return -ESPIPE guard restored (stream_open() passes
    NULL for normal read(), non-NULL only for pread())
  ~ pos = &filp->f_pos reassignment restored after lock acquisition
  ~ Restore v10 EOF signaling: clamp len to remaining bytes with
    min_t() and return 0 on the subsequent empty read, rather than
    the broken eof-flag approach that never signaled EOF when
    QEMU's read buffer did not divide evenly into config_sz

Patch 08/14: Transition device migration state from STOP to RESUMING
* vfio_ap_resuming_init()
  ~ Changed parameter from ap_matrix_mdev * to
    vfio_ap_migration_file *
  ~ Store ap_config, config_sz, and filp directly into mig_file
    instead of via mig_data->resuming_mig_file indirection
* vfio_ap_transition_to_state()
  ~ Pass &mig_data->resuming_mig_file to vfio_ap_resuming_init()
    instead of matrix_mdev; removed the now-redundant assignment
    of mig_data->resuming_mig_file.filp after the call

Patch 09/14: File ops called to resume the vfio device migration
* validate_resuming_write_parms()
  ~ Added mig_file parameter; validates filp against
    mig_file->matrix_mdev->mig_data->resuming_mig_file.filp
  ~ Added lockdep_assert_held(&mig_file->lock)
  ~ Moved NULL check on mig_data before dereference of
    mig_data->resuming_mig_file.filp
* vfio_ap_resuming_write()
  ~ filp->private_data cast to vfio_ap_migration_file *; reach
    matrix_mdev via mig_file->matrix_mdev
  ~ Acquire mig_file->lock before ap_attr_mutex and mdevs_lock to
    serialize concurrent chunk writes on f_pos and ap_config
  ~ if (pos) return -ESPIPE guard restored
  ~ Replace get_update_locks_for_mdev() with direct mdevs_lock
    acquisition; guests_lock and kvm->lock are not needed because
    the pre-configured destination mdev model never modifies the
    guest APCB during migration write
  ~ do_post_copy_validation(): replace assert_has_update_locks_for_mdev()
    with lockdep_assert_held(&matrix_dev->mdevs_lock)

Patch 10/14: Transition device migration state to/from STOP
* do_post_copy_processing() wrapper collapsed directly into
  do_post_copy_validation(); lockdep_assert_held() calls moved
  into do_post_copy_validation()

Patch 12/14: Set up for vfio-ap device migration
* vfio_ap_setup_vfio_device_migration() kdoc
  ~ Removed stray "Enable" word from description
  ~ Collapsed double blank line between description and @matrix_mdev

Dropped patch 09/15: Prepare lock helpers and matrix init for cross-file use
* Removed: get_update_locks_for_mdev(), release_update_locks_for_mdev(),
  and assert_has_update_locks_for_mdev() are no longer needed in
  vfio_ap_migration.c; vfio_ap_resuming_write() now holds only
  mdevs_lock directly
* Removed: vfio_ap_matrix_init() no longer needs to be public;
  it was exposed so vfio_ap_resuming_write() could hot-plug the
  source AP configuration into the destination guest, which the
  pre-configured mdev model does not do

Anthony Krowiak (14):
  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: File ops called to resume the vfio device migration
  s390/vfio-ap: Transition device migration state to from/to STOP
  s390/vfio-ap: Callback to get the size of data to be migrated during
    guest migration
  s390/vfio-ap: Set up for vfio-ap device migration
  s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute
  s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst

 Documentation/arch/s390/vfio-ap.rst     |  643 ++++++++--
 drivers/s390/crypto/Makefile            |    2 +-
 drivers/s390/crypto/vfio_ap_drv.c       |   14 +-
 drivers/s390/crypto/vfio_ap_migration.c | 1454 +++++++++++++++++++++++
 drivers/s390/crypto/vfio_ap_ops.c       |   70 +-
 drivers/s390/crypto/vfio_ap_private.h   |   12 +
 6 files changed, 2089 insertions(+), 106 deletions(-)
 create mode 100644 drivers/s390/crypto/vfio_ap_migration.c

-- 
2.53.0


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

* [PATCH v11 01/14] s390/vfio-ap: Provide function to get the number of queues assigned to mdev
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 15:23   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 02/14] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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>
Reviewed-by: Jason J. Herne <jjherne@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_ops.c     | 8 ++++++++
 drivers/s390/crypto/vfio_ap_private.h | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 373ce574c431..83ba4193bdc3 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -2113,6 +2113,14 @@ static void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)
 	release_update_locks_for_mdev(matrix_mdev);
 }
 
+unsigned int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix)
+{
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	return bitmap_weight(ap_matrix->apm, AP_DEVICES) *
+	       bitmap_weight(ap_matrix->aqm, AP_DOMAINS);
+}
+
 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..12d7d240addd 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;
 };
 
+unsigned 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] 29+ messages in thread

* [PATCH v11 02/14] s390/vfio-ap: Data structures for facilitating vfio device migration
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
  2026-11-02 15:14 ` [PATCH v11 01/14] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 15:29   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 03/14] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
Reviewed-by: Jason J. Herne <jjherne@linux.ibm.com>
---
 drivers/s390/crypto/Makefile            |   2 +-
 drivers/s390/crypto/vfio_ap_migration.c | 125 ++++++++++++++++++++++++
 drivers/s390/crypto/vfio_ap_private.h   |   5 +
 3 files changed, 131 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..2a0a8c76f820
--- /dev/null
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Drives vfio_ap mdev migration.
+ *
+ * Copyright IBM Corp. 2025
+ */
+#include "vfio_ap_private.h"
+
+/* Magic number and version for the vfio_ap_config migration blob */
+#define VFIO_AP_MIG_MAGIC			0x76666170U  /* "vfap" */
+#define VFIO_AP_MIG_VERSION			1U
+
+/**
+ * struct vfio_ap_migration_file:
+ *
+ * This object is used for chunk processing of multiple reads and writes of
+ * AP configuration information.
+ *
+ * @matrix_mdev:	object used for managing the state of the mdev
+ * @lock:		mutex used for synchronizing read/write operations
+ * @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 ap_matrix_mdev	*matrix_mdev;
+	struct mutex		lock; /* protects read/write operations */
+	struct file		*filp;
+	struct vfio_ap_config	*ap_config;
+	unsigned long		config_sz;
+};
+
+/**
+ * struct 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_data_complete:	indicates whether a complete, valid AP
+ *				configuration blob was received (true) or not (false)
+ *				during the RESUMING phase. This must be checked
+ *				on RESUMING->STOP to enforce the vfio.h
+ *				requirement that SET_STATE must fail if the data
+ *				is incomplete or invalid.
+ * @resuming_mig_file:		the object used to restore the state of the
+ *				vfio-ap device on 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;
+	bool				resuming_mig_data_complete;
+	struct vfio_ap_migration_file	resuming_mig_file;
+	struct vfio_ap_migration_file	stop_copy_mig_file;
+};
+
+/**
+ * struct 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];
+};
+
+/**
+ * struct vfio_ap_config:
+ *
+ * Stores the state of a guest's AP configuration.
+ *
+ * VFIO device migration state transition from STOP to STOP_COPY:
+ * -------------------------------------------------------------
+ * When the migration state transitions from STOP to STOP_COPY, the vfio_ap device
+ * driver will open a file stream in read-only mode and return the fd to userspace.
+ * This fd is used during the STOP_COPY phase to read the current state of the
+ * vfio-ap device on the source host. In response, the driver will store the
+ * source guest's AP configuration data in a vfio_ap_config object and copy it to
+ * userspace.
+ *
+ * VFIO device migration state transition from STOP to RESUMING:
+ * ------------------------------------------------------------
+ * When the VFIO migration state transitions from STOP to RESUMING,
+ * the vfio_ap device driver will open a file stream in write-only mode and
+ * return the fd to userspace. This fd is used during the RESUMING phase to
+ * write the source guest's vfio_ap_config data that was read in during the
+ * STOP_COPY phase to the vfio_ap device driver on the destination host. In
+ * response, the device driver will copy the data sent from userspace to a
+ * vfio_ap_config instance which is then used to update the destination guest's
+ * AP configuration.
+ *
+ * Since the source and destination hosts may be running different versions of
+ * the linux kernel, the vfio_ap_config object provides two fields (@magic and
+ * @version) which must be set by the source device driver and verified by the
+ * destination device driver to ensure the migration ABI of the source and
+ * destination hosts are compatible.
+ *
+ * Note: Since a guest's AP configuration could be comprised of a large number of
+ *	 AP queue devices, a vfio_ap_config object should be allocated using
+ *	 kvzalloc.
+ *
+ * @magic:	identifies this as a valid vfio_ap_config migration blob;
+ *		must equal VFIO_AP_MIG_MAGIC
+ * @version:	layout version; must equal VFIO_AP_MIG_VERSION
+ * @num_queues:	the number of queues passed through to the guest
+ * @reserved:	padding to ensure proper alignment of @adm
+ * @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				magic;
+	u32				version;
+	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 12d7d240addd..172471793e22 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] 29+ messages in thread

* [PATCH v11 03/14] s390/vfio-ap: Functions to initialize/release vfio device migration data
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
  2026-11-02 15:14 ` [PATCH v11 01/14] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
  2026-11-02 15:14 ` [PATCH v11 02/14] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 15:46   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 04/14] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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

* 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.

* vfio_ap_release_mig_files

  This function is called from the vfio_ap_release_migration_data
  function (above) and releases the vfio_ap_migration_file objects
  contained within the vfio_ap_migation_data object used to maintain
  the state of the VFIO migration process.

* vfio_ap_release_stop_copy_file

  This function is called from the vfio_ap_release_mig_files function
  (above) to release the vfio_ap_migration_file used during the
  STOP_COPY phase of migration. For now, this is a stub function that
  will be fully implemented in a subsequent patch after the
  vfio_ap_config object referenced within is allocated, as it will
  need to be freed according to how it is allocated.

* vfio_ap_release_resuming_file

  This function is called from the vfio_ap_release_mig_files function
  (above) to release the vfio_ap_migration_file used during the
  RESUMING phase of migration. For now, this is a stub function that
  will be fully implemented in a subsequent patch after the
  vfio_ap_config object referenced within is allocated, as it will
  need to be freed according to how it is allocated.

* vfio_ap_set_state, vfio_ap_get_state and vfio_ap_get_data_size

  These three functions are callback functions assigned to the
  vfio_ap_migration_ops (.migration_set_state, .migration_get_state
  and .migration_get_data_size function pointers).

Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 103 ++++++++++++++++++++++++
 drivers/s390/crypto/vfio_ap_ops.c       |  41 ++++++++--
 drivers/s390/crypto/vfio_ap_private.h   |   3 +
 3 files changed, 142 insertions(+), 5 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 2a0a8c76f820..d525dec782a1 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"
 
 /* Magic number and version for the vfio_ap_config migration blob */
@@ -123,3 +124,105 @@ struct vfio_ap_config {
 	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
 	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)
+{
+	kvfree(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 void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_data)
+{
+	kvfree(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;
+	mig_data->resuming_mig_data_complete = false;
+}
+
+/**
+ * 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);
+
+	/*
+	 * Live guest migration is not supported for SE guests, so there is no
+	 * need to initialize the migration data.
+	 */
+	if (ap_is_se_guest())
+		return 0;
+
+	mig_data = kzalloc_obj(struct vfio_ap_migration_data, GFP_KERNEL);
+	if (!mig_data)
+		return -ENOMEM;
+
+	mig_data->mig_state = VFIO_DEVICE_STATE_RUNNING;
+	mig_data->resuming_mig_data_complete = false;
+	mig_data->resuming_mig_file.matrix_mdev = matrix_mdev;
+	mutex_init(&mig_data->resuming_mig_file.lock);
+	mig_data->stop_copy_mig_file.matrix_mdev = matrix_mdev;
+	mutex_init(&mig_data->stop_copy_mig_file.lock);
+	matrix_mdev->mig_data = mig_data;
+
+	return 0;
+}
+
+/**
+ * vfio_ap_release_mig_files:
+ *
+ * Free the ap_config buffers for any open migration FDs. Although a
+ * migration FD may still be held open by userspace, it is safe to free
+ * mig_data here because:
+ *
+ *   1. matrix_mdev remains valid for the lifetime of any open migration
+ *      FD via the vfio_device registration reference taken in
+ *      vfio_ap_open_file_stream() and dropped in
+ *      vfio_ap_release_mig_file().
+ *
+ *   2. mig_data is only accessed by the migration file ops
+ *      (vfio_ap_stop_copy_read, vfio_ap_resuming_write) under
+ *      mdevs_lock. Once mig_data is set to NULL by the caller, those
+ *      paths will see NULL and return -ENODEV before dereferencing it.
+ *
+ * @matrix_mdev: The object used to maintain the state for a mediated device
+ */
+static void vfio_ap_release_mig_files(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct vfio_ap_migration_data *mig_data;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	mig_data = matrix_mdev->mig_data;
+	vfio_ap_release_stop_copy_file(mig_data);
+	vfio_ap_release_resuming_file(mig_data);
+}
+
+/**
+ * vfio_ap_release_migration_data: reclaim private migration data
+ *
+ * @matrix_mdev: pointer to the object containing the state of 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;
+
+	vfio_ap_release_mig_files(matrix_mdev);
+	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
+	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
+	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 83ba4193bdc3..8464d2e86d3a 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -794,6 +794,16 @@ static int vfio_ap_mdev_init_dev(struct vfio_device *vdev)
 	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;
@@ -2067,19 +2077,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);
 }
@@ -2405,6 +2435,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 172471793e22..aa8a22585204 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -172,4 +172,7 @@ 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);
 
+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] 29+ messages in thread

* [PATCH v11 04/14] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (2 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 03/14] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 15:49   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 05/14] s390/vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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 any open migration file buffers and resets the
migration state to RUNNING.

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

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index d525dec782a1..6bcb910526c9 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -226,3 +226,23 @@ void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev)
 	kfree(matrix_mdev->mig_data);
 	matrix_mdev->mig_data = NULL;
 }
+
+/**
+ * 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 8464d2e86d3a..79f016a43f56 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -2328,6 +2328,17 @@ static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev,
 		break;
 	case VFIO_DEVICE_RESET:
 		ret = vfio_ap_mdev_reset_queues(matrix_mdev);
+		/*
+		 * Reset the migration state unconditionally, regardless of
+		 * whether the hardware queue reset succeeded. The migration
+		 * state tracks the AP configuration transfer lifecycle and is
+		 * independent of the hardware queue reset outcome. The VFIO
+		 * migration spec designates VFIO_DEVICE_RESET as the mechanism
+		 * to return device_state to RUNNING (e.g. to recover from
+		 * ERROR), so the migration state must be cleaned up even when
+		 * one or more queues fail to reset.
+		 */
+		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 aa8a22585204..c6e577064e7e 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -174,5 +174,6 @@ void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
 
 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] 29+ messages in thread

* [PATCH v11 05/14] s390/vfio-ap: Callback to get/set vfio device mig state during guest migration
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (3 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 04/14] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 16:06   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 06/14] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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 | 144 ++++++++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 6bcb910526c9..8e4e04e03965 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -143,6 +143,150 @@ static void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_dat
 	mig_data->resuming_mig_data_complete = false;
 }
 
+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)
+{
+	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;
+
+	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.
+	 * 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 != 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;
+		}
+	}
+
+	if (mig_data->mig_state == VFIO_DEVICE_STATE_ERROR)
+		filp = ERR_PTR(-EIO);
+
+	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)
+{
+	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,
+				 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_migration_data - initialize migration data and functions
  *
-- 
2.53.0


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

* [PATCH v11 06/14] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (4 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 05/14] s390/vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 16:18   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 07/14] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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 | 74 ++++++++++++++++++++++++-
 1 file changed, 72 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 8e4e04e03965..48028b12b26f 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"
 
@@ -143,6 +144,62 @@ static void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_dat
 	mig_data->resuming_mig_data_complete = false;
 }
 
+static ssize_t
+vfio_ap_stop_copy_read(struct file *filp, char __user *buf, size_t len,
+		       loff_t *pos)
+{
+	/* TODO */
+	return -EOPNOTSUPP;
+}
+
+static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
+{
+	struct vfio_ap_migration_file *mig_file = filp->private_data;
+
+	/*
+	 * The mig_file (and by extension mig_file->matrix_mdev) is tied to
+	 * migration FD lifetime (see vfio_ap_open_file_stream() function) to
+	 * guarantee that the matrix_mdev remains valid for every read/write
+	 * operation. Let's drop it here since the migration FD is being
+	 * released.
+	 */
+	vfio_device_put_registration(&mig_file->matrix_mdev->vdev);
+	return 0;
+}
+
+static const struct file_operations vfio_ap_stop_copy_fops = {
+	.owner = THIS_MODULE,
+	.read = vfio_ap_stop_copy_read,
+	.release = vfio_ap_release_mig_file,
+};
+
+static struct file *vfio_ap_open_file_stream(struct vfio_ap_migration_file *mig_file,
+					     const struct file_operations *fops,
+					     int flags)
+{
+	struct file *filp;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	/*
+	 * Pin the vfio_device registration so that matrix_mdev cannot be freed
+	 * while the migration FD is still open. The matching put is in
+	 * vfio_ap_release_mig_file().
+	 */
+	if (!vfio_device_try_get_registration(&mig_file->matrix_mdev->vdev))
+		return ERR_PTR(-ENODEV);
+
+	filp = anon_inode_getfile("vfio_ap_mig_file", fops, mig_file, flags);
+	if (IS_ERR(filp)) {
+		vfio_device_put_registration(&mig_file->matrix_mdev->vdev);
+		return 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)
@@ -156,10 +213,23 @@ 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 vfio_ap_migration_file *mig_file = &mig_data->stop_copy_mig_file;
+		struct file *filp = vfio_ap_open_file_stream(mig_file,
+							     &vfio_ap_stop_copy_fops,
+							     O_RDONLY);
+		if (IS_ERR(filp))
+			return ERR_CAST(filp);
+
+		mig_file->filp = filp;
+
+		return filp;
 	}
 
 	if (cur_state == VFIO_DEVICE_STATE_STOP &&
-- 
2.53.0


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

* [PATCH v11 07/14] s390/vfio-ap: File ops called to save the vfio device migration state
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (5 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 06/14] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 16:32   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 08/14] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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 | 272 +++++++++++++++++++++++-
 1 file changed, 264 insertions(+), 8 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 48028b12b26f..b64ce092a41b 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -6,6 +6,7 @@
  */
 #include <linux/anon_inodes.h>
 #include <linux/file.h>
+#include <linux/slab.h>
 #include "vfio_ap_private.h"
 
 /* Magic number and version for the vfio_ap_config migration blob */
@@ -144,14 +145,6 @@ static void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_dat
 	mig_data->resuming_mig_data_complete = false;
 }
 
-static ssize_t
-vfio_ap_stop_copy_read(struct file *filp, char __user *buf, size_t len,
-		       loff_t *pos)
-{
-	/* TODO */
-	return -EOPNOTSUPP;
-}
-
 static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 {
 	struct vfio_ap_migration_file *mig_file = filp->private_data;
@@ -167,6 +160,269 @@ static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 	return 0;
 }
 
+/**
+ * validate_stop_copy_read_parms: Validate the input parameters to the
+ *                                vfio_ap_stop_copy_read function
+ *
+ * @mig_file:	Pointer to the object used for storing the vfio-ap device state
+ * @filp:	File stream to which to write source guest's AP configuration
+ * @len:	The length of the data to be read
+ *
+ * Verify the following:
+ * - @filp is the instance opened when state transitioned from STOP to STOP_COPY
+ * - @filp->f_pos + @len does not cause integer overflow
+ * - @mig_file->matrix_mdev is not NULL
+ * - @mig_file->matrix_mdev->mig_data is not NULL
+ *
+ * Returns: 0 if the parameters pass validation; otherwise returns an error
+ */
+static int validate_stop_copy_read_parms(struct vfio_ap_migration_file *mig_file,
+					 struct file *filp,
+					 size_t len)
+{
+	loff_t total_len;
+
+	lockdep_assert_held(&mig_file->lock);
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (filp != mig_file->filp)
+		return -EINVAL;
+
+	if (check_add_overflow((loff_t)len, filp->f_pos, &total_len))
+		return -EIO;
+
+	if (!mig_file->matrix_mdev || !mig_file->matrix_mdev->mig_data)
+		return -ENODEV;
+
+	return 0;
+}
+
+static size_t vfio_ap_config_size(struct ap_matrix_mdev *matrix_mdev,
+				  unsigned 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_test_queue(apqn, 1, 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 */
+		break;
+	default:
+		pr_err_ratelimited("vfio_ap_mdev %s: Failed to get hwinfo for queue %02lx.%04lx: TAPQ rc=%d\n",
+				   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
+				   status.response_code);
+		return -EIO;
+	}
+
+	/* There should be at least one of the mode bits set */
+	if (!hwinfo->value) {
+		pr_err_ratelimited("vfio_ap_mdev %s: Empty hwinfo for queue %02lx.%04lx: TAPQ rc=%d\n",
+				   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
+				   status.response_code);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * vfio_ap_store_queue_info:
+ *
+ * Stores the hardware information returned from the PQAP(TAPQ) command for each
+ * queue device identified in the 'qinfo' field of the a vfio_ap_config
+ * object. The APQNs in the 'qinfo' field must already have been snapshotted
+ * prior to calling this function.
+ *
+ * @mdev_name:	The name (UUID) of the mediated device to use in log messages
+ * @ap_config:	A reference to the vfio_ap_config instance in which to store
+ *		the queue information. It is expected that each APQN identifying
+ *		a queue device for which hardware information is to be retrieved
+ *		shall be snapshotted prior to calling this function.
+ *
+ * Returns:	Zero (0) if the hardware information is retrieved for each queue
+ *		device in the AP configuration; otherwise, returns an error.
+ */
+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;
+
+	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)
+{
+	struct vfio_ap_config *ap_configuration;
+	unsigned long *apm, *aqm, apid, apqi;
+	unsigned int num_queues;
+	const char *mdev_name;
+	size_t ap_config_size;
+	int ret, qindex;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	ap_config_size = vfio_ap_config_size(matrix_mdev, &num_queues);
+
+	ap_configuration = kvzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);
+	if (!ap_configuration)
+		return -ENOMEM;
+
+	ap_configuration->magic   = VFIO_AP_MIG_MAGIC;
+	ap_configuration->version = VFIO_AP_MIG_VERSION;
+
+	/*
+	 * num_queues must be set before writing qinfo[] elements; the
+	 * __counted_by(num_queues) annotation on qinfo[] causes the compiler to
+	 * insert bounds checks that evaluate against ap_configuration->num_queues.
+	 * Writing through qinfo[i] with num_queues still 0 would trap.
+	 */
+	ap_configuration->num_queues = num_queues;
+
+	apm = matrix_mdev->shadow_apcb.apm;
+	aqm = matrix_mdev->shadow_apcb.aqm;
+	qindex = 0;
+	for_each_set_bit_inv(apid, apm, AP_DEVICES) {
+		for_each_set_bit_inv(apqi, aqm, AP_DOMAINS) {
+			ap_configuration->qinfo[qindex].apqn =
+				AP_MKQID(apid, apqi);
+			qindex += 1;
+		}
+	}
+	memcpy(ap_configuration->adm, matrix_mdev->shadow_apcb.adm,
+	       sizeof(ap_configuration->adm));
+	mdev_name = dev_name(matrix_mdev->vdev.dev);
+
+	ret = vfio_ap_store_queue_info(mdev_name, ap_configuration);
+	if (ret) {
+		kvfree(ap_configuration);
+		return ret;
+	}
+
+	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 vfio_ap_migration_file *mig_file;
+	void *bounce;
+	ssize_t ret;
+
+	/*
+	 * This file was opened with stream_open(), so pos should be NULL for
+	 * sequential read() calls; a non-NULL pointer will be passed only
+	 * for positional pread() calls in which case we return an error
+	 * indicating broken pipe/illegal seek on a non-seekable file
+	 */
+	if (pos)
+		return -ESPIPE;
+
+	/*
+	 * mig_file is guaranteed live here: vfio_ap_open_file_stream() took
+	 * a vfio_device registration reference that is held until
+	 * vfio_ap_release_mig_file() runs, so the embedding mig_file cannot
+	 * be freed while this file descriptor is open.
+	 */
+	mig_file = filp->private_data;
+	mutex_lock(&mig_file->lock);
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	ret = validate_stop_copy_read_parms(mig_file, filp, len);
+	if (ret) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		mutex_unlock(&mig_file->lock);
+		return ret;
+	}
+
+	pos = &filp->f_pos;
+
+	/*
+	 * The data to be returned to the caller - i.e., the source guest's
+	 * AP configuration data - is generated on the first read() call. The
+	 * result is cached in mig_file->ap_config for subsequent read() calls.
+	 */
+	if (!mig_file->ap_config) {
+		ret = vfio_ap_get_config(mig_file->matrix_mdev);
+		if (ret) {
+			mutex_unlock(&matrix_dev->mdevs_lock);
+			mutex_unlock(&mig_file->lock);
+			return ret;
+		}
+	}
+
+	/* Return EOF if all of the AP configuration data has been read */
+	if (*pos >= mig_file->config_sz) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		mutex_unlock(&mig_file->lock);
+		return 0;
+	}
+
+	/*
+	 * Don't read past the end of the AP configuration data. Limit len to
+	 * the number of bytes remaining.
+	 */
+	len = min_t(size_t, mig_file->config_sz - *pos, len);
+
+	/*
+	 * Copy into a bounce buffer under mdevs_lock and advance *pos, then
+	 * perform copy_to_user() outside the lock. This avoids stalling
+	 * all host mdev operations if userspace passes a buffer that faults.
+	 */
+	bounce = kmalloc(len, GFP_KERNEL);
+	if (!bounce) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		mutex_unlock(&mig_file->lock);
+		return -ENOMEM;
+	}
+
+	memcpy(bounce, (char *)mig_file->ap_config + *pos, len);
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	if (copy_to_user(buf, bounce, len)) {
+		kfree(bounce);
+		mutex_unlock(&mig_file->lock);
+		return -EFAULT;
+	}
+
+	*pos += len;
+	mutex_unlock(&mig_file->lock);
+	kfree(bounce);
+	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] 29+ messages in thread

* [PATCH v11 08/14] s390/vfio-ap: Transition device migration state from STOP to RESUMING
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (6 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 07/14] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 16:42   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 09/14] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
                   ` (5 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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>
Reviewed-by: Jason J. Herne <jjherne@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 84 ++++++++++++++++++++++++-
 1 file changed, 82 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index b64ce092a41b..526d83e8590d 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -13,6 +13,16 @@
 #define VFIO_AP_MIG_MAGIC			0x76666170U  /* "vfap" */
 #define VFIO_AP_MIG_VERSION			1U
 
+/* The maximum number of queues that can be installed in an s390 system */
+#define MAX_AP_QUEUES				(AP_DEVICES * AP_DOMAINS)
+
+/* The maximum size of a vfio_ap_config blob, used to pre-allocate the receive
+ * buffer on the destination host during the RESUMING phase of migration.
+ */
+#define VFIO_AP_CONFIG_MAX_SIZE \
+	(sizeof(struct vfio_ap_config) + \
+	 MAX_AP_QUEUES * sizeof(struct vfio_ap_queue_info))
+
 /**
  * struct vfio_ap_migration_file:
  *
@@ -456,6 +466,67 @@ static struct file *vfio_ap_open_file_stream(struct vfio_ap_migration_file *mig_
 	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,
+};
+
+/**
+ * vfio_ap_resuming_init
+ *
+ * Initializes the ap_config, config_sz and filp fields of a
+ * struct vfio_ap_migration_file instance:
+ *
+ * 1. Pre-allocates the maximum possible receive buffer using kvzalloc so that
+ *    vfio_ap_resuming_write() can copy directly into it without needing
+ *    to know num_queues up front.  kvzalloc falls back to vmalloc for
+ *    large allocations rather than failing a high-order kmalloc.
+ * 2. Sets num_queues to MAX_AP_QUEUES immediately after allocation so
+ *    that the __counted_by(num_queues) annotation on qinfo[] covers
+ *    the full pre-allocated buffer. Without this, fortified memcpy()
+ *    computes object size from num_queues == 0 and will fortify_panic()
+ *    on the first write chunk that reaches into qinfo[].  num_queues
+ *    will be overwritten with the correct value when the source guest's
+ *    AP configuration data is received via vfio_ap_resuming_write().
+ *
+ * @mig_file:	pointer to object that maintains the state of the migration
+ *		data stream.
+ */
+static struct file *vfio_ap_resuming_init(struct vfio_ap_migration_file *mig_file)
+{
+	struct vfio_ap_config *ap_config;
+	struct file *filp;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	ap_config = kvzalloc(VFIO_AP_CONFIG_MAX_SIZE, GFP_KERNEL_ACCOUNT);
+	if (!ap_config)
+		return ERR_PTR(-ENOMEM);
+
+	ap_config->num_queues = MAX_AP_QUEUES;
+
+	filp = vfio_ap_open_file_stream(mig_file, &vfio_ap_resume_fops, O_WRONLY);
+
+	if (IS_ERR(filp)) {
+		kvfree(ap_config);
+		return filp;
+	}
+
+	mig_file->ap_config = ap_config;
+	mig_file->config_sz = VFIO_AP_CONFIG_MAX_SIZE;
+	mig_file->filp = filp;
+
+	return filp;
+}
+
 static struct file *
 vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 			    enum vfio_device_mig_state new_state)
@@ -488,10 +559,19 @@ 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(&mig_data->resuming_mig_file);
+
+		if (IS_ERR(filp))
+			return ERR_CAST(filp);
+
+		return filp;
 	}
 
 	if ((cur_state == VFIO_DEVICE_STATE_RESUMING &&
-- 
2.53.0


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

* [PATCH v11 09/14] s390/vfio-ap: File ops called to resume the vfio device migration
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (7 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 08/14] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 17:09   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 10/14] s390/vfio-ap: Transition device migration state to from/to STOP Anthony Krowiak
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

Implements the 'write' callback function for the file stream created to
restore the state of the vfio-ap device on the destination host during the
RESUMING phase.

Userspace reads the source guest's saved AP configuration metadata from the
STOP_COPY stream and writes it to this stream in chunks. The write callback
accumulates the stream into a pre-allocated vfio_ap_config buffer. Once all
data has arrived, the configuration is validated against the destination
mdev's pre-configured shadow_apcb and hardware capabilities.

In order for the source guest's configuration to be considered compatible:
* The migration data must carry the expected magic number and version.
* Every APQN and control domain in the source configuration must be present
  in the destination mdev's shadow_apcb.
* The hardware facilities and AP type of each queue must be compatible
  between source and destination.

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

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 526d83e8590d..47f05e43b9e0 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -13,6 +13,51 @@
 #define VFIO_AP_MIG_MAGIC			0x76666170U  /* "vfap" */
 #define VFIO_AP_MIG_VERSION			1U
 
+/*
+ * 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-15
+ *     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-39:
+ */
+#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)
 
@@ -466,11 +511,562 @@ static struct file *vfio_ap_open_file_stream(struct vfio_ap_migration_file *mig_
 	return filp;
 }
 
+static int validate_resuming_write_parms(struct vfio_ap_migration_file *mig_file,
+					 struct file *filp, size_t len)
+{
+	loff_t total_len;
+
+	lockdep_assert_held(&mig_file->lock);
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (!len || filp->f_pos < 0)
+		return -EINVAL;
+
+	if (!mig_file->matrix_mdev->mig_data)
+		return -ENODEV;
+
+	/* Reject any write after the full configuration has been received */
+	if (mig_file->matrix_mdev->mig_data->resuming_mig_data_complete)
+		return -EPROTO;
+
+	if (filp != mig_file->filp)
+		return -ENXIO;
+
+	if (check_add_overflow((loff_t)len, filp->f_pos, &total_len))
+		return -ERANGE;
+
+	/* Reject writes that would overflow the pre-allocated buffer */
+	if (filp->f_pos + len > VFIO_AP_CONFIG_MAX_SIZE)
+		return -EIO;
+
+	return 0;
+}
+
+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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("vfio_ap_mdev %s: Coprocessor-mode facility not installed in target queue %02lx.%04lx\n",
+					   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err_ratelimited("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_ratelimited("vfio_ap_mdev %s: Coprocessor-mode facility installed in 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_ratelimited("vfio_ap_mdev %s: Accelerator-mode facility installed in source queue %02lx.%04lx\n",
+					   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err_ratelimited("vfio_ap_mdev %s: Accelerator-mode facility not installed in target queue %02lx.%04lx\n",
+					   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err_ratelimited("vfio_ap_mdev %s: Accelerator-mode facility not installed in source queue %02lx.%04lx\n",
+					   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err_ratelimited("vfio_ap_mdev %s: Accelerator-mode facility installed in 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_ratelimited("vfio_ap_mdev %s: XCP-mode facility installed in source queue %02lx.%04lx\n",
+					   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err_ratelimited("vfio_ap_mdev %s: XCP-mode facility not installed in target queue %02lx.%04lx\n",
+					   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err_ratelimited("vfio_ap_mdev %s: XCP-mode facility not installed in source queue %02lx.%04lx\n",
+					   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err_ratelimited("vfio_ap_mdev %s: XCP-mode facility installed in 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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("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_ratelimited("vfio_ap_mdev %s: AP type of source queue %02lx.%04lx (%02x) not compatible with target (%02x)\n",
+				   mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
+				   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_ratelimited("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_ratelimited("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_ratelimited("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;
+}
+
+/**
+ * ap_config_is_subset_of_shadow_apcb:
+ *
+ * Verify that each APQN and control domain in the source guest's AP
+ * configuration is present in the destination mdev's shadow_apcb. The
+ * destination mdev must be pre-configured by the administrator with at least
+ * the adapters, domains and control domains the source guest is actively
+ * using. Since shadow_apcb is derived from the administrator's assignment
+ * filtered to only installed and vfio_ap-bound queues, any APQN or control
+ * domain present in shadow_apcb is guaranteed to be available.
+ *
+ * All missing APQNs and control domains are logged before returning so the
+ * administrator sees all problems at once rather than one per retry.
+ *
+ * @mdev_name:     The mdev name to use in error messages
+ * @matrix_mdev:   The destination mdev whose shadow_apcb to check against
+ * @source_config: The source guest's AP configuration
+ *
+ * Returns: true if every APQN and control domain in @source_config is present
+ *          in @matrix_mdev->shadow_apcb; otherwise returns false.
+ */
+static bool ap_config_is_subset_of_shadow_apcb(const char *mdev_name,
+					       struct ap_matrix_mdev *matrix_mdev,
+					       struct vfio_ap_config *source_config)
+{
+	unsigned long apid, apqi, apqn, domain_num;
+	bool ret = true;
+
+	for (int i = 0; i < source_config->num_queues; i++) {
+		apqn = source_config->qinfo[i].apqn;
+		apid = AP_QID_CARD(apqn);
+		apqi = AP_QID_QUEUE(apqn);
+
+		if (!test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) ||
+		    !test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) {
+			pr_err_ratelimited("vfio_ap_mdev %s: Queue %02lx.%04lx not in destination mdev's AP configuration\n",
+					   mdev_name, apid, apqi);
+			ret = false;
+		}
+	}
+
+	for_each_set_bit_inv(domain_num, (unsigned long *)source_config->adm,
+			     AP_DOMAINS) {
+		if (!test_bit_inv(domain_num, matrix_mdev->shadow_apcb.adm)) {
+			pr_err_ratelimited("vfio_ap_mdev %s: Control domain %04lx not in destination mdev's AP configuration\n",
+					   mdev_name, domain_num);
+			ret = false;
+		}
+	}
+
+	return ret;
+}
+
+static int do_post_copy_validation(struct ap_matrix_mdev *matrix_mdev,
+				   struct vfio_ap_config *source_config)
+{
+	const char *mdev_name = dev_name(matrix_mdev->vdev.dev);
+
+	lockdep_assert_held(&ap_attr_mutex);
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (source_config->magic != VFIO_AP_MIG_MAGIC ||
+	    source_config->version != VFIO_AP_MIG_VERSION)
+		return -EINVAL;
+
+	if (!ap_config_is_subset_of_shadow_apcb(mdev_name, matrix_mdev,
+						source_config))
+		return -EINVAL;
+
+	return verify_ap_configs_are_compatible(mdev_name, source_config);
+}
+
 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_file *mig_file;
+	struct vfio_ap_config *ap_config;
+	ssize_t ret;
+	void *kbuf;
+
+	/*
+	 * This file was opened with stream_open(), so pos should be NULL for
+	 * sequential write() calls; a non-NULL pointer will be passed only
+	 * for positional pwrite() calls in which case we return an error
+	 * indicating broken pipe/illegal seek on a non-seekable file.
+	 */
+	if (pos)
+		return -ESPIPE;
+
+	/*
+	 * Reject absurd lengths before allocating the bounce buffer below.
+	 * This only caps the allocation: a write can never be larger than
+	 * the receive buffer. The authoritative check against the space
+	 * remaining is made by validate_resuming_write_parms() under the
+	 * locks.
+	 */
+	if (len > VFIO_AP_CONFIG_MAX_SIZE)
+		return -EIO;
+
+	/*
+	 * Copy the user data into a kernel buffer before taking any locks.
+	 * copy_from_user() can fault and block for an unbounded time, which
+	 * must not happen while holding ap_attr_mutex or mdevs_lock.
+	 */
+	kbuf = vmemdup_user(buf, len);
+	if (IS_ERR(kbuf))
+		return PTR_ERR(kbuf);
+
+	/*
+	 * mig_file->lock is taken first to serialize concurrent write()
+	 * calls on the same migration fd, preventing two chunks from
+	 * racing to update f_pos and ap_config simultaneously.
+	 *
+	 * ap_attr_mutex is held for the full duration of the write, just
+	 * as (un)assign_adapter_store() and (un)assign_domain_store() do.
+	 * This prevents the AP bus apmask/aqmask attributes from being
+	 * changed while the guest's AP configuration is being validated.
+	 *
+	 * mdevs_lock is held to protect access to matrix_mdev->shadow_apcb,
+	 * mig_data, and mig_data->resuming_mig_data_complete. guests_lock
+	 * and kvm->lock are not needed because the pre-configured destination
+	 * mdev model does not hot-plug the source guest's AP configuration
+	 * into the running guest; the guest APCB is never modified here.
+	 *
+	 * Holding these locks for the full duration of a potentially chunked
+	 * write could in theory affect performance, but this is acceptable
+	 * for several reasons: migration is an infrequent operation;
+	 * real-world guest AP configurations are small (each CEX8 card has
+	 * 2 adapters and up to 16 domains, and the 85 cards in a system are
+	 * shared across all LPARs and guests); and only a memcpy() and some
+	 * integer checks are done for each chunk while the locks are held.
+	 */
+	mig_file = filp->private_data;
+	mutex_lock(&mig_file->lock);
+	mutex_lock(&ap_attr_mutex);
+	mutex_lock(&matrix_dev->mdevs_lock);
+	pos = &filp->f_pos;
+
+	ret = validate_resuming_write_parms(mig_file, filp, len);
+	if (ret)
+		goto out_unlock;
+
+	ap_config = mig_file->ap_config;
+	memcpy((char *)ap_config + *pos, kbuf, len);
+	*pos += len;
+
+	/*
+	 * Check whether we have received all the data.  We know the total
+	 * expected size once num_queues has arrived, i.e. once *pos is past
+	 * the fixed header.  Until then keep accumulating.
+	 */
+	if (*pos >= offsetofend(struct vfio_ap_config, num_queues)) {
+		size_t expected_sz;
+
+		if (ap_config->num_queues > MAX_AP_QUEUES) {
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
+		expected_sz = sizeof(struct vfio_ap_config) +
+			      ap_config->num_queues *
+			      sizeof(struct vfio_ap_queue_info);
+
+		if (*pos > (loff_t)expected_sz) {
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
+		if (*pos == (loff_t)expected_sz) {
+			ret = do_post_copy_validation(mig_file->matrix_mdev, ap_config);
+			mig_file->matrix_mdev->mig_data->resuming_mig_data_complete = !ret;
+		}
+	}
+
+out_unlock:
+	mutex_unlock(&matrix_dev->mdevs_lock);
+	mutex_unlock(&ap_attr_mutex);
+	mutex_unlock(&mig_file->lock);
+	kvfree(kbuf);
+	return ret ? ret : (ssize_t)len;
 }
 
 static const struct file_operations vfio_ap_resume_fops = {
-- 
2.53.0


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

* [PATCH v11 10/14] s390/vfio-ap: Transition device migration state to from/to STOP
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (8 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 09/14] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 17:23   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 11/14] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

VFIO_DEVICE_STATE_RESUMING to VFIO_DEVICE_STATE_STOP
VFIO_DEVICE_STATE_STOP_COPY to VFIO_DEVICE_STATE_STOP
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Terminates the data transfer session of the vfio-ap device state
between the source and target hosts. Release the migration file
state (ap_config buffers and file pointers) so that a subsequent
migration captures a fresh AP configuration rather than serving
stale data. 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, so return NULL since no file stream
is needed in the STOP state.

VFIO_DEVICE_STATE_STOP to VFIO_DEVICE_STATE_RUNNING
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Migration has been cancelled. Release the migration file state so
that a subsequent migration captures a fresh AP configuration
rather than serving stale data.

VFIO_DEVICE_STATE_RUNNING to VFIO_DEVICE_STATE_STOP
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Migration has not yet been initiated. No migration file state
exists to release, so just return NULL.

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

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 47f05e43b9e0..c5534c6ee3ce 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -200,6 +200,8 @@ static void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_dat
 	mig_data->resuming_mig_data_complete = false;
 }
 
+static void vfio_ap_release_mig_files(struct ap_matrix_mdev *matrix_mdev);
+
 static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 {
 	struct vfio_ap_migration_file *mig_file = filp->private_data;
@@ -1170,20 +1172,46 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
 		return filp;
 	}
 
-	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);
+	/*
+	 * Terminates the data transfer session of the vfio-ap device state
+	 * to the destination guest.
+	 */
+	if (cur_state == VFIO_DEVICE_STATE_RESUMING &&
+	    new_state == VFIO_DEVICE_STATE_STOP) {
+		if (!mig_data->resuming_mig_data_complete)
+			return ERR_PTR(-EINVAL);
+
+		/* Set resuming_mig_data_complete to false for clean reuse */
+		matrix_mdev->mig_data->resuming_mig_data_complete = false;
+		vfio_ap_release_resuming_file(matrix_mdev->mig_data);
+		return NULL;
+	}
+
+	/*
+	 * Terminates the transfer session of the vfio-ap device state from
+	 * the source guest.
+	 */
+	if (cur_state == VFIO_DEVICE_STATE_STOP_COPY &&
+	    new_state == VFIO_DEVICE_STATE_STOP) {
+		vfio_ap_release_stop_copy_file(matrix_mdev->mig_data);
+		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) {
+		vfio_ap_release_mig_files(matrix_mdev);
+		return NULL;
 	}
 
-	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);
+	if (cur_state == VFIO_DEVICE_STATE_RUNNING &&
+	    new_state == VFIO_DEVICE_STATE_STOP) {
+		return NULL;
 	}
 
 	/* vfio_mig_get_next_state() does not use arcs other than the above */
-- 
2.53.0


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

* [PATCH v11 11/14] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (9 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 10/14] s390/vfio-ap: Transition device migration state to from/to STOP Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 17:39   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 12/14] s390/vfio-ap: Set up for vfio-ap device migration Anthony Krowiak
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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>
Reviewed-by: Jason J. Herne <jjherne@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_migration.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index c5534c6ee3ce..7c282d396f50 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -1308,7 +1308,15 @@ 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;
+	int num_queues;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	*stop_copy_length = vfio_ap_config_size(matrix_mdev, &num_queues);
+	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] 29+ messages in thread

* [PATCH v11 12/14] s390/vfio-ap: Set up for vfio-ap device migration
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (10 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 11/14] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 17:57   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 13/14] s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute Anthony Krowiak
  2026-11-02 15:14 ` [PATCH v11 14/14] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

In order to facilitate vfio-ap device migration:

* The function pointers to the callbacks used to facilitate migration of a
  vfio-ap device must be plugged in. These function pointers are specified
  via the fields contained in a struct vfio_migration_ops instance:

  	migration_set_state: Set the migration state of the vfio-ap device
  	migration_get_state: Get the migration state of the vfio-ap device
  	migration_get_data_size: Get the estimated data number bytes of
  				 data that will be required to complete
  				 STOP_COPY
* The migration_flags field of the struct vfio_device instance is set to
  VFIO_MIGRATION_STOP_COPY to indicate that STOP, STOP_COPY and RESUMING
  are the migration states supported for vfio-ap device migration.

* The initial migration state of the vfio-ap device is set to RUNNING

* The boolean flag indicating that all data sent from userspace during the
  RESUMINMG phase has been received is initialized to false.

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

diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
index 7c282d396f50..57cfaafec4d9 100644
--- a/drivers/s390/crypto/vfio_ap_migration.c
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -217,6 +217,30 @@ static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
 	return 0;
 }
 
+static const struct vfio_migration_ops vfio_ap_migration_ops;
+
+/**
+ * vfio_ap_setup_vfio_device_migration:
+ *
+ * Plug the object containing the function pointers to the migration callbacks
+ * used to facilitate migration into the vfio device and set the flags
+ * indicating migration states STOP, STOP_COPY and RESUMING are supported.
+ *
+ * @matrix_mdev: pointer to object containing the vfio device (vdev)
+ */
+void vfio_ap_setup_vfio_device_migration(struct ap_matrix_mdev *matrix_mdev)
+{
+	/*
+	 * Live guest migration is not supported for SE guests, so there is no
+	 * need to initialize the migration capabilities.
+	 */
+	if (ap_is_se_guest())
+		return;
+
+	matrix_mdev->vdev.migration_flags = VFIO_MIGRATION_STOP_COPY;
+	matrix_mdev->vdev.mig_ops = &vfio_ap_migration_ops;
+}
+
 /**
  * validate_stop_copy_read_parms: Validate the input parameters to the
  *                                vfio_ap_stop_copy_read function
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 79f016a43f56..d4585ea73383 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -811,9 +811,19 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)
 
 	matrix_mdev = vfio_alloc_device(ap_matrix_mdev, vdev, &mdev->dev,
 					&vfio_ap_matrix_dev_ops);
+
 	if (IS_ERR(matrix_mdev))
 		return PTR_ERR(matrix_mdev);
 
+	/*
+	 * Wet up vfio-ap device migration 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_setup_vfio_device_migration(matrix_mdev);
+
 	ret = vfio_register_emulated_iommu_dev(&matrix_mdev->vdev);
 	if (ret)
 		goto err_put_vdev;
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index c6e577064e7e..19772e59fdc6 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -173,6 +173,7 @@ void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
 			      struct ap_config_info *old_config_info);
 
 int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev);
+void vfio_ap_setup_vfio_device_migration(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);
 
-- 
2.53.0


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

* [PATCH v11 13/14] s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (11 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 12/14] s390/vfio-ap: Set up for vfio-ap device migration Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 18:10   ` sashiko-bot
  2026-11-02 15:14 ` [PATCH v11 14/14] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

Add the 'migration' feature to the features provided by the sysfs
/sys/devices/vfio_ap/matrix/feature attribute to indicate that migration
of vfio devices is supported. Since live guest migration is not supported
for SE guests, the 'migration' feature will not be included when the
feature attribute is displayed.

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

diff --git a/drivers/s390/crypto/vfio_ap_drv.c b/drivers/s390/crypto/vfio_ap_drv.c
index 8e69ed286bb9..c63a050a2356 100644
--- a/drivers/s390/crypto/vfio_ap_drv.c
+++ b/drivers/s390/crypto/vfio_ap_drv.c
@@ -26,9 +26,21 @@ MODULE_LICENSE("GPL v2");
 struct ap_matrix_dev *matrix_dev;
 debug_info_t *vfio_ap_dbf_info;
 
+#define VFIO_AP_BASE_FEATURES	"guest_matrix hotplug ap_config"
+#define VFIO_AP_SE_EXCLUDED_FEATURES "migration"
+
 static ssize_t features_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	return sysfs_emit(buf, "guest_matrix hotplug ap_config\n");
+	/*
+	 * SE guests do not support live guest migration; emit only the base
+	 * feature set for them.
+	 */
+	if (ap_is_se_guest())
+		return sysfs_emit(buf, "%s\n", VFIO_AP_BASE_FEATURES);
+
+	/* Non-SE: base features plus those not available on SE guests */
+	return sysfs_emit(buf, "%s %s\n", VFIO_AP_BASE_FEATURES,
+			  VFIO_AP_SE_EXCLUDED_FEATURES);
 }
 static DEVICE_ATTR_RO(features);
 
-- 
2.53.0


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

* [PATCH v11 14/14] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst
  2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
                   ` (12 preceding siblings ...)
  2026-11-02 15:14 ` [PATCH v11 13/14] s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute Anthony Krowiak
@ 2026-11-02 15:14 ` Anthony Krowiak
  2026-11-02 18:19   ` sashiko-bot
  13 siblings, 1 reply; 29+ messages in thread
From: Anthony Krowiak @ 2026-11-02 15:14 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: jjherne, freude, borntraeger, cohuck, mjrosato, pasic,
	alex.williamson, kwankhede, fiuczy

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 | 643 +++++++++++++++++++++++-----
 drivers/s390/crypto/vfio_ap_ops.c   |   2 +-
 2 files changed, 545 insertions(+), 100 deletions(-)

diff --git a/Documentation/arch/s390/vfio-ap.rst b/Documentation/arch/s390/vfio-ap.rst
index ac0c07f76ddd..559c24219749 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,550 @@ 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 |
 +--------------+---------------------------------------------------------------+
+| migration    | 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 destination 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) and
+control and usage 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.
+
+    * 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.
+
+3. Assign adapters, domains and control domains to the destination mediated device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Assign the same adapters, domains and control domains to the destination
+mediated device as are assigned to the source guest's mediated device. For
+example, using the same adapters, domains and control domains from the
+previous step:
+
+.. code-block:: bash
+
+   echo 0x0 > /sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804/assign_adapter
+   echo 0x14 > /sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804/assign_adapter
+   echo 0x16 > /sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804/assign_adapter
+   echo 0x33 > /sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804/assign_domain
+   echo 0x3a > /sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804/assign_domain
+   echo 0x33 > /sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804/assign_control_domain
+   echo 0x3a > /sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804/assign_control_domain
+
+.. note::
+
+    * The destination mediated device must be configured with at least the
+      adapters, domains and control domains that are actively in use by the
+      source guest (i.e., present in the source mdev's AP configuration
+      shadow). Migration will fail if any APQN or control domain from the
+      source guest's AP configuration is not present in the destination
+      mdev's AP configuration shadow.
+
+    * The destination mediated device may be configured with a superset of
+      the source guest's active AP configuration. Any additionally assigned
+      resources that are available on the destination host will be accessible
+      to the guest after migration completes.
+
+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: Invalid argument: Failed to load vmstate version_id:   |
+|              | 1, ret: -22                                                    |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Kernel log:  | vfio_ap_mdev $UUID: Queue $APID.$APQI not in destination       |
+|              | mdev's AP configuration                                        |
++--------------+----------------------------------------------------------------+
+| 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: Invalid argument: Failed to load vmstate version_id:   |
+|              | 1, ret: -22                                                    |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Kernel log:  | vfio_ap_mdev $UUID: Control domain $DOMAIN_NUM not in          |
+|              | destination mdev's AP configuration                            |
++--------------+----------------------------------------------------------------+
+| 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: Failed to get hwinfo for queue $APQN:      |
+|              | TAPQ 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: Invalid argument: Failed to load vmstate               |
+|              | version_id: 1, ret: -22                                        |
++--------------+----------------------------------------------------------------+
+| 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``                                                  |
+|              |                                                                |
+|              | ``vfio_ap_mdev $UUID: AP type of source queue $APQN ($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.
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index d4585ea73383..7f195f42c9c0 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -816,7 +816,7 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)
 		return PTR_ERR(matrix_mdev);
 
 	/*
-	 * Wet up vfio-ap device migration before calling
+	 * Setting up for vfio device migration must be done 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
-- 
2.53.0


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

* Re: [PATCH v11 01/14] s390/vfio-ap: Provide function to get the number of queues assigned to mdev
  2026-11-02 15:14 ` [PATCH v11 01/14] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
@ 2026-11-02 15:23   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 15:23 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

> 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>

Sashiko has reviewed this patch and found no issues.

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


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

* Re: [PATCH v11 02/14] s390/vfio-ap: Data structures for facilitating vfio device migration
  2026-11-02 15:14 ` [PATCH v11 02/14] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
@ 2026-11-02 15:29   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 15:29 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

> Creates the data structures used to facilitate state transitions during
> vfio device migration.
> 
> Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Sashiko has reviewed this patch and found no issues.

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


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

* Re: [PATCH v11 03/14] s390/vfio-ap: Functions to initialize/release vfio device migration data
  2026-11-02 15:14 ` [PATCH v11 03/14] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
@ 2026-11-02 15:46   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 15:46 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] s390/vfio-ap: use-after-free of mig_data when migration FD outlives device FD
- [Medium] s390/vfio-ap: commit message does not match code in vfio_ap_migration.c
- [Low] s390/vfio-ap: malformed kernel-doc comment on vfio_ap_release_mig_files()
--

commit 2bff67e6f9b81966cd8ed45a78897f37868b65ad
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

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

This commit adds functions to allocate, initialize, and release the data
structures used to maintain live guest migration state for vfio_ap mediated
devices when the VFIO device is opened, closed, or released.

>     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:
>     
>     * 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.
>     
>     * vfio_ap_release_mig_files
>     
>       This function is called from the vfio_ap_release_migration_data
>       function (above) and releases the vfio_ap_migration_file objects
>       contained within the vfio_ap_migation_data object used to maintain
>       the state of the VFIO migration process.
>     
>     * vfio_ap_release_stop_copy_file
>     
>       This function is called from the vfio_ap_release_mig_files function
>       (above) to release the vfio_ap_migration_file used during the
>       STOP_COPY phase of migration. For now, this is a stub function that
>       will be fully implemented in a subsequent patch after the
>       vfio_ap_config object referenced within is allocated, as it will
>       need to be freed according to how it is allocated.
>     
>     * vfio_ap_release_resuming_file
>     
>       This function is called from the vfio_ap_release_mig_files function
>       (above) to release the vfio_ap_migration_file used during the
>       RESUMING phase of migration. For now, this is a stub function that
>       will be fully implemented in a subsequent patch after the
>       vfio_ap_config object referenced within is allocated, as it will
>       need to be freed according to how it is allocated.
>     
>     * vfio_ap_set_state, vfio_ap_get_state and vfio_ap_get_data_size
>     
>       These three functions are callback functions assigned to the
>       vfio_ap_migration_ops (.migration_set_state, .migration_get_state
>       and .migration_get_data_size function pointers).
>     
>     Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
> 
> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 2a0a8c76f820e..d525dec782a18 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"
>  
>  /* Magic number and version for the vfio_ap_config migration blob */
> @@ -123,3 +124,105 @@ struct vfio_ap_config {
>  	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
>  	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)
> +{
> +	kvfree(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 void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_data)
> +{
> +	kvfree(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;
> +	mig_data->resuming_mig_data_complete = false;
> +}

[Severity: Medium]
Is the commit message out of sync with the changes in
drivers/s390/crypto/vfio_ap_migration.c?

The changelog describes vfio_ap_release_stop_copy_file() and
vfio_ap_release_resuming_file() as stubs to be implemented in a subsequent
patch, but both functions are already fully implemented here to call
kvfree() on ap_config and reset the fields on stop_copy_mig_file and
resuming_mig_file.

The changelog also states that this commit provides vfio_ap_set_state(),
vfio_ap_get_state(), and vfio_ap_get_data_size() assigned to
vfio_ap_migration_ops, which are not present in this patch and instead get
introduced in later patches in the series, along with the usage of the newly
added linux/file.h include.

> +
> +/**
> + * 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);
> +
> +	/*
> +	 * Live guest migration is not supported for SE guests, so there is no
> +	 * need to initialize the migration data.
> +	 */
> +	if (ap_is_se_guest())
> +		return 0;
> +
> +	mig_data = kzalloc_obj(struct vfio_ap_migration_data, GFP_KERNEL);
> +	if (!mig_data)
> +		return -ENOMEM;
> +
> +	mig_data->mig_state = VFIO_DEVICE_STATE_RUNNING;
> +	mig_data->resuming_mig_data_complete = false;
> +	mig_data->resuming_mig_file.matrix_mdev = matrix_mdev;
> +	mutex_init(&mig_data->resuming_mig_file.lock);
> +	mig_data->stop_copy_mig_file.matrix_mdev = matrix_mdev;
> +	mutex_init(&mig_data->stop_copy_mig_file.lock);
> +	matrix_mdev->mig_data = mig_data;
> +
> +	return 0;
> +}
> +
> +/**
> + * vfio_ap_release_mig_files:
> + *
> + * Free the ap_config buffers for any open migration FDs. Although a
> + * migration FD may still be held open by userspace, it is safe to free
> + * mig_data here because:
> + *
> + *   1. matrix_mdev remains valid for the lifetime of any open migration
> + *      FD via the vfio_device registration reference taken in
> + *      vfio_ap_open_file_stream() and dropped in
> + *      vfio_ap_release_mig_file().
> + *
> + *   2. mig_data is only accessed by the migration file ops
> + *      (vfio_ap_stop_copy_read, vfio_ap_resuming_write) under
> + *      mdevs_lock. Once mig_data is set to NULL by the caller, those
> + *      paths will see NULL and return -ENODEV before dereferencing it.
> + *
> + * @matrix_mdev: The object used to maintain the state for a mediated device
> + */
> +static void vfio_ap_release_mig_files(struct ap_matrix_mdev *matrix_mdev)

[Severity: Low]
This isn't a bug, but the kernel-doc comment on vfio_ap_release_mig_files()
in drivers/s390/crypto/vfio_ap_migration.c omits the short description on the
identifier line and places the @matrix_mdev parameter description after the
body text, which triggers a scripts/kernel-doc warning about a missing
initial short description. Should the short description and @matrix_mdev
parameter be moved to the top of the kernel-doc block?

> +{
> +	struct vfio_ap_migration_data *mig_data;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	mig_data = matrix_mdev->mig_data;
> +	vfio_ap_release_stop_copy_file(mig_data);
> +	vfio_ap_release_resuming_file(mig_data);
> +}
> +
> +/**
> + * vfio_ap_release_migration_data: reclaim private migration data
> + *
> + * @matrix_mdev: pointer to the object containing the state of 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;
> +
> +	vfio_ap_release_mig_files(matrix_mdev);
> +	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
> +	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
> +	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 01439f0ca42ab..1aada3068df5f 100644
> --- a/drivers/s390/crypto/vfio_ap_ops.c
> +++ b/drivers/s390/crypto/vfio_ap_ops.c

[ ... ]

> @@ -2052,19 +2062,39 @@ static int vfio_ap_mdev_reset_qlist(struct list_head *qlist)

[ ... ]

>  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);
>  }

[Severity: Critical]
As reported by Sashiko on v3 [1], can closing the VFIO device file descriptor
while a migration file descriptor remains open trigger a use-after-free of
matrix_mdev->mig_data?

In drivers/s390/crypto/vfio_ap_migration.c, struct vfio_ap_migration_data
embeds resuming_mig_file and stop_copy_mig_file by value:

struct vfio_ap_migration_data {
	enum vfio_device_mig_state	mig_state;
	bool				resuming_mig_data_complete;
	struct vfio_ap_migration_file	resuming_mig_file;
	struct vfio_ap_migration_file	stop_copy_mig_file;
};

When a migration stream file descriptor is opened in
vfio_ap_open_file_stream() (added in later patches in this series),
anon_inode_getfile() sets filp->private_data to &mig_data->stop_copy_mig_file
or &mig_data->resuming_mig_file, and takes a registration reference on
vdev via vfio_device_try_get_registration().

That registration reference keeps struct ap_matrix_mdev alive until
vfio_ap_mdev_release_dev(), but it does not prevent the last close of the
VFIO device file descriptor from calling
vfio_ap_mdev_close_device()->vfio_ap_release_migration_data(), which destroys
stop_copy_mig_file.lock and resuming_mig_file.lock and frees
matrix_mdev->mig_data with kfree().

When userspace subsequently reads, writes, or closes the open migration file
descriptor (including during process exit when close_files() closes the
lower-numbered VFIO device FD before the migration FD),
vfio_ap_stop_copy_read(), vfio_ap_resuming_write(), and
vfio_ap_release_mig_file() dereference filp->private_data (mig_file->lock,
mig_file->filp, and mig_file->matrix_mdev) inside the freed mig_data object.

[1] https://lore.kernel.org/r/20260630111142.08F0A1F00A3A@smtp.kernel.org

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

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

* Re: [PATCH v11 04/14] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler
  2026-11-02 15:14 ` [PATCH v11 04/14] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
@ 2026-11-02 15:49   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 15:49 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

> 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 any open migration file buffers and resets the
> migration state to RUNNING.
> 
> Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Sashiko has reviewed this patch and found no issues.

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


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

* Re: [PATCH v11 05/14] s390/vfio-ap: Callback to get/set vfio device mig state during guest migration
  2026-11-02 15:14 ` [PATCH v11 05/14] s390/vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
@ 2026-11-02 16:06   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 16:06 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] s390/vfio-ap: wrong SE guest check for migration in vfio_ap_set_state()
--

commit 02c085ad3d72e0ded4762a0989bd8c4480446385
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Callback to get/set vfio device mig state during guest migration

This commit implements the vfio_ap_set_state() and vfio_ap_get_state()
migration callbacks and defines vfio_ap_migration_ops for vfio-ap mediated
devices.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 6bcb910526c9e..8e4e04e039658 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c
> @@ -143,6 +143,150 @@ static void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_dat

[ ... ]

> +static struct file *vfio_ap_set_state(struct vfio_device *vdev,
> +				      enum vfio_device_mig_state  new_state)
> +{
> +	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;
> +
> +	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.
> +	 * 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);
> +	}

[Severity: Medium]
Does vfio_ap_set_state() allow migration state transitions for Protected
Virtualization (Secure Execution) KVM guests?

In vfio_ap_init_migration_data(), the check intended to skip initializing
migration data for SE guests calls ap_is_se_guest():

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_init_migration_data() {
	/*
	 * Live guest migration is not supported for SE guests, so there is no
	 * need to initialize the migration data.
	 */
	if (ap_is_se_guest())
		return 0;
}

However, ap_is_se_guest() in drivers/s390/crypto/ap_bus.c checks whether the
running host kernel itself is a Protected Virtualization guest:

bool ap_is_se_guest(void)
{
	return is_prot_virt_guest() && ap_sb_available();
}

On a KVM host, is_prot_virt_guest() is false, so ap_is_se_guest() always
evaluates to false and matrix_mdev->mig_data is unconditionally allocated
when vfio_ap_mdev_open_device() calls vfio_ap_init_migration_data().

In addition, a KVM guest only enters Protected Virtualization mode later
during guest boot via kvm_s390_pv_init_vm(), after open_device and
vfio_ap_mdev_set_kvm() have already run. Because vfio_ap_set_state() only
checks !mig_data and does not check whether matrix_mdev->kvm is a protected
guest (for example via kvm_s390_pv_is_protected(matrix_mdev->kvm)), wouldn't
migration state transitions be permitted on SE guests?

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

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

* Re: [PATCH v11 06/14] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY
  2026-11-02 15:14 ` [PATCH v11 06/14] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
@ 2026-11-02 16:18   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 16:18 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] s390/vfio-ap: use-after-free in vfio_ap_release_mig_file() after device close
- [Critical] s390/vfio-ap: use-after-free of mig_file in vfio_ap_release_mig_file()
--

commit d4f19cfbfd83130bd2529cc5a305e86f834b23bb
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY

This commit implements the migration state transition from
VFIO_DEVICE_STATE_STOP to VFIO_DEVICE_STATE_STOP_COPY by opening an
anonymous file stream for reading the guest's AP configuration.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 8e4e04e039658..48028b12b26f9 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c
[ ... ]
> @@ -143,6 +144,62 @@ static void vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_dat
>  	mig_data->resuming_mig_data_complete = false;
>  }
>  
> +static ssize_t
> +vfio_ap_stop_copy_read(struct file *filp, char __user *buf, size_t len,
> +		       loff_t *pos)
> +{
> +	/* TODO */
> +	return -EOPNOTSUPP;
> +}
> +
> +static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
> +{
> +	struct vfio_ap_migration_file *mig_file = filp->private_data;
> +
> +	/*
> +	 * The mig_file (and by extension mig_file->matrix_mdev) is tied to
> +	 * migration FD lifetime (see vfio_ap_open_file_stream() function) to
> +	 * guarantee that the matrix_mdev remains valid for every read/write
> +	 * operation. Let's drop it here since the migration FD is being
> +	 * released.
> +	 */
> +	vfio_device_put_registration(&mig_file->matrix_mdev->vdev);
> +	return 0;
> +}
> +
> +static const struct file_operations vfio_ap_stop_copy_fops = {
> +	.owner = THIS_MODULE,
> +	.read = vfio_ap_stop_copy_read,
> +	.release = vfio_ap_release_mig_file,
> +};
> +
> +static struct file *vfio_ap_open_file_stream(struct vfio_ap_migration_file *mig_file,
> +					     const struct file_operations *fops,
> +					     int flags)
> +{
> +	struct file *filp;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	/*
> +	 * Pin the vfio_device registration so that matrix_mdev cannot be freed
> +	 * while the migration FD is still open. The matching put is in
> +	 * vfio_ap_release_mig_file().
> +	 */
> +	if (!vfio_device_try_get_registration(&mig_file->matrix_mdev->vdev))
> +		return ERR_PTR(-ENODEV);
> +
> +	filp = anon_inode_getfile("vfio_ap_mig_file", fops, mig_file, flags);
> +	if (IS_ERR(filp)) {
> +		vfio_device_put_registration(&mig_file->matrix_mdev->vdev);
> +		return 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)
> @@ -156,10 +213,23 @@ 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 vfio_ap_migration_file *mig_file = &mig_data->stop_copy_mig_file;
> +		struct file *filp = vfio_ap_open_file_stream(mig_file,
> +							     &vfio_ap_stop_copy_fops,
> +							     O_RDONLY);
> +		if (IS_ERR(filp))
> +			return ERR_CAST(filp);
> +
> +		mig_file->filp = filp;
> +
> +		return filp;
>  	}

[Severity: Critical]
Can this trigger a use-after-free of mig_file in vfio_ap_release_mig_file()
(and in vfio_ap_stop_copy_read() in the subsequent patch) if the VFIO device
file descriptor is closed while the migration stream file descriptor remains
open?

In vfio_ap_transition_to_state(), mig_file points to
&mig_data->stop_copy_mig_file, which is embedded inside struct
vfio_ap_migration_data:

struct vfio_ap_migration_data {
	enum vfio_device_mig_state	mig_state;
	bool				resuming_mig_data_complete;
	struct vfio_ap_migration_file	resuming_mig_file;
	struct vfio_ap_migration_file	stop_copy_mig_file;
};

vfio_ap_open_file_stream() stores mig_file in filp->private_data and calls
vfio_device_try_get_registration(&mig_file->matrix_mdev->vdev). However,
vfio_device_try_get_registration() only pins struct vfio_device (and thus
matrix_mdev) against unregistration; it does not prevent vfio_df_close()
from invoking vfio_ap_mdev_close_device() when the VFIO device FD is closed
(which also happens during normal process exit when close_files() closes the
lower-numbered VFIO device FD before the higher-numbered migration data FD).

When the VFIO device FD is closed, vfio_ap_mdev_close_device() calls
vfio_ap_release_migration_data(), which destroys stop_copy_mig_file.lock and
frees matrix_mdev->mig_data:

void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev)
{
	...
	vfio_ap_release_mig_files(matrix_mdev);
	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
	kfree(matrix_mdev->mig_data);
	matrix_mdev->mig_data = NULL;
}

When the migration stream FD is subsequently closed (or read),
vfio_ap_release_mig_file() retrieves mig_file from filp->private_data and
dereferences mig_file->matrix_mdev from the freed mig_data allocation (and
if the VFIO device was reopened in the meantime, a new mig_data is allocated
while the old migration FD still points to the freed mig_data instance).

Should filp->private_data point to matrix_mdev (or should mig_file/mig_data
lifetime be tied to the open migration FDs) so that releasing or reading the
migration FD does not dereference freed memory?

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

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

* Re: [PATCH v11 07/14] s390/vfio-ap: File ops called to save the vfio device migration state
  2026-11-02 15:14 ` [PATCH v11 07/14] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
@ 2026-11-02 16:32   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 16:32 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] s390/vfio-ap: use-after-free of mig_file in vfio_ap_stop_copy_read()
--

commit 6f641fc7194e9bced026956cec2ed8bc278c6d77
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: File ops called to save the vfio device migration state

This commit implements the vfio_ap_stop_copy_read() callback for the migration
file created when transitioning from STOP to STOP_COPY, snapshotting and
copying the guest's AP configuration and queue hardware information to
userspace.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 48028b12b26f9..b64ce092a41b1 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> @@ -167,6 +160,269 @@ static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
>  	return 0;
>  }
>  
> +/**
> + * validate_stop_copy_read_parms: Validate the input parameters to the
> + *                                vfio_ap_stop_copy_read function
> + *
> + * @mig_file:	Pointer to the object used for storing the vfio-ap device state
> + * @filp:	File stream to which to write source guest's AP configuration
> + * @len:	The length of the data to be read
> + *
> + * Verify the following:
> + * - @filp is the instance opened when state transitioned from STOP to STOP_COPY
> + * - @filp->f_pos + @len does not cause integer overflow
> + * - @mig_file->matrix_mdev is not NULL
> + * - @mig_file->matrix_mdev->mig_data is not NULL
> + *
> + * Returns: 0 if the parameters pass validation; otherwise returns an error
> + */
> +static int validate_stop_copy_read_parms(struct vfio_ap_migration_file *mig_file,
> +					 struct file *filp,
> +					 size_t len)
> +{
> +	loff_t total_len;
> +
> +	lockdep_assert_held(&mig_file->lock);
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	if (filp != mig_file->filp)
> +		return -EINVAL;
> +
> +	if (check_add_overflow((loff_t)len, filp->f_pos, &total_len))
> +		return -EIO;
> +
> +	if (!mig_file->matrix_mdev || !mig_file->matrix_mdev->mig_data)
> +		return -ENODEV;
> +
> +	return 0;
> +}

[ ... ]

> +static ssize_t vfio_ap_stop_copy_read(struct file *filp, char __user *buf,
> +				      size_t len, loff_t *pos)
> +{
> +	struct vfio_ap_migration_file *mig_file;
> +	void *bounce;
> +	ssize_t ret;
> +
> +	/*
> +	 * This file was opened with stream_open(), so pos should be NULL for
> +	 * sequential read() calls; a non-NULL pointer will be passed only
> +	 * for positional pread() calls in which case we return an error
> +	 * indicating broken pipe/illegal seek on a non-seekable file
> +	 */
> +	if (pos)
> +		return -ESPIPE;
> +
> +	/*
> +	 * mig_file is guaranteed live here: vfio_ap_open_file_stream() took
> +	 * a vfio_device registration reference that is held until
> +	 * vfio_ap_release_mig_file() runs, so the embedding mig_file cannot
> +	 * be freed while this file descriptor is open.
> +	 */
> +	mig_file = filp->private_data;
> +	mutex_lock(&mig_file->lock);
> +	mutex_lock(&matrix_dev->mdevs_lock);
> +
> +	ret = validate_stop_copy_read_parms(mig_file, filp, len);
> +	if (ret) {
> +		mutex_unlock(&matrix_dev->mdevs_lock);
> +		mutex_unlock(&mig_file->lock);
> +		return ret;
> +	}

[ ... ]

> +	memcpy(bounce, (char *)mig_file->ap_config + *pos, len);
> +	mutex_unlock(&matrix_dev->mdevs_lock);
> +
> +	if (copy_to_user(buf, bounce, len)) {
> +		kfree(bounce);
> +		mutex_unlock(&mig_file->lock);
> +		return -EFAULT;
> +	}
> +
> +	*pos += len;
> +	mutex_unlock(&mig_file->lock);
> +	kfree(bounce);
> +	return len;
> +}

[Severity: Critical]
As reported by Sashiko on v6 [1], can closing the vfio device file descriptor
while the migration file descriptor remains open cause a use-after-free of
mig_file in vfio_ap_stop_copy_read() and vfio_ap_release_mig_file()?

The comment in vfio_ap_stop_copy_read() notes that vfio_ap_open_file_stream()
takes a vfio_device registration reference via
vfio_device_try_get_registration() until vfio_ap_release_mig_file() runs.
However, that reference only keeps matrix_mdev alive; stop_copy_mig_file is
embedded inside struct vfio_ap_migration_data (matrix_mdev->mig_data), which
is allocated separately in vfio_ap_init_migration_data().

When userspace closes the vfio device file descriptor,
vfio_ap_mdev_close_device() -> vfio_ap_release_migration_data() runs under
matrix_dev->mdevs_lock:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_release_migration_data() {
	if (!matrix_mdev->mig_data)
		return;

	vfio_ap_release_mig_files(matrix_mdev);
	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
	kfree(matrix_mdev->mig_data);
	matrix_mdev->mig_data = NULL;
}

Because filp->private_data on the still-open migration file descriptor points
to &mig_data->stop_copy_mig_file, freeing matrix_mdev->mig_data frees
mig_file itself:

1. If vfio_ap_stop_copy_read() runs after vfio_ap_mdev_close_device(),
   mig_file = filp->private_data points into freed memory.
   mutex_lock(&mig_file->lock) operates on a destroyed mutex in freed slab
   memory, and validate_stop_copy_read_parms() dereferences mig_file->filp and
   mig_file->matrix_mdev from freed memory before checking
   !mig_file->matrix_mdev->mig_data.

2. If vfio_ap_stop_copy_read() is already in progress and drops
   matrix_dev->mdevs_lock before copy_to_user() while holding mig_file->lock
   (or is waiting to acquire matrix_dev->mdevs_lock after locking
   mig_file->lock), a concurrent vfio_ap_mdev_close_device() ->
   vfio_ap_release_migration_data() will destroy mig_file->lock while it is
   locked and kfree(matrix_mdev->mig_data), causing the subsequent
   mutex_unlock(&mig_file->lock) in vfio_ap_stop_copy_read() to write to freed
   memory.

3. When the migration file descriptor is later closed,
   vfio_ap_release_mig_file() dereferences mig_file->matrix_mdev from the
   freed mig_data structure.

[1] https://lore.kernel.org/r/20260727180224.027021F000E9@smtp.kernel.org

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

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

* Re: [PATCH v11 08/14] s390/vfio-ap: Transition device migration state from STOP to RESUMING
  2026-11-02 15:14 ` [PATCH v11 08/14] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
@ 2026-11-02 16:42   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 16:42 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] s390/vfio-ap: use-after-free of mig_file in vfio_ap_release_mig_file()
--

commit 8958f5c47ab29cef8a0a38994bae03335dbb5b69
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Transition device migration state from STOP to RESUMING

This commit implements the transition from VFIO_DEVICE_STATE_STOP to
VFIO_DEVICE_STATE_RESUMING in vfio_ap_transition_to_state() by pre-allocating
the receive buffer for the guest's AP configuration and opening a write-only
anonymous inode file stream for the resuming migration data.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index b64ce092a41b1..526d83e8590d9 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> @@ -456,6 +466,67 @@ static struct file *vfio_ap_open_file_stream(struct vfio_ap_migration_file *mig_
>  	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 vfio_ap_migration_file *mig_file)
> +{
> +	struct vfio_ap_config *ap_config;
> +	struct file *filp;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	ap_config = kvzalloc(VFIO_AP_CONFIG_MAX_SIZE, GFP_KERNEL_ACCOUNT);
> +	if (!ap_config)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ap_config->num_queues = MAX_AP_QUEUES;
> +
> +	filp = vfio_ap_open_file_stream(mig_file, &vfio_ap_resume_fops, O_WRONLY);
> +
> +	if (IS_ERR(filp)) {
> +		kvfree(ap_config);
> +		return filp;
> +	}
> +
> +	mig_file->ap_config = ap_config;
> +	mig_file->config_sz = VFIO_AP_CONFIG_MAX_SIZE;
> +	mig_file->filp = filp;
> +
> +	return filp;
> +}
> +
>  static struct file *
>  vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
>  			    enum vfio_device_mig_state new_state)
> @@ -488,10 +559,19 @@ 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(&mig_data->resuming_mig_file);
> +
> +		if (IS_ERR(filp))
> +			return ERR_CAST(filp);
> +
> +		return filp;
>  	}

[Severity: Critical]
Can closing the VFIO device file descriptor before the migration stream file
descriptor trigger a use-after-free in vfio_ap_release_mig_file()?

In vfio_ap_transition_to_state(), vfio_ap_resuming_init() passes
&mig_data->resuming_mig_file (and the STOP_COPY path similarly passes
&mig_data->stop_copy_mig_file) to vfio_ap_open_file_stream(), which stores
mig_file in filp->private_data and pins the vfio_device registration:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_open_file_stream() {
	if (!vfio_device_try_get_registration(&mig_file->matrix_mdev->vdev))
		return ERR_PTR(-ENODEV);

	filp = anon_inode_getfile("vfio_ap_mig_file", fops, mig_file, flags);
}

Pinning the registration keeps matrix_mdev alive, but resuming_mig_file and
stop_copy_mig_file are embedded in struct vfio_ap_migration_data
(matrix_mdev->mig_data), which is dynamically allocated when the VFIO device
file descriptor is opened and freed when it is closed via
vfio_ap_mdev_close_device() -> vfio_ap_release_migration_data():

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_release_migration_data() {
	if (!matrix_mdev->mig_data)
		return;

	vfio_ap_release_mig_files(matrix_mdev);
	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
	kfree(matrix_mdev->mig_data);
	matrix_mdev->mig_data = NULL;
}

If the VFIO device file descriptor is closed while the migration stream file
descriptor is still open (for example, if a userspace VMM exits or crashes
during migration and close_files() closes descriptors in ascending order),
matrix_mdev->mig_data is freed while filp->private_data still points into it.

When the migration stream file descriptor is subsequently accessed or closed,
vfio_ap_release_mig_file() dereferences mig_file->matrix_mdev from the freed
mig_data allocation:

drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_release_mig_file() {
	struct vfio_ap_migration_file *mig_file = filp->private_data;
	...
	vfio_device_put_registration(&mig_file->matrix_mdev->vdev);
	return 0;
}

And any concurrent or subsequent read/write on that migration file descriptor
will also lock the destroyed and freed mig_file->lock.

Should mig_data (or the vfio_ap_migration_file structures) be embedded in
struct ap_matrix_mdev or otherwise tied to the lifetime of the open migration
file descriptors?

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

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

* Re: [PATCH v11 09/14] s390/vfio-ap: File ops called to resume the vfio device migration
  2026-11-02 15:14 ` [PATCH v11 09/14] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
@ 2026-11-02 17:09   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 17:09 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] s390/vfio-ap: use-after-free of mig_file in vfio_ap_resuming_write()
- [High] s390/vfio-ap: fortify panic in vfio_ap_resuming_write() on chunked writes
--

commit 05b49746b964ad1916518e2c783de5c6220f1f14
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: File ops called to resume the vfio device migration

This commit implements the write callback for the migration file stream used
during the RESUMING phase to receive and validate the source guest's AP
configuration on the destination host.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 526d83e8590d9..47f05e43b9e0e 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> @@ -466,11 +511,562 @@ static struct file *vfio_ap_open_file_stream(struct vfio_ap_migration_file *mig_
>  	return filp;
>  }
>  
> +static int validate_resuming_write_parms(struct vfio_ap_migration_file *mig_file,
> +					 struct file *filp, size_t len)
> +{
> +	loff_t total_len;
> +
> +	lockdep_assert_held(&mig_file->lock);
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	if (!len || filp->f_pos < 0)
> +		return -EINVAL;
> +
> +	if (!mig_file->matrix_mdev->mig_data)
> +		return -ENODEV;

[ ... ]

>  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_file *mig_file;
> +	struct vfio_ap_config *ap_config;
> +	ssize_t ret;
> +	void *kbuf;

[ ... ]

> +	mig_file = filp->private_data;
> +	mutex_lock(&mig_file->lock);
> +	mutex_lock(&ap_attr_mutex);
> +	mutex_lock(&matrix_dev->mdevs_lock);
> +	pos = &filp->f_pos;
> +
> +	ret = validate_resuming_write_parms(mig_file, filp, len);
> +	if (ret)
> +		goto out_unlock;

[Severity: High]
Can this trigger a use-after-free on mig_file if the VFIO device file
descriptor is closed while the migration file descriptor remains open?

In vfio_ap_resuming_init() -> vfio_ap_open_file_stream(), filp->private_data
is set to &mig_data->resuming_mig_file, which is embedded inside
matrix_mdev->mig_data:

drivers/s390/crypto/vfio_ap_migration.c:
struct vfio_ap_migration_data {
	enum vfio_device_mig_state	mig_state;
	bool				resuming_mig_data_complete;
	struct vfio_ap_migration_file	resuming_mig_file;
	struct vfio_ap_migration_file	stop_copy_mig_file;
};

While vfio_ap_open_file_stream() calls vfio_device_try_get_registration() to
keep matrix_mdev alive until vfio_ap_release_mig_file() runs, closing the
VFIO device file descriptor invokes:

vfio_ap_mdev_close_device()
  vfio_ap_release_migration_data() {
	...
	vfio_ap_release_mig_files(matrix_mdev);
	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
	kfree(matrix_mdev->mig_data);
	matrix_mdev->mig_data = NULL;
  }

Although validate_resuming_write_parms() checks
!mig_file->matrix_mdev->mig_data under matrix_dev->mdevs_lock, mig_file
itself lives inside the freed matrix_mdev->mig_data object. Calling
vfio_ap_resuming_write() after or concurrently with
vfio_ap_mdev_close_device() will lock and unlock the destroyed
mig_file->lock and dereference mig_file->matrix_mdev in freed memory.

> +
> +	ap_config = mig_file->ap_config;
> +	memcpy((char *)ap_config + *pos, kbuf, len);
> +	*pos += len;
> +
> +	/*
> +	 * Check whether we have received all the data.  We know the total
> +	 * expected size once num_queues has arrived, i.e. once *pos is past
> +	 * the fixed header.  Until then keep accumulating.
> +	 */
> +	if (*pos >= offsetofend(struct vfio_ap_config, num_queues)) {
> +		size_t expected_sz;
> +
> +		if (ap_config->num_queues > MAX_AP_QUEUES) {
> +			ret = -EINVAL;
> +			goto out_unlock;
> +		}
> +
> +		expected_sz = sizeof(struct vfio_ap_config) +
> +			      ap_config->num_queues *
> +			      sizeof(struct vfio_ap_queue_info);
> +
> +		if (*pos > (loff_t)expected_sz) {
> +			ret = -EINVAL;
> +			goto out_unlock;
> +		}

[Severity: High]
As reported by Sashiko on v9 [1], can the memcpy() in
vfio_ap_resuming_write() trigger a fortify_panic() on chunked writes when
CONFIG_FORTIFY_SOURCE and CONFIG_CC_HAS_COUNTED_BY are enabled?

In struct vfio_ap_config, qinfo[] is annotated with
__counted_by(num_queues):

struct vfio_ap_config {
	u32				magic;
	u32				version;
	u32				num_queues;
	u8				reserved[4];
	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
};

Although vfio_ap_resuming_init() initializes ap_config->num_queues to
MAX_AP_QUEUES, fortified memcpy((char *)ap_config + *pos, kbuf, len)
evaluates __struct_size((char *)ap_config + *pos) using the current value of
ap_config->num_queues in memory before the expected_sz check runs:

1. Once an earlier chunk writes the header with num_queues < MAX_AP_QUEUES,
   the dynamic object size of *ap_config shrinks to expected_sz. If a
   subsequent write() supplies more bytes than expected_sz - *pos (while
   staying within VFIO_AP_CONFIG_MAX_SIZE), memcpy() executes before the
   *pos > (loff_t)expected_sz check and calls fortify_panic() instead of
   returning -EINVAL.

2. On big-endian s390, MAX_AP_QUEUES (0x00010000) has bytes 0x00, 0x01,
   0x00, 0x00 at offsets 8..11. If an initial chunk of 10 or 11 bytes writes
   the upper two bytes of a valid num_queues < 65536 (0x00, 0x00),
   ap_config->num_queues in memory becomes 0. If the next chunk writes past
   the header into qinfo[], fortified memcpy() evaluates __struct_size()
   with ap_config->num_queues == 0 before copying the remaining bytes of
   num_queues, triggering fortify_panic() on valid migration data.

[1] https://lore.kernel.org/r/sashiko-outbox-170099@kernel.org

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

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

* Re: [PATCH v11 10/14] s390/vfio-ap: Transition device migration state to from/to STOP
  2026-11-02 15:14 ` [PATCH v11 10/14] s390/vfio-ap: Transition device migration state to from/to STOP Anthony Krowiak
@ 2026-11-02 17:23   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 17:23 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] s390/vfio-ap: use-after-free of mig_data when device FD closes before data FD
- [High] s390/vfio-ap: fortify panic on chunked writes in vfio_ap_resuming_write()
--

commit 6775fb7f340ff449dd183124e50a9831181c55db
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Transition device migration state to from/to STOP

This commit implements the VFIO migration state transitions to and from
VFIO_DEVICE_STATE_STOP in vfio_ap_transition_to_state(), releasing
migration file state when leaving RESUMING, STOP_COPY, or STOP.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 47f05e43b9e0e..c5534c6ee3cef 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> @@ -1170,20 +1172,46 @@ vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
>  		return filp;
>  	}
>  
> -	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);
> +	/*
> +	 * Terminates the data transfer session of the vfio-ap device state
> +	 * to the destination guest.
> +	 */
> +	if (cur_state == VFIO_DEVICE_STATE_RESUMING &&
> +	    new_state == VFIO_DEVICE_STATE_STOP) {
> +		if (!mig_data->resuming_mig_data_complete)
> +			return ERR_PTR(-EINVAL);
> +
> +		/* Set resuming_mig_data_complete to false for clean reuse */
> +		matrix_mdev->mig_data->resuming_mig_data_complete = false;
> +		vfio_ap_release_resuming_file(matrix_mdev->mig_data);
> +		return NULL;
> +	}
> +
> +	/*
> +	 * Terminates the transfer session of the vfio-ap device state from
> +	 * the source guest.
> +	 */
> +	if (cur_state == VFIO_DEVICE_STATE_STOP_COPY &&
> +	    new_state == VFIO_DEVICE_STATE_STOP) {
> +		vfio_ap_release_stop_copy_file(matrix_mdev->mig_data);
> +		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) {
> +		vfio_ap_release_mig_files(matrix_mdev);
> +		return NULL;
>  	}
>  
> -	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);
> +	if (cur_state == VFIO_DEVICE_STATE_RUNNING &&
> +	    new_state == VFIO_DEVICE_STATE_STOP) {
> +		return NULL;
>  	}

[Severity: Critical]
As reported by Sashiko on v5 [1], with the transitions to and from
VFIO_DEVICE_STATE_STOP enabled in vfio_ap_transition_to_state(), userspace can
now enter VFIO_DEVICE_STATE_STOP_COPY or VFIO_DEVICE_STATE_RESUMING and open a
migration stream file descriptor.

When vfio_ap_open_file_stream() creates that file descriptor, it sets
filp->private_data to &matrix_mdev->mig_data->stop_copy_mig_file or
&matrix_mdev->mig_data->resuming_mig_file and pins the registration of
matrix_mdev->vdev via vfio_device_try_get_registration(). However, Both
stop_copy_mig_file and resuming_mig_file are embedded inside struct
vfio_ap_migration_data (matrix_mdev->mig_data) rather than struct
ap_matrix_mdev:

struct vfio_ap_migration_data {
	enum vfio_device_mig_state	mig_state;
	bool				resuming_mig_data_complete;
	struct vfio_ap_migration_file	resuming_mig_file;
	struct vfio_ap_migration_file	stop_copy_mig_file;
};

When the VFIO device file descriptor is closed while the migration stream file
descriptor is still open (including during process exit, since close_files()
closes file descriptors in ascending order), vfio_ap_mdev_close_device() calls
vfio_ap_release_migration_data(), which destroys the mutexes and frees
matrix_mdev->mig_data:

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;

	vfio_ap_release_mig_files(matrix_mdev);
	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
	kfree(matrix_mdev->mig_data);
	matrix_mdev->mig_data = NULL;
}

Doesn't this cause a use-after-free when the open migration file descriptor is
subsequently read in vfio_ap_stop_copy_read(), written in
vfio_ap_resuming_write(), or closed in vfio_ap_release_mig_file(), where
filp->private_data (mig_file) is dereferenced and mig_file->lock is locked or
unlocked after matrix_mdev->mig_data has been freed?

[Severity: High]
In addition, with VFIO_DEVICE_STATE_RESUMING now reachable via
vfio_ap_transition_to_state(), can chunked writes in vfio_ap_resuming_write()
trigger a fortify_panic() when CONFIG_FORTIFY_SOURCE is enabled?

In struct vfio_ap_config, qinfo[] is annotated with __counted_by(num_queues).
While vfio_ap_resuming_init() initializes ap_config->num_queues to
MAX_AP_QUEUES when the stream is opened, vfio_ap_resuming_write() copies
incoming chunks directly into ap_config before checking whether *pos exceeds
expected_sz:

	ap_config = mig_file->ap_config;
	memcpy((char *)ap_config + *pos, kbuf, len);
	*pos += len;

If an earlier write chunk overwrites ap_config->num_queues with a smaller
value (or splits the 4-byte num_queues field across two write calls so that
its upper bytes are zeroed first) and a subsequent write chunk writes past
sizeof(struct vfio_ap_config) + ap_config->num_queues *
sizeof(struct vfio_ap_queue_info), won't fortified memcpy() evaluate the
updated ap_config->num_queues and call fortify_panic() before the bounds check
after memcpy() is reached?

[1] https://lore.kernel.org/r/20260724182949.EA9C51F000E9@smtp.kernel.org

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

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

* Re: [PATCH v11 11/14] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration
  2026-11-02 15:14 ` [PATCH v11 11/14] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
@ 2026-11-02 17:39   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 17:39 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

> 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.

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


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

* Re: [PATCH v11 12/14] s390/vfio-ap: Set up for vfio-ap device migration
  2026-11-02 15:14 ` [PATCH v11 12/14] s390/vfio-ap: Set up for vfio-ap device migration Anthony Krowiak
@ 2026-11-02 17:57   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 17:57 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] s390/vfio-ap: use-after-free of mig_file in vfio_ap_release_mig_file()
- [High] s390/vfio-ap: fortify_panic() on chunked writes in vfio_ap_resuming_write()
--

commit 8f5bd5cbf7956f3cc86d039dd21c5869a7e36482
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Set up for vfio-ap device migration

This commit plugs in the vfio_migration_ops callbacks and sets the
VFIO_MIGRATION_STOP_COPY migration flag during vfio-ap mediated device probe
when not running in an SE guest.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> index 7c282d396f502..57cfaafec4d97 100644
> --- a/drivers/s390/crypto/vfio_ap_migration.c
> +++ b/drivers/s390/crypto/vfio_ap_migration.c
> @@ -217,6 +217,30 @@ static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
>  	return 0;
>  }
>  
> +static const struct vfio_migration_ops vfio_ap_migration_ops;
> +
> +/**
> + * vfio_ap_setup_vfio_device_migration:
> + *
> + * Plug the object containing the function pointers to the migration callbacks
> + * used to facilitate migration into the vfio device and set the flags
> + * indicating migration states STOP, STOP_COPY and RESUMING are supported.
> + *
> + * @matrix_mdev: pointer to object containing the vfio device (vdev)
> + */
> +void vfio_ap_setup_vfio_device_migration(struct ap_matrix_mdev *matrix_mdev)
> +{
> +	/*
> +	 * Live guest migration is not supported for SE guests, so there is no
> +	 * need to initialize the migration capabilities.
> +	 */
> +	if (ap_is_se_guest())
> +		return;
> +
> +	matrix_mdev->vdev.migration_flags = VFIO_MIGRATION_STOP_COPY;
> +	matrix_mdev->vdev.mig_ops = &vfio_ap_migration_ops;
> +}

[Severity: Critical]
As reported by Sashiko on v5 [1], now that vfio_ap_migration_ops is wired up,
can closing the VFIO device file descriptor while a migration file descriptor
remains open trigger a use-after-free of matrix_mdev->mig_data and its
embedded mig_file structs?

In drivers/s390/crypto/vfio_ap_migration.c, resuming_mig_file and
stop_copy_mig_file are embedded inside the heap-allocated struct
vfio_ap_migration_data (matrix_mdev->mig_data) rather than inside struct
ap_matrix_mdev:

struct vfio_ap_migration_data {
	enum vfio_device_mig_state	mig_state;
	bool				resuming_mig_data_complete;
	struct vfio_ap_migration_file	resuming_mig_file;
	struct vfio_ap_migration_file	stop_copy_mig_file;
};

When transitioning to VFIO_DEVICE_STATE_STOP_COPY or
VFIO_DEVICE_STATE_RESUMING, vfio_ap_open_file_stream() takes a registration
reference via vfio_device_try_get_registration(&mig_file->matrix_mdev->vdev)
and sets filp->private_data to mig_file. That registration reference keeps
struct ap_matrix_mdev alive, but it does not prevent the VFIO device FD from
being closed.

If the VFIO device FD is closed before the migration FD (for example, by
userspace or during process exit when close_files() closes a lower-numbered
VFIO device FD before a higher-numbered migration FD),
vfio_ap_mdev_close_device() -> vfio_ap_release_migration_data() destroys the
mig_file mutexes and frees matrix_mdev->mig_data:

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;

	vfio_ap_release_mig_files(matrix_mdev);
	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
	kfree(matrix_mdev->mig_data);
	matrix_mdev->mig_data = NULL;
}

Once matrix_mdev->mig_data is freed while the migration FD is still open:

1. Closing the migration FD afterwards invokes vfio_ap_release_mig_file(),
   which dereferences mig_file->matrix_mdev in freed memory:

static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
{
	struct vfio_ap_migration_file *mig_file = filp->private_data;

	vfio_device_put_registration(&mig_file->matrix_mdev->vdev);
	return 0;
}

2. Any subsequent or concurrent vfio_ap_stop_copy_read() or
   vfio_ap_resuming_write() on the open migration FD accesses
   filp->private_data (mig_file) and locks or unlocks mig_file->lock after
   mig_data has been destroyed and freed (or while vfio_ap_stop_copy_read()
   holds mig_file->lock across copy_to_user() after dropping
   matrix_dev->mdevs_lock).

[Severity: High]
With vfio_ap_migration_ops exposed to userspace, can chunked writes to the
RESUMING migration file descriptor trigger a fortify_panic() in
vfio_ap_resuming_write() under CONFIG_FORTIFY_SOURCE?

In drivers/s390/crypto/vfio_ap_migration.c, struct vfio_ap_config annotates
its flexible array member with __counted_by(num_queues):

struct vfio_ap_config {
	u32				magic;
	u32				version;
	u32				num_queues;
	u8				reserved[4];
	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
};

Although vfio_ap_resuming_init() initializes ap_config->num_queues to
MAX_AP_QUEUES when allocating the VFIO_AP_CONFIG_MAX_SIZE buffer,
vfio_ap_resuming_write() copies each incoming chunk directly into ap_config
before validating expected_sz:

	ap_config = mig_file->ap_config;
	memcpy((char *)ap_config + *pos, kbuf, len);
	*pos += len;

	if (*pos >= offsetofend(struct vfio_ap_config, num_queues)) {
		size_t expected_sz;

		if (ap_config->num_queues > MAX_AP_QUEUES) {
			ret = -EINVAL;
			goto out_unlock;
		}

		expected_sz = sizeof(struct vfio_ap_config) +
			      ap_config->num_queues *
			      sizeof(struct vfio_ap_queue_info);

		if (*pos > (loff_t)expected_sz) {
			ret = -EINVAL;
			goto out_unlock;
		}

When CONFIG_FORTIFY_SOURCE is enabled, memcpy((char *)ap_config + *pos, kbuf,
len) evaluates __builtin_dynamic_object_size() using the current in-memory
value of ap_config->num_queues before copying:

1. If an earlier write chunk sets ap_config->num_queues to a smaller value N
   (where N < MAX_AP_QUEUES) and a subsequent write chunk writes past
   expected_sz (while still within VFIO_AP_CONFIG_MAX_SIZE), the fortified
   memcpy() sees the smaller object size derived from ap_config->num_queues ==
   N and calls fortify_panic() before reaching the *pos > expected_sz check.

2. Because s390x is big-endian, MAX_AP_QUEUES (0x00010000) has bytes
   {0x00, 0x01, 0x00, 0x00} at offsets 8..11. For any valid migration stream
   with num_queues < 65536 (where the upper two bytes are 0x00, 0x00), if a
   chunked write splits across the num_queues field after its second or third
   byte, ap_config->num_queues in memory temporarily becomes 0. If the next
   write chunk copies the rest of the header and any queue info entries, the
   fortified memcpy() evaluates ap_config->num_queues == 0 before copying the
   remaining bytes of num_queues and triggers fortify_panic().

[1] https://lore.kernel.org/r/20260724182949.EA9C51F000E9@smtp.kernel.org

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

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

* Re: [PATCH v11 13/14] s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute
  2026-11-02 15:14 ` [PATCH v11 13/14] s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute Anthony Krowiak
@ 2026-11-02 18:10   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 18:10 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

> Add the 'migration' feature to the features provided by the sysfs
> /sys/devices/vfio_ap/matrix/feature attribute to indicate that migration
> of vfio devices is supported. Since live guest migration is not supported
> for SE guests, the 'migration' feature will not be included when the
> feature attribute is displayed.
> 
> Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>

Sashiko has reviewed this patch and found no issues.

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


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

* Re: [PATCH v11 14/14] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst
  2026-11-02 15:14 ` [PATCH v11 14/14] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
@ 2026-11-02 18:19   ` sashiko-bot
  0 siblings, 0 replies; 29+ messages in thread
From: sashiko-bot @ 2026-11-02 18:19 UTC (permalink / raw)
  To: Anthony Krowiak, linux-s390, kvm
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] s390/vfio-ap: use-after-free of mig_file when device FD closes before migration FD
- [Low] s390/vfio-ap: pointer signedness mismatch in vfio_ap_get_data_size()
- [Low] s390/vfio-ap: stray comment fix in vfio_ap_mdev_probe() in documentation commit
--

commit 678cd90abd6549f06a0dbfc0140b0ae844762cfe
Author: Anthony Krowiak <akrowiak@linux.ibm.com>

s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst

This series implements VFIO live guest migration support for the s390
vfio-ap mediated device driver and documents the migration requirements and
setup in Documentation/arch/s390/vfio-ap.rst.

> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
> new file mode 100644
> index 0000000000000..57cfaafec4d97
> --- /dev/null
> +++ b/drivers/s390/crypto/vfio_ap_migration.c

[ ... ]

> +static size_t vfio_ap_config_size(struct ap_matrix_mdev *matrix_mdev,
> +				  unsigned 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 vfio_ap_get_data_size(struct vfio_device *vdev,
> +				 unsigned long *stop_copy_length)
> +{
> +	struct ap_matrix_mdev *matrix_mdev;
> +	int num_queues;
> +
> +	mutex_lock(&matrix_dev->mdevs_lock);
> +	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
> +	*stop_copy_length = vfio_ap_config_size(matrix_mdev, &num_queues);
> +	mutex_unlock(&matrix_dev->mdevs_lock);
> +
> +	return 0;
> +}

[Severity: Low]
Does passing &num_queues here trigger a -Wpointer-sign compiler warning?

In vfio_ap_get_data_size(), num_queues is declared as int, whereas
vfio_ap_config_size() takes unsigned int *num_queues. Should num_queues in
vfio_ap_get_data_size() be declared as unsigned int to match
vfio_ap_get_config()?

[ ... ]

> +static void vfio_ap_release_mig_files(struct ap_matrix_mdev *matrix_mdev)
> +{
> +	struct vfio_ap_migration_data *mig_data;
> +
> +	lockdep_assert_held(&matrix_dev->mdevs_lock);
> +
> +	mig_data = matrix_mdev->mig_data;
> +	vfio_ap_release_stop_copy_file(mig_data);
> +	vfio_ap_release_resuming_file(mig_data);
> +}
> +
> +/**
> + * vfio_ap_release_migration_data: reclaim private migration data
> + *
> + * @matrix_mdev: pointer to the object containing the state of 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;
> +
> +	vfio_ap_release_mig_files(matrix_mdev);
> +	mutex_destroy(&matrix_mdev->mig_data->stop_copy_mig_file.lock);
> +	mutex_destroy(&matrix_mdev->mig_data->resuming_mig_file.lock);
> +	kfree(matrix_mdev->mig_data);
> +	matrix_mdev->mig_data = NULL;
> +}

[Severity: Critical]
Can closing the VFIO device file descriptor before an open migration file
descriptor cause a use-after-free on mig_file?

When vfio_ap_open_file_stream() creates a migration file descriptor, it pins
matrix_mdev->vdev via vfio_device_try_get_registration() and sets
filp->private_data to &mig_data->stop_copy_mig_file or
&mig_data->resuming_mig_file, which are embedded directly inside
struct vfio_ap_migration_data:

struct vfio_ap_migration_data {
	enum vfio_device_mig_state	mig_state;
	bool				resuming_mig_data_complete;
	struct vfio_ap_migration_file	resuming_mig_file;
	struct vfio_ap_migration_file	stop_copy_mig_file;
};

However, when the VFIO device file descriptor is closed,
vfio_ap_mdev_close_device() calls vfio_ap_release_migration_data(), which
destroys stop_copy_mig_file.lock and resuming_mig_file.lock and frees
matrix_mdev->mig_data with kfree() even if the migration file descriptor is
still open in userspace.

Although the vfio_device registration reference keeps matrix_mdev alive,
filp->private_data (mig_file) points into the freed matrix_mdev->mig_data
allocation:

1. When the migration file descriptor is later closed,
   vfio_ap_release_mig_file() dereferences mig_file->matrix_mdev from freed
   memory:

   static int vfio_ap_release_mig_file(struct inode *file_inode,
                                       struct file *filp)
   {
           struct vfio_ap_migration_file *mig_file = filp->private_data;
           ...
           vfio_device_put_registration(&mig_file->matrix_mdev->vdev);
           return 0;
   }

2. If userspace calls read() or write() on the migration file descriptor
   after or concurrently with vfio_ap_mdev_close_device(),
   vfio_ap_stop_copy_read() and vfio_ap_resuming_write() call
   mutex_lock(&mig_file->lock) on a destroyed mutex in freed memory and
   dereference mig_file->filp and mig_file->matrix_mdev in
   validate_stop_copy_read_parms() and validate_resuming_write_parms().

3. If vfio_ap_stop_copy_read() drops matrix_dev->mdevs_lock while copying to
   userspace (or if read()/write() is waiting on ap_attr_mutex or
   matrix_dev->mdevs_lock after acquiring mig_file->lock), a concurrent
   vfio_ap_mdev_close_device() will destroy mig_file->lock while it is held
   and free mig_data before the reader or writer unlocks mig_file->lock.

Would embedding or lifetime-tying the migration state to struct ap_matrix_mdev
(freed in vfio_ap_mdev_release_dev() after all registration references drop)
avoid freeing mig_file while a migration file descriptor is still open?

> diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
> index 969e7c6dacc18..94c524e952f79 100644
> --- a/drivers/s390/crypto/vfio_ap_ops.c
> +++ b/drivers/s390/crypto/vfio_ap_ops.c
> @@ -816,7 +816,7 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)
>  		return PTR_ERR(matrix_mdev);
>  
>  	/*
> -	 * Wet up vfio-ap device migration before calling
> +	 * Setting up for vfio device migration must be done 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

[Severity: Low]
This isn't a bug, but should this comment fix in vfio_ap_mdev_probe() be
squashed into earlier commit 8f5bd5cbf795 ("s390/vfio-ap: Set up for vfio-ap
device migration") where the comment was introduced, rather than included in
the documentation commit?

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

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

end of thread, other threads:[~2026-11-02 18:19 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-02 15:14 [PATCH v11 00/14] s390/vfio-ap: Add live guest migration support Anthony Krowiak
2026-11-02 15:14 ` [PATCH v11 01/14] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
2026-11-02 15:23   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 02/14] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
2026-11-02 15:29   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 03/14] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
2026-11-02 15:46   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 04/14] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
2026-11-02 15:49   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 05/14] s390/vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
2026-11-02 16:06   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 06/14] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
2026-11-02 16:18   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 07/14] s390/vfio-ap: File ops called to save the vfio device migration state Anthony Krowiak
2026-11-02 16:32   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 08/14] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
2026-11-02 16:42   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 09/14] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
2026-11-02 17:09   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 10/14] s390/vfio-ap: Transition device migration state to from/to STOP Anthony Krowiak
2026-11-02 17:23   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 11/14] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
2026-11-02 17:39   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 12/14] s390/vfio-ap: Set up for vfio-ap device migration Anthony Krowiak
2026-11-02 17:57   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 13/14] s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute Anthony Krowiak
2026-11-02 18:10   ` sashiko-bot
2026-11-02 15:14 ` [PATCH v11 14/14] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
2026-11-02 18:19   ` sashiko-bot

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